mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-12 14:18:31 +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",
|
MGLOG_D("Syncing program to backend. State program ID: %u, Backend ID: %u",
|
||||||
stateProgramObject->GetExternalIndex(), m_backendProgramId);
|
stateProgramObject->GetExternalIndex(), m_backendProgramId);
|
||||||
|
m_backendProgramUsable = true;
|
||||||
m_snormFallbackClampOutputMask = g_snormFallbackClampOutputMask;
|
m_snormFallbackClampOutputMask = g_snormFallbackClampOutputMask;
|
||||||
m_unormFallbackClampOutputMask = g_unormFallbackClampOutputMask;
|
m_unormFallbackClampOutputMask = g_unormFallbackClampOutputMask;
|
||||||
m_fragColorBroadcastCount = g_fragColorBroadcastCount;
|
m_fragColorBroadcastCount = g_fragColorBroadcastCount;
|
||||||
@@ -3365,6 +3366,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
r.log += spvcSession.GetLastErrorString();
|
r.log += spvcSession.GetLastErrorString();
|
||||||
r.errc = -5;
|
r.errc = -5;
|
||||||
MGLOG_E("%s", r.log.c_str());
|
MGLOG_E("%s", r.log.c_str());
|
||||||
|
m_backendProgramUsable = false;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3406,6 +3408,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
Vector<GLchar> log(logLength);
|
Vector<GLchar> log(logLength);
|
||||||
g_GLESFuncs.glGetShaderInfoLog(backendShaderId, logLength, nullptr, log.data());
|
g_GLESFuncs.glGetShaderInfoLog(backendShaderId, logLength, nullptr, log.data());
|
||||||
MGLOG_E("Shader compilation failed for backend ID %u: %s", backendShaderId, log.data());
|
MGLOG_E("Shader compilation failed for backend ID %u: %s", backendShaderId, log.data());
|
||||||
|
m_backendProgramUsable = false;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3441,6 +3444,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
|
|
||||||
GLint linkStatus;
|
GLint linkStatus;
|
||||||
g_GLESFuncs.glGetProgramiv(m_backendProgramId, GL_LINK_STATUS, &linkStatus);
|
g_GLESFuncs.glGetProgramiv(m_backendProgramId, GL_LINK_STATUS, &linkStatus);
|
||||||
|
m_backendProgramUsable = m_backendProgramUsable && linkStatus == GL_TRUE;
|
||||||
if (linkStatus != GL_TRUE) {
|
if (linkStatus != GL_TRUE) {
|
||||||
GLint logLength;
|
GLint logLength;
|
||||||
g_GLESFuncs.glGetProgramiv(m_backendProgramId, GL_INFO_LOG_LENGTH, &logLength);
|
g_GLESFuncs.glGetProgramiv(m_backendProgramId, GL_INFO_LOG_LENGTH, &logLength);
|
||||||
@@ -3566,12 +3570,18 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
#ifdef TRACY_ENABLE
|
#ifdef TRACY_ENABLE
|
||||||
ZoneScopedC(TRACY_ZONECOLOR_BACKEND);
|
ZoneScopedC(TRACY_ZONECOLOR_BACKEND);
|
||||||
#endif
|
#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;
|
return;
|
||||||
}
|
}
|
||||||
MGLOG_D("Using program %u", m_backendProgramId);
|
MGLOG_D("Using program %u", programToBind);
|
||||||
g_GLESFuncs.glUseProgram(m_backendProgramId);
|
g_GLESFuncs.glUseProgram(programToBind);
|
||||||
g_lastUsedBackendProgramId = m_backendProgramId;
|
g_lastUsedBackendProgramId = programToBind;
|
||||||
}
|
}
|
||||||
|
|
||||||
void BackendProgramObjectImpl::SetBaseInstance(Uint32 baseInstance) const {
|
void BackendProgramObjectImpl::SetBaseInstance(Uint32 baseInstance) const {
|
||||||
|
|||||||
@@ -600,6 +600,10 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
void SetDrawID(Uint32 drawId) const;
|
void SetDrawID(Uint32 drawId) const;
|
||||||
Int GetIndirectParamsBinding() const { return m_indirectParamsBinding; }
|
Int GetIndirectParamsBinding() const { return m_indirectParamsBinding; }
|
||||||
Uint GetBackendProgramId() const { return m_backendProgramId; }
|
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; }
|
Uint GetBackendGlobalUBOId() const { return m_backendGlobalUBOId; }
|
||||||
Uint32 GetSnormFallbackClampOutputMask() const { return m_snormFallbackClampOutputMask; }
|
Uint32 GetSnormFallbackClampOutputMask() const { return m_snormFallbackClampOutputMask; }
|
||||||
Uint32 GetUnormFallbackClampOutputMask() const { return m_unormFallbackClampOutputMask; }
|
Uint32 GetUnormFallbackClampOutputMask() const { return m_unormFallbackClampOutputMask; }
|
||||||
@@ -634,6 +638,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
// PrgramImpl::BroadcastLegacyFragColor); 1 keeps the plain single-output shader.
|
// PrgramImpl::BroadcastLegacyFragColor); 1 keeps the plain single-output shader.
|
||||||
Uint m_fragColorBroadcastCount = 1;
|
Uint m_fragColorBroadcastCount = 1;
|
||||||
Bool m_isInitialized = false;
|
Bool m_isInitialized = false;
|
||||||
|
Bool m_backendProgramUsable = false;
|
||||||
|
|
||||||
Int m_globalUboBackendBlockIndex = -1;
|
Int m_globalUboBackendBlockIndex = -1;
|
||||||
Int m_globalUboBackendBlockSize = 0;
|
Int m_globalUboBackendBlockSize = 0;
|
||||||
|
|||||||
Reference in New Issue
Block a user