From ef4ce8cec5fb130136ef575e544100ea68193bee Mon Sep 17 00:00:00 2001 From: Frank Praznik Date: Sun, 25 Jun 2023 10:18:58 -0400 Subject: [PATCH] test: Add flag to suspend drawing when occluded Add the flag "--suspend-when-occluded" to testgl, testgles2, and testsprite, which, when used, will suspend rendering and throttle the event loop when the occlusion flag is set on the window. --- test/testgl.c | 16 +++++++-- test/testgles2.c | 84 ++++++++++++++++++++++++++++++++++++++--------- test/testsprite.c | 15 ++++++++- 3 files changed, 97 insertions(+), 18 deletions(-) diff --git a/test/testgl.c b/test/testgl.c index 8ceb50932..0f592812b 100644 --- a/test/testgl.c +++ b/test/testgl.c @@ -31,6 +31,7 @@ typedef struct GL_Context static SDLTest_CommonState *state; static SDL_GLContext context; static GL_Context ctx; +static SDL_bool suspend_when_occluded; static int LoadContext(GL_Context *data) { @@ -237,12 +238,15 @@ int main(int argc, char *argv[]) } else if (SDL_strcasecmp(argv[i], "--accel") == 0 && i + 1 < argc) { accel = SDL_atoi(argv[i + 1]); consumed = 2; + } else if(SDL_strcasecmp(argv[i], "--suspend-when-occluded") == 0) { + suspend_when_occluded = SDL_TRUE; + consumed = 1; } else { consumed = -1; } } if (consumed < 0) { - static const char *options[] = { "[--fsaa n]", "[--accel n]", NULL }; + static const char *options[] = { "[--fsaa n]", "[--accel n]", "[--suspend-when-occluded]", NULL }; SDLTest_CommonLogUsage(state, argv[0], options); quit(1); } @@ -386,6 +390,7 @@ int main(int argc, char *argv[]) done = 0; while (!done) { SDL_bool update_swap_interval = SDL_FALSE; + int active_windows = 0; /* Check for events */ ++frames; @@ -408,9 +413,11 @@ int main(int argc, char *argv[]) for (i = 0; i < state->num_windows; ++i) { int w, h; - if (state->windows[i] == NULL) { + if (state->windows[i] == NULL || + (suspend_when_occluded && (SDL_GetWindowFlags(state->windows[i]) & SDL_WINDOW_OCCLUDED))) { continue; } + ++active_windows; SDL_GL_MakeCurrent(state->windows[i], context); if (update_swap_interval) { SDL_GL_SetSwapInterval(swap_interval); @@ -420,6 +427,11 @@ int main(int argc, char *argv[]) Render(); SDL_GL_SwapWindow(state->windows[i]); } + + /* If all windows are occluded, throttle event polling to 15hz. */ + if (!active_windows) { + SDL_DelayNS(SDL_NS_PER_SECOND / 15); + } } /* Print out some timing information */ diff --git a/test/testgles2.c b/test/testgles2.c index 05c49847b..6975adbaf 100644 --- a/test/testgles2.c +++ b/test/testgles2.c @@ -46,9 +46,18 @@ typedef struct shader_data GLuint color_buffer; } shader_data; +typedef enum wait_state +{ + WAIT_STATE_GO = 0, + WAIT_STATE_ENTER_SEM, + WAIT_STATE_WAITING_ON_SEM, +} wait_state; + typedef struct thread_data { SDL_Thread *thread; + SDL_Semaphore *suspend_sem; + SDL_AtomicInt suspended; int done; int index; } thread_data; @@ -56,6 +65,7 @@ typedef struct thread_data static SDLTest_CommonState *state; static SDL_GLContext *context = NULL; static int depth = 16; +static SDL_bool suspend_when_occluded; static GLES2_Context ctx; static int LoadContext(GLES2_Context *data) @@ -557,6 +567,9 @@ render_thread_fn(void *render_ctx) thread_data *thread = render_ctx; while (!done && !thread->done && state->windows[thread->index]) { + if (SDL_AtomicCAS(&thread->suspended, WAIT_STATE_ENTER_SEM, WAIT_STATE_WAITING_ON_SEM)) { + SDL_WaitSemaphore(thread->suspend_sem); + } render_window(thread->index); } @@ -564,29 +577,54 @@ render_thread_fn(void *render_ctx) return 0; } +static thread_data *GetThreadDataForWindow(SDL_WindowID id) +{ + int i; + SDL_Window *window = SDL_GetWindowFromID(id); + if (window) { + for (i = 0; i < state->num_windows; ++i) { + if (window == state->windows[i]) { + return &threads[i]; + } + } + } + return NULL; +} + static void loop_threaded(void) { SDL_Event event; - int i; + thread_data *tdata; /* Wait for events */ while (SDL_WaitEvent(&event) && !done) { - if (event.type == SDL_EVENT_WINDOW_CLOSE_REQUESTED) { - SDL_Window *window = SDL_GetWindowFromID(event.window.windowID); - if (window) { - for (i = 0; i < state->num_windows; ++i) { - if (window == state->windows[i]) { - /* Stop the render thread when the window is closed */ - threads[i].done = 1; - if (threads[i].thread) { - SDL_WaitThread(threads[i].thread, NULL); - threads[i].thread = NULL; - } - break; - } + if (suspend_when_occluded && event.type == SDL_EVENT_WINDOW_OCCLUDED) { + tdata = GetThreadDataForWindow(event.window.windowID); + if (tdata) { + SDL_AtomicCAS(&tdata->suspended, WAIT_STATE_GO, WAIT_STATE_ENTER_SEM); + } + } else if (suspend_when_occluded && event.type == SDL_EVENT_WINDOW_EXPOSED) { + tdata = GetThreadDataForWindow(event.window.windowID); + if (tdata) { + if (SDL_AtomicSet(&tdata->suspended, WAIT_STATE_GO) == WAIT_STATE_WAITING_ON_SEM) { + SDL_PostSemaphore(tdata->suspend_sem); } } + } else if (event.type == SDL_EVENT_WINDOW_CLOSE_REQUESTED) { + tdata = GetThreadDataForWindow(event.window.windowID); + if (tdata) { + /* Stop the render thread when the window is closed */ + tdata->done = 1; + if (tdata->thread) { + SDL_AtomicSet(&tdata->suspended, WAIT_STATE_GO); + SDL_PostSemaphore(tdata->suspend_sem); + SDL_WaitThread(tdata->thread, NULL); + tdata->thread = NULL; + SDL_DestroySemaphore(tdata->suspend_sem); + } + break; + } } SDLTest_CommonEvent(state, &event, &done); } @@ -598,6 +636,7 @@ loop(void) { SDL_Event event; int i; + int active_windows = 0; /* Check for events */ while (SDL_PollEvent(&event) && !done) { @@ -605,6 +644,11 @@ loop(void) } if (!done) { for (i = 0; i < state->num_windows; ++i) { + if (state->windows[i] == NULL || + (suspend_when_occluded && (SDL_GetWindowFlags(state->windows[i]) & SDL_WINDOW_OCCLUDED))) { + continue; + } + ++active_windows; render_window(i); } } @@ -613,6 +657,11 @@ loop(void) emscripten_cancel_main_loop(); } #endif + + /* If all windows are occluded, throttle event polling to 15hz. */ + if (!done && !active_windows) { + SDL_DelayNS(SDL_NS_PER_SECOND / 15); + } } int main(int argc, char *argv[]) @@ -649,6 +698,9 @@ int main(int argc, char *argv[]) } else if (SDL_strcasecmp(argv[i], "--threaded") == 0) { ++threaded; consumed = 1; + } else if(SDL_strcasecmp(argv[i], "--suspend-when-occluded") == 0) { + suspend_when_occluded = SDL_TRUE; + consumed = 1; } else if (SDL_strcasecmp(argv[i], "--zdepth") == 0) { i++; if (!argv[i]) { @@ -667,7 +719,7 @@ int main(int argc, char *argv[]) } } if (consumed < 0) { - static const char *options[] = { "[--fsaa]", "[--accel]", "[--zdepth %d]", "[--threaded]", NULL }; + static const char *options[] = { "[--fsaa]", "[--accel]", "[--zdepth %d]", "[--threaded]", "[--suspend-when-occluded]",NULL }; SDLTest_CommonLogUsage(state, argv[0], options); quit(1); } @@ -869,6 +921,8 @@ int main(int argc, char *argv[]) /* Start a render thread for each window */ for (i = 0; i < state->num_windows; ++i) { threads[i].index = i; + SDL_AtomicSet(&threads[i].suspended, 0); + threads[i].suspend_sem = SDL_CreateSemaphore(0); threads[i].thread = SDL_CreateThread(render_thread_fn, "RenderThread", &threads[i]); } diff --git a/test/testsprite.c b/test/testsprite.c index 68b3b7b2d..9afa6f0da 100644 --- a/test/testsprite.c +++ b/test/testsprite.c @@ -42,6 +42,7 @@ static Uint64 next_fps_check; static Uint32 frames; static const int fps_check_delay = 5000; static int use_rendergeometry = 0; +static SDL_bool suspend_when_occluded; /* Number of iterations to move sprites - used for visual tests. */ /* -1: infinite random moves (default); >=0: enables N deterministic moves */ @@ -398,6 +399,7 @@ static void loop(void) { Uint64 now; int i; + int active_windows = 0; SDL_Event event; /* Check for events */ @@ -405,9 +407,11 @@ static void loop(void) SDLTest_CommonEvent(state, &event, &done); } for (i = 0; i < state->num_windows; ++i) { - if (state->windows[i] == NULL) { + if (state->windows[i] == NULL || + (suspend_when_occluded && (SDL_GetWindowFlags(state->windows[i]) & SDL_WINDOW_OCCLUDED))) { continue; } + ++active_windows; MoveSprites(state->renderers[i], sprites[i]); } #ifdef __EMSCRIPTEN__ @@ -416,6 +420,11 @@ static void loop(void) } #endif + /* If all windows are occluded, throttle the event polling to 15hz. */ + if (!done && !active_windows) { + SDL_DelayNS(SDL_NS_PER_SECOND / 15); + } + frames++; now = SDL_GetTicks(); if (now >= next_fps_check) { @@ -485,6 +494,9 @@ int main(int argc, char *argv[]) } else if (SDL_strcasecmp(argv[i], "--cyclealpha") == 0) { cycle_alpha = SDL_TRUE; consumed = 1; + } else if(SDL_strcasecmp(argv[i], "--suspend-when-occluded") == 0) { + suspend_when_occluded = SDL_TRUE; + consumed = 1; } else if (SDL_strcasecmp(argv[i], "--use-rendergeometry") == 0) { if (argv[i + 1]) { if (SDL_strcasecmp(argv[i + 1], "mode1") == 0) { @@ -512,6 +524,7 @@ int main(int argc, char *argv[]) "[--blend none|blend|add|mod|mul|sub]", "[--cyclecolor]", "[--cyclealpha]", + "[--suspend-when-occluded]", "[--iterations N]", "[--use-rendergeometry mode1|mode2]", "[num_sprites]",