Commit 9f4e4be8e0fb69467889fef42cfb364b99ff99f2

Brandon Schaefer 2017-11-07T09:10:32

Fixed bug 3943 - General SDL_HINT_VIDEO_DOUBLE_BUFFER hint support

diff --git a/include/SDL_hints.h b/include/SDL_hints.h
index f487868..05e0cbe 100644
--- a/include/SDL_hints.h
+++ b/include/SDL_hints.h
@@ -798,14 +798,22 @@ extern "C" {
 #define SDL_HINT_RPI_VIDEO_LAYER           "SDL_RPI_VIDEO_LAYER"
 
 /**
- * \brief Tell SDL the KMS/DRM video driver that we want double buffer only.
- *
- * By default KMS/DRM will use a triple buffer solution that wastes no CPU
- * time on waiting for vsync after issuing a flip, but introduces a frame of
- * latency. Waiting for vsync immediately after issuing a flip on the other
- * hand is recommended for cases where low latency is an important factor.
- */
-#define SDL_HINT_KMSDRM_DOUBLE_BUFFER      "SDL_KMSDRM_DOUBLE_BUFFER"
+ * \brief Tell the video driver that we only want a double buffer.
+ *
+ * By default, most lowlevel 2D APIs will use a triple buffer scheme that 
+ * wastes no CPU time on waiting for vsync after issuing a flip, but
+ * introduces a frame of latency. On the other hand, using a double buffer
+ * scheme instead is recommended for cases where low latency is an important
+ * factor because we save a whole frame of latency.
+ * We do so by waiting for vsync immediately after issuing a flip, usually just
+ * after eglSwapBuffers call in the backend's *_SwapWindow function.
+ *
+ * Since it's driver-specific, it's only supported where possible and
+ * implemented. Currently supported the following drivers:
+ * - KMSDRM (kmsdrm)
+ * - Raspberry Pi (raspberrypi)
+ */
+#define SDL_HINT_VIDEO_DOUBLE_BUFFER      "SDL_VIDEO_DOUBLE_BUFFER"
 
 /**
  *  \brief  A variable controlling what driver to use for OpenGL ES contexts.
diff --git a/src/video/kmsdrm/SDL_kmsdrmvideo.c b/src/video/kmsdrm/SDL_kmsdrmvideo.c
index 3181915..9c1b291 100644
--- a/src/video/kmsdrm/SDL_kmsdrmvideo.c
+++ b/src/video/kmsdrm/SDL_kmsdrmvideo.c
@@ -525,7 +525,7 @@ KMSDRM_CreateWindow(_THIS, SDL_Window * window)
 
     /* In case we want low-latency, double-buffer video, we take note here */
     wdata->double_buffer = SDL_FALSE;
-    if (SDL_GetHintBoolean(SDL_HINT_KMSDRM_DOUBLE_BUFFER, SDL_FALSE)) {
+    if (SDL_GetHintBoolean(SDL_HINT_VIDEO_DOUBLE_BUFFER, SDL_FALSE)) {
         wdata->double_buffer = SDL_TRUE;
     }
 
diff --git a/src/video/raspberry/SDL_rpiopengles.c b/src/video/raspberry/SDL_rpiopengles.c
index 5b23a3f..7b7353d 100644
--- a/src/video/raspberry/SDL_rpiopengles.c
+++ b/src/video/raspberry/SDL_rpiopengles.c
@@ -19,6 +19,8 @@
   3. This notice may not be removed or altered from any source distribution.
 */
 #include "../../SDL_internal.h"
+#include "SDL_hints.h"
+#include "SDL_log.h"
 
 #if SDL_VIDEO_DRIVER_RPI && SDL_VIDEO_OPENGL_EGL
 
@@ -40,8 +42,27 @@ RPI_GLES_LoadLibrary(_THIS, const char *path) {
     return SDL_EGL_LoadLibrary(_this, path, EGL_DEFAULT_DISPLAY, 0);
 }
 
+int
+RPI_GLES_SwapWindow(_THIS, SDL_Window * window) {
+    SDL_WindowData *wdata = ((SDL_WindowData *) window->driverdata);
+
+    if (!(_this->egl_data->eglSwapBuffers(_this->egl_data->egl_display, wdata->egl_surface))) {
+        SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "eglSwapBuffers failed.");
+        return 0;
+    }
+
+    /* Wait immediately for vsync (as if we only had two buffers), for low input-lag scenarios.
+     * Run your SDL2 program with "SDL_RPI_DOUBLE_BUFFER=1 <program_name>" to enable this. */
+    if (wdata->double_buffer) {
+        SDL_LockMutex(wdata->vsync_cond_mutex);
+        SDL_CondWait(wdata->vsync_cond, wdata->vsync_cond_mutex);
+        SDL_UnlockMutex(wdata->vsync_cond_mutex);
+    }
+
+    return 0;
+}
+
 SDL_EGL_CreateContext_impl(RPI)
-SDL_EGL_SwapWindow_impl(RPI)
 SDL_EGL_MakeCurrent_impl(RPI)
 
 #endif /* SDL_VIDEO_DRIVER_RPI && SDL_VIDEO_OPENGL_EGL */
diff --git a/src/video/raspberry/SDL_rpivideo.c b/src/video/raspberry/SDL_rpivideo.c
index 1e1f569..bbd2f07 100644
--- a/src/video/raspberry/SDL_rpivideo.c
+++ b/src/video/raspberry/SDL_rpivideo.c
@@ -214,6 +214,16 @@ RPI_SetDisplayMode(_THIS, SDL_VideoDisplay * display, SDL_DisplayMode * mode)
     return 0;
 }
 
+static void
+RPI_vsync_callback(DISPMANX_UPDATE_HANDLE_T u, void *data)
+{
+   SDL_WindowData *wdata = ((SDL_WindowData *) data);
+
+   SDL_LockMutex(wdata->vsync_cond_mutex);
+   SDL_CondSignal(wdata->vsync_cond);
+   SDL_UnlockMutex(wdata->vsync_cond_mutex);
+}
+
 int
 RPI_CreateWindow(_THIS, SDL_Window * window)
 {
@@ -289,9 +299,18 @@ RPI_CreateWindow(_THIS, SDL_Window * window)
         return SDL_SetError("Could not create GLES window surface");
     }
 
+    /* Start generating vsync callbacks if necesary */
+    wdata->double_buffer = SDL_FALSE;
+    if (SDL_GetHintBoolean(SDL_HINT_VIDEO_DOUBLE_BUFFER, SDL_FALSE)) {
+        wdata->vsync_cond = SDL_CreateCond();
+        wdata->vsync_cond_mutex = SDL_CreateMutex();
+        wdata->double_buffer = SDL_TRUE;
+        vc_dispmanx_vsync_callback(displaydata->dispman_display, RPI_vsync_callback, (void*)wdata);
+    }
+
     /* Setup driver data for this window */
     window->driverdata = wdata;
-    
+
     /* One window, it always has focus */
     SDL_SetMouseFocus(window);
     SDL_SetKeyboardFocus(window);
@@ -304,7 +323,22 @@ void
 RPI_DestroyWindow(_THIS, SDL_Window * window)
 {
     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
+    SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window);
+    SDL_DisplayData *displaydata = (SDL_DisplayData *) display->driverdata;
+
     if(data) {
+	if (data->double_buffer) {
+	    /* Wait for vsync, and then stop vsync callbacks and destroy related stuff, if needed */
+	    SDL_LockMutex(data->vsync_cond_mutex);
+	    SDL_CondWait(data->vsync_cond, data->vsync_cond_mutex);
+	    SDL_UnlockMutex(data->vsync_cond_mutex);
+
+	    vc_dispmanx_vsync_callback(displaydata->dispman_display, NULL, NULL);
+
+	    SDL_DestroyCond(data->vsync_cond);
+	    SDL_DestroyMutex(data->vsync_cond_mutex);
+	}
+
 #if SDL_VIDEO_OPENGL_EGL
         if (data->egl_surface != EGL_NO_SURFACE) {
             SDL_EGL_DestroySurface(_this, data->egl_surface);
diff --git a/src/video/raspberry/SDL_rpivideo.h b/src/video/raspberry/SDL_rpivideo.h
index ba1f0b2..0678b9f 100644
--- a/src/video/raspberry/SDL_rpivideo.h
+++ b/src/video/raspberry/SDL_rpivideo.h
@@ -48,6 +48,12 @@ typedef struct SDL_WindowData
 #if SDL_VIDEO_OPENGL_EGL  
     EGLSurface egl_surface;
 #endif    
+
+    /* Vsync callback cond and mutex */
+    SDL_cond  *vsync_cond;
+    SDL_mutex *vsync_cond_mutex;
+    SDL_bool double_buffer;
+
 } SDL_WindowData;
 
 #define SDL_RPI_VIDEOLAYER 10000 /* High enough so to occlude everything */