Summary
Follow-up to #3575 / #3576. After deferring + coalescing the waterfall-history reprojection (#3576), continuous frequency scrolling is smooth (uiLag ~30 ms). But the history reprojection itself still costs ~108 ms (full ~0.5 GB QImage reproject) and now fires once per scroll gesture on the main thread — when scrolling stops or during slow/pausey tuning (gap > debounce). Telemetry shows this as uiLagMaxMs spikes of 75–120 ms in low-panCenterCmds windows and in the idle window right after a scroll burst.
It is far less frequent than before (#3575: every pan step) and lands on a near-static display, so it is "mostly smooth" — but it is a real residual hitch for careful/slow tuning.
Goal
Eliminate the per-gesture main-thread reprojection hitch entirely.
Candidate approaches (implement both, keep the cleanest)
- Off-thread reprojection. Run the ~108 ms
QImage reprojection on a worker (history is COW; appends are already paused while pending), then swap the result in on the main thread. rebuildWaterfallViewport waits-for/flushes the in-flight job. Main thread never blocks. Lower structural change, some concurrency surface.
- Per-row frequency frames (no global reproject). Tag each history row with its capture center/bandwidth; never reproject the whole image. Map only the visible viewport rows (~700) from their frame to the current frame in
rebuildWaterfallViewport. Removes the full reproject entirely (also removes the append-pause and the debounce). Larger change, architecturally cleanest.
Plan: prototype both, measure (aether.perf), and merge whichever is cleaner for the same result.
Acceptance
- No
uiLagMaxMs spike attributable to history reprojection in any scroll/stop/slow-scroll window (target < ~30 ms throughout).
- Time-scrollback remains correct (frequency-aligned history).
Evidence baseline (post-#3576)
Continuous scroll: uiLag 27–38 ms ✅. Gesture-end / slow scroll: uiLag 75–120 ms (the residual this issue targets).
Summary
Follow-up to #3575 / #3576. After deferring + coalescing the waterfall-history reprojection (#3576), continuous frequency scrolling is smooth (uiLag ~30 ms). But the history reprojection itself still costs ~108 ms (full ~0.5 GB
QImagereproject) and now fires once per scroll gesture on the main thread — when scrolling stops or during slow/pausey tuning (gap > debounce). Telemetry shows this asuiLagMaxMsspikes of 75–120 ms in low-panCenterCmdswindows and in the idle window right after a scroll burst.It is far less frequent than before (#3575: every pan step) and lands on a near-static display, so it is "mostly smooth" — but it is a real residual hitch for careful/slow tuning.
Goal
Eliminate the per-gesture main-thread reprojection hitch entirely.
Candidate approaches (implement both, keep the cleanest)
QImagereprojection on a worker (history is COW; appends are already paused while pending), then swap the result in on the main thread.rebuildWaterfallViewportwaits-for/flushes the in-flight job. Main thread never blocks. Lower structural change, some concurrency surface.rebuildWaterfallViewport. Removes the full reproject entirely (also removes the append-pause and the debounce). Larger change, architecturally cleanest.Plan: prototype both, measure (
aether.perf), and merge whichever is cleaner for the same result.Acceptance
uiLagMaxMsspike attributable to history reprojection in any scroll/stop/slow-scroll window (target < ~30 ms throughout).Evidence baseline (post-#3576)
Continuous scroll: uiLag 27–38 ms ✅. Gesture-end / slow scroll: uiLag 75–120 ms (the residual this issue targets).