Fixed bug 3943 - General SDL_HINT_VIDEO_DOUBLE_BUFFER hint support
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171
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 */