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. Any consumer that tears one down leaks a full-window RGBA texture and a 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.

No caller in the tree deletes a framebuffer yet, so the delete() chain has no consumer until vtkOpenGLHardwareSelector gains one in #3620. The attachment release does take effect immediately on the paths that already run: setOpenGLRenderWindow() releases when the render window changes, which the volume mapper, hardware selector and the four render passes reach on every render, and create() releases before it rebuilds. Wiring the volume mapper and the render passes to free their framebuffers on teardown is left to a follow-up.

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 assigned the result of splice back to colorBuffers, which keeps the removed entry and drops every later one. Clearing in place keeps array indices aligned with GL color attachment points (COLOR_ATTACHMENT0 + n) for consumers using multiple attachments (SurfaceLIC attaches 0, 2, 3), and 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. It also repoints a saved binding that named the framebuffer it released, which would otherwise restore a deleted object.
  • 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

@PaulHax
PaulHax marked this pull request as ready for review August 28, 2026 21:32
@PaulHax
PaulHax force-pushed the framebuffer-owned-resources branch 2 times, most recently from a583311 to cae8e14 Compare August 30, 2026 20:59
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.

No caller in the tree deletes a framebuffer yet, so the delete chain has no
consumer until vtkOpenGLHardwareSelector gains one. The attachment release
does take effect immediately on the paths that already run:
setOpenGLRenderWindow releases when the render window changes, which the
volume mapper, hardware selector and the four render passes reach on every
render, and create releases before it rebuilds.

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 assigned the result of splice back to colorBuffers, which
keeps the removed entry and drops every later one. Clear the slot in place
instead, so indices stay aligned with GL color attachment points, and let bind
skip 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, and repoints a saved binding that named the released framebuffer.

Tests cover attachment ownership, repopulation, binding restoration and delete
through the public API, counting live WebGL objects.
@PaulHax
PaulHax force-pushed the framebuffer-owned-resources branch from cae8e14 to a9d004c Compare August 30, 2026 22:48
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