mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-07 19:58:32 +09:00
[Fix] (DirectGLES, MG_Util): keep 16-bit SNORM precision through the widening
GL_RGB16_SNORM widened to GL_RGBA16F to stay renderable as multisample storage, and a half float's 11-bit mantissa cannot hold a 16-bit signed-normalized channel: KHR-GL33.texture_swizzle's blue channel came back several units of 32767 away from the value the reference computes, well outside its one-unit tolerance. GL_EXT_render_snorm makes the signed-normalized formats colour-renderable on ES, so widen to GL_RGBA16_SNORM instead wherever it and EXT_texture_norm16 are both present, and only fall back to the half float otherwise. Threaded through as its own normalize option so the capability probe and the runtime pick the same format, the way every other driver-dependent substitution here is decided.
This commit is contained in:
@@ -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<PixelFormatNormalizeOptionBit> targetOptions;
|
||||
if (IsGLESProbeMultisampleTarget(target)) {
|
||||
targetOptions |= PixelFormatNormalizeOptionBit::NoThreeChannelRenderTarget;
|
||||
if (!capabilities.SupportsRenderSnorm || !capabilities.SupportsNorm16Texture) {
|
||||
targetOptions |= PixelFormatNormalizeOptionBit::NoSnorm16RenderTarget;
|
||||
}
|
||||
}
|
||||
GLESProbeFormatInfo fallbackInfo = outerFallbackInfo;
|
||||
Bool hasForcedFallback = outerHasForcedFallback;
|
||||
|
||||
@@ -49,12 +49,9 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
}
|
||||
|
||||
Flags<PixelFormatNormalizeOptionBit>
|
||||
GetRuntimeFallbackNormalizeOptions(GLenum requestedInternalFormat, Bool mustStayRenderable) {
|
||||
GetRuntimeFallbackNormalizeOptions(GLenum requestedInternalFormat,
|
||||
Flags<PixelFormatNormalizeOptionBit> extraOptions) {
|
||||
using namespace MG_Util::TextureFormatProcessor;
|
||||
Flags<PixelFormatNormalizeOptionBit> extraOptions;
|
||||
if (mustStayRenderable) {
|
||||
extraOptions |= PixelFormatNormalizeOptionBit::NoThreeChannelRenderTarget;
|
||||
}
|
||||
const Flags<PixelFormatNormalizeOptionBit> forcedOptions = GetApplicablePixelFormatNormalizeOptions(
|
||||
requestedInternalFormat, GetForcedPixelFormatNormalizeOptions() | extraOptions);
|
||||
if (forcedOptions) {
|
||||
@@ -75,6 +72,18 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
targetIndex == static_cast<SizeT>(TextureTarget::Texture2DMultisampleArray);
|
||||
}
|
||||
|
||||
Flags<PixelFormatNormalizeOptionBit> GetRenderTargetNormalizeOptions(SizeT targetIndex) {
|
||||
Flags<PixelFormatNormalizeOptionBit> 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<PixelFormatNormalizeOptionBit> options;
|
||||
if (!pActiveBackendObject || ShouldUseCaveatFormat(internalFormat, targetIndex)) {
|
||||
options = GetRuntimeFallbackNormalizeOptions(requestedInternalFormat,
|
||||
TargetRequiresRenderableFormat(targetIndex));
|
||||
GetRenderTargetNormalizeOptions(targetIndex));
|
||||
}
|
||||
NormalizePixelFormat(requestedInternalFormat, options, outInternalFormat, outFormat, outType);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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) ||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user