BabylonJS / BabylonJS/Babylon.js

[WebGPU-XR] Land the end-to-end WebGPU-XR harness, then run it in CI (tab crashes on the ADO Linux agent)

Open
#18,765 0 comments 1 reaction 1 assignee Claimed by @RaananW View on GitHub
bug
Dominant language
TypeScript
Stars
26.1k
Forks
3.7k
Avg merge
1d 15h
Merged PRs (30d)
73

Description

Follow-up from #18688 (Phase 3 of the WebGPU-for-WebXR epic #18635).

> **Update:** the harness was removed from #18688 so that PR carries only the Phase-3 engine fix and its
> unit tests. It is preserved verbatim on the branch **`raananw-webgpu-xr-harness`** (at `a820e23521`) and
> should be proposed as its own PR once #18688 lands. This issue therefore covers **two** things: landing
> the harness at all, and then wiring it into CI.
>
> The Phase-3 regression itself is *not* left unguarded in the meantime — it is covered in CI by
> `packages/dev/core/test/unit/XR/webXRWebGPUProjectionLayer.test.ts`, which fails if the `frameId` guard
> is reverted. The harness is the broader end-to-end pixel oracle, not the only guard.

## What exists today

`packages/tools/tests/test/webgpuXR/` is a headless-Chromium harness that drives the **real** Babylon WebGPU XR render path through a mock `XRGPUBinding`/`XRSession`/`XRFrame`/`XRProjectionLayer` and judges each frame by pixel readback. It reproduces the Phase-3 per-eye clear regression objectively:

```bash
npm run test:webgpu-xr
```

| Tree | Result |
|---|---|
| With the fix | `PASS` — geometry on both eyes, 40/40 frames |
| With the fix reverted | `FAIL` — 39/40 frames without geometry on both eyes (the device signature was 1/60) |

It runs on a **GPU-less** machine via the SwiftShader software adapter, which is the CI-relevant case:

```bash
CHROME_ARGS="--use-angle=swiftshader --enable-features=Vulkan" npm run test:webgpu-xr
```

Locally that is ~3 s and **10/10 runs passed with zero flake**.

**It is not wired into CI.** It runs by hand.

## Why it isn't in CI yet

Wiring it into the agent-local *Interaction tests* job was attempted and reverted. The blocker is **not** WebGPU availability — the agent does provide an adapter.

Measured on the ADO Linux agent across four runs:

```
WEBGPU-XR: RAN - WebGPU adapter = google/swiftshader <- WebGPU IS available
WEBGPU-XR: phase=rendering frames (+1708ms)
[harness] +45ms engine ready isWebGPU=true halfZ=true
[harness] +75ms scene built, starting render loop
[harness] +117ms first frame rendered <- shader compilation is FINE
... nothing further ...
page.evaluate: Target crashed <- the renderer process dies
```

So: the adapter comes up, the engine initialises, and the **first frame renders in ~120 ms**. The renderer process then dies. Shader-compilation slowness and frame budget were both ruled out — trimming the frame budget from 40 to 6 changed nothing, because the failure is a crash, not a timeout.

Things already tried that did **not** fix it:
- `FRAME_BUDGET=6` (regression appears on frame 1, so 6 frames is sufficient — verified: 5/6 failures with the fix reverted)
- raising the budget to 15 minutes
- `--disable-dev-shm-usage` (the classic small-`/dev/shm`-in-a-container cause — did not help)

## Leads for whoever picks this up

- **Memory.** The mock allocates a 3-deep pool of 2-layer texture arrays plus per-frame readback buffers, and the bundle is ~16 MB. A hosted agent is 2 cores / 7 GB. Try a smaller `scaleFactor`/texture size and a shallower pool.
- Capture the actual crash reason (`chrome://crash`, `--enable-logging=stderr --v=1`, or core dump) rather than inferring it — that is the single highest-value next step.
- Consider a container image with a larger `/dev/shm`, or a self-hosted agent.

Useful knobs already built into the runner: `FRAME_BUDGET`, `STEP_TIMEOUT_MS`, `HARNESS_WAIT_MS`, `ADAPTER_TIMEOUT_MS`, `ALLOW_NO_WEBGPU`, `CHANNEL`, `CHROME_ARGS`. It prints `WEBGPU-XR: RAN`/`SKIPPED` and `WEBGPU-XR: phase= (+ms)` so a CI log says exactly where it gave up, and it reports a tab crash explicitly instead of throwing an opaque `Target crashed`.

## Disproved beliefs — do not re-derive these

Both of these were believed, acted on, and then proved false during the four measurement runs. They cost real hours. Recorded verbatim so the next person does not pay for them again.

**1. "Playwright's bundled Chromium has no WebGPU." FALSE.**
The probe that produced that conclusion ran against `about:blank`, which is an **opaque origin** and therefore exposes no `navigator.gpu` — regardless of what the browser supports. The same browser on a `localhost` page has WebGPU fully available. No `channel: "chrome"` install is needed. **Always probe on a localhost page, never `about:blank`.**

**2. "The repo's WebGPU visualization suite is green in CI, therefore CI agents have WebGPU." FALSE.**
That suite runs on **BrowserStack**, not on an agent-local browser. Only *Interaction tests* and *Viewer tests* run agent-local, and `--project=viewer-lite` — the WebGPU-only Playwright project — is **never invoked in CI**. Existing WebGPU coverage is not a precedent for agent-local WebGPU.

**3. Method note.** Runs 2 and 3 were spent on a timeout hypothesis (smaller frame budget, then a bigger budget). It was a reasonable model until `--disable-dev-shm-usage` also failed to move the number. The generalisable rule: **a fix that does not move the number means the model is wrong — stop fixing and start measuring.** Capturing the actual crash reason is cheaper than the third hypothesis.

## Notes

- Ruled out as causes, with evidence above: WebGPU availability, adapter startup, WGSL/pipeline compilation cost, frame count, and overall time budget.
- The regression itself **is** covered in CI by the unit test at `packages/dev/core/test/unit/XR/webXRWebGPUProjectionLayer.test.ts`. This issue is about the end-to-end pixel-oracle harness, which is broader but not currently automated.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.