Add support for (GLX|WGL)_EXT_swap_control_tear.
authorRyan C. Gordon <icculus@icculus.org>
Wed, 01 Aug 2012 20:29:36 -0400
changeset 6382 64d54101773a
parent 6381 9da3d60cf531
child 6383 869effc44c41
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
src/video/SDL_video.c
src/video/cocoa/SDL_cocoaopengl.m
src/video/directfb/SDL_DirectFB_opengl.c
src/video/pandora/SDL_pandora.c
src/video/windows/SDL_windowsopengl.c
src/video/windows/SDL_windowsopengl.h
src/video/x11/SDL_x11opengl.c
src/video/x11/SDL_x11opengl.h
test/testgl2.c
--- a/include/SDL_video.h	Tue Jul 31 16:55:09 2012 -0700
+++ b/include/SDL_video.h	Wed Aug 01 20:29:36 2012 -0400
@@ -789,7 +789,9 @@
  *  \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 @@
  *  \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()
  */
--- a/src/video/SDL_video.c	Tue Jul 31 16:55:09 2012 -0700
+++ b/src/video/SDL_video.c	Wed Aug 01 20:29:36 2012 -0400
@@ -2578,16 +2578,13 @@
 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;
     }
 }
 
--- a/src/video/cocoa/SDL_cocoaopengl.m	Tue Jul 31 16:55:09 2012 -0700
+++ b/src/video/cocoa/SDL_cocoaopengl.m	Wed Aug 01 20:29:36 2012 -0400
@@ -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];
--- a/src/video/directfb/SDL_DirectFB_opengl.c	Tue Jul 31 16:55:09 2012 -0700
+++ b/src/video/directfb/SDL_DirectFB_opengl.c	Wed Aug 01 20:29:36 2012 -0400
@@ -250,8 +250,7 @@
 int
 DirectFB_GL_GetSwapInterval(_THIS)
 {
-    SDL_Unsupported();
-    return -1;
+    return 0;
 }
 
 void
--- a/src/video/pandora/SDL_pandora.c	Tue Jul 31 16:55:09 2012 -0700
+++ b/src/video/pandora/SDL_pandora.c	Wed Aug 01 20:29:36 2012 -0400
@@ -796,15 +796,7 @@
 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
--- a/src/video/windows/SDL_windowsopengl.c	Tue Jul 31 16:55:09 2012 -0700
+++ b/src/video/windows/SDL_windowsopengl.c	Wed Aug 01 20:29:36 2012 -0400
@@ -112,10 +112,6 @@
         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 @@
     }
 
     /* 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 @@
 
         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_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 @@
 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 @@
     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. */
     }
 }
 
--- a/src/video/windows/SDL_windowsopengl.h	Tue Jul 31 16:55:09 2012 -0700
+++ b/src/video/windows/SDL_windowsopengl.h	Wed Aug 01 20:29:36 2012 -0400
@@ -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);
--- a/src/video/x11/SDL_x11opengl.c	Tue Jul 31 16:55:09 2012 -0700
+++ b/src/video/x11/SDL_x11opengl.c	Wed Aug 01 20:29:36 2012 -0400
@@ -105,6 +105,10 @@
 #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 <dlfcn.h>
@@ -318,11 +322,15 @@
         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 @@
 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 @@
         status = _this->gl_data->glXSwapIntervalEXT(display,drawable,interval);
         if (status != 0) {
             SDL_SetError("glxSwapIntervalEXT failed");
-            status = -1;
         } else {
             swapinterval = interval;
         }
@@ -633,7 +642,6 @@
         status = _this->gl_data->glXSwapIntervalMESA(interval);
         if (status != 0) {
             SDL_SetError("glxSwapIntervalMESA failed");
-            status = -1;
         } else {
             swapinterval = interval;
         }
@@ -641,13 +649,11 @@
         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 @@
         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 {
--- a/src/video/x11/SDL_x11opengl.h	Tue Jul 31 16:55:09 2012 -0700
+++ b/src/video/x11/SDL_x11opengl.h	Wed Aug 01 20:29:36 2012 -0400
@@ -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*);
--- a/test/testgl2.c	Tue Jul 31 16:55:09 2012 -0700
+++ b/test/testgl2.c	Wed Aug 01 20:29:36 2012 -0400
@@ -240,18 +240,21 @@
     }
 
     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);