From 43a43c118050d701e994240470b8e62b9ae96052 Mon Sep 17 00:00:00 2001 From: BZLZHH Date: Sun, 2 Aug 2026 04:10:13 -0400 Subject: [PATCH] [Fix] (DirectGLES, MG_Util): raw framebuffer writes while GL_FRAMEBUFFER_SRGB is off GLES core always encodes a fragment written into an sRGB colour attachment, and offers no switch to stop it. Desktop GL has one, GL_FRAMEBUFFER_SRGB, and it starts out disabled - so a GL application that never touches it expects its writes to land raw. The frontend models exactly that (the capability reads as disabled and DirectVulkan attaches the UNORM twin to honour it), but DirectGLES was passing the draw straight to a driver that encodes anyway. The value therefore came back one conversion short of the reference wherever it was written and then read again: rendering into an sRGB texture and fetching it in a shader decodes once but had encoded twice, which is how KHR-GL32.texture_size_promotion read 0.0142 for GL_SRGB8_ALPHA8 where 0.00111 was expected. Detect GL_EXT_sRGB_write_control and sync GL_FRAMEBUFFER_SRGB from the frontend capability alongside the other enables, starting from the driver's enabled state so the first sync always pushes the disable down. --- MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp | 16 ++++++++++++++++ .../MG_Util/BackendLoaders/OpenGL/Loader.cpp | 3 +++ MobileGL/MG_Util/BackendLoaders/OpenGL/Loader.h | 4 ++++ 3 files changed, 23 insertions(+) diff --git a/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp b/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp index 01783abd..6c517949 100644 --- a/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp +++ b/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp @@ -738,6 +738,9 @@ namespace MobileGL::MG_Backend::DirectGLES { static Bool g_hasSyncedRenderState = false; static RenderStateParameters g_syncedRenderStateParameters; static IntVec4 g_syncedBackendViewport = IntVec4(-1, -1, -1, -1); + // GLES starts with sRGB framebuffer encoding on, so the first sync always has to push the + // frontend's (desktop-GL default) disabled state down. + static Bool g_syncedSrgbFramebufferWrites = true; void SyncRenderState() { #ifdef TRACY_ENABLE ZoneScopedC(TRACY_ZONECOLOR_BACKEND); @@ -784,6 +787,19 @@ namespace MobileGL::MG_Backend::DirectGLES { #undef SYNC_CAPABILITY + { // sRGB framebuffer writes. GLES core always encodes a write into an sRGB attachment, + // while GL_FRAMEBUFFER_SRGB is disabled by default in desktop GL and the frontend + // never turns it on, so the driver has to be told to write raw. Without this a render + // into an sRGB colour buffer comes back encoded once too often (the shader's own + // decode on the next fetch then leaves the value one conversion short). + const Bool srgbWrites = MG_State::pGLContext->IsCapabilityEnabled(CapabilityInput::FramebufferSrgb); + if (g_GLESCapabilities.SupportsSrgbWriteControl && srgbWrites != g_syncedSrgbFramebufferWrites) { + srgbWrites ? g_GLESFuncs.glEnable(GL_FRAMEBUFFER_SRGB) + : g_GLESFuncs.glDisable(GL_FRAMEBUFFER_SRGB); + g_syncedSrgbFramebufferWrites = srgbWrites; + } + } + { // Primitive restart. GLES core has only GL_PRIMITIVE_RESTART_FIXED_INDEX (fixed all-ones // value); both the fixed cap and the (fixed-valued) arbitrary GL_PRIMITIVE_RESTART map to // it. An arbitrary non-fixed restart index is rejected at draw time (see DrawElements). diff --git a/MobileGL/MG_Util/BackendLoaders/OpenGL/Loader.cpp b/MobileGL/MG_Util/BackendLoaders/OpenGL/Loader.cpp index 9b98c942..ac006e8e 100644 --- a/MobileGL/MG_Util/BackendLoaders/OpenGL/Loader.cpp +++ b/MobileGL/MG_Util/BackendLoaders/OpenGL/Loader.cpp @@ -824,6 +824,9 @@ namespace MobileGL::MG_Util::BackendLoader { if (std::strcmp(extension, "GL_EXT_texture_norm16") == 0) { caps.SupportsNorm16Texture = true; } + if (std::strcmp(extension, "GL_EXT_sRGB_write_control") == 0) { + caps.SupportsSrgbWriteControl = true; + } if (std::strcmp(extension, "GL_EXT_texture_filter_anisotropic") == 0) { caps.SupportsTextureFilterAnisotropy = true; } diff --git a/MobileGL/MG_Util/BackendLoaders/OpenGL/Loader.h b/MobileGL/MG_Util/BackendLoaders/OpenGL/Loader.h index fa544e40..f9196485 100644 --- a/MobileGL/MG_Util/BackendLoaders/OpenGL/Loader.h +++ b/MobileGL/MG_Util/BackendLoaders/OpenGL/Loader.h @@ -1031,6 +1031,10 @@ namespace MobileGL { String GLESShadingLanguageVersionString; Bool SupportsPersistentMapping = false; Bool SupportsNorm16Texture = false; + // GL_EXT_sRGB_write_control is present, so GL_FRAMEBUFFER_SRGB can be turned off. + // GLES has no such switch in core: writes into an sRGB attachment are ALWAYS encoded, + // while desktop GL leaves GL_FRAMEBUFFER_SRGB disabled by default and writes raw. + Bool SupportsSrgbWriteControl = false; // GL_EXT_texture_filter_anisotropic is present, so sampler/texture // anisotropy may be forwarded without raising GL_INVALID_ENUM in GLES. Bool SupportsTextureFilterAnisotropy = false;