fix: report whether the machine is actually rendering on a GPU - #268
Merged
Conversation
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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 byrenderer.js.preview.jshas always been able to show it (EXTRAS = [splitFlap]); the sweepjust never asked.
At 6000×1200 on a real GPU, same seed:
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.jsalready sets
ignore-gpu-blocklistandenable-gpu-rasterization, so that fight has happenedbefore — but nothing reported the outcome, leaving the hypothesis unfalsifiable.
What this adds
userData/gpu-report.txtat startup. The line that answers the question is the WebGL rendererstring; on a healthy machine it reads like this:
A renderer containing
SwiftShaderorSoftware, orgpu_compositingreadingdisabled_software, means every shader in the app is on the CPU — which no amount ofoptimisation rescues.
EXT_color_buffer_floatis there because the HDR path everypost-processed saver uses needs float targets.
Echoed to the console too, so
npm run devshows 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 thepage, and this must not be the call that claims it.
An existing hazard fixed while here
diag-logappended one line per detection cycle with no bound whatsoever — roughly 54klines 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
requireselectronat load, so it cannot be imported from vitest. That is thesame 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.