[Fix] (MG_Backend/DirectGLES): fix Complementary shaders on Mali

- emulate RGBA8 SNORM fallback writes
This commit is contained in:
2026-06-26 23:33:46 +08:00
parent f6114c9e15
commit 195330ccea
8 changed files with 134 additions and 7 deletions
@@ -184,6 +184,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
Flags<PixelFormatNormalizeOptionBit> GetDriverPixelFormatNormalizeOptions() {
Flags<PixelFormatNormalizeOptionBit> options = PixelFormatNormalizeOptionBit::NoDepthComponent32;
options |= PixelFormatNormalizeOptionBit::NoRGBA8Snorm;
if (!g_GLESCapabilities.SupportsNorm16Texture) {
options |= PixelFormatNormalizeOptionBit::NoNorm16;
}
@@ -207,6 +208,10 @@ namespace MobileGL::MG_Backend::DirectGLES {
reasons.push_back(forced ? "SNORM8 fallback forced by backend policy"
: "SNORM8 native path is not supported");
}
if (options & PixelFormatNormalizeOptionBit::NoRGBA8Snorm) {
reasons.push_back(forced ? "RGBA8_SNORM fallback forced by backend policy"
: "RGBA8_SNORM render target path is not supported");
}
if (options & PixelFormatNormalizeOptionBit::NoDepthComponent32) {
reasons.push_back("GL_DEPTH_COMPONENT32 native probe failed on OpenGL ES");
}
@@ -711,7 +711,8 @@ namespace MobileGL::MG_Backend::DirectGLES {
backendObj = MakeShared<BackendProgramObjectImpl>();
backendObj->SyncToBackend(currentProgram);
} else {
if (!backendObj->GetBackendProgramId()) {
if (!backendObj->GetBackendProgramId() ||
backendObj->GetRGBA8SnormClampOutputMask() != g_rgba8SnormClampOutputMask) {
backendObj->SyncToBackend(currentProgram);
}
}
@@ -1316,6 +1316,22 @@ namespace MobileGL::MG_Backend::DirectGLES {
return true;
}
static Bool IsRGBA8SnormFallbackAttachment(
const MG_State::GLState::FramebufferAttachmentObject& attachmentObject) {
if (attachmentObject.IsTexture()) {
const auto& textureObject = attachmentObject.GetTexture();
return textureObject && textureObject->GetFormat() == TextureInternalFormat::RGBA8Snorm &&
TextureImpl::ShouldUseCaveatTextureFormat(textureObject->GetFormat(), textureObject->GetTarget());
}
if (attachmentObject.IsRenderbuffer()) {
const auto& renderbufferObject = attachmentObject.GetRenderbuffer();
return renderbufferObject &&
renderbufferObject->GetInternalFormat() == TextureInternalFormat::RGBA8Snorm &&
TextureImpl::ShouldUseCaveatRenderbufferFormat(renderbufferObject->GetInternalFormat());
}
return false;
}
void BackendFramebufferObject::SyncToBackend(
const SharedPtr<MG_State::GLState::FramebufferObject>& stateFBOObject, FramebufferTarget asTarget) {
#ifdef TRACY_ENABLE
@@ -1366,6 +1382,22 @@ namespace MobileGL::MG_Backend::DirectGLES {
g_GLESFuncs.glDrawBuffers(nEffectiveBuffers, m_backendDrawBuffers);
}
if (asTarget == FramebufferTarget::Draw) {
Uint32 clampOutputMask = 0;
for (Uint i = 0; i < FramebufferObject::MAX_DRAW_BUFFERS && i < 32; ++i) {
const auto frontendBuf = stateDrawBuffers[i];
if (frontendBuf < FramebufferAttachmentType::Color0 ||
frontendBuf > FramebufferAttachmentType::Color31) {
continue;
}
const auto& attachmentObject = stateFBOObject->GetAttachment(frontendBuf);
if (IsRGBA8SnormFallbackAttachment(attachmentObject)) {
clampOutputMask |= (1u << i);
}
}
PrgramImpl::g_rgba8SnormClampOutputMask = clampOutputMask;
}
// 2. Remap read buffer
auto frontendReadBuf = stateFBOObject->GetReadBuffer();
if (frontendReadBuf != m_frontendReadBuffer) {
@@ -1478,6 +1510,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
} // namespace FramebufferImpl
namespace PrgramImpl {
Uint32 g_rgba8SnormClampOutputMask = 0;
StateBackendObjectRegistry<MG_State::GLState::ProgramObject, BackendProgramObjectImpl> g_backendProgramObjects;
BackendProgramObjectImpl::BackendProgramObjectImpl() {
@@ -1522,6 +1555,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
MGLOG_D("Syncing program to backend. State program ID: %u, Backend ID: %u",
stateProgramObject->GetExternalIndex(), m_backendProgramId);
m_rgba8SnormClampOutputMask = g_rgba8SnormClampOutputMask;
// Detach all existing shaders
GLint attachedCount = 0;
@@ -1597,6 +1631,8 @@ namespace MobileGL::MG_Backend::DirectGLES {
source = ForceFlatIntegerVaryings(source, glShaderType);
source = EmulateBaseInstanceInVertexShader(std::move(source), glShaderType);
source = ForceSupporterOutput(source);
source = ClampRGBA8SnormFallbackOutputs(std::move(source), glShaderType,
m_rgba8SnormClampOutputMask);
// Patch for Photon compiler precision issue
String findStr = "1000000.0";
@@ -284,14 +284,17 @@ namespace MobileGL::MG_Backend::DirectGLES {
void SetBaseInstance(Uint32 baseInstance) const;
Uint GetBackendProgramId() const { return m_backendProgramId; }
Uint GetBackendGlobalUBOId() const { return m_backendGlobalUBOId; }
Uint32 GetRGBA8SnormClampOutputMask() const { return m_rgba8SnormClampOutputMask; }
private:
Uint m_backendProgramId = 0;
Uint m_backendGlobalUBOId = 0;
Int m_baseInstanceUniformLocation = -1;
Uint32 m_rgba8SnormClampOutputMask = 0;
Bool m_isInitialized = false;
};
extern Uint32 g_rgba8SnormClampOutputMask;
extern StateBackendObjectRegistry<MG_State::GLState::ProgramObject, BackendProgramObjectImpl>
g_backendProgramObjects;
} // namespace PrgramImpl
+71 -4
View File
@@ -33,6 +33,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
Flags<PixelFormatNormalizeOptionBit> GetDriverPixelFormatNormalizeOptions() {
Flags<PixelFormatNormalizeOptionBit> options = PixelFormatNormalizeOptionBit::NoDepthComponent32;
options |= PixelFormatNormalizeOptionBit::NoRGBA8Snorm;
if (!g_GLESCapabilities.SupportsNorm16Texture) {
options |= PixelFormatNormalizeOptionBit::NoNorm16;
}
@@ -81,10 +82,15 @@ namespace MobileGL::MG_Backend::DirectGLES {
Bool ShouldUseCaveatFormat(TextureInternalFormat internalFormat, SizeT targetIndex) {
if (targetIndex < kFormatCapabilityTargetCount) {
if (HasCachedFormatCapability(internalFormat, targetIndex, false, FormatCapability::Creatable)) {
return false;
}
return HasCachedFormatCapability(internalFormat, targetIndex, true, FormatCapability::Creatable);
const Bool fullCreatable =
HasCachedFormatCapability(internalFormat, targetIndex, false, FormatCapability::Creatable);
const Bool caveatCreatable =
HasCachedFormatCapability(internalFormat, targetIndex, true, FormatCapability::Creatable);
const Bool fullRenderable =
HasCachedFormatCapability(internalFormat, targetIndex, false, FormatCapability::FramebufferRenderable);
const Bool caveatRenderable =
HasCachedFormatCapability(internalFormat, targetIndex, true, FormatCapability::FramebufferRenderable);
return (!fullCreatable && caveatCreatable) || (!fullRenderable && caveatRenderable);
}
if (HasAnyCachedFormatCapability(internalFormat, false, FormatCapability::Creatable)) {
@@ -127,6 +133,16 @@ namespace MobileGL::MG_Backend::DirectGLES {
GenerateFormatInfo(internalFormat, GetRenderbufferFormatCapabilityTargetIndex(), outInternalFormat,
outFormat, outType);
}
Bool ShouldUseCaveatTextureFormat(TextureInternalFormat internalFormat, TextureTarget target) {
const SizeT targetIndex =
target == TextureTarget::Unknown ? kFormatCapabilityTargetCount : GetFormatCapabilityTargetIndex(target);
return ShouldUseCaveatFormat(internalFormat, targetIndex);
}
Bool ShouldUseCaveatRenderbufferFormat(TextureInternalFormat internalFormat) {
return ShouldUseCaveatFormat(internalFormat, GetRenderbufferFormatCapabilityTargetIndex());
}
} // namespace TextureImpl
namespace PrgramImpl {
String ProcessOutColorLocations(const String& glslCode) {
@@ -197,6 +213,57 @@ namespace MobileGL::MG_Backend::DirectGLES {
return result;
}
String ClampRGBA8SnormFallbackOutputs(String glslCode, GLenum shaderType, Uint32 outputMask) {
#ifdef TRACY_ENABLE
ZoneScopedC(TRACY_ZONECOLOR_BACKEND);
#endif
if (shaderType != GL_FRAGMENT_SHADER || outputMask == 0) {
return glslCode;
}
const std::regex outputPattern(
R"(layout\s*\(\s*location\s*=\s*([0-9]+)\s*\)\s*out\s+(?:(?:lowp|mediump|highp)\s+)?vec4\s+([A-Za-z_][A-Za-z0-9_]*)\s*;)");
std::sregex_iterator outputIt(glslCode.begin(), glslCode.end(), outputPattern);
std::sregex_iterator outputEnd;
Vector<String> outputNames;
for (; outputIt != outputEnd; ++outputIt) {
const Uint location = static_cast<Uint>(std::stoul((*outputIt)[1].str()));
if (location < 32 && (outputMask & (1u << location))) {
outputNames.push_back((*outputIt)[2].str());
}
}
if (outputNames.empty()) {
return glslCode;
}
const std::regex mainPattern(R"(void\s+main\s*\([^)]*\)\s*\{)");
std::smatch mainMatch;
if (!std::regex_search(glslCode, mainMatch, mainPattern)) {
return glslCode;
}
SizeT bracePos = static_cast<SizeT>(mainMatch.position(0) + mainMatch.length(0) - 1);
Int depth = 0;
for (SizeT pos = bracePos; pos < glslCode.size(); ++pos) {
if (glslCode[pos] == '{') {
++depth;
} else if (glslCode[pos] == '}') {
--depth;
if (depth == 0) {
String clampLine;
for (const String& outputName : outputNames) {
clampLine += "\n " + outputName + " = clamp(" + outputName +
", vec4(-1.0), vec4(1.0));";
}
clampLine += "\n";
glslCode.insert(pos, clampLine);
return glslCode;
}
}
}
return glslCode;
}
String ForceFlatIntegerVaryings(const String& glslCode, GLenum shaderType) {
#ifdef TRACY_ENABLE
ZoneScopedC(TRACY_ZONECOLOR_BACKEND);
+3
View File
@@ -39,6 +39,8 @@ namespace MobileGL::MG_Backend::DirectGLES {
TextureTarget target = TextureTarget::Unknown);
void GenerateRenderbufferFormatInfo(TextureInternalFormat internalFormat, GLenum* outInternalFormat,
GLenum* outFormat, GLenum* outType);
Bool ShouldUseCaveatTextureFormat(TextureInternalFormat internalFormat, TextureTarget target);
Bool ShouldUseCaveatRenderbufferFormat(TextureInternalFormat internalFormat);
} // namespace TextureImpl
namespace FramebufferImpl {} // namespace FramebufferImpl
@@ -46,6 +48,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
namespace PrgramImpl {
String ProcessOutColorLocations(const String& glslCode);
String ForceSupporterOutput(const String& glslCode);
String ClampRGBA8SnormFallbackOutputs(String glslCode, GLenum shaderType, Uint32 outputMask);
String ForceFlatIntegerVaryings(const String& glslCode, GLenum shaderType);
String RemoveLayoutBinding(const String& glslCode);
} // namespace PrgramImpl
@@ -35,6 +35,9 @@ namespace MobileGL::MG_Util::TextureFormatProcessor {
applicableOptions |= options & PixelFormatNormalizeOptionBit::NoSnorm16;
break;
case GL_RGBA8_SNORM:
applicableOptions |= options & PixelFormatNormalizeOptionBit::NoSnorm8;
applicableOptions |= options & PixelFormatNormalizeOptionBit::NoRGBA8Snorm;
break;
case GL_RGB8_SNORM:
case GL_RG8_SNORM:
case GL_R8_SNORM:
@@ -123,7 +126,8 @@ namespace MobileGL::MG_Util::TextureFormatProcessor {
*outInternalFormat = internalFormat;
break;
case GL_RGBA8_SNORM:
if (options & PixelFormatNormalizeOptionBit::NoSnorm8) {
if ((options & PixelFormatNormalizeOptionBit::NoSnorm8) ||
(options & PixelFormatNormalizeOptionBit::NoRGBA8Snorm)) {
*outInternalFormat = GL_RGBA16F;
break;
}
@@ -360,7 +364,6 @@ namespace MobileGL::MG_Util::TextureFormatProcessor {
*outType = GL_SHORT;
break;
}
case GL_RGBA8_SNORM:
case GL_RGB8_SNORM:
case GL_RG8_SNORM:
case GL_R8_SNORM:
@@ -370,6 +373,14 @@ namespace MobileGL::MG_Util::TextureFormatProcessor {
}
*outType = GL_BYTE;
break;
case GL_RGBA8_SNORM:
if ((options & PixelFormatNormalizeOptionBit::NoSnorm8) ||
(options & PixelFormatNormalizeOptionBit::NoRGBA8Snorm)) {
*outType = GL_FLOAT;
break;
}
*outType = GL_BYTE;
break;
// Color Unsigned Integer
case GL_RGBA32UI:
@@ -16,6 +16,7 @@ namespace MobileGL {
NoRgb16 = 1 << 2,
NoSnorm8 = 1 << 3,
NoDepthComponent32 = 1 << 4,
NoRGBA8Snorm = 1 << 5,
None = 0,
};
namespace MG_Util::TextureFormatProcessor {