mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-08 12:18:30 +09:00
[Fix] (DirectGLES): never leave a stale program bound when the new one is broken
When a shader stage fails to transpile or compile, SyncToBackend logs it and
carries on, so the program is linked without that stage - or does not link at
all. Use() then issued glUseProgram for it, which is an INVALID_OPERATION for an
unlinked program and, crucially, leaves the *previous* program current. The draw
went ahead and rendered with an entirely unrelated shader.
That is how KHR-GL3x.texture_size_promotion's GL_TEXTURE_RECTANGLE cases
produced 1.0 for a red channel: SPIRV-Cross refuses sampler2DRect for ESSL
("Rectangle textures are not supported on OpenGL ES"), so every rectangle
program was broken, and the draws kept running the previous case's 1D-array
alpha shader - whose alpha is 1.0. Wrong pixels from a shader the app never
bound are far worse to debug than a blank result.
The program now records whether the last sync produced something usable, and
Use() binds 0 rather than the broken program, making the draw a visible no-op.
The redundancy cache tracks whatever was actually bound, so it stays correct
across the switch.
Does not fix the rectangle cases themselves - those need a SPIR-V pass lowering
Dim::Rect to Dim::2D before SPIRV-Cross runs (plus the coordinate divide for
non-texelFetch lookups), alongside mapping the target to GL_TEXTURE_2D.
This commit is contained in:
@@ -3258,6 +3258,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
|
||||
MGLOG_D("Syncing program to backend. State program ID: %u, Backend ID: %u",
|
||||
stateProgramObject->GetExternalIndex(), m_backendProgramId);
|
||||
m_backendProgramUsable = true;
|
||||
m_snormFallbackClampOutputMask = g_snormFallbackClampOutputMask;
|
||||
m_unormFallbackClampOutputMask = g_unormFallbackClampOutputMask;
|
||||
m_fragColorBroadcastCount = g_fragColorBroadcastCount;
|
||||
@@ -3365,6 +3366,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
r.log += spvcSession.GetLastErrorString();
|
||||
r.errc = -5;
|
||||
MGLOG_E("%s", r.log.c_str());
|
||||
m_backendProgramUsable = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -3406,6 +3408,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
Vector<GLchar> log(logLength);
|
||||
g_GLESFuncs.glGetShaderInfoLog(backendShaderId, logLength, nullptr, log.data());
|
||||
MGLOG_E("Shader compilation failed for backend ID %u: %s", backendShaderId, log.data());
|
||||
m_backendProgramUsable = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -3441,6 +3444,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
|
||||
GLint linkStatus;
|
||||
g_GLESFuncs.glGetProgramiv(m_backendProgramId, GL_LINK_STATUS, &linkStatus);
|
||||
m_backendProgramUsable = m_backendProgramUsable && linkStatus == GL_TRUE;
|
||||
if (linkStatus != GL_TRUE) {
|
||||
GLint logLength;
|
||||
g_GLESFuncs.glGetProgramiv(m_backendProgramId, GL_INFO_LOG_LENGTH, &logLength);
|
||||
@@ -3566,12 +3570,18 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
#ifdef TRACY_ENABLE
|
||||
ZoneScopedC(TRACY_ZONECOLOR_BACKEND);
|
||||
#endif
|
||||
if (g_lastUsedBackendProgramId == m_backendProgramId) {
|
||||
// glUseProgram on a program that did not link is an INVALID_OPERATION and
|
||||
// leaves the *previous* program current, so the draw would silently render
|
||||
// with an unrelated shader (KHR-GL3x.texture_size_promotion read another
|
||||
// test case's alpha that way once a sampler2DRect stage failed to
|
||||
// transpile). Bind nothing instead: the draw is then a visible no-op.
|
||||
const Uint programToBind = m_backendProgramUsable ? m_backendProgramId : 0;
|
||||
if (g_lastUsedBackendProgramId == programToBind) {
|
||||
return;
|
||||
}
|
||||
MGLOG_D("Using program %u", m_backendProgramId);
|
||||
g_GLESFuncs.glUseProgram(m_backendProgramId);
|
||||
g_lastUsedBackendProgramId = m_backendProgramId;
|
||||
MGLOG_D("Using program %u", programToBind);
|
||||
g_GLESFuncs.glUseProgram(programToBind);
|
||||
g_lastUsedBackendProgramId = programToBind;
|
||||
}
|
||||
|
||||
void BackendProgramObjectImpl::SetBaseInstance(Uint32 baseInstance) const {
|
||||
|
||||
@@ -600,6 +600,10 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
void SetDrawID(Uint32 drawId) const;
|
||||
Int GetIndirectParamsBinding() const { return m_indirectParamsBinding; }
|
||||
Uint GetBackendProgramId() const { return m_backendProgramId; }
|
||||
// False when the last SyncToBackend could not produce a usable program (a
|
||||
// shader failed to transpile or compile, or the link itself failed). Use()
|
||||
// must not leave the previously bound program current in that case.
|
||||
Bool IsBackendProgramUsable() const { return m_backendProgramUsable; }
|
||||
Uint GetBackendGlobalUBOId() const { return m_backendGlobalUBOId; }
|
||||
Uint32 GetSnormFallbackClampOutputMask() const { return m_snormFallbackClampOutputMask; }
|
||||
Uint32 GetUnormFallbackClampOutputMask() const { return m_unormFallbackClampOutputMask; }
|
||||
@@ -634,6 +638,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
// PrgramImpl::BroadcastLegacyFragColor); 1 keeps the plain single-output shader.
|
||||
Uint m_fragColorBroadcastCount = 1;
|
||||
Bool m_isInitialized = false;
|
||||
Bool m_backendProgramUsable = false;
|
||||
|
||||
Int m_globalUboBackendBlockIndex = -1;
|
||||
Int m_globalUboBackendBlockSize = 0;
|
||||
|
||||
Reference in New Issue
Block a user