From 2f2f95498f6743059ccf8f630bf7f68b687a5e06 Mon Sep 17 00:00:00 2001 From: BZLZHH Date: Sun, 2 Aug 2026 07:25:07 -0400 Subject: [PATCH] [Fix] (MG_State): detach a deleted texture from the framebuffer that is bound GL 3.3 core 4.4.2: deleting a texture whose image is attached to the framebuffer currently bound acts as if FramebufferTexture* had been called with texture zero for every attachment point it occupied there. Framebuffers that are not bound keep the orphaned attachment, so only the bound ones are touched. MobileGL unbound a deleted texture from every texture unit and image binding but left framebuffer attachments alone, so the framebuffer went on holding the dead texture alive as its attachment and reads through it returned that texture's contents rather than those of whatever the application put in its place - and since the deleted name usually comes straight back out of the next glGenTextures, the two are indistinguishable from the outside. --- MobileGL/MG_State/GLState/Core.cpp | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/MobileGL/MG_State/GLState/Core.cpp b/MobileGL/MG_State/GLState/Core.cpp index 856a5d18..b8602c31 100644 --- a/MobileGL/MG_State/GLState/Core.cpp +++ b/MobileGL/MG_State/GLState/Core.cpp @@ -241,6 +241,32 @@ namespace MobileGL::MG_State { } void GLContext::MarkTextureObjectForDeletion(Uint index) { + // GL 3.3 core 4.4.2: deleting a texture whose image is attached to the framebuffer + // that is currently bound acts as if FramebufferTexture* had been called with texture + // zero for every attachment point it occupied there. Framebuffers that are NOT bound + // keep the orphaned attachment, so only the bound ones are touched. + // + // Without this the framebuffer object goes on holding the deleted texture alive as its + // attachment, and a later read through that framebuffer returns the dead texture's + // contents rather than those of whatever the application put in its place - the name + // it deleted usually comes straight back from the next glGenTextures, so the two are + // indistinguishable from the outside (KHR-GL32.packed_pixels read a stale gradient). + if (const auto& textureObject = m_textureState.GetTextureObject(index)) { + for (SizeT targetIndex = 0; targetIndex < SizeT(FramebufferTarget::FramebufferTargetCount); + ++targetIndex) { + const auto& framebuffer = + GetFramebufferBindingSlot(static_cast(targetIndex)).GetBoundObject(); + if (!framebuffer || framebuffer->IsDefaultFramebuffer()) { + continue; + } + const auto& attachments = framebuffer->GetAllAttachmentObjects(); + for (SizeT i = 0; i < attachments.size(); ++i) { + if (attachments[i].IsTexture() && attachments[i].GetTexture() == textureObject) { + framebuffer->Detach(static_cast(i)); + } + } + } + } m_textureState.MarkTextureObjectForDeletion(index, IsRelaxedSemanticsActive()); }