From e5c2fb907dd9f01101a071482397ee93faa31747 Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Wed, 1 Aug 2012 20:29:36 -0400 Subject: [PATCH] Add support for (GLX|WGL)_EXT_swap_control_tear. This required a small public API change: SDL_GL_SetSwapInterval() now accepts negative values, and SDL_GL_GetSwapInterval() doesn't report errors anymore (if it can't work, it'll return zero as a reasonable default). If you need to test for errors, such as a lack of swap_control_tear support, check the results of SDL_GL_SetSwapInterval() when you set your desired value. --- include/SDL_video.h | 10 ++++-- src/video/SDL_video.c | 9 ++---- src/video/cocoa/SDL_cocoaopengl.m | 5 +-- src/video/directfb/SDL_DirectFB_opengl.c | 3 +- src/video/pandora/SDL_pandora.c | 10 +----- src/video/windows/SDL_windowsopengl.c | 22 +++++++------ src/video/windows/SDL_windowsopengl.h | 3 +- src/video/x11/SDL_x11opengl.c | 39 ++++++++++++++++++------ src/video/x11/SDL_x11opengl.h | 1 + test/testgl2.c | 17 ++++++----- 10 files changed, 67 insertions(+), 52 deletions(-) diff --git a/include/SDL_video.h b/include/SDL_video.h index 7e3ef3897..ba366903e 100644 --- a/include/SDL_video.h +++ b/include/SDL_video.h @@ -789,7 +789,9 @@ extern DECLSPEC int SDLCALL SDL_GL_MakeCurrent(SDL_Window * window, * \brief Set the swap interval for the current OpenGL context. * * \param interval 0 for immediate updates, 1 for updates synchronized with the - * vertical retrace. + * vertical retrace. If the system supports it, you may + * specify -1 to allow late swaps to happen immediately + * instead of waiting for the next retrace. * * \return 0 on success, or -1 if setting the swap interval is not supported. * @@ -801,8 +803,10 @@ extern DECLSPEC int SDLCALL SDL_GL_SetSwapInterval(int interval); * \brief Get the swap interval for the current OpenGL context. * * \return 0 if there is no vertical retrace synchronization, 1 if the buffer - * swap is synchronized with the vertical retrace, and -1 if getting - * the swap interval is not supported. + * swap is synchronized with the vertical retrace, and -1 if late + * swaps happen immediately instead of waiting for the next retrace. + * If the system can't determine the swap interval, or there isn't a + * valid current context, this will return 0 as a safe default. * * \sa SDL_GL_SetSwapInterval() */ diff --git a/src/video/SDL_video.c b/src/video/SDL_video.c index dfcac7382..0778c233a 100755 --- a/src/video/SDL_video.c +++ b/src/video/SDL_video.c @@ -2578,16 +2578,13 @@ int SDL_GL_GetSwapInterval(void) { if (!_this) { - SDL_UninitializedVideo(); - return -1; + return 0; } else if (_this->current_glctx == NULL) { - SDL_SetError("No OpenGL context has been made current"); - return -1; + return 0; } else if (_this->GL_GetSwapInterval) { return _this->GL_GetSwapInterval(_this); } else { - SDL_SetError("Getting the swap interval is not supported"); - return -1; + return 0; } } diff --git a/src/video/cocoa/SDL_cocoaopengl.m b/src/video/cocoa/SDL_cocoaopengl.m index 44dcc30d6..cfd0a3b64 100755 --- a/src/video/cocoa/SDL_cocoaopengl.m +++ b/src/video/cocoa/SDL_cocoaopengl.m @@ -250,7 +250,7 @@ NSAutoreleasePool *pool; NSOpenGLContext *nscontext; GLint value; - int status; + int status = 0; pool = [[NSAutoreleasePool alloc] init]; @@ -258,9 +258,6 @@ if (nscontext != nil) { [nscontext getValues:&value forParameter:NSOpenGLCPSwapInterval]; status = (int)value; - } else { - SDL_SetError("No current OpenGL context"); - status = -1; } [pool release]; diff --git a/src/video/directfb/SDL_DirectFB_opengl.c b/src/video/directfb/SDL_DirectFB_opengl.c index fa37850a5..3d2806bb3 100755 --- a/src/video/directfb/SDL_DirectFB_opengl.c +++ b/src/video/directfb/SDL_DirectFB_opengl.c @@ -250,8 +250,7 @@ DirectFB_GL_SetSwapInterval(_THIS, int interval) int DirectFB_GL_GetSwapInterval(_THIS) { - SDL_Unsupported(); - return -1; + return 0; } void diff --git a/src/video/pandora/SDL_pandora.c b/src/video/pandora/SDL_pandora.c index 3c3651282..1d07ed973 100755 --- a/src/video/pandora/SDL_pandora.c +++ b/src/video/pandora/SDL_pandora.c @@ -796,15 +796,7 @@ PND_gl_setswapinterval(_THIS, int interval) int PND_gl_getswapinterval(_THIS) { - SDL_VideoData *phdata = (SDL_VideoData *) _this->driverdata; - - if (phdata->egl_initialized != SDL_TRUE) { - SDL_SetError("PND: GLES initialization failed, no OpenGL ES support"); - return -1; - } - - /* Return default swap interval value */ - return phdata->swapinterval; + return ((SDL_VideoData *) _this->driverdata)->swapinterval; } void diff --git a/src/video/windows/SDL_windowsopengl.c b/src/video/windows/SDL_windowsopengl.c index bb81364e4..61afa7e5f 100755 --- a/src/video/windows/SDL_windowsopengl.c +++ b/src/video/windows/SDL_windowsopengl.c @@ -112,10 +112,6 @@ WIN_GL_LoadLibrary(_THIS, const char *path) GetProcAddress(handle, "wglDeleteContext"); _this->gl_data->wglMakeCurrent = (BOOL(WINAPI *) (HDC, HGLRC)) GetProcAddress(handle, "wglMakeCurrent"); - _this->gl_data->wglSwapIntervalEXT = (void (WINAPI *) (int)) - GetProcAddress(handle, "wglSwapIntervalEXT"); - _this->gl_data->wglGetSwapIntervalEXT = (int (WINAPI *) (void)) - GetProcAddress(handle, "wglGetSwapIntervalEXT"); if (!_this->gl_data->wglGetProcAddress || !_this->gl_data->wglCreateContext || @@ -341,7 +337,7 @@ WIN_GL_InitExtensions(_THIS, HDC hdc) } /* Check for WGL_ARB_pixel_format */ - _this->gl_data->WGL_ARB_pixel_format = 0; + _this->gl_data->HAS_WGL_ARB_pixel_format = SDL_FALSE; if (HasExtension("WGL_ARB_pixel_format", extensions)) { _this->gl_data->wglChoosePixelFormatARB = (BOOL(WINAPI *) (HDC, const int *, @@ -354,16 +350,20 @@ WIN_GL_InitExtensions(_THIS, HDC hdc) if ((_this->gl_data->wglChoosePixelFormatARB != NULL) && (_this->gl_data->wglGetPixelFormatAttribivARB != NULL)) { - _this->gl_data->WGL_ARB_pixel_format = 1; + _this->gl_data->HAS_WGL_ARB_pixel_format = SDL_TRUE; } } /* Check for WGL_EXT_swap_control */ + _this->gl_data->HAS_WGL_EXT_swap_control_tear = SDL_FALSE; if (HasExtension("WGL_EXT_swap_control", extensions)) { _this->gl_data->wglSwapIntervalEXT = WIN_GL_GetProcAddress(_this, "wglSwapIntervalEXT"); _this->gl_data->wglGetSwapIntervalEXT = WIN_GL_GetProcAddress(_this, "wglGetSwapIntervalEXT"); + if (HasExtension("WGL_EXT_swap_control_tear", extensions)) { + _this->gl_data->HAS_WGL_EXT_swap_control_tear = SDL_TRUE; + } } else { _this->gl_data->wglSwapIntervalEXT = NULL; _this->gl_data->wglGetSwapIntervalEXT = NULL; @@ -397,7 +397,7 @@ WIN_GL_ChoosePixelFormatARB(_THIS, int *iAttribs, float *fAttribs) WIN_GL_InitExtensions(_this, hdc); - if (_this->gl_data->WGL_ARB_pixel_format) { + if (_this->gl_data->HAS_WGL_ARB_pixel_format) { _this->gl_data->wglChoosePixelFormatARB(hdc, iAttribs, fAttribs, 1, &pixel_format, &matching); @@ -611,7 +611,9 @@ WIN_GL_MakeCurrent(_THIS, SDL_Window * window, SDL_GLContext context) int WIN_GL_SetSwapInterval(_THIS, int interval) { - if (_this->gl_data->wglSwapIntervalEXT) { + if ((interval < 0) && (!_this->gl_data->HAS_WGL_EXT_swap_control_tear)) { + SDL_SetError("Negative swap interval unsupported in this GL"); + } else if (_this->gl_data->wglSwapIntervalEXT) { _this->gl_data->wglSwapIntervalEXT(interval); return 0; } else { @@ -626,8 +628,8 @@ WIN_GL_GetSwapInterval(_THIS) if (_this->gl_data->wglGetSwapIntervalEXT) { return _this->gl_data->wglGetSwapIntervalEXT(); } else { - SDL_Unsupported(); - return -1; + /*SDL_Unsupported();*/ + return 0; /* just say we're unsync'd. */ } } diff --git a/src/video/windows/SDL_windowsopengl.h b/src/video/windows/SDL_windowsopengl.h index 4a63915cd..86b36be07 100755 --- a/src/video/windows/SDL_windowsopengl.h +++ b/src/video/windows/SDL_windowsopengl.h @@ -27,7 +27,8 @@ struct SDL_GLDriverData { - int WGL_ARB_pixel_format; + SDL_bool HAS_WGL_ARB_pixel_format; + SDL_bool HAS_WGL_EXT_swap_control_tear; void *(WINAPI * wglGetProcAddress) (const char *proc); HGLRC(WINAPI * wglCreateContext) (HDC hdc); diff --git a/src/video/x11/SDL_x11opengl.c b/src/video/x11/SDL_x11opengl.c index 1268deed4..0678875bd 100755 --- a/src/video/x11/SDL_x11opengl.c +++ b/src/video/x11/SDL_x11opengl.c @@ -105,6 +105,10 @@ typedef GLXContext(*PFNGLXCREATECONTEXTATTRIBSARBPROC) (Display * dpy, #define GLX_MAX_SWAP_INTERVAL_EXT 0x20F2 #endif +#ifndef GLX_EXT_swap_control_tear +#define GLX_LATE_SWAPS_TEAR_EXT 0x20F3 +#endif + #define OPENGL_REQUIRES_DLOPEN #if defined(OPENGL_REQUIRES_DLOPEN) && defined(SDL_LOADSO_DLOPEN) #include @@ -318,11 +322,15 @@ X11_GL_InitExtensions(_THIS) extensions = NULL; } - /* Check for GLX_EXT_swap_control */ + /* Check for GLX_EXT_swap_control(_tear) */ + _this->gl_data->HAS_GLX_EXT_swap_control_tear = SDL_FALSE; if (HasExtension("GLX_EXT_swap_control", extensions)) { _this->gl_data->glXSwapIntervalEXT = (int (*)(Display*,GLXDrawable,int)) X11_GL_GetProcAddress(_this, "glXSwapIntervalEXT"); + if (HasExtension("GLX_EXT_swap_control_tear", extensions)) { + _this->gl_data->HAS_GLX_EXT_swap_control_tear = SDL_TRUE; + } } /* Check for GLX_MESA_swap_control */ @@ -615,9 +623,11 @@ static int swapinterval = -1; int X11_GL_SetSwapInterval(_THIS, int interval) { - int status; + int status = -1; - if (_this->gl_data->glXSwapIntervalEXT) { + if ((interval < 0) && (!_this->gl_data->HAS_GLX_EXT_swap_control_tear)) { + SDL_SetError("Negative swap interval unsupported in this GL"); + } else if (_this->gl_data->glXSwapIntervalEXT) { Display *display = ((SDL_VideoData *) _this->driverdata)->display; const SDL_WindowData *windowdata = (SDL_WindowData *) _this->current_glwin->driverdata; @@ -625,7 +635,6 @@ X11_GL_SetSwapInterval(_THIS, int interval) status = _this->gl_data->glXSwapIntervalEXT(display,drawable,interval); if (status != 0) { SDL_SetError("glxSwapIntervalEXT failed"); - status = -1; } else { swapinterval = interval; } @@ -633,7 +642,6 @@ X11_GL_SetSwapInterval(_THIS, int interval) status = _this->gl_data->glXSwapIntervalMESA(interval); if (status != 0) { SDL_SetError("glxSwapIntervalMESA failed"); - status = -1; } else { swapinterval = interval; } @@ -641,13 +649,11 @@ X11_GL_SetSwapInterval(_THIS, int interval) status = _this->gl_data->glXSwapIntervalSGI(interval); if (status != 0) { SDL_SetError("glxSwapIntervalSGI failed"); - status = -1; } else { swapinterval = interval; } } else { SDL_Unsupported(); - status = -1; } return status; } @@ -660,10 +666,23 @@ X11_GL_GetSwapInterval(_THIS) const SDL_WindowData *windowdata = (SDL_WindowData *) _this->current_glwin->driverdata; Window drawable = windowdata->xwindow; - unsigned int value = 0; + unsigned int allow_late_swap_tearing = 0; + unsigned int interval = 0; + + if (_this->gl_data->HAS_GLX_EXT_swap_control_tear) { + _this->gl_data->glXQueryDrawable(display, drawable, + GLX_LATE_SWAPS_TEAR_EXT, + &allow_late_swap_tearing); + } + _this->gl_data->glXQueryDrawable(display, drawable, - GLX_SWAP_INTERVAL_EXT, &value); - return (int) value; + GLX_SWAP_INTERVAL_EXT, &interval); + + if ((allow_late_swap_tearing) && (interval > 0)) { + return -((int) interval); + } + + return (int) interval; } else if (_this->gl_data->glXGetSwapIntervalMESA) { return _this->gl_data->glXGetSwapIntervalMESA(); } else { diff --git a/src/video/x11/SDL_x11opengl.h b/src/video/x11/SDL_x11opengl.h index cbdbd3e0a..c87fbc2fe 100755 --- a/src/video/x11/SDL_x11opengl.h +++ b/src/video/x11/SDL_x11opengl.h @@ -30,6 +30,7 @@ struct SDL_GLDriverData { SDL_bool HAS_GLX_EXT_visual_rating; + SDL_bool HAS_GLX_EXT_swap_control_tear; void *(*glXGetProcAddress) (const GLubyte*); XVisualInfo *(*glXChooseVisual) (Display*,int,int*); diff --git a/test/testgl2.c b/test/testgl2.c index 9bf978536..fa553cf48 100644 --- a/test/testgl2.c +++ b/test/testgl2.c @@ -240,18 +240,21 @@ main(int argc, char *argv[]) } if (state->render_flags & SDL_RENDERER_PRESENTVSYNC) { - SDL_GL_SetSwapInterval(1); + /* try late-swap-tearing first. If not supported, try normal vsync. */ + if (SDL_GL_SetSwapInterval(-1) == -1) { + SDL_GL_SetSwapInterval(1); + } } else { - SDL_GL_SetSwapInterval(0); + SDL_GL_SetSwapInterval(0); /* disable vsync. */ } SDL_GetCurrentDisplayMode(0, &mode); - printf("Screen BPP: %d\n", SDL_BITSPERPIXEL(mode.format)); + printf("Screen BPP : %d\n", SDL_BITSPERPIXEL(mode.format)); + printf("Swap Interval : %d\n", SDL_GL_GetSwapInterval()); printf("\n"); - printf("Vendor : %s\n", glGetString(GL_VENDOR)); - printf("Renderer : %s\n", glGetString(GL_RENDERER)); - printf("Version : %s\n", glGetString(GL_VERSION)); - printf("Extensions : %s\n", glGetString(GL_EXTENSIONS)); + printf("Vendor : %s\n", glGetString(GL_VENDOR)); + printf("Renderer : %s\n", glGetString(GL_RENDERER)); + printf("Version : %s\n", glGetString(GL_VERSION)); printf("\n"); status = SDL_GL_GetAttribute(SDL_GL_RED_SIZE, &value);