mirror of
https://github.com/libsdl-org/SDL.git
synced 2026-10-28 09:34:46 +08:00
gdk: Update suspend/resume docs and testgdk with new best practices
This commit is contained in:
1 parent
f035301ff8
commit
d25e3b8d69
2 files changed
+75
-45
No files matched your search
@@ -31,8 +31,6 @@ extern "C" {
|
||||
|
||||
#define NUM_SPRITES 100
|
||||
#define MAX_SPEED 1
|
||||
#define SUSPEND_CODE 0
|
||||
#define RESUME_CODE 1
|
||||
|
||||
static SDLTest_CommonState *state;
|
||||
static int num_sprites;
|
||||
@@ -44,6 +42,8 @@ static int current_alpha = 0;
|
||||
static int current_color = 0;
|
||||
static int sprite_w, sprite_h;
|
||||
static SDL_BlendMode blendMode = SDL_BLENDMODE_BLEND;
|
||||
static SDL_AtomicInt suspend_requested;
|
||||
static bool suspended;
|
||||
|
||||
int done;
|
||||
|
||||
@@ -293,7 +293,7 @@ static void DrawSprites(SDL_Renderer * renderer, SDL_Texture * sprite)
|
||||
SDL_RenderPresent(renderer);
|
||||
}
|
||||
|
||||
static void update(bool *suppressdraw)
|
||||
static void update()
|
||||
{
|
||||
SDL_Event event;
|
||||
|
||||
@@ -307,29 +307,33 @@ static void update(bool *suppressdraw)
|
||||
if (event.type != SDL_EVENT_KEY_DOWN) {
|
||||
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
|
||||
SDLTest_CommonEvent(state, &event, &done);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES)
|
||||
/* Handle suspend and resume events */
|
||||
if (SDL_GetAtomicInt(&suspend_requested)) {
|
||||
if (!suspended) {
|
||||
for (int i = 0; i < state->num_windows; ++i) {
|
||||
if (state->windows[i] != NULL) {
|
||||
SDL_GDKSuspendRenderer(state->renderers[i]);
|
||||
}
|
||||
}
|
||||
suspended = true;
|
||||
SDL_GDKSuspendComplete();
|
||||
}
|
||||
} else if (suspended) {
|
||||
for (int i = 0; i < state->num_windows; ++i) {
|
||||
if (state->windows[i] != NULL) {
|
||||
SDL_GDKResumeRenderer(state->renderers[i]);
|
||||
}
|
||||
}
|
||||
suspended = false;
|
||||
}
|
||||
#endif
|
||||
|
||||
fillerup();
|
||||
}
|
||||
|
||||
@@ -346,22 +350,15 @@ static void draw()
|
||||
static bool SDLCALL GDKEventWatch(void* userdata, SDL_Event* event)
|
||||
{
|
||||
/* This callback may be on a different thread, so we'll
|
||||
* push these events as USER events so they appear
|
||||
* in the main thread's event loop.
|
||||
* set an atomic flag that can be read in our main 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) {
|
||||
SDL_Event evt;
|
||||
evt.type = SDL_EVENT_USER;
|
||||
evt.user.code = 0;
|
||||
SDL_PushEvent(&evt);
|
||||
SDL_SetAtomicInt(&suspend_requested, 1);
|
||||
} else if (event->type == SDL_EVENT_WILL_ENTER_FOREGROUND) {
|
||||
SDL_Event evt;
|
||||
evt.type = SDL_EVENT_USER;
|
||||
evt.user.code = 1;
|
||||
SDL_PushEvent(&evt);
|
||||
SDL_SetAtomicInt(&suspend_requested, 0);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -371,7 +368,6 @@ int main(int argc, char *argv[])
|
||||
int i;
|
||||
const char *icon = "icon.png";
|
||||
char *soundname = NULL;
|
||||
bool suppressdraw = false;
|
||||
|
||||
/* Initialize parameters */
|
||||
num_sprites = NUM_SPRITES;
|
||||
@@ -492,8 +488,8 @@ int main(int argc, char *argv[])
|
||||
AddUserSilent();
|
||||
|
||||
while (!done) {
|
||||
update(&suppressdraw);
|
||||
if (!suppressdraw) {
|
||||
update();
|
||||
if (!suspended) {
|
||||
draw();
|
||||
}
|
||||
}
|
||||
|
||||
+44
-10
@@ -168,22 +168,56 @@ Please read the official GDK documentation for all the details, but here's what
|
||||
- If any GPU API call happens in between suspension and resuming, the application will crash or hang.
|
||||
|
||||
Unfortunately it's impossible for SDL to know for certain when your application is ready for suspension, so it cannot handle these events internally.
|
||||
Instead, SDL provides a few APIs you must use to handle these events yourself.
|
||||
Instead, SDL provides a few `SDL_GDK*` APIs you must use to handle these events yourself.
|
||||
|
||||
On startup, your application should call `SDL_AddEventWatch` to register a callback function which will listen for the `SDL_EVENT_DID_ENTER_BACKGROUND` and `SDL_EVENT_WILL_ENTER_FOREGROUND` events.
|
||||
This callback function will be called from a background OS thread, so when one of these events occurs, you should notify your render thread about it rather than responding directly in the callback.
|
||||
A simple strategy for this is to make your callback function post custom `SDL_EVENT_USER` events that your main loop's event processor can respond to.
|
||||
A simple thread-safe strategy for this is to add an `SDL_AtomicInt` to act as a flag for whether the OS has requested suspension, like so:
|
||||
|
||||
When your render thread receives your notification of the `SDL_EVENT_DID_ENTER_BACKGROUND` event, it should do the following:
|
||||
- Set a flag to suppress the entire render loop until the application resumes.
|
||||
- Call `SDL_GDKSuspendRenderer` if you are using the Render API, or call `SDL_GDKSuspendGPU` if you are using the GPU API.
|
||||
- Finally, regardless of what graphics API you are using, call `SDL_GDKSuspendComplete`.
|
||||
```c
|
||||
static SDL_AtomicInt suspend_requested; // has the OS requested suspension?
|
||||
static bool suspended; // have we responded and entered into a "suspended" state?
|
||||
|
||||
When your render thread receives your notification of an `SDL_EVENT_WILL_ENTER_FOREGROUND` event, it should do the following:
|
||||
- Call `SDL_GDKResumeRenderer` if you are using the Render API, or call `SDL_GDKResumeGPU` if you are using the GPU API.
|
||||
- Unset the flag suppressing your render loop to allow drawing again.
|
||||
static bool SDLCALL GDKEventWatch(void *userdata, SDL_Event *event)
|
||||
{
|
||||
if (event->type == SDL_EVENT_DID_ENTER_BACKGROUND) {
|
||||
SDL_SetAtomicInt(&suspend_requested, 1); // suspend
|
||||
} else if (event->type == SDL_EVENT_WILL_ENTER_FOREGROUND) {
|
||||
SDL_SetAtomicInt(&suspend_requested, 0); // resume
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
An example of this flow can be found in the testgdk project.
|
||||
SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[])
|
||||
{
|
||||
// during startup...
|
||||
SDL_AddEventWatch(GDKEventWatch, NULL);
|
||||
}
|
||||
|
||||
SDL_AppResult SDL_AppIterate(void *appstate)
|
||||
{
|
||||
// check for suspend/resume state changes
|
||||
if (SDL_GetAtomicInt(&suspend_requested)) {
|
||||
if (!suspended) {
|
||||
// call SDL_GDKSuspendRenderer() if you're using the Render API
|
||||
// call SDL_GDKSuspendGPU() if you're using the GPU API
|
||||
suspended = true;
|
||||
SDL_GDKSuspendComplete(); // this call is always required!
|
||||
}
|
||||
} else if (suspended) {
|
||||
// call SDL_GDKResumeRenderer() if you're using the Render API
|
||||
// call SDL_GDKResumeGPU() if you're using the GPU API
|
||||
suspended = false;
|
||||
}
|
||||
|
||||
// skip drawing if the app is suspended
|
||||
if (!suspended) {
|
||||
draw();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
An example of this flow with a traditional SDL main function can be found in the testgdk project.
|
||||
|
||||
Troubleshooting
|
||||
---------------
|
||||
|
||||
Reference in new issue
Block a user