From 02751b10dfc86574ff9ed89554620cbef5b308c5 Mon Sep 17 00:00:00 2001 From: Geoffrey McRae Date: Sun, 19 Jul 2026 06:33:59 +1000 Subject: [PATCH] [client] egl: fix SPICE video rendering to a HDR surface If the last state had HDR enabled, the spice video surface was being rendered as HDR even though the SPICE video content is SDR. This corrects this. --- client/renderers/EGL/desktop.c | 5 ++- client/renderers/EGL/egl.c | 4 +- client/src/main.c | 21 +--------- client/src/render_queue.c | 76 +++++++++++++++++++++++++++++++++- client/src/render_queue.h | 12 ++++++ 5 files changed, 93 insertions(+), 25 deletions(-) diff --git a/client/renderers/EGL/desktop.c b/client/renderers/EGL/desktop.c index f43d16b5..e2c9d4ae 100644 --- a/client/renderers/EGL/desktop.c +++ b/client/renderers/EGL/desktop.c @@ -464,12 +464,13 @@ bool egl_desktopRender(EGL_Desktop * desktop, unsigned int outputWidth, egl_desktopRectsUpdate(desktop->mesh, rects, width, height); *fullFrame = false; + const bool hdr = desktop->hdr && !desktop->useSpice; const bool processFrame = atomic_exchange(&desktop->processFrame, false) || egl_postProcessConfigModified(desktop->pp); if (processFrame && egl_postProcessRun(desktop->pp, tex, desktop->mesh, width, height, outputWidth, outputHeight, dma, - desktop->hdr && desktop->hdrPQ) && + hdr && desktop->hdrPQ) && egl_postProcessNeedsFullFrame(desktop->pp)) { /* The filter output may have changed everywhere, but this only applies to @@ -529,7 +530,7 @@ bool egl_desktopRender(EGL_Desktop * desktop, unsigned int outputWidth, egl_uniformMatrix3x2fv(shader->uTransform , 1, GL_FALSE, desktop->matrix); egl_uniform1f (shader->uNVGain , desktop->nvGain); egl_uniform1i (shader->uCBMode , desktop->cbMode); - egl_uniform1i (shader->uIsHDR , desktop->hdr); + egl_uniform1i (shader->uIsHDR , hdr); egl_uniform1i (shader->uMapHDRtoSDR, desktop->mapHDRtoSDR && !desktop->nativeHDR); egl_uniform1f (shader->uMapHDRGain , mapHDRGain); egl_uniform1i (shader->uMapHDRPQ , desktop->hdrPQ); diff --git a/client/renderers/EGL/egl.c b/client/renderers/EGL/egl.c index 1a1cf2dc..1150b548 100644 --- a/client/renderers/EGL/egl.c +++ b/client/renderers/EGL/egl.c @@ -619,8 +619,8 @@ static bool egl_updateHDRState(struct Inst * this, bool force) app_getProp(LG_DS_NATIVE_HDR, &nativeHDR); const bool surfaceCompatible = this->format.hdrPQ ? this->surfaceSupportsPQ : this->surfaceSupportsSCRGB; - const bool useNativeHDR = this->format.hdr && surfaceCompatible && nativeHDR && - !app_getHDRDescFailed(); + const bool useNativeHDR = !this->showSpice && this->format.hdr && + surfaceCompatible && nativeHDR && !app_getHDRDescFailed(); if (!force && this->nativeHDR == useNativeHDR) return false; diff --git a/client/src/main.c b/client/src/main.c index 5d211f0a..15d05ee3 100644 --- a/client/src/main.c +++ b/client/src/main.c @@ -778,26 +778,7 @@ int main_frameThread(void * unused) !g_state.lgr->ops.supports || RENDERER(supports, lgrFormat.hdrPQ ? LG_SUPPORTS_HDR_PQ : LG_SUPPORTS_HDR_SCRGB); LG_UNLOCK(g_state.lgrLock); - - if (g_state.ds->setHDRImageDescription) - { - if (lgrFormat.hdr && !rendererSupportsNativeHDR) - { - LG_RendererFormat sdrSurfaceFormat = lgrFormat; - sdrSurfaceFormat.hdr = false; - g_state.ds->setHDRImageDescription(&sdrSurfaceFormat); - DEBUG_WARN("Renderer surface cannot represent the native HDR " - "encoding; using software HDR mapping"); - atomic_store(&g_state.hdrDescFailed, true); - } - else if (!g_state.ds->setHDRImageDescription(&lgrFormat)) - { - DEBUG_WARN("Display server failed to apply HDR image description"); - atomic_store(&g_state.hdrDescFailed, true); - } - else - atomic_store(&g_state.hdrDescFailed, false); - } + renderQueue_surfaceFormat(lgrFormat, rendererSupportsNativeHDR); g_state.srcSize.x = lgrFormat.screenWidth; g_state.srcSize.y = lgrFormat.screenHeight; diff --git a/client/src/render_queue.c b/client/src/render_queue.c index 37b46d8a..fd734d29 100644 --- a/client/src/render_queue.c +++ b/client/src/render_queue.c @@ -22,15 +22,67 @@ #include +#include "common/debug.h" #include "common/ll.h" #include "main.h" #include "overlays.h" struct ll * l_renderQueue = NULL; +static bool l_showSpice; +static bool l_surfaceFormatValid; +static bool l_rendererSupportsNativeHDR; +static LG_RendererFormat l_surfaceFormat; + +static void updateSurfaceFormat(void) +{ + if (!g_state.ds->setHDRImageDescription) + return; + + LG_RendererFormat format = {}; + if (l_surfaceFormatValid) + format = l_surfaceFormat; + + if (l_showSpice) + { + // The SPICE display is always 8-bit SDR, regardless of the last format + // received from the Looking Glass host. + format.hdr = false; + format.hdrPQ = false; + format.hdrMetadata = false; + g_state.ds->setHDRImageDescription(&format); + atomic_store(&g_state.hdrDescFailed, false); + return; + } + + if (!l_surfaceFormatValid) + return; + + if (format.hdr && !l_rendererSupportsNativeHDR) + { + format.hdr = false; + format.hdrPQ = false; + format.hdrMetadata = false; + g_state.ds->setHDRImageDescription(&format); + DEBUG_WARN("Renderer surface cannot represent the native HDR encoding; " + "using software HDR mapping"); + atomic_store(&g_state.hdrDescFailed, true); + } + else if (!g_state.ds->setHDRImageDescription(&format)) + { + DEBUG_WARN("Display server failed to apply HDR image description"); + atomic_store(&g_state.hdrDescFailed, true); + } + else + atomic_store(&g_state.hdrDescFailed, false); +} void renderQueue_init(void) { - l_renderQueue = ll_new(); + l_renderQueue = ll_new(); + l_showSpice = false; + l_surfaceFormatValid = false; + l_rendererSupportsNativeHDR = false; + memset(&l_surfaceFormat, 0, sizeof(l_surfaceFormat)); } void renderQueue_free(void) @@ -103,6 +155,18 @@ void renderQueue_spiceShow(bool show) app_invalidateWindow(true); } +void renderQueue_surfaceFormat(const LG_RendererFormat format, + bool rendererSupportsNativeHDR) +{ + RenderCommand * cmd = malloc(sizeof(*cmd)); + cmd->op = SURFACE_OP_FORMAT; + cmd->surfaceFormat.format = format; + cmd->surfaceFormat.rendererSupportsNativeHDR = + rendererSupportsNativeHDR; + ll_push(l_renderQueue, cmd); + app_invalidateWindow(true); +} + void renderQueue_cursorState(bool visible, int x, int y, int hx, int hy) { RenderCommand * cmd = malloc(sizeof(*cmd)); @@ -157,11 +221,21 @@ void renderQueue_process(void) break; case SPICE_OP_SHOW: + l_showSpice = cmd->spiceShow.show; RENDERER(spiceShow, cmd->spiceShow.show); + updateSurfaceFormat(); if (cmd->spiceShow.show) overlaySplash_show(false); break; + case SURFACE_OP_FORMAT: + l_surfaceFormat = cmd->surfaceFormat.format; + l_surfaceFormatValid = true; + l_rendererSupportsNativeHDR = + cmd->surfaceFormat.rendererSupportsNativeHDR; + updateSurfaceFormat(); + break; + case CURSOR_OP_STATE: RENDERER(onMouseEvent, cmd->cursorState.visible, cmd->cursorState.x, cmd->cursorState.y, cmd->cursorState.hx, cmd->cursorState.hy); diff --git a/client/src/render_queue.h b/client/src/render_queue.h index 18c27d67..2f7e947d 100644 --- a/client/src/render_queue.h +++ b/client/src/render_queue.h @@ -19,6 +19,7 @@ */ #include "common/ll.h" +#include "interface/renderer.h" typedef struct { @@ -28,6 +29,7 @@ typedef struct SPICE_OP_DRAW_FILL, SPICE_OP_DRAW_BITMAP, SPICE_OP_SHOW, + SURFACE_OP_FORMAT, CURSOR_OP_STATE, CURSOR_OP_IMAGE, } @@ -65,6 +67,13 @@ typedef struct } spiceShow; + struct + { + LG_RendererFormat format; + bool rendererSupportsNativeHDR; + } + surfaceFormat; + struct { bool visible; @@ -103,6 +112,9 @@ void renderQueue_spiceDrawBitmap(int x, int y, int width, int height, int stride void renderQueue_spiceShow(bool show); +void renderQueue_surfaceFormat(const LG_RendererFormat format, + bool rendererSupportsNativeHDR); + void renderQueue_cursorState(bool visible, int x, int y, int hx, int hy); void renderQueue_cursorImage(bool monochrome, int width, int height, int pitch,