Cache per-output palettes instead of re-parsing them every frame #5

Merged
Breadway merged 1 commit from fix/cache-per-output-palette into main 2026-09-03 13:15:23 +08:00
Owner

Audit finding #6 (Medium, perf)

state.rs palette_for_surface called bread_theme::load_palette_for(&name) — a blocking file read plus JSON parse — from inside redraw_surface, which redraw_all drives at 60fps during the entrance / unlock / breath animations and on every 1s clock tick, once per surface, times N monitors.

Change

  • AppState.output_palettes: a RefCell<HashMap<String, Palette>> caching the resolved palette per wl_output name. palette_for_surface reads it, populating on first use. RefCell because the call sits in the &self-shaped middle of redraw_surface with other &self borrows live; the event loop is single-threaded so there's no aliasing risk.
  • theme_watch.rs: a notify watcher on the runtime bread/palettes/ and bread/themes/ directories, bridged onto the calloop event loop through a calloop::channel — the same pattern status.rs and auth.rs already use for D-Bus and PAM. On a palette/theme file change it clears the cache and repaints once, so live per-monitor recolouring (new wallpaper, bread-theme reload, breadpaper set_on) still works.
  • Graceful degradation: if the directory watch can't be armed, theme_watch is None and palette_for_surface falls back to the previous per-call disk read. Correctness never regresses; only the fast path is lost.
  • Filters out the .<name>.tmp.<pid> scratch files bread-theme's atomic writes create, reacting only to the final *.json / *.css. Unit-tested.

New dependency

notify = "7" (already used elsewhere in the ecosystem, e.g. bos-settings). Pulls inotify + a handful of small support crates. An alternative would be enabling calloop's own inotify feature for a lighter tree; notify was chosen for consistency with the rest of the ecosystem and its graceful cross-platform degradation.

Tests

cargo fmt --check, cargo clippy --workspace --all-targets -D warnings, cargo test --workspace --all-targets (140 tests), cargo build --release --locked — all pass.

## Audit finding #6 (Medium, perf) `state.rs` `palette_for_surface` called `bread_theme::load_palette_for(&name)` — a blocking file read plus JSON parse — from inside `redraw_surface`, which `redraw_all` drives at 60fps during the entrance / unlock / breath animations and on every 1s clock tick, once per surface, times N monitors. ## Change - `AppState.output_palettes`: a `RefCell<HashMap<String, Palette>>` caching the resolved palette per `wl_output` name. `palette_for_surface` reads it, populating on first use. `RefCell` because the call sits in the `&self`-shaped middle of `redraw_surface` with other `&self` borrows live; the event loop is single-threaded so there's no aliasing risk. - `theme_watch.rs`: a `notify` watcher on the runtime `bread/palettes/` and `bread/themes/` directories, bridged onto the calloop event loop through a `calloop::channel` — the same pattern `status.rs` and `auth.rs` already use for D-Bus and PAM. On a palette/theme file change it clears the cache and repaints once, so live per-monitor recolouring (new wallpaper, `bread-theme reload`, `breadpaper set_on`) still works. - Graceful degradation: if the directory watch can't be armed, `theme_watch` is `None` and `palette_for_surface` falls back to the previous per-call disk read. Correctness never regresses; only the fast path is lost. - Filters out the `.<name>.tmp.<pid>` scratch files `bread-theme`'s atomic writes create, reacting only to the final `*.json` / `*.css`. Unit-tested. ## New dependency `notify = "7"` (already used elsewhere in the ecosystem, e.g. bos-settings). Pulls `inotify` + a handful of small support crates. An alternative would be enabling calloop's own `inotify` feature for a lighter tree; `notify` was chosen for consistency with the rest of the ecosystem and its graceful cross-platform degradation. ## Tests `cargo fmt --check`, `cargo clippy --workspace --all-targets -D warnings`, `cargo test --workspace --all-targets` (140 tests), `cargo build --release --locked` — all pass.
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
Breadway/breadlock!5
No description provided.