WebGL 2 only renderer + Vertex Array Objects (#1509) — 20.0.0 - #1553
Merged
Conversation
Phase 0 of the WebGPU groundwork: drop the WebGL 1 path and give every
batcher an immutable vertex state, so the layout description stops being
re-issued per draw and starts looking like a pipeline descriptor.
BREAKING: the WebGL renderer now requires WebGL 2.
- `video.AUTO` falls back to the Canvas renderer on WebGL-1-only devices;
`video.WEBGL` throws there
- `preferWebGL1` setting and the `#webgl1` URL flag removed
- `device.isWebGLSupported()` probes for WebGL 2 — it now agrees with what
renderer construction actually requests (it probed WebGL 1 before, so
the gate and the context could disagree)
- `renderer.type` is always "WebGL2"; `renderer.WebGLVersion` deprecated
- corrections on ex-WebGL-1 configs: NPOT `repeat` genuinely tiles,
darken/lighten use true MIN/MAX, `createPattern()` accepts NPOT
- user shaders need NO changes: GLSL ES 1.00 compiles on WebGL 2 contexts
Vertex Array Objects:
- new `WebGLVertexState` (buffer/vertexstate.js, sibling of
WebGLIndexBuffer) owns the VAO lifecycle; its binding save/restore is
private, so no caller can half-apply the protocol
- built from a {attributes, stride, buffer, indexBuffer} descriptor — a
GPUVertexBufferLayout + arrayStride, so #1492 becomes a class swap
- batcher switches cost one bindVertexArray; steady-state frames issue
zero attribute-specification calls (19 at startup, 0 per frame after)
- the attribute-leak machinery is gone: state is swapped, not disabled
- custom shaders on a built-in batcher must declare that batcher's
attributes first, in layout order — warns once per shader on mismatch
- TMX GPU tilemap eligibility now uses `renderer.supportsShaderTileLayers`
(a backend capability flag) instead of a WebGL-version check
Tests: 6 new specs (vertex-state units, VAO state/call-counts/recreation/
contract/adversarial, teardown), the attribute-leak spec retired and its
invariant re-expressed, 4888 passing.
Closes #1509
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JvxaXQRgQn5AoR8DNkv6Wg
- `device.isWebGLSupported()` documented as probing WebGL 2, and as the same probe renderer construction uses, with the AUTO-falls-back / WEBGL-throws consequence spelled out (the old text claimed the renderer "will switch to CANVAS mode", true only on the AUTO path) - `failIfMajorPerformanceCaveat` notes that melonJS defaults it to `true` where the WebGL default is `false`, and what that means combined with the WebGL 2 requirement - changelog: record the resulting narrowing of which devices get the WebGL renderer Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JvxaXQRgQn5AoR8DNkv6Wg
This was referenced Jul 29, 2026
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.
Phase 0 of the WebGPU groundwork (#1184): drop the WebGL 1 path, and give every batcher an immutable vertex state so the layout stops being re-issued per draw and starts looking like a pipeline descriptor.
Breaking: the WebGL renderer requires WebGL 2
renderer: video.AUTOfalls back to Canvas on WebGL-1-only devices;renderer: video.WEBGLthrows therepreferWebGL1setting and the#webgl1URL flag are removed (#webgl/#webgl2are synonyms)device.isWebGLSupported()now probes for a WebGL 2 context. This fixes a latent inconsistency: it probed WebGL 1 while context creation requested WebGL 2, so the support gate and the actual renderer could disagree.renderer.typeis always"WebGL2";renderer.WebGLVersionis deprecated (always2)repeatwrap genuinely tiles NPOT textures (was clamp + warning),"darken"/"lighten"use true MIN/MAX equations (were silently downgraded to"normal"),createPattern()accepts NPOT sources (threw before)User shaders need no changes.
ShaderEffectbodies and rawGLShadersources stay GLSL ES 1.00 — WebGL 2 compiles them by spec, and every modern install already ran them on a WebGL 2 context. Engine shaders are deliberately not migrated to ES 3.00 here; the rule going forward is that a shader migrates only when it needs a 3.00 feature (first such case ticketed as #1552).Vertex Array Objects
WebGLVertexState(buffer/vertexstate.js, sibling ofWebGLIndexBuffer) owns the VAO lifecycle. Its binding save/restore is private, so no caller can half-apply the protocol — which is precisely the omission that produced the one high-severity bug found in review.{attributes, stride, buffer, indexBuffer}descriptor — aGPUVertexBufferLayout+arrayStride— so [WebGPU port] Backend-neutral vertex layout descriptor #1492 becomes a class swap rather than an extraction.bindVertexArray; steady-state frames issue zero attribute-specification calls (19vertexAttribPointer+ 19enableVertexAttribArrayat startup, 0 per frame after; previously 3–5 of each per switch plus 3–4 per mesh flush).renderer.supportsShaderTileLayers, a backend capability flag on the baseRenderer, instead of a WebGL-version check.Performance, honestly
Measured on the bundled examples, this removes ~2 GL calls/frame in a single-batcher scene and ~40 in one mixing sprites, meshes and primitives — well under a millisecond either way. The structural benefits (impossible state leaks, pipeline-shaped layout) are the real payoff; this is not an FPS change.
Verification
webgl_vertexstate(13 direct unit tests on the new class), plus VAO state / call-counts / recreation / contract / adversarial / teardown. The oldbatcher_attribute_leakspec is retired and its invariant re-expressed for the VAO era.ARRAY_BUFFERbinding, so rebuilding a non-current batcher corrupted the current one's uploads — silently, withNO_ERROR). Fixed at the root, with a regression test. Also fixed: VAOs were never released on destroy (plus pre-existing buffer/shader leaks), spurious warnings during lost-context re-init, and a swallowed error in renderer negotiation.Closes #1509
🤖 Generated with Claude Code
https://claude.ai/code/session_01JvxaXQRgQn5AoR8DNkv6Wg