st/wgl: pseudo-implementation of WGL_EXT_swap_control

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 <jfonseca@vmware.com>
This commit is contained in:
Brian Paul 2017-04-04 13:11:44 -06:00
parent ab96d1baf4
commit c78fc70e8c
7 changed files with 86 additions and 12 deletions

View File

@ -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;

View File

@ -78,6 +78,10 @@ struct stw_device
unsigned long memdbg_no;
#endif
/** WGL_EXT_swap_control */
int refresh_rate;
int swap_interval;
bool initialized;
};

View File

@ -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";

View File

@ -33,25 +33,30 @@
#include <GL/gl.h>
#include <GL/wglext.h>
#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;
}

View File

@ -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);
}

View File

@ -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.
*

View File

@ -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 ),