gdk: Update Suspend/Resume best practices.

Updated testgdk to demonstrate correct handling of suspend/resume and the new Render APIs, and updated the docs to explain the correct usage of these GDK functions.
This commit is contained in:
Caleb Cornett
2026-03-07 07:16:36 -05:00
committed by Ethan Lee
parent ee5c5cf755
commit 12a435e11d
4 changed files with 61 additions and 20 deletions

View File

@@ -31,6 +31,8 @@ extern "C" {
#define NUM_SPRITES 100 #define NUM_SPRITES 100
#define MAX_SPEED 1 #define MAX_SPEED 1
#define SUSPEND_CODE 0
#define RESUME_CODE 1
static SDLTest_CommonState *state; static SDLTest_CommonState *state;
static int num_sprites; static int num_sprites;
@@ -291,7 +293,7 @@ static void DrawSprites(SDL_Renderer * renderer, SDL_Texture * sprite)
SDL_RenderPresent(renderer); SDL_RenderPresent(renderer);
} }
static void update() static void update(bool *suppressdraw)
{ {
SDL_Event event; SDL_Event event;
@@ -305,6 +307,25 @@ static void update()
if (event.type != SDL_EVENT_KEY_DOWN) { if (event.type != SDL_EVENT_KEY_DOWN) {
SDLTest_CommonEvent(state, &event, &done); SDLTest_CommonEvent(state, &event, &done);
} }
if (event.type == SDL_EVENT_USER) {
if (event.user.code == SUSPEND_CODE) {
for (int i = 0; i < state->num_windows; ++i) {
if (state->windows[i] != NULL) {
SDL_GDKSuspendRenderer(state->renderers[i]);
}
}
*suppressdraw = true;
SDL_GDKSuspendComplete();
} else if (event.user.code == RESUME_CODE) {
for (int i = 0; i < state->num_windows; ++i) {
if (state->windows[i] != NULL) {
SDL_GDKResumeRenderer(state->renderers[i]);
}
}
*suppressdraw = false;
}
}
#else #else
SDLTest_CommonEvent(state, &event, &done); SDLTest_CommonEvent(state, &event, &done);
#endif #endif
@@ -316,24 +337,33 @@ static void draw()
{ {
int i; int i;
for (i = 0; i < state->num_windows; ++i) { for (i = 0; i < state->num_windows; ++i) {
if (state->windows[i] == NULL) { if (state->windows[i] != NULL) {
continue;
}
DrawSprites(state->renderers[i], sprites[i]); DrawSprites(state->renderers[i], sprites[i]);
} }
}
} }
static bool SDLCALL GDKEventWatch(void* userdata, SDL_Event* event) static bool SDLCALL GDKEventWatch(void* userdata, SDL_Event* event)
{ {
bool *suppressdraw = (bool *)userdata; /* This callback may be on a different thread, so we'll
SDL_assert(suppressdraw != NULL); * push these events as USER events so they appear
* in the main thread's event loop.
*
* That allows us to cancel drawing before/after we finish
* drawing a frame, rather than mid-draw (which can crash).
*/
if (event->type == SDL_EVENT_DID_ENTER_BACKGROUND) { if (event->type == SDL_EVENT_DID_ENTER_BACKGROUND) {
*suppressdraw = true; SDL_Event evt;
SDL_GDKSuspendComplete(); evt.type = SDL_EVENT_USER;
evt.user.code = 0;
SDL_PushEvent(&evt);
} else if (event->type == SDL_EVENT_WILL_ENTER_FOREGROUND) { } else if (event->type == SDL_EVENT_WILL_ENTER_FOREGROUND) {
*suppressdraw = false; SDL_Event evt;
evt.type = SDL_EVENT_USER;
evt.user.code = 1;
SDL_PushEvent(&evt);
} }
return true; return false;
} }
int main(int argc, char *argv[]) int main(int argc, char *argv[])
@@ -408,8 +438,8 @@ int main(int argc, char *argv[])
quit(2); quit(2);
} }
/* By this point the renderers are made, so we can now add this watcher */ /* Set up the lifecycle event watcher */
SDL_AddEventWatch(GDKEventWatch, &suppressdraw); SDL_AddEventWatch(GDKEventWatch, NULL);
/* Create the windows, initialize the renderers, and load the textures */ /* Create the windows, initialize the renderers, and load the textures */
sprites = sprites =
@@ -462,7 +492,7 @@ int main(int argc, char *argv[])
AddUserSilent(); AddUserSilent();
while (!done) { while (!done) {
update(); update(&suppressdraw);
if (!suppressdraw) { if (!suppressdraw) {
draw(); draw();
} }

View File

@@ -4581,12 +4581,14 @@ extern SDL_DECLSPEC SDL_GPUTextureFormat SDLCALL SDL_GetGPUTextureFormatFromPixe
#ifdef SDL_PLATFORM_GDK #ifdef SDL_PLATFORM_GDK
/** /**
* Call this to suspend GPU operation on Xbox when you receive the * Call this to suspend GPU operation on Xbox after receiving the
* SDL_EVENT_DID_ENTER_BACKGROUND event. * SDL_EVENT_DID_ENTER_BACKGROUND event.
* *
* Do NOT call any SDL_GPU functions after calling this function! This must * Do NOT call any SDL_GPU functions after calling this function! This must
* also be called before calling SDL_GDKSuspendComplete. * also be called before calling SDL_GDKSuspendComplete.
* *
* This function MUST be called from the application's render thread.
*
* \param device a GPU context. * \param device a GPU context.
* *
* \since This function is available since SDL 3.2.0. * \since This function is available since SDL 3.2.0.
@@ -4596,12 +4598,14 @@ extern SDL_DECLSPEC SDL_GPUTextureFormat SDLCALL SDL_GetGPUTextureFormatFromPixe
extern SDL_DECLSPEC void SDLCALL SDL_GDKSuspendGPU(SDL_GPUDevice *device); extern SDL_DECLSPEC void SDLCALL SDL_GDKSuspendGPU(SDL_GPUDevice *device);
/** /**
* Call this to resume GPU operation on Xbox when you receive the * Call this to resume GPU operation on Xbox after receiving the
* SDL_EVENT_WILL_ENTER_FOREGROUND event. * SDL_EVENT_WILL_ENTER_FOREGROUND event.
* *
* When resuming, this function MUST be called before calling any other * When resuming, this function MUST be called before calling any other
* SDL_GPU functions. * SDL_GPU functions.
* *
* This function MUST be called from the application's render thread.
*
* \param device a GPU context. * \param device a GPU context.
* *
* \since This function is available since SDL 3.2.0. * \since This function is available since SDL 3.2.0.

View File

@@ -664,8 +664,11 @@ extern SDL_DECLSPEC void SDLCALL SDL_UnregisterApp(void);
/** /**
* Callback from the application to let the suspend continue. * Callback from the application to let the suspend continue.
* *
* This should be called from an event watch in response to an * This should be called in response to an `SDL_EVENT_DID_ENTER_BACKGROUND` event,
* `SDL_EVENT_DID_ENTER_BACKGROUND` event. * which can be detected via event watch. However, do NOT call this function
* directly from within an event watch callback. Instead, wait until the app has
* suppressed all rendering operations, then call this from the application
* render thread.
* *
* When using SDL_Render, this should be called after calling SDL_GDKSuspendRenderer. * When using SDL_Render, this should be called after calling SDL_GDKSuspendRenderer.
* *

View File

@@ -3085,12 +3085,14 @@ extern SDL_DECLSPEC void SDLCALL SDL_DestroyGPURenderState(SDL_GPURenderState *s
#ifdef SDL_PLATFORM_GDK #ifdef SDL_PLATFORM_GDK
/** /**
* Call this to suspend Render operations on Xbox when you receive the * Call this to suspend Render operations on Xbox after receiving the
* SDL_EVENT_DID_ENTER_BACKGROUND event. * SDL_EVENT_DID_ENTER_BACKGROUND event.
* *
* Do NOT call any SDL_Render functions after calling this function! This must * Do NOT call any SDL_Render functions after calling this function! This must
* also be called before calling SDL_GDKSuspendComplete. * also be called before calling SDL_GDKSuspendComplete.
* *
* This function MUST be called on the application's render thread.
*
* \param renderer the renderer which should suspend operation * \param renderer the renderer which should suspend operation
* *
* \since This function is available since SDL 3.6.0. * \since This function is available since SDL 3.6.0.
@@ -3100,12 +3102,14 @@ extern SDL_DECLSPEC void SDLCALL SDL_DestroyGPURenderState(SDL_GPURenderState *s
extern SDL_DECLSPEC void SDLCALL SDL_GDKSuspendRenderer(SDL_Renderer *renderer); extern SDL_DECLSPEC void SDLCALL SDL_GDKSuspendRenderer(SDL_Renderer *renderer);
/** /**
* Call this to resume Render operations on Xbox when you receive the * Call this to resume Render operations on Xbox after receiving the
* SDL_EVENT_WILL_ENTER_FOREGROUND event. * SDL_EVENT_WILL_ENTER_FOREGROUND event.
* *
* When resuming, this function MUST be called before calling any other * When resuming, this function MUST be called before calling any other
* SDL_Render functions. * SDL_Render functions.
* *
* This function MUST be called on the application's render thread.
*
* \param renderer the renderer which should resume operation * \param renderer the renderer which should resume operation
* *
* \since This function is available since SDL 3.6.0. * \since This function is available since SDL 3.6.0.