Fixed bug 3943 - General SDL_HINT_VIDEO_DOUBLE_BUFFER hint support

This commit is contained in:
Brandon Schaefer 2017-11-07 09:10:32 -08:00
parent 5cc46f3d30
commit 9f4e4be8e0
5 changed files with 78 additions and 9 deletions

View File

@ -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.
* \brief Tell the video driver that we only want a double buffer.
*
* 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.
* 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_KMSDRM_DOUBLE_BUFFER "SDL_KMSDRM_DOUBLE_BUFFER"
#define SDL_HINT_VIDEO_DOUBLE_BUFFER "SDL_VIDEO_DOUBLE_BUFFER"
/**
* \brief A variable controlling what driver to use for OpenGL ES contexts.

View File

@ -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;
}

View File

@ -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 */

View File

@ -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);

View File

@ -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 */