Skip to content

Detach textures from framebuffers before deleting#986

Open
struktured wants to merge 1 commit intomasterfrom
fix/framebuffer-detach-before-delete
Open

Detach textures from framebuffers before deleting#986
struktured wants to merge 1 commit intomasterfrom
fix/framebuffer-detach-before-delete

Conversation

@struktured
Copy link
Copy Markdown
Contributor

Summary

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.

This commit detaches all textures from their FBOs before deletion in both ~Framebuffer() and SetSize(). The SetSize() path uses a three-phase detach → resize → reattach pattern to avoid referencing stale texture IDs after SetSize() destroys and recreates the underlying GL texture.

Changes

  • 1 file: src/libprojectM/Renderer/Framebuffer.cpp (+25, -1)

Test plan

  • Clean build on Linux (GCC, RelWithDebInfo)
  • Runtime test with rapid preset switching (N-key)
  • No NVIDIA driver crashes during sustained preset cycling

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>
// 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)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any real technical reason to add two new loops here? Each attachment is independently managed in an FBO, so the detach->reallocate->attach code can be placed in the very same loop without bloating the code.

// 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)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please do not use C++17 constructs, instead replace with a standard range-based loop. projectM must still compile with C++14.

Comment on lines +30 to +38
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);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of this loop, as the FBO is deleted anyways, it should also suffice to just move up glDeleteFramebuffers() before m_attachments.clear(); instead of adding more bloat. Deleting the FBO will also delete all registered attachments.

@struktured
Copy link
Copy Markdown
Contributor Author

Addressed all review feedback:

  1. Destructor simplified — moved glDeleteFramebuffers() before m_attachments.clear() instead of manually detaching. FBO deletion releases driver references to attached textures.
  2. SetSize merged into single loop — detach → resize → reattach now happens per-attachment in one loop instead of three.
  3. C++17 structured bindings removed — replaced auto& [index, attachments] with standard range-based auto& + .first/.second for C++14 compatibility.

Tested on NVIDIA RTX 3090 (driver 580.126.09), KDE Wayland, Qt 6.9.2.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants