From b488c2e4a022931a184696916489adb50ed8b20c Mon Sep 17 00:00:00 2001 From: Ethan Lee Date: Wed, 25 Feb 2026 09:20:25 -0500 Subject: [PATCH] gdk: Render/GPU can call SuspendX, document when to call SuspendComplete (cherry picked from commit 5770e013c26a666a1e314e09dfd7c5ace3fcb29c) --- VisualC-GDK/tests/testgdk/src/testgdk.cpp | 32 ++++++++++++++++++++--- include/SDL3/SDL_main.h | 7 +++++ src/render/direct3d12/SDL_render_d3d12.c | 20 ++++++++++++++ src/render/gpu/SDL_render_gpu.c | 21 +++++++++++++++ 4 files changed, 76 insertions(+), 4 deletions(-) diff --git a/VisualC-GDK/tests/testgdk/src/testgdk.cpp b/VisualC-GDK/tests/testgdk/src/testgdk.cpp index b8fffd30b6..d69e73e533 100644 --- a/VisualC-GDK/tests/testgdk/src/testgdk.cpp +++ b/VisualC-GDK/tests/testgdk/src/testgdk.cpp @@ -291,9 +291,8 @@ static void DrawSprites(SDL_Renderer * renderer, SDL_Texture * sprite) SDL_RenderPresent(renderer); } -static void loop() +static void update() { - int i; SDL_Event event; /* Check for events */ @@ -310,13 +309,31 @@ static void loop() SDLTest_CommonEvent(state, &event, &done); #endif } + fillerup(); +} + +static void draw() +{ + int i; for (i = 0; i < state->num_windows; ++i) { if (state->windows[i] == NULL) { continue; } DrawSprites(state->renderers[i], sprites[i]); } - fillerup(); +} + +static bool SDLCALL GDKEventWatch(void* userdata, SDL_Event* event) +{ + bool *suppressdraw = (bool *)userdata; + SDL_assert(suppressdraw != NULL); + if (event->type == SDL_EVENT_DID_ENTER_BACKGROUND) { + *suppressdraw = true; + SDL_GDKSuspendComplete(); + } else if (event->type == SDL_EVENT_WILL_ENTER_FOREGROUND) { + *suppressdraw = false; + } + return true; } int main(int argc, char *argv[]) @@ -324,6 +341,7 @@ int main(int argc, char *argv[]) int i; const char *icon = "icon.bmp"; char *soundname = NULL; + bool suppressdraw = false; /* Initialize parameters */ num_sprites = NUM_SPRITES; @@ -390,6 +408,9 @@ int main(int argc, char *argv[]) quit(2); } + /* By this point the renderers are made, so we can now add this watcher */ + SDL_AddEventWatch(GDKEventWatch, &suppressdraw); + /* Create the windows, initialize the renderers, and load the textures */ sprites = (SDL_Texture **) SDL_malloc(state->num_windows * sizeof(*sprites)); @@ -441,7 +462,10 @@ int main(int argc, char *argv[]) AddUserSilent(); while (!done) { - loop(); + update(); + if (!suppressdraw) { + draw(); + } } quit(0); diff --git a/include/SDL3/SDL_main.h b/include/SDL3/SDL_main.h index c036b57416..daa10aabbc 100644 --- a/include/SDL3/SDL_main.h +++ b/include/SDL3/SDL_main.h @@ -664,6 +664,13 @@ extern SDL_DECLSPEC void SDLCALL SDL_UnregisterApp(void); /** * Callback from the application to let the suspend continue. * + * When using SDL_Render or SDL_GPU, this function should be called _after_ + * creating the `SDL_Renderer` or `SDL_GPUDevice`; this allows the timing of the + * D3D12 command queue suspension to execute in the correct order. + * + * If you're writing your own D3D12 renderer, this should be called after + * calling `ID3D12CommandQueue::SuspendX`. + * * This function is only needed for Xbox GDK support; all other platforms will * do nothing and set an "unsupported" error message. * diff --git a/src/render/direct3d12/SDL_render_d3d12.c b/src/render/direct3d12/SDL_render_d3d12.c index 4eb2a92601..7f4a9b59e3 100644 --- a/src/render/direct3d12/SDL_render_d3d12.c +++ b/src/render/direct3d12/SDL_render_d3d12.c @@ -586,10 +586,26 @@ static HRESULT D3D12_IssueBatch(D3D12_RenderData *data) return result; } +#ifdef SDL_PLATFORM_GDK +static bool SDLCALL D3D12_GDKEventFilter(void* userdata, SDL_Event* event) +{ + 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; +} +#endif + static void D3D12_DestroyRenderer(SDL_Renderer *renderer) { D3D12_RenderData *data = (D3D12_RenderData *)renderer->internal; if (data) { +#ifdef SDL_PLATFORM_GDK + SDL_RemoveEventWatch(D3D12_GDKEventFilter, data); +#endif D3D12_WaitForGPU(data); D3D12_ReleaseAll(renderer); SDL_free(data); @@ -1136,6 +1152,10 @@ 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); +#ifdef SDL_PLATFORM_GDK + SDL_AddEventWatch(D3D12_GDKEventFilter, data); +#endif + done: D3D_SAFE_RELEASE(d3dDevice); return result; diff --git a/src/render/gpu/SDL_render_gpu.c b/src/render/gpu/SDL_render_gpu.c index b589333850..7b15e4adc0 100644 --- a/src/render/gpu/SDL_render_gpu.c +++ b/src/render/gpu/SDL_render_gpu.c @@ -1546,6 +1546,20 @@ static void GPU_DestroyTexture(SDL_Renderer *renderer, SDL_Texture *texture) texture->internal = NULL; } +#ifdef SDL_PLATFORM_GDK +static bool SDLCALL GPU_GDKEventFilter(void *userdata, SDL_Event *event) +{ + 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; +} +#endif + static void GPU_DestroyRenderer(SDL_Renderer *renderer) { GPU_RenderData *data = (GPU_RenderData *)renderer->internal; @@ -1579,6 +1593,9 @@ 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); } } @@ -1742,6 +1759,10 @@ 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)) {