Skip to content

fix(OpenGL/Framebuffer): release owned resources on delete - #3619

Open
PaulHax wants to merge 1 commit into
polydata-vbo-releasefrom
framebuffer-owned-resources
Open

fix(OpenGL/Framebuffer): release owned resources on delete#3619
PaulHax wants to merge 1 commit into
polydata-vbo-releasefrom
framebuffer-owned-resources

Conversation

@PaulHax

@PaulHax PaulHax commented Aug 28, 2026

Copy link
Copy Markdown
Collaborator

Context

vtkFramebuffer.populateFramebuffer() creates a color texture and a depth renderbuffer that nothing ever frees: releaseGraphicsResources() deletes the framebuffer but not the attachments, and delete() never calls it. Every torn-down consumer (hardware selector, volume mapper, render passes) leaks a full-window RGBA texture and DEPTH_COMPONENT16 renderbuffer.

Results

Releasing or deleting a framebuffer now frees everything populateFramebuffer created along with the GL framebuffer; the live WebGL object count returns to baseline in the new tests. Caller-supplied attachments stay borrowed. Nulling glFramebuffer on release also repairs the getSize()/getGLFramebuffer() resize checks in OpenGL/VolumeMapper, ForwardPass, Convolution2DPass and RadialDistortionPass, which previously reused a deleted framebuffer object.

Changes

  • The framebuffer tracks ownership of the texture and depth renderbuffer populateFramebuffer() creates and frees them on releaseGraphicsResources(), which is now chained into delete(). When the render window is already deleted but the shared GL context is still alive (multi-view teardown order), the owned texture is freed directly on the context.
  • removeColorBuffer clears the slot instead of splicing. The previous code shifted later entries down, desynchronizing array indices from GL color attachment points (COLOR_ATTACHMENT0 + n) for consumers using multiple attachments (SurfaceLIC attaches 0, 2, 3). bind() skips empty slots.
  • create() re-binds the new framebuffer when the one it released was bound: deleting a bound framebuffer resets the GL binding to the default, which would send bind-then-create callers (SurfaceLIC) rendering to the canvas.
  • populateFramebuffer still releases attachments before creating new ones. That is redundant with create() at every current call site, but it is what keeps a repeat populateFramebuffer() from leaking, and both paths now share one releaseAttachments() implementation.
  • Documentation and TypeScript definitions were updated to match those changes

PR and Code Checklist

  • semantic-release commit messages
  • Run npm run reformat to have correctly formatted code

populateFramebuffer creates a color texture and a depth renderbuffer that
nothing ever freed: releaseGraphicsResources deleted the framebuffer but not
the attachments, and delete did not call it. Track ownership of what
populateFramebuffer creates and free it together with the framebuffer when
the framebuffer is released or deleted. Caller-supplied attachments stay
borrowed. When the render window is already deleted but the shared GL
context is still alive, the owned texture is freed directly on the context.

Nulling glFramebuffer on release also repairs the getSize and
getGLFramebuffer resize checks in OpenGL/VolumeMapper, ForwardPass,
Convolution2DPass and RadialDistortionPass, which previously reused a
deleted framebuffer object.

removeColorBuffer clears the slot instead of splicing, keeping array indices
aligned with GL color attachment points, and bind skips the holes. create
re-binds the new framebuffer when the one it released was bound, since
deleting a bound framebuffer resets the GL binding to the default one.

Tests cover attachment ownership, repopulation and delete through the public
API, counting live WebGL objects.
@PaulHax
PaulHax marked this pull request as ready for review August 28, 2026 21:32
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.

1 participant