From c78fc70e8c8931c28dfa8be1ea9522fe7b16687b Mon Sep 17 00:00:00 2001 From: Brian Paul Date: Tue, 4 Apr 2017 13:11:44 -0600 Subject: [PATCH] st/wgl: pseudo-implementation of WGL_EXT_swap_control MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit This implementation is based on querying the time just before swap/present and doing a Sleep() if needed. There is no sync to vblank or actual coordination with the GPU. This isn't perfect, but basically works. We've had some request for this functionality, and it sounds like there are some Windows GL apps that refuse to start if the driver doesn't advertise this extension. Note: NVIDIA's Windows OpenGL driver advertises the WGL_EXT_swap_control string both with wglGetExtensionsStringEXT() and with glGetString(GL_EXTENSIONS). We're only advertising it with the former at this time. Tested with asst. Mesa demos, Google Earth, Lightsmark, etc. VMware bug 1591534. Reviewed-by: José Fonseca --- src/gallium/state_trackers/wgl/stw_device.c | 25 +++++++++++++ src/gallium/state_trackers/wgl/stw_device.h | 4 ++ .../wgl/stw_ext_extensionsstring.c | 2 +- .../state_trackers/wgl/stw_ext_swapinterval.c | 25 ++++++++----- .../state_trackers/wgl/stw_framebuffer.c | 37 +++++++++++++++++++ .../state_trackers/wgl/stw_framebuffer.h | 3 ++ .../state_trackers/wgl/stw_getprocaddress.c | 2 +- 7 files changed, 86 insertions(+), 12 deletions(-) diff --git a/src/gallium/state_trackers/wgl/stw_device.c b/src/gallium/state_trackers/wgl/stw_device.c index b57f96f2e87..42a2f0e82f9 100644 --- a/src/gallium/state_trackers/wgl/stw_device.c +++ b/src/gallium/state_trackers/wgl/stw_device.c @@ -63,6 +63,24 @@ stw_get_param(struct st_manager *smapi, } } + +/** Get the refresh rate for the monitor, in Hz */ +static int +get_refresh_rate(void) +{ + DEVMODE devModes; + + if (EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &devModes)) { + /* clamp the value, just in case we get garbage */ + return CLAMP(devModes.dmDisplayFrequency, 30, 120); + } + else { + /* reasonable default */ + return 60; + } +} + + boolean stw_init(const struct stw_winsys *stw_winsys) { @@ -116,6 +134,13 @@ stw_init(const struct stw_winsys *stw_winsys) stw_pixelformat_init(); + /* env var override for WGL_EXT_swap_control, useful for testing/debugging */ + const char *s = os_get_option("SVGA_SWAP_INTERVAL"); + if (s) { + stw_dev->swap_interval = atoi(s); + } + stw_dev->refresh_rate = get_refresh_rate(); + stw_dev->initialized = true; return TRUE; diff --git a/src/gallium/state_trackers/wgl/stw_device.h b/src/gallium/state_trackers/wgl/stw_device.h index ecf212d7cb6..766975c292c 100644 --- a/src/gallium/state_trackers/wgl/stw_device.h +++ b/src/gallium/state_trackers/wgl/stw_device.h @@ -78,6 +78,10 @@ struct stw_device unsigned long memdbg_no; #endif + /** WGL_EXT_swap_control */ + int refresh_rate; + int swap_interval; + bool initialized; }; diff --git a/src/gallium/state_trackers/wgl/stw_ext_extensionsstring.c b/src/gallium/state_trackers/wgl/stw_ext_extensionsstring.c index 06af8b1e994..996eb586d94 100644 --- a/src/gallium/state_trackers/wgl/stw_ext_extensionsstring.c +++ b/src/gallium/state_trackers/wgl/stw_ext_extensionsstring.c @@ -45,7 +45,7 @@ static const char *stw_extension_string = "WGL_EXT_create_context_es_profile " "WGL_EXT_create_context_es2_profile " "WGL_ARB_make_current_read " -/* "WGL_EXT_swap_interval " */ + "WGL_EXT_swap_control " "WGL_EXT_extensions_string"; diff --git a/src/gallium/state_trackers/wgl/stw_ext_swapinterval.c b/src/gallium/state_trackers/wgl/stw_ext_swapinterval.c index b960913ccbb..b2074a73fb7 100644 --- a/src/gallium/state_trackers/wgl/stw_ext_swapinterval.c +++ b/src/gallium/state_trackers/wgl/stw_ext_swapinterval.c @@ -33,25 +33,30 @@ #include #include #include "util/u_debug.h" +#include "stw_device.h" -/* A dummy implementation of this extension. - * - * Required as some applications retrieve and call these functions - * regardless of the fact that we don't advertise the extension and - * further more the results of wglGetProcAddress are NULL. + +/** + * Note that our implementation of swap intervals is a bit of a hack. + * We implement it based on querying the time and Sleep()'ing. We don't + * sync to the vblank. */ WINGDIAPI BOOL APIENTRY wglSwapIntervalEXT(int interval) { - (void) interval; - debug_printf("%s: %d\n", __FUNCTION__, interval); + if (interval < 0) { + SetLastError(ERROR_INVALID_DATA); + return FALSE; + } + if (stw_dev) { + stw_dev->swap_interval = interval; + } return TRUE; } + WINGDIAPI int APIENTRY wglGetSwapIntervalEXT(void) { - return 0; + return stw_dev ? stw_dev->swap_interval : 0; } - - diff --git a/src/gallium/state_trackers/wgl/stw_framebuffer.c b/src/gallium/state_trackers/wgl/stw_framebuffer.c index 09b57766dc0..321fbb6ea77 100644 --- a/src/gallium/state_trackers/wgl/stw_framebuffer.c +++ b/src/gallium/state_trackers/wgl/stw_framebuffer.c @@ -30,6 +30,7 @@ #include "pipe/p_screen.h" #include "util/u_memory.h" #include "hud/hud_context.h" +#include "os/os_time.h" #include "state_tracker/st_api.h" #include "stw_icd.h" @@ -580,6 +581,38 @@ stw_framebuffer_present_locked(HDC hdc, } +/** + * This is called just before issuing the buffer swap/present. + * We query the current time and determine if we should sleep before + * issuing the swap/present. + * This is a bit of a hack and is certainly not very accurate but it + * basically works. + * This is for the WGL_ARB_swap_interval extension. + */ +static void +wait_swap_interval(struct stw_framebuffer *fb) +{ + /* Note: all time variables here are in units of microseconds */ + int64_t cur_time = os_time_get_nano() / 1000; + + if (fb->prev_swap_time != 0) { + /* Compute time since previous swap */ + int64_t delta = cur_time - fb->prev_swap_time; + int64_t min_swap_period = + 1.0e6 / stw_dev->refresh_rate * stw_dev->swap_interval; + + /* if time since last swap is less than wait period, wait */ + if (delta < min_swap_period) { + float fudge = 1.75f; /* emperical fudge factor */ + int64_t wait = (min_swap_period - delta) * fudge; + os_time_sleep(wait); + } + } + + fb->prev_swap_time = cur_time; +} + + BOOL APIENTRY DrvSwapBuffers(HDC hdc) { @@ -615,6 +648,10 @@ DrvSwapBuffers(HDC hdc) } } + if (stw_dev->swap_interval != 0) { + wait_swap_interval(fb); + } + return stw_st_swap_framebuffer_locked(hdc, fb->stfb); } diff --git a/src/gallium/state_trackers/wgl/stw_framebuffer.h b/src/gallium/state_trackers/wgl/stw_framebuffer.h index 029fb9ffa34..d44c3a6634a 100644 --- a/src/gallium/state_trackers/wgl/stw_framebuffer.h +++ b/src/gallium/state_trackers/wgl/stw_framebuffer.h @@ -108,6 +108,9 @@ struct stw_framebuffer HANDLE hSharedSurface; struct stw_shared_surface *shared_surface; + /* For WGL_EXT_swap_control */ + int64_t prev_swap_time; + /** * This is protected by stw_device::fb_mutex, not the mutex above. * diff --git a/src/gallium/state_trackers/wgl/stw_getprocaddress.c b/src/gallium/state_trackers/wgl/stw_getprocaddress.c index 9273d103310..c8138f61fa4 100644 --- a/src/gallium/state_trackers/wgl/stw_getprocaddress.c +++ b/src/gallium/state_trackers/wgl/stw_getprocaddress.c @@ -67,7 +67,7 @@ static const struct stw_extension_entry stw_extension_entries[] = { /* WGL_EXT_extensions_string */ STW_EXTENSION_ENTRY( wglGetExtensionsStringEXT ), - /* WGL_EXT_swap_interval */ + /* WGL_EXT_swap_control */ STW_EXTENSION_ENTRY( wglGetSwapIntervalEXT ), STW_EXTENSION_ENTRY( wglSwapIntervalEXT ), -- 2.30.2