Skip to content

Commit e4e20b8

Browse files
strukturedclaude
andcommitted
Detach textures from framebuffers before deleting
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) <noreply@anthropic.com>
1 parent 3649044 commit e4e20b8

1 file changed

Lines changed: 25 additions & 1 deletion

File tree

src/libprojectM/Renderer/Framebuffer.cpp

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,19 @@ Framebuffer::~Framebuffer()
2424
{
2525
if (!m_framebufferIds.empty())
2626
{
27-
// Delete attached textures first
27+
// Detach all textures from each framebuffer before destroying them.
28+
// The GL driver keeps internal references to attached textures, so we must
29+
// detach before deleting to avoid use-after-free in driver memory.
30+
for (auto& [index, attachments] : m_attachments)
31+
{
32+
glBindFramebuffer(GL_FRAMEBUFFER, m_framebufferIds.at(index));
33+
for (auto& [type, _] : attachments)
34+
{
35+
glFramebufferTexture2D(GL_FRAMEBUFFER, type, GL_TEXTURE_2D, 0, 0);
36+
}
37+
}
38+
glBindFramebuffer(GL_FRAMEBUFFER, 0);
39+
2840
m_attachments.clear();
2941

3042
glDeleteFramebuffers(static_cast<int>(m_framebufferIds.size()), m_framebufferIds.data());
@@ -92,9 +104,21 @@ bool Framebuffer::SetSize(int width, int height)
92104
for (auto& attachments : m_attachments)
93105
{
94106
Bind(attachments.first);
107+
// First detach all textures from framebuffer before destroying them.
108+
// The GL driver keeps internal references to attached textures, so we must
109+
// detach before deleting to avoid use-after-free in driver memory.
110+
for (auto& texture : attachments.second)
111+
{
112+
glFramebufferTexture2D(GL_FRAMEBUFFER, texture.first, GL_TEXTURE_2D, 0, 0);
113+
}
114+
// Now safe to resize (which destroys old textures and creates new ones)
95115
for (auto& texture : attachments.second)
96116
{
97117
texture.second->SetSize(width, height);
118+
}
119+
// Reattach the new textures
120+
for (auto& texture : attachments.second)
121+
{
98122
glFramebufferTexture2D(GL_FRAMEBUFFER, texture.first, GL_TEXTURE_2D, texture.second->Texture()->TextureID(), 0);
99123
}
100124
}

0 commit comments

Comments
 (0)