dotfiles/pkgs/osu-wine/patches/ps0417-p0002-winex11.drv-Add-OpenGL-latency-reduction-cod.patch
2024-05-15 08:35:49 +07:00

325 lines
9.8 KiB
Diff

From ec87cc3742130c138e4caa37084c92c46b9cb9ad Mon Sep 17 00:00:00 2001
From: Torge Matthies <openglfreak@googlemail.com>
Date: Sun, 3 Jul 2022 15:54:01 +0200
Subject: [PATCH 2/2] winex11.drv: Add OpenGL latency reduction code.
---
dlls/winex11.drv/opengl.c | 255 +++++++++++++++++++++++++++++++++++++-
1 file changed, 252 insertions(+), 3 deletions(-)
diff --git a/dlls/winex11.drv/opengl.c b/dlls/winex11.drv/opengl.c
index 11111111111..11111111111 100644
--- a/dlls/winex11.drv/opengl.c
+++ b/dlls/winex11.drv/opengl.c
@@ -42,6 +42,8 @@
#include "xcomposite.h"
#include "winternl.h"
#include "wine/debug.h"
+#include "wine/server.h"
+#include "../win32u/ntuser_private.h"
#ifdef SONAME_LIBGL
@@ -225,6 +229,8 @@ enum dc_gl_layered_type
DC_GL_LAYERED_ATTRIBUTES,
};
+typedef LONGLONG rtime_t;
+
struct gl_drawable
{
LONG ref; /* reference count */
@@ -3443,6 +3454,130 @@ static void X11DRV_WineGL_LoadExtensions(void)
}
}
+static inline BOOL allow_latency_reduction( void )
+{
+ static int status = -1;
+ if (status == -1)
+ {
+ const char *env = getenv( "WINE_OPENGL_LATENCY_REDUCTION" );
+ status = !!(env && atoi(env));
+ }
+ return status == 1;
+}
+
+#define TICKSPERSEC 10000000
+
+typedef struct ftime_t {
+ LONGLONG time;
+ ULONGLONG freq;
+} ftime_t;
+
+static inline ftime_t current_ftime( void )
+{
+ LARGE_INTEGER counter, freq;
+ ftime_t ret;
+ NtQueryPerformanceCounter( &counter, &freq );
+ ret.time = counter.QuadPart;
+ ret.freq = (ULONGLONG)freq.QuadPart;
+ return ret;
+}
+
+static inline rtime_t ftime_to_rtime( ftime_t ftime, BOOL round_up )
+{
+ ftime.time *= TICKSPERSEC;
+ if (round_up)
+ ftime.time += ftime.freq - 1;
+ return ftime.time / ftime.freq;
+}
+
+static inline rtime_t current_rtime( BOOL round_up )
+{
+ return ftime_to_rtime( current_ftime(), round_up );
+}
+
+static rtime_t get_vblank_interval( HWND hwnd )
+{
+ HMONITOR monitor;
+ UNICODE_STRING device_name;
+ MONITORINFOEXW moninfo = { sizeof(MONITORINFOEXW) };
+ DEVMODEW devmode = { {0}, 0, 0, sizeof(DEVMODEW) };
+
+ monitor = NtUserMonitorFromWindow( hwnd, MONITOR_DEFAULTTONEAREST );
+ if (!monitor || !NtUserGetMonitorInfo( monitor, (MONITORINFO*)&moninfo ))
+ return 0;
+
+ RtlInitUnicodeString( &device_name, moninfo.szDevice );
+ if (!NtUserEnumDisplaySettings( &device_name, ENUM_CURRENT_SETTINGS, &devmode, 0 )
+ || devmode.dmDisplayFrequency <= 1)
+ return 0;
+ MESSAGE("detected display frequency: %u\n", devmode.dmDisplayFrequency);
+ return TICKSPERSEC / devmode.dmDisplayFrequency;
+}
+
+#define FRAMETIME_MARGIN_SHIFT 2
+
+static inline rtime_t frame_time_with_margin( rtime_t frame_time )
+{
+ return frame_time + (frame_time >> FRAMETIME_MARGIN_SHIFT) + 3500;
+}
+
+static void get_swap_interval(GLXDrawable drawable, int *interval)
+{
+ /* HACK: does not work correctly with __GL_SYNC_TO_VBLANK */
+ /*pglXQueryDrawable(gdi_display, gl->drawable, GLX_SWAP_INTERVAL_EXT, (unsigned int*)interval);*/
+ *interval = 0;
+}
+
+#define WAIT_MASK (QS_MOUSEBUTTON | QS_KEY | QS_SENDMESSAGE | QS_TIMER | QS_HOTKEY)
+
+static void msg_wait( const LARGE_INTEGER *timeout )
+{
+ LARGE_INTEGER to = *timeout, to2 = to;
+ rtime_t start, end;
+ DWORD ret;
+
+ /* HACK: __wine_msg_wait_objects likes to wait for about 1 ms too long */
+
+ if (to2.QuadPart < 0)
+ {
+ to2.QuadPart += 10000;
+ if (to2.QuadPart >= 0)
+ {
+ end = current_rtime( TRUE );
+ goto busy_loop;
+ }
+ }
+ else if (to2.QuadPart >= 10000)
+ to2.QuadPart -= 10000;
+
+ if (to2.QuadPart >= 0)
+ {
+ __wine_msg_wait_objects( 0, NULL, &to2, WAIT_MASK, MWMO_INPUTAVAILABLE );
+ return;
+ }
+
+again:
+ start = current_rtime( FALSE );
+ ret = __wine_msg_wait_objects( 0, NULL, &to2, WAIT_MASK, MWMO_INPUTAVAILABLE );
+ if (ret == WAIT_OBJECT_0)
+ return;
+ end = current_rtime( TRUE );
+
+ to.QuadPart += end - start;
+ if (to.QuadPart < -11000)
+ {
+ to2.QuadPart = to.QuadPart + 10000;
+ goto again;
+ }
+
+busy_loop:
+ if (to.QuadPart < -1000)
+ {
+ end = end - to.QuadPart - 1000;
+ while (current_rtime( TRUE ) < end)
+ YieldProcessor();
+ }
+}
/**
* glxdrv_SwapBuffers
@@ -3457,6 +3592,11 @@ static BOOL glxdrv_wglSwapBuffers( HDC hdc )
INT64 ust, msc, sbc, target_sbc = 0;
HWND hwnd;
+ BOOL enable_latency_reduction = FALSE;
+ BOOL synchronize_to_vblank = FALSE;
+ rtime_t frame_end_time;
+ rtime_t next_vblank_time = 0;
+
TRACE("(%p)\n", hdc);
escape.code = X11DRV_PRESENT_DRAWABLE;
@@ -3469,18 +3609,78 @@ static BOOL glxdrv_wglSwapBuffers( HDC hdc )
return FALSE;
}
+ if (allow_latency_reduction())
+ enable_latency_reduction = gl->type == DC_GL_WINDOW
+ || gl->type == DC_GL_CHILD_WIN || gl->type == DC_GL_PIXMAP_WIN;
+
+ if (enable_latency_reduction)
+ {
+ if (ctx && (gl->type == DC_GL_WINDOW || gl->type == DC_GL_CHILD_WIN
+ || gl->type == DC_GL_PIXMAP_WIN))
+ sync_context( ctx );
+ pglFinish();
+ frame_end_time = current_rtime( TRUE );
+ }
+
pthread_mutex_lock( &context_mutex );
- if (gl->refresh_swap_interval)
+
+ if (enable_latency_reduction)
+ {
+ if (!gl->vblank_interval)
+ {
+ HWND hwnd = 0;
+ assert(!XFindContext( gdi_display, gl->window, winContext, (char **)&hwnd ));
+ assert(hwnd);
+ gl->vblank_interval = get_vblank_interval( hwnd );
+ assert(gl->vblank_interval);
+ }
+
+ if (gl->last_vblank_time)
+ {
+ next_vblank_time = gl->last_vblank_time + gl->vblank_interval;
+ while (next_vblank_time < frame_end_time)
+ next_vblank_time += gl->vblank_interval;
+ }
+
+ if (gl->last_swap_time)
+ {
+ rtime_t new_frame_time = frame_end_time - gl->last_swap_time;
+ if (new_frame_time >= gl->frame_time)
+ gl->frame_time = new_frame_time;
+ else if (gl->frame_time > new_frame_time * 3)
+ gl->frame_time = frame_time_with_margin( new_frame_time );
+ else
+ gl->frame_time = (gl->frame_time * 20 + new_frame_time) / 21;
+ }
+
+ if (frame_end_time - gl->last_vblank_time >= TICKSPERSEC
+ || (!gl->refresh_swap_interval && next_vblank_time - frame_end_time <= frame_time_with_margin( gl->frame_time )))
+ synchronize_to_vblank = TRUE;
+ }
+
+ if (synchronize_to_vblank)
+ {
+ if (!gl->previous_frame_synchronized)
+ {
+ get_swap_interval(gl->drawable, &gl->swap_interval);
+ if (!set_swap_interval(gl->drawable, 1))
+ synchronize_to_vblank = FALSE;
+ gl->previous_frame_synchronized = TRUE;
+ }
+ }
+ else if (gl->refresh_swap_interval || gl->previous_frame_synchronized)
{
set_swap_interval(gl->drawable, gl->swap_interval);
gl->refresh_swap_interval = FALSE;
+ gl->previous_frame_synchronized = FALSE;
}
+
pthread_mutex_unlock( &context_mutex );
switch (gl->type)
{
case DC_GL_PIXMAP_WIN:
- if (ctx) sync_context( ctx );
+ if (!enable_latency_reduction && ctx) sync_context( ctx );
escape.drawable = gl->pixmap;
if (pglXCopySubBufferMESA) {
/* (glX)SwapBuffers has an implicit glFlush effect, however
@@ -3501,7 +3701,7 @@ static BOOL glxdrv_wglSwapBuffers( HDC hdc )
break;
case DC_GL_WINDOW:
case DC_GL_CHILD_WIN:
- if (ctx) sync_context( ctx );
+ if (!enable_latency_reduction && ctx) sync_context( ctx );
if (gl->type == DC_GL_CHILD_WIN) escape.drawable = gl->window;
/* fall through */
default:
@@ -3519,5 +3719,54 @@ static BOOL glxdrv_wglSwapBuffers( HDC hdc )
pglXWaitForSbcOML( gdi_display, gl->drawable, target_sbc, &ust, &msc, &sbc );
+
+ if (enable_latency_reduction)
+ {
+ rtime_t current_time = current_rtime( FALSE );
+
+ if (!synchronize_to_vblank && gl->last_vblank_time && gl->frame_time)
+ {
+ LARGE_INTEGER timeout;
+
+ next_vblank_time = gl->last_vblank_time + gl->vblank_interval;
+ while (next_vblank_time < current_time + frame_time_with_margin( gl->frame_time ))
+ next_vblank_time += gl->vblank_interval;
+
+ timeout.QuadPart = -(next_vblank_time - frame_time_with_margin( gl->frame_time ) - current_time);
+ if (timeout.QuadPart < 0 && -timeout.QuadPart < TICKSPERSEC)
+ msg_wait( &timeout );
+
+ current_time = current_rtime( FALSE );
+ }
+
+ pthread_mutex_lock( &context_mutex );
+
+ gl->last_swap_time = current_time;
+ if (synchronize_to_vblank)
+ gl->last_vblank_time = current_time;
+
+ pthread_mutex_unlock( &context_mutex );
+
+ if (synchronize_to_vblank && gl->frame_time)
+ {
+ LARGE_INTEGER timeout;
+
+ next_vblank_time = gl->last_vblank_time + gl->vblank_interval;
+ while (next_vblank_time < current_time + frame_time_with_margin( gl->frame_time ))
+ next_vblank_time += gl->vblank_interval;
+
+ timeout.QuadPart = -(next_vblank_time - frame_time_with_margin( gl->frame_time ) - current_time);
+ if (timeout.QuadPart < 0 && -timeout.QuadPart < TICKSPERSEC)
+ {
+ msg_wait( &timeout );
+
+ current_time = current_rtime( FALSE );
+ pthread_mutex_lock( &context_mutex );
+ gl->last_swap_time = current_time;
+ pthread_mutex_unlock( &context_mutex );
+ }
+ }
+ }
+
release_gl_drawable( gl );
if (ctx && escape.drawable)
--
2.40.0