1
0

The capture harness uses the graphics card that was there all along

`launch()` asks for `--use-angle=vulkan` and gets the Radeon RX 6700 XT. On the
real film loop at 1440x900 that is 7.56 s a frame to 0.47, and about twelve
cores pegged down to less than one. A 180-frame film goes from twenty-two
minutes to ninety seconds; the three shipped reels go from over an hour to four
minutes.

The SwiftShader flag was inherited from the share-card script and its comment
said "a box with no display and no GPU". Half of that was never true. The card
has been in this machine the whole time, with the amdgpu driver and a working
RADV ICD, and Chrome reaches it with **no display server at all** — no DISPLAY,
no XDG_RUNTIME_DIR, nothing but read access to /dev/dri/renderD128, which group
`render` already grants. So the portability the flag was protecting is intact:
this still runs over ssh and under cron.

Naming the backend is the whole trick, and most of the plausible spellings are
traps. `--use-angle=gl`, `--use-gl=egl`, `--use-gl=desktop`,
`--enable-features=Vulkan` and passing no GL flags at all were each measured on
this box, and all five land on SwiftShader while reporting success. Only
`--use-angle=vulkan` and `--use-angle=gl-egl` reach the card.

So `launch()` verifies instead of assuming. `--use-angle=vulkan` is a demand,
not a preference: with the driver hidden it yields no WebGL context at all
rather than falling back, the boot curtain never lifts, and the caller sits in
`waitForFunction` until a three-minute timeout before failing with something
that looks nothing like "there is no GPU here". Reading
`UNMASKED_RENDERER_WEBGL` once per run costs about half a second and turns that
into a printed line and a slow, correct render. Both branches are tested; the
sabotaged one was verified with `VK_LOADER_DRIVERS_DISABLE`.

The pictures are the same pictures: mean absolute difference of half a level out
of 255, confined to MSAA edges and to cloud shadow that already drifts between
any two runs of the same renderer.

Also corrects every comment across the four scripts that asserted three frames a
second or twenty-two minutes a reel. Those were true when written and are not
now, and the `reducedMotion` rationale in two of them was resting on the number:
the real reason a chapter flight cannot be waited out is that it is ~40 frames
however fast they are drawn, which was twenty seconds under software GL and is a
shorter race on the GPU — still a race.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-06 19:38:14 -07:00
parent 68a5165d6f
commit 0cc2126e85
4 changed files with 112 additions and 41 deletions
+6 -6
View File
@@ -252,8 +252,8 @@ const preview = process.argv.includes("--preview");
* Rewrite the site's manifest from the shot list without opening a browser.
*
* A caption is prose and gets edited like prose — several times, in a row, to
* hear how it reads. Charging three minutes of software rasterisation for each
* pass is how a caption ends up shipped in the first wording anybody tried,
* hear how it reads. Charging a full render for each pass is how a caption
* ends up shipped in the first wording anybody tried,
* which is the same failure the share-card README describes. The images this
* points at are the ones already on disk, and the commit it stamps is today's;
* both are true, because the pictures did not change.
@@ -349,10 +349,10 @@ async function shoot(browser, shot, frame) {
* one that quietly photographs the camera mid-flight.
*
* `scenekit.ts` eases a chapter change over about two seconds of *scene*
* time, and `stage.ts` clamps `dt` to 50 ms a frame. Under SwiftShader on a
* box with no GPU the app runs at roughly three frames a second, so those
* two seconds take about twenty of wall clock, and any fixed wait short of
* that lands somewhere over the bay. Reduced motion is the app's own
* time, and `stage.ts` clamps `dt` to 50 ms a frame — so a flight is about
* forty frames however fast they are drawn, and any fixed wait shorter than
* they take lands somewhere over the bay. Under the software fallback that
* was twenty seconds. Reduced motion is the app's own
* documented answer to "I only clicked a name in a list": `flyTo` calls
* `setPose` and the pose is simply true on the next frame.
*/