Skip to content

Commit

Permalink
Fixed bug 3943 - General SDL_HINT_VIDEO_DOUBLE_BUFFER hint support
Browse files Browse the repository at this point in the history
  • Loading branch information
BrandonSchaefer committed Nov 7, 2017
1 parent 5cc46f3 commit 9f4e4be
Show file tree
Hide file tree
Showing 5 changed files with 80 additions and 11 deletions.
24 changes: 16 additions & 8 deletions include/SDL_hints.h
Expand Up @@ -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.
Expand Down
2 changes: 1 addition & 1 deletion src/video/kmsdrm/SDL_kmsdrmvideo.c
Expand Up @@ -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;
}

Expand Down
23 changes: 22 additions & 1 deletion src/video/raspberry/SDL_rpiopengles.c
Expand Up @@ -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

Expand All @@ -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 */
Expand Down
36 changes: 35 additions & 1 deletion src/video/raspberry/SDL_rpivideo.c
Expand Up @@ -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)
{
Expand Down Expand Up @@ -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);
Expand All @@ -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);
Expand Down
6 changes: 6 additions & 0 deletions src/video/raspberry/SDL_rpivideo.h
Expand Up @@ -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 */
Expand Down

0 comments on commit 9f4e4be

Please sign in to comment.