Fixed bug 3943 - General SDL_HINT_VIDEO_DOUBLE_BUFFER hint support
authorBrandon Schaefer <brandontschaefer@gmail.com>
Tue, 07 Nov 2017 09:10:32 -0800
changeset 116942ce56475ad57
parent 11693 4c8bd26f1aab
child 11696 7dc3cec9ac85
Fixed bug 3943 - General SDL_HINT_VIDEO_DOUBLE_BUFFER hint support
include/SDL_hints.h
src/video/kmsdrm/SDL_kmsdrmvideo.c
src/video/raspberry/SDL_rpiopengles.c
src/video/raspberry/SDL_rpivideo.c
src/video/raspberry/SDL_rpivideo.h
     1.1 --- a/include/SDL_hints.h	Mon Nov 06 15:29:24 2017 -0500
     1.2 +++ b/include/SDL_hints.h	Tue Nov 07 09:10:32 2017 -0800
     1.3 @@ -798,14 +798,22 @@
     1.4  #define SDL_HINT_RPI_VIDEO_LAYER           "SDL_RPI_VIDEO_LAYER"
     1.5  
     1.6  /**
     1.7 - * \brief Tell SDL the KMS/DRM video driver that we want double buffer only.
     1.8 + * \brief Tell the video driver that we only want a double buffer.
     1.9   *
    1.10 - * By default KMS/DRM will use a triple buffer solution that wastes no CPU
    1.11 - * time on waiting for vsync after issuing a flip, but introduces a frame of
    1.12 - * latency. Waiting for vsync immediately after issuing a flip on the other
    1.13 - * hand is recommended for cases where low latency is an important factor.
    1.14 + * By default, most lowlevel 2D APIs will use a triple buffer scheme that 
    1.15 + * wastes no CPU time on waiting for vsync after issuing a flip, but
    1.16 + * introduces a frame of latency. On the other hand, using a double buffer
    1.17 + * scheme instead is recommended for cases where low latency is an important
    1.18 + * factor because we save a whole frame of latency.
    1.19 + * We do so by waiting for vsync immediately after issuing a flip, usually just
    1.20 + * after eglSwapBuffers call in the backend's *_SwapWindow function.
    1.21 + *
    1.22 + * Since it's driver-specific, it's only supported where possible and
    1.23 + * implemented. Currently supported the following drivers:
    1.24 + * - KMSDRM (kmsdrm)
    1.25 + * - Raspberry Pi (raspberrypi)
    1.26   */
    1.27 -#define SDL_HINT_KMSDRM_DOUBLE_BUFFER      "SDL_KMSDRM_DOUBLE_BUFFER"
    1.28 +#define SDL_HINT_VIDEO_DOUBLE_BUFFER      "SDL_VIDEO_DOUBLE_BUFFER"
    1.29  
    1.30  /**
    1.31   *  \brief  A variable controlling what driver to use for OpenGL ES contexts.
     2.1 --- a/src/video/kmsdrm/SDL_kmsdrmvideo.c	Mon Nov 06 15:29:24 2017 -0500
     2.2 +++ b/src/video/kmsdrm/SDL_kmsdrmvideo.c	Tue Nov 07 09:10:32 2017 -0800
     2.3 @@ -525,7 +525,7 @@
     2.4  
     2.5      /* In case we want low-latency, double-buffer video, we take note here */
     2.6      wdata->double_buffer = SDL_FALSE;
     2.7 -    if (SDL_GetHintBoolean(SDL_HINT_KMSDRM_DOUBLE_BUFFER, SDL_FALSE)) {
     2.8 +    if (SDL_GetHintBoolean(SDL_HINT_VIDEO_DOUBLE_BUFFER, SDL_FALSE)) {
     2.9          wdata->double_buffer = SDL_TRUE;
    2.10      }
    2.11  
     3.1 --- a/src/video/raspberry/SDL_rpiopengles.c	Mon Nov 06 15:29:24 2017 -0500
     3.2 +++ b/src/video/raspberry/SDL_rpiopengles.c	Tue Nov 07 09:10:32 2017 -0800
     3.3 @@ -19,6 +19,8 @@
     3.4    3. This notice may not be removed or altered from any source distribution.
     3.5  */
     3.6  #include "../../SDL_internal.h"
     3.7 +#include "SDL_hints.h"
     3.8 +#include "SDL_log.h"
     3.9  
    3.10  #if SDL_VIDEO_DRIVER_RPI && SDL_VIDEO_OPENGL_EGL
    3.11  
    3.12 @@ -40,8 +42,27 @@
    3.13      return SDL_EGL_LoadLibrary(_this, path, EGL_DEFAULT_DISPLAY, 0);
    3.14  }
    3.15  
    3.16 +int
    3.17 +RPI_GLES_SwapWindow(_THIS, SDL_Window * window) {
    3.18 +    SDL_WindowData *wdata = ((SDL_WindowData *) window->driverdata);
    3.19 +
    3.20 +    if (!(_this->egl_data->eglSwapBuffers(_this->egl_data->egl_display, wdata->egl_surface))) {
    3.21 +        SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "eglSwapBuffers failed.");
    3.22 +        return 0;
    3.23 +    }
    3.24 +
    3.25 +    /* Wait immediately for vsync (as if we only had two buffers), for low input-lag scenarios.
    3.26 +     * Run your SDL2 program with "SDL_RPI_DOUBLE_BUFFER=1 <program_name>" to enable this. */
    3.27 +    if (wdata->double_buffer) {
    3.28 +        SDL_LockMutex(wdata->vsync_cond_mutex);
    3.29 +        SDL_CondWait(wdata->vsync_cond, wdata->vsync_cond_mutex);
    3.30 +        SDL_UnlockMutex(wdata->vsync_cond_mutex);
    3.31 +    }
    3.32 +
    3.33 +    return 0;
    3.34 +}
    3.35 +
    3.36  SDL_EGL_CreateContext_impl(RPI)
    3.37 -SDL_EGL_SwapWindow_impl(RPI)
    3.38  SDL_EGL_MakeCurrent_impl(RPI)
    3.39  
    3.40  #endif /* SDL_VIDEO_DRIVER_RPI && SDL_VIDEO_OPENGL_EGL */
     4.1 --- a/src/video/raspberry/SDL_rpivideo.c	Mon Nov 06 15:29:24 2017 -0500
     4.2 +++ b/src/video/raspberry/SDL_rpivideo.c	Tue Nov 07 09:10:32 2017 -0800
     4.3 @@ -214,6 +214,16 @@
     4.4      return 0;
     4.5  }
     4.6  
     4.7 +static void
     4.8 +RPI_vsync_callback(DISPMANX_UPDATE_HANDLE_T u, void *data)
     4.9 +{
    4.10 +   SDL_WindowData *wdata = ((SDL_WindowData *) data);
    4.11 +
    4.12 +   SDL_LockMutex(wdata->vsync_cond_mutex);
    4.13 +   SDL_CondSignal(wdata->vsync_cond);
    4.14 +   SDL_UnlockMutex(wdata->vsync_cond_mutex);
    4.15 +}
    4.16 +
    4.17  int
    4.18  RPI_CreateWindow(_THIS, SDL_Window * window)
    4.19  {
    4.20 @@ -289,9 +299,18 @@
    4.21          return SDL_SetError("Could not create GLES window surface");
    4.22      }
    4.23  
    4.24 +    /* Start generating vsync callbacks if necesary */
    4.25 +    wdata->double_buffer = SDL_FALSE;
    4.26 +    if (SDL_GetHintBoolean(SDL_HINT_VIDEO_DOUBLE_BUFFER, SDL_FALSE)) {
    4.27 +        wdata->vsync_cond = SDL_CreateCond();
    4.28 +        wdata->vsync_cond_mutex = SDL_CreateMutex();
    4.29 +        wdata->double_buffer = SDL_TRUE;
    4.30 +        vc_dispmanx_vsync_callback(displaydata->dispman_display, RPI_vsync_callback, (void*)wdata);
    4.31 +    }
    4.32 +
    4.33      /* Setup driver data for this window */
    4.34      window->driverdata = wdata;
    4.35 -    
    4.36 +
    4.37      /* One window, it always has focus */
    4.38      SDL_SetMouseFocus(window);
    4.39      SDL_SetKeyboardFocus(window);
    4.40 @@ -304,7 +323,22 @@
    4.41  RPI_DestroyWindow(_THIS, SDL_Window * window)
    4.42  {
    4.43      SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
    4.44 +    SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window);
    4.45 +    SDL_DisplayData *displaydata = (SDL_DisplayData *) display->driverdata;
    4.46 +
    4.47      if(data) {
    4.48 +	if (data->double_buffer) {
    4.49 +	    /* Wait for vsync, and then stop vsync callbacks and destroy related stuff, if needed */
    4.50 +	    SDL_LockMutex(data->vsync_cond_mutex);
    4.51 +	    SDL_CondWait(data->vsync_cond, data->vsync_cond_mutex);
    4.52 +	    SDL_UnlockMutex(data->vsync_cond_mutex);
    4.53 +
    4.54 +	    vc_dispmanx_vsync_callback(displaydata->dispman_display, NULL, NULL);
    4.55 +
    4.56 +	    SDL_DestroyCond(data->vsync_cond);
    4.57 +	    SDL_DestroyMutex(data->vsync_cond_mutex);
    4.58 +	}
    4.59 +
    4.60  #if SDL_VIDEO_OPENGL_EGL
    4.61          if (data->egl_surface != EGL_NO_SURFACE) {
    4.62              SDL_EGL_DestroySurface(_this, data->egl_surface);
     5.1 --- a/src/video/raspberry/SDL_rpivideo.h	Mon Nov 06 15:29:24 2017 -0500
     5.2 +++ b/src/video/raspberry/SDL_rpivideo.h	Tue Nov 07 09:10:32 2017 -0800
     5.3 @@ -48,6 +48,12 @@
     5.4  #if SDL_VIDEO_OPENGL_EGL  
     5.5      EGLSurface egl_surface;
     5.6  #endif    
     5.7 +
     5.8 +    /* Vsync callback cond and mutex */
     5.9 +    SDL_cond  *vsync_cond;
    5.10 +    SDL_mutex *vsync_cond_mutex;
    5.11 +    SDL_bool double_buffer;
    5.12 +
    5.13  } SDL_WindowData;
    5.14  
    5.15  #define SDL_RPI_VIDEOLAYER 10000 /* High enough so to occlude everything */