From 13a34b383dbdb3926d212396d2e3dd5a2956b30b Mon Sep 17 00:00:00 2001 From: Geoffrey McRae Date: Wed, 5 Aug 2026 18:04:26 +1000 Subject: [PATCH] [client] egl: retain damage until texture is drawable Treat a failed desktop texture bind as an incomplete render so full target restoration remains latched until a frame can actually be drawn. Carry retired damage into the pending record when the drawable texture does not contain it, closing the publication gap between texture slots and their matching damage metadata. --- client/renderers/EGL/desktop.c | 11 ++++++- client/renderers/EGL/desktop.h | 1 + client/renderers/EGL/egl.c | 56 +++++++++++++++++++++++++++++----- 3 files changed, 59 insertions(+), 9 deletions(-) diff --git a/client/renderers/EGL/desktop.c b/client/renderers/EGL/desktop.c index dd214bc3..1c4a2e64 100644 --- a/client/renderers/EGL/desktop.c +++ b/client/renderers/EGL/desktop.c @@ -459,6 +459,7 @@ bool egl_desktopRender(EGL_Desktop * desktop, unsigned int outputWidth, LG_RendererFrameToken damageFrameToken, LG_RendererFrameToken frameTokenLimit, bool * fullFrame, LG_RendererFrameToken * consumedFrameToken, + LG_RendererFrameToken * renderedFrameToken, uint64_t * effectsTime, EGL_Framebuffer * target) { EGL_Texture * tex; @@ -489,6 +490,7 @@ bool egl_desktopRender(EGL_Desktop * desktop, unsigned int outputWidth, const bool textureUpdated = status == EGL_TEX_STATUS_UPDATED; *consumedFrameToken = frameToken; + *renderedFrameToken = LG_RENDERER_FRAME_TOKEN_NONE; *effectsTime = 0; if (unlikely(status != EGL_TEX_STATUS_OK && !textureUpdated)) { @@ -567,7 +569,14 @@ bool egl_desktopRender(EGL_Desktop * desktop, unsigned int outputWidth, egl_resetViewport(desktop->egl); } - egl_textureBind(texture); + const EGL_TexStatus bindStatus = egl_textureBind(texture); + if (unlikely(bindStatus != EGL_TEX_STATUS_OK)) + { + if (bindStatus != EGL_TEX_STATUS_NOTREADY) + DEBUG_ERROR("Failed to bind the desktop texture"); + return false; + } + *renderedFrameToken = tex->frameToken; if (finalSizeX > width || finalSizeY > height) scaleType = EGL_DESKTOP_DOWNSCALE; diff --git a/client/renderers/EGL/desktop.h b/client/renderers/EGL/desktop.h index 093b7c17..56944431 100644 --- a/client/renderers/EGL/desktop.h +++ b/client/renderers/EGL/desktop.h @@ -60,6 +60,7 @@ bool egl_desktopRender(EGL_Desktop * desktop, unsigned int outputWidth, LG_RendererFrameToken damageFrameToken, LG_RendererFrameToken frameTokenLimit, bool * fullFrame, LG_RendererFrameToken * consumedFrameToken, + LG_RendererFrameToken * renderedFrameToken, uint64_t * effectsTime, EGL_Framebuffer * target); void egl_desktopSpiceConfigure(EGL_Desktop * desktop, int width, int height); diff --git a/client/renderers/EGL/egl.c b/client/renderers/EGL/egl.c index 43eda728..2d8e7252 100644 --- a/client/renderers/EGL/egl.c +++ b/client/renderers/EGL/egl.c @@ -1300,6 +1300,31 @@ static int egl_mergeSurfaceDamage(struct Rect * damage, int count) return count; } +static void egl_requeueDesktopDamage( + struct Inst * this, const struct DesktopDamage * damage) +{ + if (damage->count == 0) + return; + + INTERLOCKED_SECTION(this->desktopDamageLock, { + struct DesktopDamage * pending = + this->desktopDamage + this->desktopDamageIdx; + + if (damage->count < 0 || pending->count < 0 || + damage->count + pending->count >= LG_MAX_FRAME_DAMAGE_RECTS) + pending->count = -1; + else + { + memcpy(pending->rects + pending->count, damage->rects, + damage->count * sizeof(FrameDamageRect)); + pending->count += damage->count; + } + + if (pending->frameToken == LG_RENDERER_FRAME_TOKEN_NONE) + pending->frameToken = damage->frameToken; + }); +} + inline static void renderLetterBox(struct Inst * this) { bool hLB = this->destRect.x > 0; @@ -1461,10 +1486,11 @@ static bool egl_render(LG_Renderer * renderer, LG_RendererRotate rotate, bool deferredLogicalCursor = false; const bool haveDesktop = likely( this->destRect.w > 0 && this->destRect.h > 0); - bool desktopRendered = false; - const uint64_t desktopStart = nanotime(); - uint64_t effectsTime = 0; - LG_RendererFrameToken frameToken = LG_RENDERER_FRAME_TOKEN_NONE; + bool desktopRendered = false; + const uint64_t desktopStart = nanotime(); + uint64_t effectsTime = 0; + LG_RendererFrameToken frameToken = LG_RENDERER_FRAME_TOKEN_NONE; + LG_RendererFrameToken renderedFrameToken = LG_RENDERER_FRAME_TOKEN_NONE; timing->setupTime = desktopStart - setupStart; if (haveDesktop) @@ -1474,8 +1500,8 @@ static bool egl_render(LG_Renderer * renderer, LG_RendererRotate rotate, this->translateX, this->translateY, this->scaleX , this->scaleY , this->scaleType , rotate, renderAll ? NULL : accumulated, - desktopDamage->frameToken, frameTokenLimit, &fullFrame, &frameToken, - &effectsTime, + desktopDamage->frameToken, frameTokenLimit, &fullFrame, + &frameToken, &renderedFrameToken, &effectsTime, egl_hdrComposeGetFramebuffer(this->hdrCompose)); } const uint64_t composeStart = nanotime(); @@ -1489,8 +1515,10 @@ static bool egl_render(LG_Renderer * renderer, LG_RendererRotate rotate, else timing->setupTime += composeStart - desktopStart; - timing->frameToken = frameToken; - const bool frameConsumed = frameToken != LG_RENDERER_FRAME_TOKEN_NONE; + const bool frameConsumed = desktopRendered && + frameToken != LG_RENDERER_FRAME_TOKEN_NONE; + timing->frameToken = frameConsumed ? + frameToken : LG_RENDERER_FRAME_TOKEN_NONE; if (haveDesktop) { @@ -1510,6 +1538,18 @@ static bool egl_render(LG_Renderer * renderer, LG_RendererRotate rotate, if (fullFrame) desktopDamage->count = -1; + /* A texture slot can be published before its matching damage and timing + * record. If this render could not consume that slot, retain the damage so + * it is merged into the next drawable frame instead of retiring it. */ + const bool damageRendered = desktopRendered && + (desktopDamage->frameToken == LG_RENDERER_FRAME_TOKEN_NONE || + desktopDamage->frameToken == renderedFrameToken || + ((renderAll || fullFrame) && + renderedFrameToken != LG_RENDERER_FRAME_TOKEN_NONE && + renderedFrameToken > desktopDamage->frameToken)); + if (!damageRendered) + egl_requeueDesktopDamage(this, desktopDamage); + renderLetterBox(this); hasOverlay |=