fix(shader): emit contextLost when shader creation fails on a lost GL context#110
Merged
Merged
Conversation
… context A lost GL context makes gl.createShader() return null and getError() report CONTEXT_LOST_WEBGL (0x9242 / 37442), surfacing as an "Unable to create the shader" / "Vertex shader creation failed" throw. Shader programs compile lazily on first use of each distinct shader key, so this can fire mid-session — and it runs on the consumer's reactive stack, which can beat the async `webglcontextlost` DOM event. The throw then propagates out of renderer.createShader() with no `contextLost` signal, so consumers can't react. - WebGlContextWrapper: add an isContextLost() passthrough. getError() can't be reused for detection here — its CONTEXT_LOST_WEBGL flag is cleared on first read (already consumed building the error message). - WebGlRenderer.createShaderProgram: detect a lost context on failure and trip stage.setContextLost() (idempotent; emits `contextLost`), then rethrow. Closes the race with the async webglcontextlost event. - CoreShaderManager.createShader: on a lost context, fail soft by returning the default shader node so the throw doesn't tear through the consumer's reactive layer (recovery is an app reload). A genuine GLSL compile error (context not lost) still rethrows loudly. Adds unit tests covering both the lost-context (fail-soft + event) and healthy-context (rethrow) paths. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Production telemetry on modern Chrome/Edge surfaced:
37442=0x9242=CONTEXT_LOST_WEBGL. The GL context was lost, sogl.createShader()returned null andWebGlShaderProgramthrew. Two facts make this nasty:convertToShader→renderer.createShader()), which can execute before the asyncwebglcontextlostDOM event is processed. The throw then propagates out of the public API with nocontextLostsignal, so consumers have nothing to react to and the exception tears through their reactive layer.Fix
Detect the lost context at the point of failure and route it through the existing
contextLostchannel, splitting responsibility cleanly:WebGlContextWrapper— add anisContextLost()passthrough.getError()can't be reused for detection here: itsCONTEXT_LOST_WEBGLflag is cleared on first read (already consumed building the error message).isContextLost()is stateful and reliable.WebGlRenderer.createShaderProgram(backend-specific) — wrap in try/catch; on a genuinely lost context, tripstage.setContextLost()(idempotent; emitscontextLost), then rethrow. Closes the race with the asyncwebglcontextlostevent.CoreShaderManager.createShader(backend-agnostic) — on a lost context, fail soft by returning the default shader node so the throw doesn't propagate through the consumer's reactive layer (recovery is an app reload, per BROWSERS.md). A genuine GLSL compile error (context not lost) still rethrows loudly so real shader bugs aren't masked.The try/catch sits on the cache-miss path only (once per shader variant), not a hot path.
Behavior note
On context loss this fails soft rather than rethrowing, since the context is unrecoverable in-place and the render loop has already stopped. If a rethrow (e.g. to hit a SolidJS
<ErrorBoundary>) is preferred, theCoreShaderManagerbranch is a one-line flip.Consumers still need a
contextLostlistener to actually recover (prompt/auto reload) — the engine deliberately does not rebuild GPU resources in place.Tests
CoreShaderManager.contextLoss.test.ts— fail-soft returns default node on loss; rethrows on genuine compile error; failures aren't cached.WebGlRenderer.contextLoss.test.ts— reproduces the exactUnable to create the shader: VERTEX_SHADER. WebGlContext Error: 37442throw; verifiessetContextLost()fires only on a lost context.Full suite: 251 passing. Build + lint clean.
🤖 Generated with Claude Code