Skip to content

Pin agent-browser; check the terrain gallery's WebGPU from the lab's own pixels - #5220

Merged
djizus merged 7 commits into
nextfrom
fix/terrain-gallery-browser-launch
Nov 10, 2026
Merged

djizus merged 7 commits into
nextfrom
fix/terrain-gallery-browser-launch

Conversation

@djizus

@djizus djizus commented Nov 10, 2026 •

Copy link
Copy Markdown
Collaborator

terrain / gallery failed on every run that reached it from 08:42Z on 10 November, next included: the two webgpu-auto captures came out blank.

Cause

The scripts ran npx -y agent-browser unpinned, so each run fetched the newest. agent-browser 0.42.0 was published at 00:40Z, four minutes after the last passing run started.

last pass (42729504176, 00:36Z) first fail on next (42784883691, 08:45Z)
Runner image, Chrome ubuntu26 20261101.186, 156 same
agent-browser 0.41.2 0.42.0
webgpu-auto rendered on webgl2-fallback webgpu
Capture terrain blank
  • The gallery gave its GPU flags on one command and --ignore-https-errors on another.
  • Both versions relaunched the browser without the flags at set viewport, so webgpu-auto had quietly rendered on WebGL2 on every passing run.
  • 0.42.0 also relaunched for --ignore-https-errors. In that browser three.js got WebGPU, which headless Linux Chrome never puts into a screenshot.

No merged change caused it.

Fix

1. Pin agent-browser (339c00dffb9).

  • apps/game/scripts/lib/agent-browser.mjs holds one version, 0.41.2, and all four spawns read it.
  • lint-guards (client CI) refuses a script that spawns agent-browser by name.
  • This alone turned the job green: 42809209991.

2. Check WebGPU on WebGPU (d462aec28f9).

  • The terrain lab's captureFrame draws one frame into a render target as the screen output (tone mapped, sRGB) and reads its pixels back.
  • On a GPU-less runner, headless Chrome puts the WebGPU canvas into neither a screenshot nor a canvas copy, but the texture readback works.
  • The gallery saves that PNG as the capture and checks the share drawn over the clear colour.
  • It also fails a capture rendered on another backend than it asked for.
  • createBrowserSession sends its launch settings on every command and refuses them on a single one. Its eight callers move theirs onto the session.

3. Keep the WebGPU device alive (652152a984f).

  • On the runner (no GPU, no Vulkan driver), Chrome destroys the first WebGPU device about 2 s after it starts. Its first readback failed with "A valid external Instance reference no longer exists" (42811058562).
  • three skips its onDeviceLost for a device lost as "destroyed", so the lab stayed ready and drew nothing. That is likely what blanked the original captures too.
  • The lab now watches the device's lost promise and ends in an error that names it; the gallery reports it.
  • CI installs Mesa's software Vulkan driver (libvulkan1, mesa-vulkan-drivers), which keeps the device alive.

Verification

  • Local, GPU hidden (as on the runner): all four captures pass. webgpu-auto renders on WebGPU (SwiftShader) with coverage 0.59; with step 1's canvas copy it read 0.
  • Local, on the GPU: all four pass, and the captures match the screen.
  • Tests: the backend check and the session refusal fail on next's code and pass here. The guard flags both an argv spawn and a shell-string spawn.
  • Gates: typecheck, format, knip, lint:guards.
  • Local, GPU and Vulkan drivers hidden: the gallery fails with "browser reported GPU device lost: Device was destroyed."
  • CI: terrain / gallery green on 652152a984f (42813439054). webgpu-auto renders on WebGPU (coverage 0.58), webgpu-force-webgl on WebGL2 (0.58–0.59), and the contact sheet shows the terrain from both.

Follow-up, not here

The game's own renderer (webgpu-renderer-backend.ts) also learns of a lost device only through three's onDeviceLost, so it would miss a device Chrome destroys this way.

The other agent-browser wrappers (runAgentBrowser in the two smoke runners, behind prove-run.mjs, capture-hud-lab and about 25 prove-* scripts) still pass --ignore-https-errors on single commands. Under 0.41.2 that does not relaunch the browser. Moving them onto createBrowserSession would let the pin move to 0.42.0.

agent-browser 0.42.0 (published 00:40Z) relaunches the browser when a command
names launch settings the running one lacks. The gallery gave its GPU flags on
one command and --ignore-https-errors on another, so the page loaded in a fresh
browser without its viewport. There three.js got a WebGPU adapter for the first
time, and headless Linux Chrome never puts a WebGPU canvas into a screenshot:
blank captures since 08:42Z. Under 0.41.2 the same flags had been dropped too,
and "webgpu-auto" had quietly rendered on WebGL2 every run.

- Pin agent-browser in one module that every script spawns from.
- createBrowserSession gives its launch settings (GPU flags, the dev server's
  certificate, init script, extra Chrome flags) on every command and refuses
  them on a single one. Its callers move theirs onto the session.
- The gallery fails a capture that rendered on another backend than asked.
- CI runs the gallery headed on Xvfb with agent-browser's WebGPU preset.
@vercel

vercel Bot commented Nov 10, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

3 Skipped Deployments
Project Deployment Actions Updated
eternum Ignored Ignored Nov 10, 2026 12:10pm UTC
eternum-docs Ignored Ignored Nov 10, 2026 12:10pm UTC
eternum-mobile Ignored Ignored Nov 10, 2026 12:10pm UTC

Request Review

agent-browser allows SwiftShader only for headless launches. CI runs the terrain
gallery headed on Xvfb with no GPU, so its first run had no WebGL context and no
WebGPU adapter ("Cannot read properties of null (reading 'getSupportedExtensions')").
Its run (42807386669) answered the question: on the runner, headless Chrome renders
WebGPU but never captures it, and a headed launch gets no WebGPU adapter.
The class: a CI tool floating to latest. agent-browser 0.42.0 (published 00:40Z)
turned the terrain gallery's WebGPU captures blank on every run since.

- lib/agent-browser.mjs holds the one version; the four spawns (the session
  behind the prove and playtest scripts, both smoke runners behind prove-run,
  the terrain benchmark) read it.
- lint-guards refuses a script that spawns agent-browser by name.
- The session, gallery, workflow and callers go back to next's code. The
  launch-settings and WebGPU-check work returns with the in-page pixel check,
  which it needs to pass on a GPU-less runner.
webgpu-auto had rendered on WebGL2 on every passing run: the session's GPU flags
were dropped when a later command relaunched the browser. With them kept, the
captures came out blank, because headless Chrome on Linux (SwiftShader, no GPU)
puts a WebGPU canvas into neither a screenshot nor a canvas copy.

- The terrain lab's captureFrame draws one frame into a render target as the
  screen output and reads its pixels back; the gallery saves that PNG and
  checks the share drawn over the clear colour.
- The gallery fails a capture rendered on another backend than it asked for.
- createBrowserSession gives its launch settings on every command and refuses
  them on a single one; its eight callers move theirs onto the session, and two
  "open the address again" workarounds go.
@djizus djizus changed the title Terrain gallery: one browser launch per session, WebGPU checked for real Pin agent-browser; check the terrain gallery's WebGPU from the lab's own pixels Nov 10, 2026
… driver

On the runner (no GPU, no Vulkan driver) Chrome destroys the first WebGPU device
about two seconds after it starts. three skips its onDeviceLost for a device lost
as "destroyed", so the lab stayed ready and drew nothing; the readback then failed
with "A valid external Instance reference no longer exists" (run 42811058562).

- The lab watches its device's lost promise and ends in an error that names it;
  the gallery reports the lab's error.
- CI installs Mesa's software Vulkan driver (libvulkan1, mesa-vulkan-drivers),
  which keeps the device alive.
@djizus
djizus marked this pull request as ready for review November 10, 2026 12:30
@djizus
djizus merged commit e9cad5a into next Nov 10, 2026
20 checks passed
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.

1 participant