Skip to content

WebGL save/restore: transform not preserved across save/restore (3 tests fail when WebGL2 runs) #1481

@obiot

Description

@obiot

Problem

`tests/webgl_save_restore.spec.js` has three tests that fail when WebGL2 is actually available in the test environment:

  1. "should preserve transform across save/restore"
  2. "should handle nested save/restore correctly"
  3. "should handle deep nesting (20 levels)"

All three fail at the same assertion shape: after a `save()` → mutate → `restore()` sequence, `renderer.currentTransform.val[0]` reads as `0` instead of the expected `1` (the identity-matrix top-left).

These were silently masked because the tests guard with:

```js
if (!isWebGL) {
return;
}
```

The `failIfMajorPerformanceCaveat: true` option in the test setup means chromium headless without GPU flags falls back to Canvas, the guard returns early, and the failure never reports.

Why this is being filed now

While landing #1480 (mesh-depth single-clear refactor), I temporarily added chromium swiftshader launch flags to `vitest.config.ts` so the new WebGL2 pixel-level tests would run in CI. That surfaced these three failures. The flags were reverted to keep #1480's scope tight; this issue tracks the underlying bug.

Repro

Add to `vitest.config.ts`:

```ts
provider: playwright({
launchOptions: {
args: [
"--use-gl=angle",
"--use-angle=swiftshader",
"--enable-unsafe-swiftshader",
"--ignore-gpu-blocklist",
],
},
}),
```

Then `pnpm vitest run tests/webgl_save_restore.spec.js` produces:

```
3 failed | 10 passed (13)

  • should preserve transform across save/restore
    AssertionError: expected +0 to be close to 1
  • should handle nested save/restore correctly
    AssertionError: expected +0 to be close to 1
  • should handle deep nesting (20 levels)
    AssertionError: expected +0 to be close to 1
    ```

Reproduces on a clean checkout of master (no #1480 changes) — pre-existing bug.

Suspected area

The test predates the RenderState refactor (commit af34dd3 "Refactor renderer state into RenderState with zero-allocation save/restore"). One of:

  1. The pre-allocated index-based stack indexes the wrong slot for transform state
  2. `save()` captures by reference instead of cloning — mutations to currentTransform between save/restore leak into the saved value
  3. `restore()` writes the post-mutation matrix back instead of the saved one

Color save/restore tests pass; only transform fails. So the bug is specific to how the transform matrix is captured / restored, not the broader save/restore stack mechanics.

Acceptance

  • All 13 tests in `tests/webgl_save_restore.spec.js` pass under WebGL2 (verified by re-enabling swiftshader flags + running locally)
  • `vitest.config.ts` can ship the swiftshader flags so WebGL2-dependent tests run in CI instead of skipping (this unlocks the mesh-depth pixel tests from feat(renderer): single depth clear per target for mesh draws (#1468) #1480 to actually run in CI)
  • No regression in Canvas-renderer behaviour (the early-out guard becomes irrelevant once the bug is fixed)

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions