diff --git a/MobileGL/MG_Backend/DirectGLES/BackendObject_DirectGLES.cpp b/MobileGL/MG_Backend/DirectGLES/BackendObject_DirectGLES.cpp index 490b5ebe..a8cea1b5 100644 --- a/MobileGL/MG_Backend/DirectGLES/BackendObject_DirectGLES.cpp +++ b/MobileGL/MG_Backend/DirectGLES/BackendObject_DirectGLES.cpp @@ -213,6 +213,9 @@ namespace MobileGL::MG_Backend::DirectGLES { if (options & PixelFormatNormalizeOptionBit::NoThreeChannelRenderTarget) { reasons.push_back("no three-channel multisample storage format on OpenGL ES"); } + if (options & PixelFormatNormalizeOptionBit::NoSnorm16RenderTarget) { + reasons.push_back("EXT_render_snorm not supported"); + } String reason; for (SizeT i = 0; i < reasons.size(); ++i) { @@ -576,6 +579,9 @@ namespace MobileGL::MG_Backend::DirectGLES { Flags targetOptions; if (IsGLESProbeMultisampleTarget(target)) { targetOptions |= PixelFormatNormalizeOptionBit::NoThreeChannelRenderTarget; + if (!capabilities.SupportsRenderSnorm || !capabilities.SupportsNorm16Texture) { + targetOptions |= PixelFormatNormalizeOptionBit::NoSnorm16RenderTarget; + } } GLESProbeFormatInfo fallbackInfo = outerFallbackInfo; Bool hasForcedFallback = outerHasForcedFallback; diff --git a/MobileGL/MG_Backend/DirectGLES/Utils.cpp b/MobileGL/MG_Backend/DirectGLES/Utils.cpp index fdb16627..1ccb8aa7 100644 --- a/MobileGL/MG_Backend/DirectGLES/Utils.cpp +++ b/MobileGL/MG_Backend/DirectGLES/Utils.cpp @@ -49,12 +49,9 @@ namespace MobileGL::MG_Backend::DirectGLES { } Flags - GetRuntimeFallbackNormalizeOptions(GLenum requestedInternalFormat, Bool mustStayRenderable) { + GetRuntimeFallbackNormalizeOptions(GLenum requestedInternalFormat, + Flags extraOptions) { using namespace MG_Util::TextureFormatProcessor; - Flags extraOptions; - if (mustStayRenderable) { - extraOptions |= PixelFormatNormalizeOptionBit::NoThreeChannelRenderTarget; - } const Flags forcedOptions = GetApplicablePixelFormatNormalizeOptions( requestedInternalFormat, GetForcedPixelFormatNormalizeOptions() | extraOptions); if (forcedOptions) { @@ -75,6 +72,18 @@ namespace MobileGL::MG_Backend::DirectGLES { targetIndex == static_cast(TextureTarget::Texture2DMultisampleArray); } + Flags GetRenderTargetNormalizeOptions(SizeT targetIndex) { + Flags options; + if (!TargetRequiresRenderableFormat(targetIndex)) { + return options; + } + options |= PixelFormatNormalizeOptionBit::NoThreeChannelRenderTarget; + if (!g_GLESCapabilities.SupportsRenderSnorm || !g_GLESCapabilities.SupportsNorm16Texture) { + options |= PixelFormatNormalizeOptionBit::NoSnorm16RenderTarget; + } + return options; + } + Bool HasCachedFormatCapability(TextureInternalFormat internalFormat, SizeT targetIndex, Bool caveat, @@ -133,7 +142,7 @@ namespace MobileGL::MG_Backend::DirectGLES { Flags options; if (!pActiveBackendObject || ShouldUseCaveatFormat(internalFormat, targetIndex)) { options = GetRuntimeFallbackNormalizeOptions(requestedInternalFormat, - TargetRequiresRenderableFormat(targetIndex)); + GetRenderTargetNormalizeOptions(targetIndex)); } NormalizePixelFormat(requestedInternalFormat, options, outInternalFormat, outFormat, outType); } diff --git a/MobileGL/MG_Util/BackendLoaders/OpenGL/Loader.cpp b/MobileGL/MG_Util/BackendLoaders/OpenGL/Loader.cpp index ac006e8e..ff73b843 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_render_snorm") == 0) { + caps.SupportsRenderSnorm = true; + } if (std::strcmp(extension, "GL_EXT_sRGB_write_control") == 0) { caps.SupportsSrgbWriteControl = true; } diff --git a/MobileGL/MG_Util/BackendLoaders/OpenGL/Loader.h b/MobileGL/MG_Util/BackendLoaders/OpenGL/Loader.h index f9196485..45ba3ed3 100644 --- a/MobileGL/MG_Util/BackendLoaders/OpenGL/Loader.h +++ b/MobileGL/MG_Util/BackendLoaders/OpenGL/Loader.h @@ -1031,6 +1031,9 @@ namespace MobileGL { String GLESShadingLanguageVersionString; Bool SupportsPersistentMapping = false; Bool SupportsNorm16Texture = false; + // GL_EXT_render_snorm is present, so the signed-normalized formats are colour-renderable + // (and usable as multisample texture storage) rather than texture-only. + Bool SupportsRenderSnorm = 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. diff --git a/MobileGL/MG_Util/Texture/TextureFormatProcessor.cpp b/MobileGL/MG_Util/Texture/TextureFormatProcessor.cpp index dbd409cc..800aaeab 100644 --- a/MobileGL/MG_Util/Texture/TextureFormatProcessor.cpp +++ b/MobileGL/MG_Util/Texture/TextureFormatProcessor.cpp @@ -36,6 +36,9 @@ namespace MobileGL::MG_Util::TextureFormatProcessor { applicableOptions |= options & PixelFormatNormalizeOptionBit::NoNorm16; applicableOptions |= options & PixelFormatNormalizeOptionBit::NoSnorm16; applicableOptions |= options & PixelFormatNormalizeOptionBit::NoThreeChannelRenderTarget; + if (options & PixelFormatNormalizeOptionBit::NoThreeChannelRenderTarget) { + applicableOptions |= options & PixelFormatNormalizeOptionBit::NoSnorm16RenderTarget; + } break; case GL_RGBA16_SNORM: case GL_RG16_SNORM: @@ -130,7 +133,11 @@ namespace MobileGL::MG_Util::TextureFormatProcessor { break; case GL_RGB16_SNORM: if (options & PixelFormatNormalizeOptionBit::NoThreeChannelRenderTarget) { - *outInternalFormat = GL_RGBA16F; + // A half float loses the low bits of a 16-bit SNORM channel, so keep the + // signed-normalized encoding whenever the driver can render to it. + *outInternalFormat = (options & PixelFormatNormalizeOptionBit::NoSnorm16RenderTarget) + ? GL_RGBA16F + : GL_RGBA16_SNORM; break; } if ((options & PixelFormatNormalizeOptionBit::NoNorm16) || diff --git a/MobileGL/MG_Util/Texture/TextureFormatProcessor.h b/MobileGL/MG_Util/Texture/TextureFormatProcessor.h index 8190c558..c5fcd9fa 100644 --- a/MobileGL/MG_Util/Texture/TextureFormatProcessor.h +++ b/MobileGL/MG_Util/Texture/TextureFormatProcessor.h @@ -24,6 +24,11 @@ namespace MobileGL { // rendered into, so the extra alpha comes from the draw (1.0 for an RGB source) // and no transfer path has to expand three-channel client data. NoThreeChannelRenderTarget = 1 << 7, + // Pairs with the bit above: the widened four-channel format has to stay renderable AND + // keep 16-bit signed-normalized precision, which needs both EXT_texture_norm16 and + // EXT_render_snorm. Without them the only renderable widening left is a half float, whose + // 11-bit mantissa cannot represent a 16-bit SNORM channel exactly. + NoSnorm16RenderTarget = 1 << 8, None = 0, }; namespace MG_Util::TextureFormatProcessor {