Skip to content

Release the WebGL context when a video processor is destroyed - #144

Merged
changt merged 1 commit into
mainfrom
release-webgl-context
Oct 20, 2026
Merged

changt merged 1 commit into
mainfrom
release-webgl-context

Conversation

@changt

@changt changt commented Oct 19, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes #136. Each setProcessor / stopProcessor cycle left one WebGL context alive until garbage collection. Chrome caps live contexts per GPU process at around 16.

VideoTransformer.destroy() now releases the context with WEBGL_lose_context.loseContext(), through a new releaseContext() on the setupWebGL pipeline.

Why not just call loseContext() in cleanup(), as the issue suggests

  • Restart: VideoTransformer.restart() runs cleanup() and then setupWebGL() on what can be the same canvas. A lost context stays bound to its canvas, and getContext() returns it again, so restart would break. The release is therefore a separate step that only destroy() calls. ProcessorWrapper.restart() is unaffected because init() always creates a new canvas.
  • Shared context: the MediaPipe ImageSegmenter is given the same canvas, so it renders into the same context. BackgroundTransformer.destroy() now closes the segmenter before super.destroy() releases the context. It also sets isDisabled before that await, so frames that arrive during the close are passed through.
  • Missing API: getExtension(...)?.loseContext() does nothing when the extension is unavailable, which leaves the context to GC as before.
  • Double destroy: ProcessorWrapper's lifecycle state already guards against it. VideoTransformer.destroy() is synchronous and clears this.gl, so a second call does nothing.

Tests

  • Unit test: the context survives cleanup(), can be set up again on the same canvas, and is lost after releaseContext().
  • e2e test B3: three attach/detach cycles release one context each. It fails without the fix.
  • Unit tests, typecheck and all 18 e2e tests pass locally.

workers: 1 in the e2e config is left as is. MediaPipe still creates contexts of its own, so raising it should be a separate check.

VideoTransformer.destroy() now calls WEBGL_lose_context.loseContext() via a new
releaseContext() on the setupWebGL pipeline, so each attach/detach cycle no
longer leaves a context alive until GC (#136).

- Kept out of cleanup(): restart() runs cleanup() then setupWebGL() on what can
  be the same canvas, and a lost context stays bound to its canvas.
- BackgroundTransformer.destroy() closes the MediaPipe segmenter, which shares
  the canvas's context, before super.destroy() releases it.
- Optional chaining leaves the context to GC where the extension is missing.

Adds a unit test pinning cleanup() vs releaseContext() and e2e test B3, which
fails without the fix.
@changeset-bot

changeset-bot Bot commented Oct 19, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 5b038be

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@livekit/track-processors Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@changt
changt merged commit 2e0a066 into main Oct 20, 2026
3 checks passed
@changt
changt deleted the release-webgl-context branch October 20, 2026 14:04
@github-actions github-actions Bot mentioned this pull request Oct 20, 2026
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.

WebGL context from setupWebGL() is never released: cleanup() does not call loseContext()

2 participants