[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.
This commit is contained in:
BZLZHH
2026-07-31 18:21:14 -04:00
parent a03817b4ee
commit 07fa84fb8d
3 changed files with 37 additions and 20 deletions
@@ -844,19 +844,13 @@ namespace MobileGL::MG_Impl::GLImpl {
} }
GLboolean IsProgram_State(GLuint program) { GLboolean IsProgram_State(GLuint program) {
/* FIXME: Handle situations that: // Deletion-flagged names stay valid while the object is still GL-visible (program in
* A program object marked for deletion with glDeleteProgram but still in use as part of current // use, shader attached), so name validity is exactly the Is* answer.
* rendering state is still considered a program object and glIsProgram will return GL_TRUE.
*/
if (program == 0) return GL_FALSE; if (program == 0) return GL_FALSE;
return MG_State::pGLContext->ValidateProgramName(program) ? GL_TRUE : GL_FALSE; return MG_State::pGLContext->ValidateProgramName(program) ? GL_TRUE : GL_FALSE;
} }
GLboolean IsShader_State(GLuint shader) { 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; if (shader == 0) return GL_FALSE;
return MG_State::pGLContext->ValidateShaderName(shader) ? GL_TRUE : GL_FALSE; return MG_State::pGLContext->ValidateShaderName(shader) ? GL_TRUE : GL_FALSE;
} }
@@ -29,10 +29,19 @@ namespace MobileGL::MG_State::GLState {
if (!CheckIndexAvail(program, m_programObjects)) return; // FIXME: add error reporting here if (!CheckIndexAvail(program, m_programObjects)) return; // FIXME: add error reporting here
auto& programObject = m_programObjects[program]; auto& programObject = m_programObjects[program];
if (programObject != nullptr) { if (programObject != nullptr) {
programObject->MarkAsDeleted();
// 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 // Snapshot the attachments: deleting the program is a detach point for shaders
// that were flagged with glDeleteShader while still attached. // that were flagged with glDeleteShader while still attached.
const Vector<SharedPtr<ShaderObject>> attachedShaders = programObject->GetAttachedShaders(); const Vector<SharedPtr<ShaderObject>> attachedShaders = programObject->GetAttachedShaders();
programObject->MarkAsDeleted();
programObject.reset(); programObject.reset();
m_programIndexGenerator.Delete(program); m_programIndexGenerator.Delete(program);
for (const auto& shader : attachedShaders) { for (const auto& shader : attachedShaders) {
@@ -42,19 +51,30 @@ namespace MobileGL::MG_State::GLState {
} }
} }
} }
}
Bool ProgramState::ValidateProgramObject(const Uint program) const { Bool ProgramState::ValidateProgramObject(const Uint program) const {
return CheckIndexAvail(program, m_programObjects) && m_programObjects[program] != nullptr; return CheckIndexAvail(program, m_programObjects) && m_programObjects[program] != nullptr;
} }
void ProgramState::UseProgram(Uint program) { void ProgramState::UseProgram(Uint program) {
const SharedPtr<ProgramObject> previous = m_currentProgram;
if (program == 0) m_currentProgram.reset(); if (program == 0) m_currentProgram.reset();
if (!CheckIndexAvail(program, m_programObjects)) return; if (CheckIndexAvail(program, m_programObjects)) {
m_currentProgram = m_programObjects[program]; 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) { Uint ProgramState::CreateShader(ShaderStage stage) {
Uint shaderId = 0; Uint shaderId = 0;
m_shaderIndexGenerator.Generate(1, &shaderId); m_shaderIndexGenerator.Generate(1, &shaderId);
@@ -35,6 +35,9 @@ namespace MobileGL::MG_State::GLState {
private: private:
Bool ShaderHasGLVisibleAttachment(const SharedPtr<ShaderObject>& shaderObject) const; Bool ShaderHasGLVisibleAttachment(const SharedPtr<ShaderObject>& 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 <typename T> template <typename T>
static Bool CheckIndexAvail(const SizeT idx, const Vector<T>& vec) { static Bool CheckIndexAvail(const SizeT idx, const Vector<T>& vec) {