From 07fa84fb8dda6d003bb68dc91b3d8a01f6da2ed0 Mon Sep 17 00:00:00 2001 From: BZLZHH Date: Fri, 31 Jul 2026 18:21:14 -0400 Subject: [PATCH] [Fix] (MG_State, MG_Impl): defer deletion of the program in use glDeleteProgram on the current program now only flags it: the name (and every glGetProgram* query) stays valid until the program stops being current, at which point UseProgram frees the slot and releases orphaned attached shaders. Previously the name died immediately, so a second glDeleteProgram - as issued by common CTS utility teardown - recorded GL_INVALID_VALUE that poisoned the next test iteration's build (KHR-GL33.clip_distance.functional now passes its build phase). glIsProgram/glIsShader piggyback on the same rule: a flagged name is still a program/shader while it stays GL-visible, which resolves the long-standing FIXMEs there. --- .../MG_Impl/GLImpl/Program/GL_Program.cpp | 10 +---- .../GLState/ProgramState/ProgramState.cpp | 44 ++++++++++++++----- .../GLState/ProgramState/ProgramState.h | 3 ++ 3 files changed, 37 insertions(+), 20 deletions(-) diff --git a/MobileGL/MG_Impl/GLImpl/Program/GL_Program.cpp b/MobileGL/MG_Impl/GLImpl/Program/GL_Program.cpp index 1a4d1274..65229d4c 100644 --- a/MobileGL/MG_Impl/GLImpl/Program/GL_Program.cpp +++ b/MobileGL/MG_Impl/GLImpl/Program/GL_Program.cpp @@ -844,19 +844,13 @@ namespace MobileGL::MG_Impl::GLImpl { } GLboolean IsProgram_State(GLuint program) { - /* FIXME: Handle situations that: - * A program object marked for deletion with glDeleteProgram but still in use as part of current - * rendering state is still considered a program object and glIsProgram will return GL_TRUE. - */ + // Deletion-flagged names stay valid while the object is still GL-visible (program in + // use, shader attached), so name validity is exactly the Is* answer. if (program == 0) return GL_FALSE; return MG_State::pGLContext->ValidateProgramName(program) ? GL_TRUE : GL_FALSE; } GLboolean IsShader_State(GLuint shader) { - /* FIXME: Handle situations that: - * A shader object marked for deletion with glDeleteShader but still attached to a program object is still - * considered a shader object and glIsShader will return GL_TRUE. - */ if (shader == 0) return GL_FALSE; return MG_State::pGLContext->ValidateShaderName(shader) ? GL_TRUE : GL_FALSE; } diff --git a/MobileGL/MG_State/GLState/ProgramState/ProgramState.cpp b/MobileGL/MG_State/GLState/ProgramState/ProgramState.cpp index 1197f7aa..2aeb76e7 100644 --- a/MobileGL/MG_State/GLState/ProgramState/ProgramState.cpp +++ b/MobileGL/MG_State/GLState/ProgramState/ProgramState.cpp @@ -29,17 +29,25 @@ namespace MobileGL::MG_State::GLState { if (!CheckIndexAvail(program, m_programObjects)) return; // FIXME: add error reporting here auto& programObject = m_programObjects[program]; if (programObject != nullptr) { - // Snapshot the attachments: deleting the program is a detach point for shaders - // that were flagged with glDeleteShader while still attached. - const Vector> attachedShaders = programObject->GetAttachedShaders(); programObject->MarkAsDeleted(); - programObject.reset(); - m_programIndexGenerator.Delete(program); - for (const auto& shader : attachedShaders) { - const Uint shaderName = shader->GetExternalIndex(); - if (CheckIndexAvail(shaderName, m_shaderObjects) && m_shaderObjects[shaderName] == shader) { - ReleaseShaderNameIfOrphaned(shaderName); - } + // A program in use is only FLAGGED: its name (and every program query) stays + // valid until it stops being current, at which point UseProgram finishes the job. + if (programObject == m_currentProgram) return; + DestroyProgramSlot(program); + } + } + + void ProgramState::DestroyProgramSlot(const Uint program) { + auto& programObject = m_programObjects[program]; + // Snapshot the attachments: deleting the program is a detach point for shaders + // that were flagged with glDeleteShader while still attached. + const Vector> attachedShaders = programObject->GetAttachedShaders(); + programObject.reset(); + m_programIndexGenerator.Delete(program); + for (const auto& shader : attachedShaders) { + const Uint shaderName = shader->GetExternalIndex(); + if (CheckIndexAvail(shaderName, m_shaderObjects) && m_shaderObjects[shaderName] == shader) { + ReleaseShaderNameIfOrphaned(shaderName); } } } @@ -49,10 +57,22 @@ namespace MobileGL::MG_State::GLState { } void ProgramState::UseProgram(Uint program) { + const SharedPtr previous = m_currentProgram; + if (program == 0) m_currentProgram.reset(); - if (!CheckIndexAvail(program, m_programObjects)) return; - m_currentProgram = m_programObjects[program]; + if (CheckIndexAvail(program, m_programObjects)) { + m_currentProgram = m_programObjects[program]; + } + + // A deletion flagged while the program was current takes effect the moment it + // stops being current. + if (previous != nullptr && previous != m_currentProgram && previous->GetDeleteStatus()) { + const Uint previousName = previous->GetExternalIndex(); + if (CheckIndexAvail(previousName, m_programObjects) && m_programObjects[previousName] == previous) { + DestroyProgramSlot(previousName); + } + } } Uint ProgramState::CreateShader(ShaderStage stage) { diff --git a/MobileGL/MG_State/GLState/ProgramState/ProgramState.h b/MobileGL/MG_State/GLState/ProgramState/ProgramState.h index c48ec59f..5fbd4553 100644 --- a/MobileGL/MG_State/GLState/ProgramState/ProgramState.h +++ b/MobileGL/MG_State/GLState/ProgramState/ProgramState.h @@ -35,6 +35,9 @@ namespace MobileGL::MG_State::GLState { private: Bool ShaderHasGLVisibleAttachment(const SharedPtr& shaderObject) const; + // Frees the name slot and releases orphaned attached shaders; the immediate half + // of glDeleteProgram (deferred while the program is current). + void DestroyProgramSlot(Uint program); template static Bool CheckIndexAvail(const SizeT idx, const Vector& vec) {