Skip to content

Survive WebGL context loss and sparse draw lists - #3

Merged
Mistium merged 1 commit into
developfrom
harden-renderer
Sep 24, 2026
Merged

Mistium merged 1 commit into
developfrom
harden-renderer

Conversation

@Mistium

@Mistium Mistium commented Sep 24, 2026

Copy link
Copy Markdown
Member

Two crash paths in the renderer and one diagnostic.

WebGL context loss. Nothing listened for webglcontextlost, so when the browser took the context away (GPU reset, too many WebGL tabs, a backgrounded mobile tab) the next draw() failed inside _buildShader with an unhelpful compile error, and because nothing called preventDefault the browser never tried to restore the context. The renderer now:

  • listens for both events on its canvas, calls preventDefault on loss, sets isContextLost, skips draw() while lost, and emits ContextLost;
  • on restore, rebuilds the shader cache, geometry, and draw region state, asks every skin to drop the textures that died with the old context, marks itself dirty, and emits ContextRestored.

Each skin gets an onContextRestored hook. SVG skins rebuild their MIPs from the still-loaded image on the next draw, text bubbles re-render from their text, the pen layer recreates its shaders, buffers, and framebuffer (its contents are gone, as they would be in any renderer), and bitmap skins stay empty until the VM sets them again. The VM and GUI can listen for the two events to reload costumes or tell the user; this PR keeps the renderer usable and quiet instead of throwing every frame.

Sparse draw list. With the experimental real layer indexes setting enabled, setDrawableOrder leaves holes in _drawList, and shifting entries turns holes into explicit undefined. _drawThese and _candidatesTouching already skip those, but pick() did not, so clicking the stage threw on the first hole. It now skips holes and destroyed ids.

Shader diagnostics. Production has reported ERROR: 0:1: 'vertex' : syntax error, which cannot come from sprite.vert itself: line 1 of every compiled shader is a #define. The compile failure now includes the first 80 characters of the vertex and fragment source that were actually compiled, so the next report shows what the bundler handed over.

Also fixes the lint errors in test/unit/ResourceDisposalTests.js so npm test no longer fails at the lint step.

Tests: eslint . clean, tap test/unit passes (new ContextLossTests.js covers the lost and restored paths and the sparse pick), and npm run build && tap test/integration passes (pick, scratch, skin size).

No visible surface.

- The renderer now listens for webglcontextlost and webglcontextrestored on its canvas. Losing the context calls preventDefault so the browser can give it back, sets isContextLost, skips draw() until then, and emits ContextLost. Restoring rebuilds the shader cache, geometry, and draw region state, asks every skin to drop the textures that died with the old context, and emits ContextRestored. SVG and text bubble skins rebuild from their source on the next draw; the pen layer recreates its buffers and framebuffer; bitmap skins stay empty until they are set again.
- pick() ignores holes and destroyed ids in the draw list. With real layer indexes enabled the list is sparse, and clicking the stage threw on the first hole.
- A shader compile failure now includes the start of the source that was compiled, so a syntax error on line 1 shows what the bundler handed over instead of the shader.
- The resource disposal test file lints again, so npm test no longer fails at the lint step.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@Mistium
Mistium merged commit 9ba0a0a into develop Sep 24, 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.

1 participant