Performance overhaul: instant startup, viewport rendering, concurrent event pump; login fixes#371
Merged
Conversation
…ounce config saves - Send tick events only when due instead of after every input, so a keypress no longer triggers two full redraws - Reserve an API pacing slot without holding the lock across the sleep and allow bursts of 5, so concurrent fan-outs (search, artist page) start immediately instead of 250ms apart; sustained rate is still 1 per 250ms - Debounce config saves from volume/resize/shuffle keys (flush 500ms after the last change and on exit) instead of a synchronous read/parse/serialize/ write per key repeat
Tables now format just the rows that fit on screen instead of the entire backing collection every frame (10k-track frame draw: 58ms -> 2.5ms debug). Album view no longer deep-clones the full album per frame, and the playing- row lookup borrows the playback context instead of cloning it. The track table also keeps its scroll position anchored: the cursor moves within the visible rows and the view only scrolls once the cursor reaches the top visible row when going up. Mouse clicks use the anchored offset so row mapping stays exact.
…ner animation gating - The changelog cache now hands out an Arc and the Home draw borrows the cached span text instead of deep-cloning every styled line each frame - The banner gradient object is cached on the three theme colors it is built from; glyph spans borrow the static banner text instead of allocating a String per cell - The fast animation tick now engages whenever the Home screen is displayed instead of only when its block has keyboard focus, which was almost never the case, so the banner gradient visibly animates again
All changelog lines are now wrapped to the area width when the cache is built (headers and plain paragraphs relied on Paragraph wrap before), and the Home draw slices out only the visible rows instead of using Paragraph::scroll, which re-composed every line above the offset each frame. Frame cost no longer grows with scroll depth. Includes tests for the scroll row mapping and scrolling past the end.
…play - Run non-Spotify IoEvents (lyrics, cover art, Subsonic/radio/local, telemetry) on a concurrent service lane so slow Spotify calls no longer head-of-line-block them; replace the try_recv/sleep poll with a blocking bridge thread - Show the UI immediately; run the update check, token validation, and librespot session init as background tasks, and reuse one /me response instead of three startup round trips - Publish playlists page 1 right away and fetch remaining pages + rootlist folders in the background; run full-pagination sorts as detached tasks - Open playlists on Enter immediately with a loading state and dedupe repeated open dispatches - Stash play requests while native streaming recovers and replay them afterward; stop swallowing transfer/activate errors and gate the playing state on a real event; park presses with no usable backend behind a status message instead of the Error screen - Cache album/artist metadata (TTL LRU); reuse HTTP clients for Subsonic, radio, and announcements; fetch top-artists mix concurrently - Throttle decoded-source seek during drags; skip the per-tick session snapshot until a save is due; precompute sort keys and lowercase friend names; cache help rows; read shuffle/repeat state without cloning the playback context
Address the pre-PR review of the concurrent event pump / deferred startup: - native load watchdog gains an attempt cap with a growing window; Loading/Preloading feed it and Unavailable disarms, ending the teardown loop - pending_playlist_open cleared at every auth-gate/invalid-id drop - cover-art and lyrics writes gated on the currently desired key/identity - deferred streaming init refreshes playlists so rootlist folders reconcile - playlist refresh preserves folder selection and restores the previous complete list on a partial pagination failure instead of publishing a stub - streaming OAuth runs before raw mode, off the reactor via spawn_blocking; deferred path stays cache-only and only wipes creds on genuine auth rejection - TrackChanged clears the parked request; replay gains a 30s recency gate - OAuth callback accept() errors are tolerated up to a cap - per-playlist in-flight guard collapses concurrent sort re-paginations
On a fresh first run the Web API and streaming logins run back-to-back on the same callback port, so the second consent could fail until a restart. Retry-bind the port before launching the streaming OAuth flow.
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.
A performance pass over startup, rendering, and the network/event architecture, plus the login fixes found along the way.
Performance measurements
Measured with headless probes on Windows 11, debug profile (absolute values are inflated vs release, but before/after comparisons on the same profile are valid; the pacing probe is sleep-dominated and profile-independent).
opt-level = 3was also measured against the current"s": 19-37% faster frames but +33% binary size on costs already down to 0.06-0.4 ms release, so"s"stays.What changed
Startup
/meround trip instead of three; playlists publish page 1 right away and fetch the rest (plus rootlist folders) in the background; the Lua VM is only constructed when user scripts exist.Rendering
Network / event loop
Playback reliability
Login fixes
Verification
cargo fmt,cargo clippy -D warnings(CI feature set, all-sources, and default features), full test suite green throughout (419 tests on the CI set at HEAD).