From ee5c5cf755f121df119c2b7fce51f96215523cd5 Mon Sep 17 00:00:00 2001 From: Ethan Lee Date: Wed, 4 Mar 2026 20:26:54 -0500 Subject: [PATCH] render: Add Suspend/Resume calls for GDK support --- include/SDL3/SDL_main.h | 4 +-- include/SDL3/SDL_render.h | 34 ++++++++++++++++++++++++ src/core/SDL_core_unsupported.c | 8 ++++++ src/core/SDL_core_unsupported.h | 2 ++ src/dynapi/SDL_dynapi.sym | 2 ++ src/dynapi/SDL_dynapi_overrides.h | 2 ++ src/dynapi/SDL_dynapi_procs.h | 2 ++ src/render/SDL_render.c | 20 ++++++++++++++ src/render/SDL_sysrender.h | 5 ++++ src/render/direct3d12/SDL_render_d3d12.c | 30 ++++++++++----------- src/render/gpu/SDL_render_gpu.c | 31 +++++++++++---------- test/testsymbols.c | 2 ++ 12 files changed, 108 insertions(+), 34 deletions(-) diff --git a/include/SDL3/SDL_main.h b/include/SDL3/SDL_main.h index 771572f29f..02733e0cc0 100644 --- a/include/SDL3/SDL_main.h +++ b/include/SDL3/SDL_main.h @@ -667,9 +667,7 @@ extern SDL_DECLSPEC void SDLCALL SDL_UnregisterApp(void); * This should be called from an event watch in response to an * `SDL_EVENT_DID_ENTER_BACKGROUND` event. * - * When using SDL_Render, your event watch should be added _after_ creating - * the `SDL_Renderer`; this allows the timing of the D3D12 command queue - * suspension to execute in the correct order. + * When using SDL_Render, this should be called after calling SDL_GDKSuspendRenderer. * * When using SDL_GPU, this should be called after calling SDL_GDKSuspendGPU. * diff --git a/include/SDL3/SDL_render.h b/include/SDL3/SDL_render.h index 1f7857d953..9dcd25e4b9 100644 --- a/include/SDL3/SDL_render.h +++ b/include/SDL3/SDL_render.h @@ -3082,6 +3082,40 @@ extern SDL_DECLSPEC bool SDLCALL SDL_SetGPURenderState(SDL_Renderer *renderer, S */ extern SDL_DECLSPEC void SDLCALL SDL_DestroyGPURenderState(SDL_GPURenderState *state); +#ifdef SDL_PLATFORM_GDK + +/** + * Call this to suspend Render operations on Xbox when you receive the + * SDL_EVENT_DID_ENTER_BACKGROUND event. + * + * Do NOT call any SDL_Render functions after calling this function! This must + * also be called before calling SDL_GDKSuspendComplete. + * + * \param renderer the renderer which should suspend operation + * + * \since This function is available since SDL 3.6.0. + * + * \sa SDL_AddEventWatch + */ +extern SDL_DECLSPEC void SDLCALL SDL_GDKSuspendRenderer(SDL_Renderer *renderer); + +/** + * Call this to resume Render operations on Xbox when you receive the + * SDL_EVENT_WILL_ENTER_FOREGROUND event. + * + * When resuming, this function MUST be called before calling any other + * SDL_Render functions. + * + * \param renderer the renderer which should resume operation + * + * \since This function is available since SDL 3.6.0. + * + * \sa SDL_AddEventWatch + */ +extern SDL_DECLSPEC void SDLCALL SDL_GDKResumeRenderer(SDL_Renderer *renderer); + +#endif /* SDL_PLATFORM_GDK */ + /* Ends C function definitions when using C++ */ #ifdef __cplusplus } diff --git a/src/core/SDL_core_unsupported.c b/src/core/SDL_core_unsupported.c index 5a70b5b4bf..09b675d6ec 100644 --- a/src/core/SDL_core_unsupported.c +++ b/src/core/SDL_core_unsupported.c @@ -69,6 +69,14 @@ void SDL_GDKResumeGPU(SDL_GPUDevice *device) { } +void SDL_GDKSuspendRenderer(SDL_Renderer *renderer) +{ +} + +void SDL_GDKResumeRenderer(SDL_Renderer *renderer) +{ +} + #endif /* !SDL_PLATFORM_GDK */ #if !defined(SDL_PLATFORM_WINDOWS) diff --git a/src/core/SDL_core_unsupported.h b/src/core/SDL_core_unsupported.h index 3a9428773c..b61a9a3a2f 100644 --- a/src/core/SDL_core_unsupported.h +++ b/src/core/SDL_core_unsupported.h @@ -35,6 +35,8 @@ extern SDL_DECLSPEC void SDLCALL SDL_GDKSuspendComplete(void); extern SDL_DECLSPEC bool SDLCALL SDL_GetGDKDefaultUser(XUserHandle *outUserHandle); extern SDL_DECLSPEC void SDLCALL SDL_GDKSuspendGPU(SDL_GPUDevice *device); extern SDL_DECLSPEC void SDLCALL SDL_GDKResumeGPU(SDL_GPUDevice *device); +extern SDL_DECLSPEC void SDLCALL SDL_GDKSuspendRenderer(SDL_Renderer *renderer); +extern SDL_DECLSPEC void SDLCALL SDL_GDKResumeRenderer(SDL_Renderer *renderer); #endif /* !SDL_PLATFORM_GDK */ #if !defined(SDL_PLATFORM_WINDOWS) diff --git a/src/dynapi/SDL_dynapi.sym b/src/dynapi/SDL_dynapi.sym index 184f8708ea..ef10030d26 100644 --- a/src/dynapi/SDL_dynapi.sym +++ b/src/dynapi/SDL_dynapi.sym @@ -1283,6 +1283,8 @@ SDL3_0.0.0 { SDL_SetGPURenderStateSamplerBindings; SDL_SetGPURenderStateStorageTextures; SDL_SetGPURenderStateStorageBuffers; + SDL_GDKSuspendRenderer; + SDL_GDKResumeRenderer; # extra symbols go here (don't modify this line) local: *; }; diff --git a/src/dynapi/SDL_dynapi_overrides.h b/src/dynapi/SDL_dynapi_overrides.h index 87762e6d7a..410917721d 100644 --- a/src/dynapi/SDL_dynapi_overrides.h +++ b/src/dynapi/SDL_dynapi_overrides.h @@ -1309,3 +1309,5 @@ #define SDL_SetGPURenderStateSamplerBindings SDL_SetGPURenderStateSamplerBindings_REAL #define SDL_SetGPURenderStateStorageTextures SDL_SetGPURenderStateStorageTextures_REAL #define SDL_SetGPURenderStateStorageBuffers SDL_SetGPURenderStateStorageBuffers_REAL +#define SDL_GDKSuspendRenderer SDL_GDKSuspendRenderer_REAL +#define SDL_GDKResumeRenderer SDL_GDKResumeRenderer_REAL diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h index d965c7b628..175f2fd4da 100644 --- a/src/dynapi/SDL_dynapi_procs.h +++ b/src/dynapi/SDL_dynapi_procs.h @@ -1317,3 +1317,5 @@ SDL_DYNAPI_PROC(SDL_Tray*,SDL_CreateTrayWithProperties,(SDL_PropertiesID a),(a), SDL_DYNAPI_PROC(bool,SDL_SetGPURenderStateSamplerBindings,(SDL_GPURenderState *a,int b,const SDL_GPUTextureSamplerBinding *c),(a,b,c),return) SDL_DYNAPI_PROC(bool,SDL_SetGPURenderStateStorageTextures,(SDL_GPURenderState *a,int b,SDL_GPUTexture *const*c),(a,b,c),return) SDL_DYNAPI_PROC(bool,SDL_SetGPURenderStateStorageBuffers,(SDL_GPURenderState *a,int b,SDL_GPUBuffer *const*c),(a,b,c),return) +SDL_DYNAPI_PROC(void,SDL_GDKSuspendRenderer,(SDL_Renderer *a),(a),) +SDL_DYNAPI_PROC(void,SDL_GDKResumeRenderer,(SDL_Renderer *a),(a),) diff --git a/src/render/SDL_render.c b/src/render/SDL_render.c index 684ad7b82d..e54ff39821 100644 --- a/src/render/SDL_render.c +++ b/src/render/SDL_render.c @@ -6347,3 +6347,23 @@ void SDL_DestroyGPURenderState(SDL_GPURenderState *state) SDL_free(state->storage_buffers); SDL_free(state); } + +#ifdef SDL_PLATFORM_GDK + +void SDLCALL SDL_GDKSuspendRenderer(SDL_Renderer *renderer) +{ + CHECK_RENDERER_MAGIC(renderer,); + if (renderer->GDKSuspendRenderer != NULL) { + renderer->GDKSuspendRenderer(renderer); + } +} + +void SDLCALL SDL_GDKResumeRenderer(SDL_Renderer *renderer) +{ + CHECK_RENDERER_MAGIC(renderer,); + if (renderer->GDKResumeRenderer != NULL) { + renderer->GDKResumeRenderer(renderer); + } +} + +#endif /* SDL_PLATFORM_GDK */ diff --git a/src/render/SDL_sysrender.h b/src/render/SDL_sysrender.h index a1bb44a12d..c1c13074ca 100644 --- a/src/render/SDL_sysrender.h +++ b/src/render/SDL_sysrender.h @@ -280,6 +280,11 @@ struct SDL_Renderer bool (*AddVulkanRenderSemaphores)(SDL_Renderer *renderer, Uint32 wait_stage_mask, Sint64 wait_semaphore, Sint64 signal_semaphore); +#ifdef SDL_PLATFORM_GDK + void (*GDKSuspendRenderer)(SDL_Renderer *renderer); + void (*GDKResumeRenderer)(SDL_Renderer *renderer); +#endif + // The current renderer info const char *name; SDL_PixelFormat *texture_formats; diff --git a/src/render/direct3d12/SDL_render_d3d12.c b/src/render/direct3d12/SDL_render_d3d12.c index ff12935595..3330a558a2 100644 --- a/src/render/direct3d12/SDL_render_d3d12.c +++ b/src/render/direct3d12/SDL_render_d3d12.c @@ -563,25 +563,25 @@ static HRESULT D3D12_IssueBatch(D3D12_RenderData *data) } #if defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES) -static bool SDLCALL D3D12_GDKEventFilter(void* userdata, SDL_Event* event) + +static void D3D12_GDKSuspendRenderer(SDL_Renderer *renderer) { - D3D12_RenderData *data = (D3D12_RenderData *)userdata; - if (event->type == SDL_EVENT_DID_ENTER_BACKGROUND) { - data->commandQueue->SuspendX(0); - } else if (event->type == SDL_EVENT_WILL_ENTER_FOREGROUND) { - data->commandQueue->ResumeX(); - } - return true; + D3D12_RenderData *data = (D3D12_RenderData *)renderer->internal; + data->commandQueue->SuspendX(0); } + +static void D3D12_GDKResumeRenderer(SDL_Renderer *renderer) +{ + D3D12_RenderData *data = (D3D12_RenderData *)renderer->internal; + data->commandQueue->ResumeX(); +} + #endif static void D3D12_DestroyRenderer(SDL_Renderer *renderer) { D3D12_RenderData *data = (D3D12_RenderData *)renderer->internal; if (data) { -#if defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES) - SDL_RemoveEventWatch(D3D12_GDKEventFilter, data); -#endif D3D12_WaitForGPU(data); D3D12_ReleaseAll(renderer); SDL_free(data); @@ -1128,10 +1128,6 @@ static HRESULT D3D12_CreateDeviceResources(SDL_Renderer *renderer) SDL_SetPointerProperty(props, SDL_PROP_RENDERER_D3D12_DEVICE_POINTER, data->d3dDevice); SDL_SetPointerProperty(props, SDL_PROP_RENDERER_D3D12_COMMAND_QUEUE_POINTER, data->commandQueue); -#if defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES) - SDL_AddEventWatch(D3D12_GDKEventFilter, data); -#endif - done: D3D_SAFE_RELEASE(d3dDevice); return result; @@ -3517,6 +3513,10 @@ bool D3D12_CreateRenderer(SDL_Renderer *renderer, SDL_Window *window, SDL_Proper renderer->DestroyTexture = D3D12_DestroyTexture; renderer->DestroyRenderer = D3D12_DestroyRenderer; renderer->SetVSync = D3D12_SetVSync; +#if defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES) + renderer->GDKSuspendRenderer = D3D12_GDKSuspendRenderer; + renderer->GDKResumeRenderer = D3D12_GDKResumeRenderer; +#endif renderer->internal = data; D3D12_InvalidateCachedState(renderer); diff --git a/src/render/gpu/SDL_render_gpu.c b/src/render/gpu/SDL_render_gpu.c index 5887023cda..b9b267324f 100644 --- a/src/render/gpu/SDL_render_gpu.c +++ b/src/render/gpu/SDL_render_gpu.c @@ -1563,17 +1563,19 @@ static void GPU_DestroyTexture(SDL_Renderer *renderer, SDL_Texture *texture) } #ifdef SDL_PLATFORM_GDK -static bool SDLCALL GPU_GDKEventFilter(void *userdata, SDL_Event *event) + +static void GPU_GDKSuspendRenderer(SDL_Renderer *renderer) { - GPU_RenderData *data = (GPU_RenderData *)userdata; - SDL_assert(!data->external_device); - if (event->type == SDL_EVENT_DID_ENTER_BACKGROUND) { - SDL_GDKSuspendGPU(data->device); - } else if (event->type == SDL_EVENT_WILL_ENTER_FOREGROUND) { - SDL_GDKResumeGPU(data->device); - } - return true; + GPU_RenderData *data = (GPU_RenderData *)renderer->internal; + SDL_GDKSuspendGPU(data->device); } + +static void GPU_GDKResumeRenderer(SDL_Renderer *renderer) +{ + GPU_RenderData *data = (GPU_RenderData *)renderer->internal; + SDL_GDKResumeGPU(data->device); +} + #endif static void GPU_DestroyRenderer(SDL_Renderer *renderer) @@ -1609,9 +1611,6 @@ static void GPU_DestroyRenderer(SDL_Renderer *renderer) if (data->device) { GPU_ReleaseShaders(&data->shaders, data->device); if (!data->external_device) { -#ifdef SDL_PLATFORM_GDK - SDL_RemoveEventWatch(GPU_GDKEventFilter, data); -#endif SDL_DestroyGPUDevice(data->device); } } @@ -1722,6 +1721,10 @@ static bool GPU_CreateRenderer(SDL_Renderer *renderer, SDL_Window *window, SDL_P renderer->DestroyTexture = GPU_DestroyTexture; renderer->DestroyRenderer = GPU_DestroyRenderer; renderer->SetVSync = GPU_SetVSync; +#ifdef SDL_PLATFORM_GDK + renderer->GDKSuspendRenderer = GPU_GDKSuspendRenderer; + renderer->GDKResumeRenderer = GPU_GDKResumeRenderer; +#endif renderer->internal = data; renderer->window = window; renderer->name = GPU_RenderDriver.name; @@ -1775,10 +1778,6 @@ static bool GPU_CreateRenderer(SDL_Renderer *renderer, SDL_Window *window, SDL_P if (!data->device) { return false; } - -#ifdef SDL_PLATFORM_GDK - SDL_AddEventWatch(GPU_GDKEventFilter, data); -#endif } if (!GPU_InitShaders(&data->shaders, data->device)) { diff --git a/test/testsymbols.c b/test/testsymbols.c index 70f14fb11f..cf363e6251 100644 --- a/test/testsymbols.c +++ b/test/testsymbols.c @@ -40,6 +40,8 @@ extern SDL_DECLSPEC void SDLCALL SDL_GDKSuspendGPU(void); extern SDL_DECLSPEC void SDLCALL SDL_GDKSuspendComplete(void); extern SDL_DECLSPEC void SDLCALL SDL_GetGDKDefaultUser(void); extern SDL_DECLSPEC void SDLCALL SDL_GetGDKTaskQueue(void); +extern SDL_DECLSPEC void SDLCALL SDL_GDKSuspendRenderer(void); +extern SDL_DECLSPEC void SDLCALL SDL_GDKResumeRenderer(void); #endif #if !defined(SDL_PLATFORM_IOS)