KeelrLeek ae99757ddd hero: repaint on a resize instead of rebuilding the canvas for it
Collapsing the sidebar dropped frames only on the artist page. The cause was
one observer: the app grid animates its columns for 280ms, a ResizeObserver
fires on every frame of that, and this canvas answered each one with a full
resize() -- 171-224ms of main thread per toggle, 10-12 frames over 16ms, worst
frame 54ms. Muting that single observer took the worst frame to 4.3ms; muting
every other observer changed nothing.

Two independent per-frame stalls inside it. Reallocating the ~3200x1630
backing store was 115-205ms of the total, and the box moved about a pixel per
frame, so every one of those reallocations bought an invisible sharpness
change. gl.getError() after every draw is a synchronous round trip to the GPU
process, and it stalls hardest exactly when the GPU is busy compositing the
animation that provoked it.

reflow() is the observer's path now: it repaints into the CURRENT store and
matches the store to the box once, 140ms after the box settles. What makes
that correct is taking the cover crop from the DISPLAYED box while outSize
normalizes by the store -- the two may then disagree, because a crop chosen
for a 1.80 box drawn into a 1.96 store is stretched by exactly the ratio the
compositor undoes painting store into box. Geometrically exact at every
intermediate width, no reallocation. The error check is skipped on that path
only; the settle pass that follows is checked, so a real failure still
surfaces.

After: 1.5-2.4ms per toggle across 46 calls, max 0.2ms each. No frame over
33ms, 0-2 over 16ms. The Now Playing collapse rides the same transition and
improved with it.

resize is no longer exposed: every caller outside this file is reacting to a
box that may still be moving, and that is reflow's job.
2026-07-29 08:11:20 -05:00
S
Description
SoulSeek Streaming app.
9.5 MiB
1 Stars 2 Watchers 1 Forks
Languages
Rust 48.3%
Svelte 38.1%
JavaScript 13.4%
CSS 0.2%