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>
This commit is contained in:
struktured
2026-03-29 17:29:12 -04:00
parent 3649044362
commit e4e20b8fce

View File

@ -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<int>(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);
}
}