perf(spectrum): defer waterfall-history reprojection off the scroll path#3576
Closed
svabi79 wants to merge 1 commit into
Closed
perf(spectrum): defer waterfall-history reprojection off the scroll path#3576svabi79 wants to merge 1 commit into
svabi79 wants to merge 1 commit into
Conversation
Horizontal frequency scrolling was choppy on wide displays because the full waterfall *history* image was reprojected on the CPU on every pan step. At ultrawide widths that image is ~0.5 GB (up to 24k rows), so the drawImage cost was ~108 ms/step (measured) versus ~3 ms for the visible waterfall — blowing the frame budget ~7x. The history is only consumed by time-scrollback (rebuildWaterfallViewport), never by the live view, so the work was wasted during normal tuning. Reproject the visible waterfall immediately (cheap), but defer and coalesce the history reprojection: remember the net transform and apply it once after scrolling settles (debounced), pause history appends while pending so frames are never mixed, and flush on demand before a viewport rebuild. The live view is visually unchanged. Measured (continuous scroll, up to 49 tune-cmds/s): uiLagMax 110-164 ms -> 27-38 ms; pan/wf frame-age p95 120-155 ms -> 24-30 ms (4-5x). A small residual (one ~108 ms reproject per scroll gesture, on stop) remains; tracked separately. Platform-independent (plain Qt QImage path) — benefits Windows/macOS/Linux. Fixes aethersdr#3575 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Contributor
Author
|
Superseded by #3578. The per-row-frequency-frame approach in #3578 was measured strictly better — it removes the residual ~108 ms gesture-end reproject hitch this defer/coalesce PR still incurred (see #3577), deletes the debounce/append-pause/flush machinery instead of adding it, and is more correct (rows placed by their own capture frame). Since this never merged, #3578 lands the final solution directly. Closing in favor of #3578. |
NF0T
pushed a commit
that referenced
this pull request
Jun 14, 2026
…n reproject) (#3578) ## Summary Fixes #3575. Horizontal frequency scrolling was choppy on wide displays because `reprojectWaterfall()` reprojected the full waterfall **history** image (up to ~0.5 GB / 24k rows at ultrawide widths) on the CPU on **every pan step** — ~108 ms each (measured) vs ~3 ms for the visible waterfall, blowing the 16 ms frame budget ~7×. The GPU render path was never the bottleneck (`renderP95` 6–8 ms throughout). The history image is only consumed by time-scrollback (`rebuildWaterfallViewport`), never by the live view. So instead of keeping the whole image aligned to the current frequency frame on every pan, **each history row now records the center/bandwidth it was captured at** (parallel array to `m_wfHistoryTimestamps`). The visible waterfall is still reprojected immediately (cheap); the history image is **never globally reprojected**. `rebuildWaterfallViewport` remaps only the ~700 visible rows from their own frame to the current one, on time-scrollback. ## Constitution principle honored **Principle XI — Fixes Are Demonstrated.** Root cause and fix were both confirmed by instrumentation and the built-in `aether.perf` telemetry (before/after below), not by code-reading alone. ## Measured (ultrawide 5144×1440, `aether.perf`) | metric | before | after | |---|---|---| | `uiLagMaxMs` — continuous scroll | 110–164 ms | **31–42 ms** | | `uiLagMaxMs` — short-scroll / stop windows | (108 ms per step) | **20–33 ms** (no hitch) | | `panAgeP95` / `wfAgeP95` | 120–155 ms | **24–30 ms** | | `renderP95` (GPU) | 6–8 ms | 6–7 ms (unchanged) | Instrumented split confirmed the cause: `histMs` avg **108 ms**/step vs `visMs` **3.3 ms**. Time-scrollback verified frequency-correct (a row captured at center A renders at the correct position after tuning to center B). ## Test plan - [x] Local build passes — Windows, Qt 6.8.3 msvc2022_64, VS 2022; GPU path enabled; no errors. - [x] Before/after measured with `QT_LOGGING_RULES=aether.perf.debug=true` (table above), two runs each. - [x] Time-scrollback manually verified frequency-correct and responsive. - [x] Existing tests pass (CI) — pending. ## History / supersedes This **supersedes #3576** (a defer-and-coalesce intermediate that left a ~108 ms gesture-end hitch — see #3577) and **resolves #3577** in the same change: the per-row approach removes the need for the debounce/append-pause/flush machinery entirely (net deletes that code) and is strictly more correct — rows captured at different centers during a fast scroll are each placed by their own frame. #3576 was never merged, so this lands the final solution directly. ## Notes Platform-independent (`reprojectWaterfall`/`rebuildWaterfallViewport` are plain Qt `QImage`) — benefits Windows/macOS/Linux. Touches the render hot path in `src/gui/SpectrumWidget.*` (not a maintainer-only CODEOWNERS path); flagging for review regardless. ## Checklist - [x] Commits are signed (SSH, verified) - [x] No new flat-key `AppSettings` calls (N/A) - [x] All meter UI uses `MeterSmoother` (N/A) - [x] Documentation updated if user-visible behavior changed (N/A — live view visually unchanged) - [x] Security-sensitive changes reference a GHSA if applicable (N/A) Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.
Summary
Fixes #3575. Horizontal frequency scrolling was choppy on wide displays because
reprojectWaterfall()reprojected the full waterfall history image (up to ~0.5 GB / 24k rows at ultrawide widths) on the CPU on every pan step — ~108 ms each (measured), vs ~3 ms for the visible waterfall. That history is only consumed by time-scrollback (rebuildWaterfallViewport), never by the live view, so the work was wasted during normal tuning.This reprojects the visible waterfall immediately, but defers + coalesces the history reprojection: the net transform is remembered and applied once after scrolling settles (debounced), history appends pause while a reprojection is pending so frames are never mixed, and a pending reprojection is flushed on demand at the top of
rebuildWaterfallViewport. The live view is visually unchanged.Constitution principle honored
Principle XI — Fixes Are Demonstrated. Root cause and fix were both confirmed by instrumentation and the built-in
aether.perftelemetry (before/after below), not by code-reading alone.Test plan
QT_LOGGING_RULES=aether.perf.debug=true, ultrawide (5144×1440), continuous scroll up to 49 tune-cmds/s:uiLagMaxMspanAgeP95Ms/wfAgeP95MsrenderP95Ms(GPU)histMsavg 108 ms / step vsvisMsavg 3.3 ms.Scope / follow-up
A small residual remains: one ~108 ms history reproject per scroll gesture (fires on stop / during slow pausey scrolling), visible as an occasional hitch. That is out of scope here and tracked in a follow-up issue — candidate fixes are moving the reprojection off the main thread or storing per-row frequency frames (no global reproject). This PR is the focused, high-leverage fix for the common continuous-scroll case.
Notes
Platform-independent change (
reprojectWaterfallis plain QtQImage) — benefits Windows/macOS/Linux.src/gui/SpectrumWidget.*is not a maintainer-only CODEOWNERS path, but flagging for review given it touches the render hot path.Checklist
AppSettingscalls (N/A)MeterSmoother(N/A)