Skip to content

fix: report whether the machine is actually rendering on a GPU - #268

Merged
BernardJen merged 1 commit into
mainfrom
fix/gpu-report
Aug 21, 2026
Merged

fix: report whether the machine is actually rendering on a GPU#268
BernardJen merged 1 commit into
mainfrom
fix/gpu-report

Conversation

@BernardJen

Copy link
Copy Markdown
Contributor

The wall PC is laggy on the split-flap board and slow on some screensavers, and there was no
way to tell why from a running wall.

First, a measurement that changes the diagnosis

The board had never been measured, on any machine. #225 swept the 30 registry savers from
STRUCTURE_BASELINES, and the board is not a registry entry — it is imported directly by
renderer.js. preview.js has always been able to show it (EXTRAS = [splitFlap]); the sweep
just never asked.

At 6000×1200 on a real GPU, same seed:

fps
Split Flap 118.9
Matrix Rain 115.3
Truchet Tiles 91.2
Mandelbrot 31.3

So the board is one of the cheapest things in the set, sitting at the vsync ceiling. Its grid
is bounded at 3 rows × 48 columns — 144 tiles maximum — and its per-frame texture upload is
~144 texels. There is nothing in its structure that should be slow.

That matters: #225 predicted a weaker GPU would shift everything down proportionally. A wall
that struggles with a 119 fps workload is a different shape of problem, and the hypothesis that
explains both symptoms at once is that it is not rendering on a GPU at all. main/index.js
already sets ignore-gpu-blocklist and enable-gpu-rasterization, so that fight has happened
before — but nothing reported the outcome, leaving the hypothesis unfalsifiable.

What this adds

userData/gpu-report.txt at startup. The line that answers the question is the WebGL renderer
string; on a healthy machine it reads like this:

gl renderer                   ANGLE (Apple, ANGLE Metal Renderer: Apple M3 Pro, ...)
gpu_compositing               enabled
EXT_color_buffer_float        yes

A renderer containing SwiftShader or Software, or gpu_compositing reading
disabled_software, means every shader in the app is on the CPU — which no amount of
optimisation rescues. EXT_color_buffer_float is there because the HDR path every
post-processed saver uses needs float targets.

Echoed to the console too, so npm run dev shows it without opening the file.

On disk, since that was the constraint

One file, overwritten, one write per launch. Not appended, nothing periodic, plus a hard
200-line ceiling. GPU capabilities are fixed for the life of a process, so there is nothing to
sample — the size is bounded by content, not by uptime, which is the property that matters on a
machine left running for months.

Demonstrated rather than asserted: launched twice, 2081 bytes both times, not 4162.

The WebGL strings come from a throwaway 1×1 context, explicitly released with
WEBGL_lose_context. Not the screensaver canvas — that gets one context for the life of the
page, and this must not be the call that claims it.

An existing hazard fixed while here

diag-log appended one line per detection cycle with no bound whatsoever — roughly 54k
lines a day on a wall, forever, and nothing trimmed it. That is the failure mode this patch was
asked to avoid, already shipping.

Now capped at 2 MiB, trimmed by keeping the newest half: a diagnostic gets read because
something just happened, so the tail is the useful end. Verified on a 6 MiB file — trimmed to
1 MiB, newest records kept, and the leading partial line dropped so the file never begins
mid-record.

It is opt-in (debug logging off by default), so this was latent rather than active.

Tests

None added, and worth being explicit about why: both changes live in the main process, which is
CommonJS and requires electron at load, so it cannot be imported from vitest. That is the
same constraint that put the launch-flag parser in its own ESM module. The trim algorithm was
verified by extracting it and running it against a real oversized file; the IPC wiring and the
report contents were verified by running the app twice.

Next step is yours

This tells us what the wall is doing, not why it is slow. Once it has run there, the report
either names a software renderer — in which case the fix is drivers or flags, not code — or it
says the GPU is fine and the next move is per-saver fps on that hardware, which is the #225
criterion still open.

The videowall PC is laggy on the split-flap board and slow on some screensavers,
and there was no way to tell why from a running wall. The app sets
ignore-gpu-blocklist and enable-gpu-rasterization -- so someone has fought this
before -- but nothing ever reported which path it got, which made "it fell back to
software rendering" an unfalsifiable guess.

Worth knowing before optimising anything: the board had never been measured, on
any machine. #225 swept the 30 registry savers from STRUCTURE_BASELINES, and the
board is not a registry entry. Measured now at 6000x1200 on a real GPU it runs at
118.9 fps -- against Matrix Rain 115.3, Truchet 91.2, Mandelbrot 31.3. It is one
of the cheapest things in the set, its grid is bounded at 3 rows x 48 columns, and
its per-frame upload is ~144 texels. A wall that struggles with that is not simply
a slower GPU, so the first thing to establish is whether it is a GPU at all.

Writes userData/gpu-report.txt at startup: the WebGL renderer string, which is the
line that answers the question, plus feature status, float-target support and
display geometry. Also echoed to the console so `npm run dev` shows it without
opening the file.

## On disk

**One file, overwritten, one write per launch.** Not appended, nothing periodic,
and a hard 200-line ceiling on top. GPU capabilities do not change while a process
runs, so there is nothing to sample -- the size is bounded by content rather than
by uptime, which is the property that matters on a wall left running for months.
Verified by launching twice: 2081 bytes both times, not 4162.

While here, the existing detection diagnostic log is capped. It appended one line
per detection cycle with **no bound whatsoever** -- roughly 54k lines a day on a
wall, forever, and nothing trimmed it. Now capped at 2 MiB, trimmed by keeping the
newest half, because a diagnostic is read after something happened and the tail is
the useful end. Verified on a 6 MiB file: trimmed to 1 MiB, newest records kept,
leading partial line dropped so the file never starts mid-record.

No new unit tests: both changes live in the main process, which is CommonJS and
requires 'electron' at load, so it cannot be imported from vitest. That is the same
constraint that put the launch-flag parser in its own ESM module. The trim
algorithm was verified by extracting it and running it against a real oversized
file; the IPC wiring was verified by running the app.
@BernardJen
BernardJen merged commit e439cd5 into main Aug 21, 2026
1 check passed
@BernardJen
BernardJen deleted the fix/gpu-report branch August 21, 2026 08:20
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