Skip to content

fix(webgl): cache support probe so it creates a context at most once#37

Merged
chiefcll merged 1 commit into
mainfrom
fix/webgl-support-context-leak
Jun 25, 2026
Merged

fix(webgl): cache support probe so it creates a context at most once#37
chiefcll merged 1 commit into
mainfrom
fix/webgl-support-context-leak

Conversation

@chiefcll

Copy link
Copy Markdown
Contributor

What

getWebglSupportedVersions (src/utils.ts) creates a real WebGL context (canvas.getContext) to detect support, but it only cached the result when called with the default id list (reference equality against the internal WEBGL_CONTEXT_IDS). Any caller passing its own array — which our app does — bypassed the cache and created a new WebGL context on every call.

This PR:

  • Caches per distinct id list (a Map keyed on the joined ids), so a probe context is created at most once per page, regardless of how the caller invokes it.
  • Releases the probe context via WEBGL_lose_context, guarded by isContextLost() so we never call loseContext() on a context the browser already evicted.

Why

Reported on Apollo/Sunrise TV boxes (Chrome 38+). The device console showed, all originating at this function:

There are too many active WebGL contexts on this page, the oldest context will be lost.
WebGL: INVALID_OPERATION: loseContext: context already lost
Could not create WebGL Texture            (renderer: WebGlCtxTexture)
Failed to upload texture: ...             (renderer: CoreTextureManager)

Embedded TV SoCs cap the number of live WebGL contexts very low. Re-probing on every call blows that budget; the browser evicts the oldest context — the live render context — and the renderer then fails every createTexture(), spamming Could not create WebGL Texture each requestAnimationFrame. A prior loseContext-only attempt didn't help (and produced the context already lost error) because the eviction happens at context creation time — the cure is to stop creating contexts repeatedly.

Notes for reviewers

  • Detection output is unchanged for any given argument. The single-canvas probe logic (and therefore supportsWebGL / supportsWebGL2 / supportsOnlyWebGL2 results) is identical — only the number of contexts created changes (now: one per arg list, ever).
  • The companion renderer-side leak (detectPremultiplyAlphaHonored) is fixed separately in solid-tv/renderer: fix(webgl): release throwaway context in premultiply-alpha probe renderer#114
  • Follow-up worth considering at the call site: invoke detection once at startup, before the renderer is created, so even the first probe can't evict a live render context.

Tests

New tests/webgl-support.test.ts (4 tests) asserts the behavioral guarantees:

  • probe context is created at most once across repeated calls
  • loseContext is skipped when the context is already lost
  • probe context is released (lose-context extension requested)
  • no throw when WebGL is unavailable

npx vitest run → full suite 134/134 pass; tsc --noEmit clean; prettier + eslint clean.

🤖 Generated with Claude Code

`getWebglSupportedVersions` creates a real WebGL context (`getContext`) to test
support, but only cached the result when called with the *default* id list
(reference equality against the internal `WEBGL_CONTEXT_IDS`). Any caller passing
its own array bypassed the cache and created a brand-new context on every call.

On embedded TV boxes (Apollo/Sunrise, Chrome 38+) the live-context budget is
tiny. Re-probing repeatedly blows that budget; the browser evicts the oldest
context — the live render context — and the renderer then fails every
`createTexture()`, spamming "Could not create WebGL Texture" each frame. The
device console showed both "too many active WebGL contexts" and
"INVALID_OPERATION: loseContext: context already lost" originating here.

Changes:
- Cache per distinct id list (Map keyed on the joined ids) so a probe context is
  created at most once per page, regardless of how the caller invokes it.
- Release the probe context via WEBGL_lose_context, guarded by isContextLost()
  so we never call loseContext on a context the browser already evicted (the
  source of the "context already lost" console spam).

Detection output is unchanged for any given argument — only the number of
contexts created changes (now: one per arg list, ever).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@chiefcll chiefcll merged commit 5f09d00 into main Jun 25, 2026
1 check passed
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