diff --git a/src/render/SDL_render.c b/src/render/SDL_render.c index 42cafee93..0930d80f2 100644 --- a/src/render/SDL_render.c +++ b/src/render/SDL_render.c @@ -24,6 +24,7 @@ #include "SDL_hints.h" #include "SDL_render.h" +#include "SDL_timer.h" #include "SDL_sysrender.h" #include "software/SDL_render_sw_c.h" #include "../video/SDL_pixels_c.h" @@ -1013,6 +1014,14 @@ SDL_CreateRenderer(SDL_Window * window, int index, Uint32 flags) } } + if ((flags & SDL_RENDERER_PRESENTVSYNC) != 0) { + renderer->wanted_vsync = SDL_TRUE; + + if ((renderer->info.flags & SDL_RENDERER_PRESENTVSYNC) == 0) { + renderer->simulate_vsync = SDL_TRUE; + renderer->info.flags |= SDL_RENDERER_PRESENTVSYNC; + } + } VerifyDrawQueueFunctions(renderer); @@ -4260,9 +4269,38 @@ SDL_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect, format, pixels, pitch); } +static void +SDL_RenderSimulateVSync(SDL_Renderer * renderer) +{ + Uint32 now, elapsed; + const Uint32 interval = (1000 / 60); /* FIXME: What FPS? */ + + if (!interval) { + /* We can't do sub-ms delay, so just return here */ + return; + } + + now = SDL_GetTicks(); + elapsed = (now - renderer->last_present); + if (elapsed < interval) { + Uint32 duration = (interval - elapsed); + SDL_Delay(duration); + now = SDL_GetTicks(); + } + + if (renderer->last_present) { + elapsed = (now - renderer->last_present); + renderer->last_present += (elapsed / interval) * interval; + } else { + renderer->last_present = now; + } +} + void SDL_RenderPresent(SDL_Renderer * renderer) { + SDL_bool presented = SDL_TRUE; + CHECK_RENDERER_MAGIC(renderer, ); FlushRenderCommands(renderer); /* time to send everything to the GPU! */ @@ -4270,11 +4308,17 @@ SDL_RenderPresent(SDL_Renderer * renderer) #if DONT_DRAW_WHILE_HIDDEN /* Don't present while we're hidden */ if (renderer->hidden) { - return; - } + presented = SDL_FALSE; + } else #endif + if (renderer->RenderPresent(renderer) < 0) { + presented = SDL_FALSE; + } - renderer->RenderPresent(renderer); + if (renderer->simulate_vsync || + (!presented && renderer->wanted_vsync)) { + SDL_RenderSimulateVSync(renderer); + } } void @@ -4530,10 +4574,15 @@ SDL_RenderSetVSync(SDL_Renderer * renderer, int vsync) return SDL_Unsupported(); } - if (renderer->SetVSync) { - return renderer->SetVSync(renderer, vsync); + renderer->wanted_vsync = vsync ? SDL_TRUE : SDL_FALSE; + + if (!renderer->SetVSync || + renderer->SetVSync(renderer, vsync) < 0) { + renderer->simulate_vsync = vsync ? SDL_TRUE : SDL_FALSE; + } else { + renderer->simulate_vsync = SDL_FALSE; } - return SDL_Unsupported(); + return 0; } /* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/render/SDL_sysrender.h b/src/render/SDL_sysrender.h index 4dcb98ac2..6deb5b2ee 100644 --- a/src/render/SDL_sysrender.h +++ b/src/render/SDL_sysrender.h @@ -188,7 +188,7 @@ struct SDL_Renderer int (*SetRenderTarget) (SDL_Renderer * renderer, SDL_Texture * texture); int (*RenderReadPixels) (SDL_Renderer * renderer, const SDL_Rect * rect, Uint32 format, void * pixels, int pitch); - void (*RenderPresent) (SDL_Renderer * renderer); + int (*RenderPresent) (SDL_Renderer * renderer); void (*DestroyTexture) (SDL_Renderer * renderer, SDL_Texture * texture); void (*DestroyRenderer) (SDL_Renderer * renderer); @@ -208,6 +208,11 @@ struct SDL_Renderer SDL_Window *window; SDL_bool hidden; + /* Whether we should simulate vsync */ + SDL_bool wanted_vsync; + SDL_bool simulate_vsync; + Uint32 last_present; + /* The logical resolution for rendering */ int logical_w; int logical_h; diff --git a/src/render/direct3d/SDL_render_d3d.c b/src/render/direct3d/SDL_render_d3d.c index ae83009a5..cad5536c2 100644 --- a/src/render/direct3d/SDL_render_d3d.c +++ b/src/render/direct3d/SDL_render_d3d.c @@ -1384,7 +1384,7 @@ D3D_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect, return status; } -static void +static int D3D_RenderPresent(SDL_Renderer * renderer) { D3D_RenderData *data = (D3D_RenderData *) renderer->driverdata; @@ -1398,15 +1398,16 @@ D3D_RenderPresent(SDL_Renderer * renderer) result = IDirect3DDevice9_TestCooperativeLevel(data->device); if (result == D3DERR_DEVICELOST) { /* We'll reset later */ - return; + return -1; } if (result == D3DERR_DEVICENOTRESET) { D3D_Reset(renderer); } result = IDirect3DDevice9_Present(data->device, NULL, NULL, NULL, NULL); if (FAILED(result)) { - D3D_SetError("Present()", result); + return D3D_SetError("Present()", result); } + return 0; } static void diff --git a/src/render/direct3d11/SDL_render_d3d11.c b/src/render/direct3d11/SDL_render_d3d11.c index f7dfefee6..8369e54f6 100644 --- a/src/render/direct3d11/SDL_render_d3d11.c +++ b/src/render/direct3d11/SDL_render_d3d11.c @@ -2288,7 +2288,7 @@ done: return status; } -static void +static int D3D11_RenderPresent(SDL_Renderer * renderer) { D3D11_RenderData *data = (D3D11_RenderData *) renderer->driverdata; @@ -2334,7 +2334,7 @@ D3D11_RenderPresent(SDL_Renderer * renderer) * * TODO, WinRT: consider throwing an exception if D3D11_RenderPresent fails, especially if there is a way to salvage debug info from users' machines */ - if ( result == DXGI_ERROR_DEVICE_REMOVED ) { + if (result == DXGI_ERROR_DEVICE_REMOVED) { D3D11_HandleDeviceLost(renderer); } else if (result == DXGI_ERROR_INVALID_CALL) { /* We probably went through a fullscreen <-> windowed transition */ @@ -2342,7 +2342,9 @@ D3D11_RenderPresent(SDL_Renderer * renderer) } else { WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("IDXGISwapChain::Present"), result); } + return -1; } + return 0; } #if WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP diff --git a/src/render/direct3d12/SDL_render_d3d12.c b/src/render/direct3d12/SDL_render_d3d12.c index 3fde465b6..e5cbd4c4f 100644 --- a/src/render/direct3d12/SDL_render_d3d12.c +++ b/src/render/direct3d12/SDL_render_d3d12.c @@ -2932,7 +2932,7 @@ done: return status; } -static void +static int D3D12_RenderPresent(SDL_Renderer * renderer) { D3D12_RenderData *data = (D3D12_RenderData *) renderer->driverdata; @@ -2982,6 +2982,7 @@ D3D12_RenderPresent(SDL_Renderer * renderer) } else { WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("IDXGISwapChain::Present"), result); } + return -1; } else { /* Wait for the GPU and move to the next frame */ result = D3D_CALL(data->commandQueue, Signal, data->fence, data->fenceValue); @@ -3013,6 +3014,7 @@ D3D12_RenderPresent(SDL_Renderer * renderer) #if defined(__XBOXONE__) || defined(__XBOXSERIES__) D3D12_XBOX_StartFrame(data->d3dDevice, &data->frameToken); #endif + return 0; } } diff --git a/src/render/metal/SDL_render_metal.m b/src/render/metal/SDL_render_metal.m index fdd0438aa..053289022 100644 --- a/src/render/metal/SDL_render_metal.m +++ b/src/render/metal/SDL_render_metal.m @@ -1497,7 +1497,7 @@ METAL_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect, return status; }} -static void +static int METAL_RenderPresent(SDL_Renderer * renderer) { @autoreleasepool { METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata; @@ -1528,6 +1528,11 @@ METAL_RenderPresent(SDL_Renderer * renderer) data.mtlcmdencoder = nil; data.mtlcmdbuffer = nil; data.mtlbackbuffer = nil; + + if (!ready) { + return -1; + } + return 0; }} static void diff --git a/src/render/opengl/SDL_render_gl.c b/src/render/opengl/SDL_render_gl.c index a3f5767e9..c55c38769 100644 --- a/src/render/opengl/SDL_render_gl.c +++ b/src/render/opengl/SDL_render_gl.c @@ -22,6 +22,7 @@ #if SDL_VIDEO_RENDER_OGL && !SDL_RENDER_DISABLED #include "SDL_hints.h" +#include "../../video/SDL_sysvideo.h" /* For SDL_GL_SwapWindowWithResult */ #include "SDL_opengl.h" #include "../SDL_sysrender.h" #include "SDL_shaders_gl.h" @@ -1502,12 +1503,12 @@ GL_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect, return status; } -static void +static int GL_RenderPresent(SDL_Renderer * renderer) { GL_ActivateRenderer(renderer); - SDL_GL_SwapWindow(renderer->window); + return SDL_GL_SwapWindowWithResult(renderer->window); } static void diff --git a/src/render/opengles/SDL_render_gles.c b/src/render/opengles/SDL_render_gles.c index 588644980..a5fbab309 100644 --- a/src/render/opengles/SDL_render_gles.c +++ b/src/render/opengles/SDL_render_gles.c @@ -23,6 +23,7 @@ #if SDL_VIDEO_RENDER_OGL_ES && !SDL_RENDER_DISABLED #include "SDL_hints.h" +#include "../../video/SDL_sysvideo.h" /* For SDL_GL_SwapWindowWithResult */ #include "SDL_opengles.h" #include "../SDL_sysrender.h" #include "../../SDL_utils_c.h" @@ -948,12 +949,12 @@ GLES_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect, return status; } -static void +static int GLES_RenderPresent(SDL_Renderer * renderer) { GLES_ActivateRenderer(renderer); - SDL_GL_SwapWindow(renderer->window); + return SDL_GL_SwapWindowWithResult(renderer->window); } static void diff --git a/src/render/opengles2/SDL_render_gles2.c b/src/render/opengles2/SDL_render_gles2.c index 76062629f..8718f7964 100644 --- a/src/render/opengles2/SDL_render_gles2.c +++ b/src/render/opengles2/SDL_render_gles2.c @@ -23,6 +23,7 @@ #if SDL_VIDEO_RENDER_OGL_ES2 && !SDL_RENDER_DISABLED #include "SDL_hints.h" +#include "../../video/SDL_sysvideo.h" /* For SDL_GL_SwapWindowWithResult */ #include "SDL_opengles2.h" #include "../SDL_sysrender.h" #include "../../video/SDL_blit.h" @@ -1983,11 +1984,11 @@ GLES2_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect, return status; } -static void +static int GLES2_RenderPresent(SDL_Renderer *renderer) { /* Tell the video driver to swap buffers */ - SDL_GL_SwapWindow(renderer->window); + return SDL_GL_SwapWindowWithResult(renderer->window); } static int diff --git a/src/render/ps2/SDL_render_ps2.c b/src/render/ps2/SDL_render_ps2.c index ec89663c7..ad2530676 100644 --- a/src/render/ps2/SDL_render_ps2.c +++ b/src/render/ps2/SDL_render_ps2.c @@ -654,7 +654,7 @@ PS2_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect, return SDL_Unsupported(); } -static void +static int PS2_RenderPresent(SDL_Renderer * renderer) { PS2_RenderData *data = (PS2_RenderData *) renderer->driverdata; @@ -676,6 +676,7 @@ PS2_RenderPresent(SDL_Renderer * renderer) } gsKit_TexManager_nextFrame(data->gsGlobal); gsKit_clear(data->gsGlobal, GS_BLACK); + return 0; } static void diff --git a/src/render/psp/SDL_render_psp.c b/src/render/psp/SDL_render_psp.c index cdc0b9156..6f0844054 100644 --- a/src/render/psp/SDL_render_psp.c +++ b/src/render/psp/SDL_render_psp.c @@ -1250,12 +1250,13 @@ PSP_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect, return SDL_Unsupported(); } -static void +static int PSP_RenderPresent(SDL_Renderer * renderer) { PSP_RenderData *data = (PSP_RenderData *) renderer->driverdata; - if(!data->displayListAvail) - return; + if (!data->displayListAvail) { + return -1; + } data->displayListAvail = SDL_FALSE; sceGuFinish(); @@ -1268,6 +1269,7 @@ PSP_RenderPresent(SDL_Renderer * renderer) data->backbuffer = data->frontbuffer; data->frontbuffer = vabsptr(sceGuSwapBuffers()); + return 0; } static void diff --git a/src/render/software/SDL_render_sw.c b/src/render/software/SDL_render_sw.c index 34092b58d..874f92b33 100644 --- a/src/render/software/SDL_render_sw.c +++ b/src/render/software/SDL_render_sw.c @@ -983,14 +983,15 @@ SW_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect, format, pixels, pitch); } -static void +static int SW_RenderPresent(SDL_Renderer * renderer) { SDL_Window *window = renderer->window; - if (window) { - SDL_UpdateWindowSurface(window); + if (!window) { + return -1; } + return SDL_UpdateWindowSurface(window); } static void diff --git a/src/render/vitagxm/SDL_render_vita_gxm.c b/src/render/vitagxm/SDL_render_vita_gxm.c index a4b2cee6f..b76a91bd1 100644 --- a/src/render/vitagxm/SDL_render_vita_gxm.c +++ b/src/render/vitagxm/SDL_render_vita_gxm.c @@ -100,7 +100,7 @@ static int VITA_GXM_RenderReadPixels(SDL_Renderer *renderer, const SDL_Rect *rec Uint32 pixel_format, void *pixels, int pitch); -static void VITA_GXM_RenderPresent(SDL_Renderer *renderer); +static int VITA_GXM_RenderPresent(SDL_Renderer *renderer); static void VITA_GXM_DestroyTexture(SDL_Renderer *renderer, SDL_Texture *texture); static void VITA_GXM_DestroyRenderer(SDL_Renderer *renderer); @@ -1185,7 +1185,7 @@ VITA_GXM_RenderReadPixels(SDL_Renderer *renderer, const SDL_Rect *rect, } -static void +static int VITA_GXM_RenderPresent(SDL_Renderer *renderer) { VITA_GXM_RenderData *data = (VITA_GXM_RenderData *) renderer->driverdata; @@ -1227,6 +1227,7 @@ VITA_GXM_RenderPresent(SDL_Renderer *renderer) data->pool_index = 0; data->current_pool = (data->current_pool + 1) % 2; + return 0; } static void diff --git a/src/video/SDL_sysvideo.h b/src/video/SDL_sysvideo.h index 4f602d305..7202f1d6b 100644 --- a/src/video/SDL_sysvideo.h +++ b/src/video/SDL_sysvideo.h @@ -519,6 +519,8 @@ extern void SDL_ToggleDragAndDropSupport(void); extern int SDL_GetPointDisplayIndex(const SDL_Point * point); +extern int SDL_GL_SwapWindowWithResult(SDL_Window * window); + #endif /* SDL_sysvideo_h_ */ /* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/video/SDL_video.c b/src/video/SDL_video.c index 3f4cfd7ec..992a76d46 100644 --- a/src/video/SDL_video.c +++ b/src/video/SDL_video.c @@ -4152,22 +4152,26 @@ SDL_GL_GetSwapInterval(void) } } -void -SDL_GL_SwapWindow(SDL_Window * window) +int +SDL_GL_SwapWindowWithResult(SDL_Window * window) { - CHECK_WINDOW_MAGIC(window,); + CHECK_WINDOW_MAGIC(window, -1); if (!(window->flags & SDL_WINDOW_OPENGL)) { - SDL_SetError("The specified window isn't an OpenGL window"); - return; + return SDL_SetError("The specified window isn't an OpenGL window"); } if (SDL_GL_GetCurrentWindow() != window) { - SDL_SetError("The specified window has not been made current"); - return; + return SDL_SetError("The specified window has not been made current"); } - _this->GL_SwapWindow(_this, window); + return _this->GL_SwapWindow(_this, window); +} + +void +SDL_GL_SwapWindow(SDL_Window * window) +{ + SDL_GL_SwapWindowWithResult(window); } void