Commit b4ee713
fix(video): six GL-core audit findings, each reproduced before fixing (#1542)
* fix(video): six GL-core audit findings, each reproduced before fixing
Beat 1 of the resumed subsystem audit (video/GL core, single-finder +
inline verification). Per protocol every bug was demonstrated red with
a reproduction test BEFORE being touched; one finding was refined
during reproduction (fillRect gradients never used the stencil path —
scoped the fix to the shape fills that do).
1. getSupportedCompressedTextureFormats: six WEBKIT fallbacks read the
never-assigned `this._gl` → TypeError on any device missing one
compression family (everything except ANGLE/Metal Macs — which is
why it survived: the dev machines expose every family). → `gl.`
2. Nested multi-effect post-effects: RenderTargetPool's two scalars
(_activeBase/_previousBase) became a proper base STACK — each
nested sprite pass gets its own lazily-allocated capture/ping-pong
pair (2/3, 4/5, …) and begin/end pairs unwind like save/restore;
the saved effect projection is a per-pass stack too. Pre-fix, a
2-effect child inside a 2-effect container cleared the parent's
capture, popped its slot, and crashed endPostEffect every frame.
3. #gradientMask: write/clip/restore phases now use a high-bit stencil
marker (collision-free with mask levels) gated on the VISIBLE value
of the active mask (0 when inverted, maskLevel otherwise), and
restore setMask's exact stencil test — the old code painted
gradients inside inverted cutouts and left a NOTEQUAL parity test
that inverted/leaked all subsequent masked draws. setMask/clearMask
now track _maskInvert.
4. disableScissor() flushes pending quads before disabling the test
(mirrors enableScissor/clipRect/restore) — batched sprites could
escape the scissor entirely.
5. WebGL clearRect() erases to actual transparent black per its JSDoc
("#000000" parses with alpha 1 → it painted opaque black; Canvas
parity).
6. Canvas clipRect() skip-optimizations now reason in DEVICE space
(rotation/skew always clips + cache poisoned): the local-space
early-outs dropped canvas-sized clips under a translate and skipped
nested same-size clips via the stale cache.
Reproductions: tests/glcore-audit.spec.js (5) +
tests/canvas-cliprect-transform.spec.js (2) — all 7 red on the unfixed
code (nested-FBO red with the exact predicted undefined.unbind).
renderTargetPool.spec.js updated to the stack internals + 2 new
nesting tests. Full suite 4736 passed / 0 failed.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019t8kP8vp58ZrvaD2FqJU6A
* fix(review): inert NaN cache poison + integer cache + reset cleanup + single-source mask ref
The pre-merge review agent caught a real defect in fix 6: currentScissor
is an Int32Array, so the rotated-clip cache poison (NaN) coerced to 0 —
leaving a frankenstein box a later axis-aligned clip could false-match,
reviving the exact stale-cache bug class the fix targets. Reproduced
red (rotated → axis-aligned sequence) before fixing.
- poison the WIDTH slot with -1 instead: device widths are normalized
to >= 0, so -1 can never match (same trick the WebGL clipRect spec
uses)
- floor/ceil the device box before compare/store so the Int32Array
cache is exact under fractional transforms (devicePixelRatio,
sub-pixel translates) — the WebGL clipRect's integer discipline
- WebGLRenderer.reset() clears _effectProjectionStack: a draw throwing
mid-pass orphans stack entries; without the cleanup every later pass
leaked a matrix and the pool's nesting depth inflated forever
(reproduced red: begin-without-end + reset)
- single-source the "which stencil value is visible" convention:
setMask now stores _maskVisibleRef (0 inverted / maskLevel otherwise)
and #gradientMask consumes it — the _maskInvert flag and the
duplicated ternary are gone (the review's one duplicated-logic
finding)
Review verdict otherwise: public API untouched, no collateral behavior
change outside the six bugs, stencil math and pool arithmetic verified
correct. 2 new red-first tests; full suite 4738 passed / 0 failed.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019t8kP8vp58ZrvaD2FqJU6A
* fix(review): clamp mask nesting below the marker bit + zero-alloc projection slots
Both Copilot comments (independently matching the review agent's
follow-up findings):
- setMask now clamps maskLevel at 127 with a one-shot warning: mask
levels live in the stencil's low 7 bits — the high bit is reserved
as #gradientMask's temporary marker, and an 8-bit stencil couldn't
represent deeper nesting anyway. Reproduced red first (130 nested
setMask calls → maskLevel 130 pre-fix).
- the per-pass saved projection no longer clones a Matrix3d per
beginPostEffect: preallocated slots + a depth counter (zero-alloc
steady state, slots kept across reset() for reuse).
Full suite 4739 passed / 0 failed.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019t8kP8vp58ZrvaD2FqJU6A
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>1 parent 2d11694 commit b4ee713
7 files changed
Lines changed: 604 additions & 73 deletions
File tree
- packages/melonjs
- src/video
- canvas
- rendertarget
- webgl
- tests
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
26 | 26 | | |
27 | 27 | | |
28 | 28 | | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
29 | 35 | | |
30 | 36 | | |
31 | 37 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1255 | 1255 | | |
1256 | 1256 | | |
1257 | 1257 | | |
| 1258 | + | |
| 1259 | + | |
| 1260 | + | |
| 1261 | + | |
| 1262 | + | |
| 1263 | + | |
| 1264 | + | |
| 1265 | + | |
| 1266 | + | |
| 1267 | + | |
| 1268 | + | |
| 1269 | + | |
| 1270 | + | |
| 1271 | + | |
| 1272 | + | |
| 1273 | + | |
| 1274 | + | |
| 1275 | + | |
| 1276 | + | |
| 1277 | + | |
| 1278 | + | |
| 1279 | + | |
| 1280 | + | |
| 1281 | + | |
| 1282 | + | |
| 1283 | + | |
| 1284 | + | |
| 1285 | + | |
| 1286 | + | |
| 1287 | + | |
| 1288 | + | |
| 1289 | + | |
| 1290 | + | |
| 1291 | + | |
| 1292 | + | |
| 1293 | + | |
| 1294 | + | |
| 1295 | + | |
| 1296 | + | |
| 1297 | + | |
| 1298 | + | |
| 1299 | + | |
1258 | 1300 | | |
1259 | | - | |
1260 | | - | |
1261 | | - | |
1262 | | - | |
1263 | | - | |
1264 | | - | |
1265 | | - | |
| 1301 | + | |
| 1302 | + | |
1266 | 1303 | | |
1267 | 1304 | | |
1268 | 1305 | | |
1269 | | - | |
1270 | | - | |
1271 | | - | |
1272 | | - | |
| 1306 | + | |
| 1307 | + | |
| 1308 | + | |
| 1309 | + | |
1273 | 1310 | | |
1274 | | - | |
1275 | 1311 | | |
1276 | 1312 | | |
1277 | 1313 | | |
1278 | | - | |
1279 | | - | |
1280 | | - | |
1281 | | - | |
1282 | | - | |
| 1314 | + | |
| 1315 | + | |
| 1316 | + | |
| 1317 | + | |
| 1318 | + | |
1283 | 1319 | | |
1284 | 1320 | | |
1285 | 1321 | | |
| |||
Lines changed: 49 additions & 24 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
7 | 7 | | |
8 | 8 | | |
9 | 9 | | |
10 | | - | |
11 | | - | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
12 | 15 | | |
13 | 16 | | |
14 | 17 | | |
| |||
21 | 24 | | |
22 | 25 | | |
23 | 26 | | |
24 | | - | |
25 | | - | |
26 | | - | |
27 | | - | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
28 | 44 | | |
29 | 45 | | |
30 | 46 | | |
| |||
53 | 69 | | |
54 | 70 | | |
55 | 71 | | |
56 | | - | |
57 | | - | |
58 | | - | |
59 | | - | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
60 | 85 | | |
61 | | - | |
62 | | - | |
63 | | - | |
| 86 | + | |
| 87 | + | |
64 | 88 | | |
65 | | - | |
| 89 | + | |
66 | 90 | | |
67 | 91 | | |
68 | 92 | | |
| |||
72 | 96 | | |
73 | 97 | | |
74 | 98 | | |
75 | | - | |
| 99 | + | |
| 100 | + | |
76 | 101 | | |
77 | 102 | | |
78 | | - | |
| 103 | + | |
79 | 104 | | |
80 | 105 | | |
81 | 106 | | |
82 | 107 | | |
83 | 108 | | |
84 | 109 | | |
85 | 110 | | |
86 | | - | |
| 111 | + | |
| 112 | + | |
87 | 113 | | |
88 | 114 | | |
89 | | - | |
| 115 | + | |
90 | 116 | | |
91 | 117 | | |
92 | 118 | | |
| |||
95 | 121 | | |
96 | 122 | | |
97 | 123 | | |
98 | | - | |
99 | | - | |
100 | | - | |
101 | | - | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
102 | 128 | | |
103 | 129 | | |
104 | 130 | | |
| |||
126 | 152 | | |
127 | 153 | | |
128 | 154 | | |
129 | | - | |
130 | | - | |
| 155 | + | |
131 | 156 | | |
132 | 157 | | |
0 commit comments