From d815ef87d52639612e0ccfd456a3fe81908d6186 Mon Sep 17 00:00:00 2001 From: Geoffrey McRae Date: Wed, 5 Aug 2026 10:53:52 +1000 Subject: [PATCH] [client] overlay: restore every splash fade frame Mark the active fade as requiring a fully restored render target. This also covers guest-driven renders between animation ticks so they cannot blend over stale splash content. --- client/include/app.h | 1 + client/include/interface/overlay.h | 4 ++++ client/src/app.c | 21 +++++++++++++++++++++ client/src/main.c | 4 +++- client/src/overlay/splash.c | 16 +++++++++++----- 5 files changed, 40 insertions(+), 6 deletions(-) diff --git a/client/include/app.h b/client/include/app.h index 3e29c33a..b0b5d003 100644 --- a/client/include/app.h +++ b/client/include/app.h @@ -114,6 +114,7 @@ void app_registerOverlay(const struct LG_OverlayOps * ops, const void * params); void app_initOverlays(void); void app_setOverlay(bool enable); bool app_overlayNeedsRender(void); +bool app_overlayNeedsFullRender(void); /** * render the overlay * returns: diff --git a/client/include/interface/overlay.h b/client/include/interface/overlay.h index 93008d92..32c66657 100644 --- a/client/include/interface/overlay.h +++ b/client/include/interface/overlay.h @@ -45,6 +45,10 @@ struct LG_OverlayOps * optional, if omitted assumes false */ bool (*needs_render)(void * udata, bool interactive); + /* return true if the overlay must be drawn over a fully restored frame + * optional, if omitted assumes false */ + bool (*needs_full_render)(void * udata); + /* return true if the overlay currently requires overlay mode * optional, if omitted assumes false */ bool (*needs_overlay)(void * udata); diff --git a/client/src/app.c b/client/src/app.c index ef1d11e0..e7f34384 100644 --- a/client/src/app.c +++ b/client/src/app.c @@ -1030,6 +1030,27 @@ bool app_overlayNeedsRender(void) return result; } +bool app_overlayNeedsFullRender(void) +{ + bool result = false; + struct Overlay * overlay; + ll_lock(g_state.overlays); + ll_forEachNL(g_state.overlays, item, overlay) + { + if (!overlay->ops->needs_full_render) + continue; + + if (overlay->ops->needs_full_render(overlay->udata)) + { + result = true; + break; + } + } + ll_unlock(g_state.overlays); + + return result; +} + int app_renderOverlay(struct Rect * rects, int maxRects) { int totalRects = 0; diff --git a/client/src/main.c b/client/src/main.c index e90be43e..3e8bc837 100644 --- a/client/src/main.c +++ b/client/src/main.c @@ -695,7 +695,9 @@ static int renderThread(void * unused) atomic_compare_exchange_weak(&g_state.lgrResize, &resize, 0); } - const bool invalidate = atomic_exchange(&g_state.invalidateWindow, false); + const bool invalidate = + atomic_exchange(&g_state.invalidateWindow, false) || + app_overlayNeedsFullRender(); const LG_RendererFrameToken frameTokenLimit = frameTimingQueuedToken(); const uint64_t prepareStart = nanotime(); diff --git a/client/src/overlay/splash.c b/client/src/overlay/splash.c index 1a6f0c51..a90c84da 100644 --- a/client/src/overlay/splash.c +++ b/client/src/overlay/splash.c @@ -247,13 +247,19 @@ static bool splash_tick(void * udata, unsigned long long tickCount) return false; } +static bool splash_needsFullRender(void * udata) +{ + return !l_show && !l_fadeDone; +} + struct LG_OverlayOps LGOverlaySplash = { - .name = "splash", - .init = splash_init, - .free = splash_free, - .render = splash_render, - .tick = splash_tick, + .name = "splash", + .init = splash_init, + .free = splash_free, + .needs_full_render = splash_needsFullRender, + .render = splash_render, + .tick = splash_tick, }; void overlaySplash_show(bool show)