perf(ui): cache-rendering-hint on static shell chrome (#617)

cache-rendering-hint: true on HeaderBar, NowPlayingBar and Sidebar roots so
femtovg caches those subtrees to a persistent texture instead of
re-tessellating them on every repaint. Sidebar root is safe: the Large-NPB
spectrum dock is an AppShell overlay, not a Sidebar child. SMOKE-WATCH:
playlists-list scroll (Flickable-inside-cache); narrow to nav-only if it
regresses.

Authored by Claude (cc) in the repaint-cache-hint worktree; committed
as-is by km so both #617 lines share one canonical commit.

km
This commit is contained in:
vicrodh committed 2026-07-19 15:18:30 -06:00
1 parent e81ed911f8
commit e32576316e
3 files changed
+35

No files matched your search

+9
View File
@@ -548,6 +548,15 @@ export component HeaderBar inherits Rectangle {
// `header-menu-navigate` route handler the dropdown menus use.
callback navigate(string /* route */);
// #617 per-frame cost: femtovg has no partial rendering, so the animated
// now-playing equalizer bars repaint the WHOLE window ~8x/sec. This header is
// static at idle (no internal scroll; the search cortinilla is a separate
// AppShell overlay), so cache its subtree to a persistent texture: every
// repaint then skips the header's tree-walk + path re-tessellation and blits
// the cache instead. Invalidates only when something inside changes (typing,
// hover, resize) — not per frame. See spec 2026-07-19-cpu-idle-repaint-617 §9.2.
cache-rendering-hint: true;
height: Layout.header-height;
background: ShellState.app-background-active
? Theme.surface-card.with-alpha(AppearanceState.app-background-surface-alpha)
@@ -19,6 +19,17 @@ export component NowPlayingBar inherits Rectangle {
callback media-action(string, string, string);
callback settings-bool(string, bool);
// #617 per-frame cost: the always-visible bottom bar is a high-item-count
// subtree (SongCard cover, seek line, transport controls, quality badges,
// volume) that is static except for the ~1 Hz seek push (verified: SeekBar's
// only animates are hover-opacity one-shots; progress = whole-second position).
// Caching it drops the bar's tree-walk + path re-tessellation from ~8x/sec
// (driven by the eq-bar full-window repaints) to ~1x/sec; the other 7 frames
// blit the cached texture. See spec 2026-07-19-cpu-idle-repaint-617 §9.2.
// NOTE: Large mode's sidebar spectrum dock is NOT inside this component; it is
// in Sidebar and must stay un-cached (30 fps visualizer).
cache-rendering-hint: true;
// EXPLICIT mode-aware height on the wrapper itself. The bottom bar lives in
// AppShell's outer VerticalLayout slot, which sizes a plain Rectangle child
// to its CONTENT height — and a Rectangle does NOT auto-adopt a conditional
+15
View File
@@ -693,6 +693,21 @@ export component Sidebar inherits Rectangle {
clip: true;
animate width { duration: 160ms; easing: ease-in-out; }
// #617 per-frame cost: cache this whole subtree to a persistent texture so
// the eq-bar's ~8x/sec full-window repaints skip the sidebar's tree-walk +
// path re-tessellation (nav rows + playlists list) and blit the cache instead.
// SAFE to cache the root here: the animated Large-NPB spectrum dock is NOT a
// child of the Sidebar — it is an AppShell-root overlay (see the dock-band
// RESERVATION comment ~line 1120); everything actually inside the Sidebar is
// static at idle (the scrollbar thumb only animates on hover; the sort/context
// menus are separate PopupWindow surfaces, not part of this texture).
// Invalidates on: playlist scroll (transient), width toggle (160ms transient),
// hover, list changes. SMOKE-WATCH: scroll the playlists list and confirm no
// clipping/offset artifact from the Flickable-inside-cache interaction; if it
// regresses, narrow the hint to the static nav section only.
// See spec 2026-07-19-cpu-idle-repaint-617 §9.2.
cache-rendering-hint: true;
VerticalLayout {
// Per-side padding so Large can zero ONLY the bottom: the reserved dock
// band (and the AppShell overlay aligned to it) then reaches the true