From e4e20b8fce64c1846754ef67c4b6ca4d267e8ffa Mon Sep 17 00:00:00 2001 From: struktured Date: Sun, 29 Mar 2026 17:29:12 -0400 Subject: [PATCH] Detach textures from framebuffers before deleting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GL drivers keep internal references to textures attached to framebuffers. Deleting a texture while still attached can cause use-after-free in driver memory, observed as crashes on NVIDIA during rapid preset switching or window resize. Detach all textures before deletion in both ~Framebuffer() and SetSize(), using a three-phase detach → resize → reattach pattern in SetSize() to avoid referencing stale texture IDs. Co-Authored-By: Claude Opus 4.6 (1M context) --- src/libprojectM/Renderer/Framebuffer.cpp | 26 +++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/src/libprojectM/Renderer/Framebuffer.cpp b/src/libprojectM/Renderer/Framebuffer.cpp index 29994d19f..1d171c229 100644 --- a/src/libprojectM/Renderer/Framebuffer.cpp +++ b/src/libprojectM/Renderer/Framebuffer.cpp @@ -24,7 +24,19 @@ Framebuffer::~Framebuffer() { if (!m_framebufferIds.empty()) { - // Delete attached textures first + // Detach all textures from each framebuffer before destroying them. + // The GL driver keeps internal references to attached textures, so we must + // detach before deleting to avoid use-after-free in driver memory. + for (auto& [index, attachments] : m_attachments) + { + glBindFramebuffer(GL_FRAMEBUFFER, m_framebufferIds.at(index)); + for (auto& [type, _] : attachments) + { + glFramebufferTexture2D(GL_FRAMEBUFFER, type, GL_TEXTURE_2D, 0, 0); + } + } + glBindFramebuffer(GL_FRAMEBUFFER, 0); + m_attachments.clear(); glDeleteFramebuffers(static_cast(m_framebufferIds.size()), m_framebufferIds.data()); @@ -92,9 +104,21 @@ bool Framebuffer::SetSize(int width, int height) for (auto& attachments : m_attachments) { Bind(attachments.first); + // First detach all textures from framebuffer before destroying them. + // The GL driver keeps internal references to attached textures, so we must + // detach before deleting to avoid use-after-free in driver memory. + for (auto& texture : attachments.second) + { + glFramebufferTexture2D(GL_FRAMEBUFFER, texture.first, GL_TEXTURE_2D, 0, 0); + } + // Now safe to resize (which destroys old textures and creates new ones) for (auto& texture : attachments.second) { texture.second->SetSize(width, height); + } + // Reattach the new textures + for (auto& texture : attachments.second) + { glFramebufferTexture2D(GL_FRAMEBUFFER, texture.first, GL_TEXTURE_2D, texture.second->Texture()->TextureID(), 0); } }