Skip to content

WebGL 2 only renderer + Vertex Array Objects (#1509) — 20.0.0 - #1553

Merged
obiot merged 2 commits into
masterfrom
webgl2-only-vao
Jul 29, 2026
Merged

WebGL 2 only renderer + Vertex Array Objects (#1509) — 20.0.0#1553
obiot merged 2 commits into
masterfrom
webgl2-only-vao

Conversation

@obiot

@obiot obiot commented Jul 28, 2026

Copy link
Copy Markdown
Member

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.AUTO falls back to Canvas on WebGL-1-only devices; renderer: video.WEBGL throws there
  • preferWebGL1 setting and the #webgl1 URL flag are removed (#webgl / #webgl2 are 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.type is always "WebGL2"; renderer.WebGLVersion is deprecated (always 2)
  • Behaviour corrections on hardware that previously fell back to WebGL 1: repeat wrap 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. ShaderEffect bodies and raw GLShader sources 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

  • 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 — which is precisely the omission that produced the one high-severity bug found in review.
  • Built from a {attributes, stride, buffer, indexBuffer} descriptor — a GPUVertexBufferLayout + arrayStride — so [WebGPU port] Backend-neutral vertex layout descriptor #1492 becomes a class swap rather than an extraction.
  • A batcher switch is one bindVertexArray; steady-state frames issue zero attribute-specification calls (19 vertexAttribPointer + 19 enableVertexAttribArray at startup, 0 per frame after; previously 3–5 of each per switch plus 3–4 per mesh flush).
  • The attribute-leak machinery is gone — state is swapped wholesale, not disabled. That class of cross-batcher leak is now impossible by construction.
  • Custom shaders hosted by a built-in batcher must declare that batcher's attributes first, in layout order (ShaderEffect-generated vertex shaders already comply); a console warning fires once per shader on mismatch.
  • TMX GPU tilemap eligibility now uses renderer.supportsShaderTileLayers, a backend capability flag on the base Renderer, 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

  • 4888 tests passing (172 files, 1 skipped). 6 new specs: webgl_vertexstate (13 direct unit tests on the new class), plus VAO state / call-counts / recreation / contract / adversarial / teardown. The old batcher_attribute_leak spec is retired and its invariant re-expressed for the VAO era.
  • Review pass by an independent agent found one high-severity bug (build steps leaked the global ARRAY_BUFFER binding, so rebuilding a non-current batcher corrupted the current one's uploads — silently, with NO_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.
  • Visual: 7 examples captured before/after. Four are pixel-identical (gltf, sprite-illuminator, mesh-3d-material, shader-effects); the three animated ones differ by less than their measured same-code capture noise, and were inspected directly.

Closes #1509

🤖 Generated with Claude Code

https://claude.ai/code/session_01JvxaXQRgQn5AoR8DNkv6Wg

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
Copilot AI review requested due to automatic review settings July 28, 2026 23:13

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

- `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
Copilot AI review requested due to automatic review settings July 28, 2026 23:16

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@obiot
obiot merged commit a6284cc into master Jul 29, 2026
6 checks passed
@obiot
obiot deleted the webgl2-only-vao branch July 29, 2026 00:43
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.

WebGL2-only: drop WebGL1 path, adopt VAOs (per-flush attribute setup → bindVertexArray)

2 participants