Skip to content

perf(editor): keep the preview updating while scrubbing - #2535

Merged
richiemcilroy merged 6 commits into
CapSoftware:mainfrom
notkainoa:perf/scrub-preview-frames
Oct 20, 2026
Merged

richiemcilroy merged 6 commits into
CapSoftware:mainfrom
notkainoa:perf/scrub-preview-frames

Conversation

@notkainoa

@notkainoa notkainoa commented Oct 14, 2026 •

Copy link
Copy Markdown

The preview keeps updating while you drag the playhead, instead of freezing until you let go.

Drag Longest freeze, before → after Frames shown, before → after
Fast, whole timeline in 2 s 2.0 s (whole drag) → 0.33–0.63 s 0 → 6–8
Fast, backwards 2.0 s (whole drag) → 0.30–0.47 s 0 → 6–8
Slow, whole timeline in 8 s 5.8–6.6 s → 0.17–0.35 s 90–168 → 59–212 (similar)
Slow, backwards 8.0 s (whole drag) → 0.30–0.35 s 0 → 30

Ranges cover 7 interleaved runs of before and after.

The problem

Every mouse move during a drag sends a new preview request. The renderer cancelled the frame it was decoding whenever a newer request arrived. A seek on a real recording takes longer than the gap between mouse events, so every frame got cancelled before it was shown.

What changed

  • A newer request now cancels the frame in progress only if a frame was shown in the last 33 ms.
  • Otherwise the renderer finishes and shows that frame, then jumps to the newest request.
  • When you stop dragging, the final frame is still the exact frame under the playhead.

On a slow forward drag both versions show a similar, noisy number of frames. The difference is timing: before, the frames come in a burst and the preview then freezes for about 6 s. After, they're spread across the drag.

Trade-off

Because the frame in progress finishes first, the final frame can arrive a little later after you stop the mouse:

Drag Time to final frame (mean), before → after
Fast 210 → 230 ms
Fast, backwards 111 → 147 ms
Slow 49 → 123 ms (one run hit 370 ms)
Slow, backwards 76 → 78 ms

Each seek here takes about 200–300 ms, which caps a fast drag at about 4 frames per second. #2492 makes seeks cheaper and would combine with this change.

Scope

  • Only the paused preview path changes. Playback and export are unchanged.
  • The preparing-editor handoff still commits only its own frame. A request that lands while another frame is in flight can now wait for that frame to finish, as with a scrub.
  • A failed render's retry still gives up as soon as a newer request arrives, as on main.

Checks

  • Unit tests cover the cancel rule: a newer request cancels only while the last frame is under 33 ms old, an overdue frame finishes, and the newer request stays queued either way. Making newer requests always cancel (the old behavior) or never cancel fails them.
  • cargo test -p cap-editor --release: 195 passed on current main.
  • cargo clippy -p cap-editor --release --all-targets -- -D warnings and cargo fmt are clean.
Benchmark setup (crates/editor/examples/scrub-benchmark.rs, new)

reference-recording.cap from Cap's performance fixtures: 30.8 s, 2 segments, 3024×1964 display plus camera. Preview at 960×540 (Half quality), mouse events at 120 Hz, Apple M1, release build. Each figure is the range over 7 benchmark invocations (each the mean of 3–5 runs), with before and after interleaved so machine load hits both equally.

cargo run -p cap-editor --release --example scrub-benchmark -- --recording-path <copy of reference-recording.cap> --runs 5

RetriggerView in GreptileConfidence Score: 5/5

The PR appears safe to merge; no new blocking issue was found.

Summary

The paused preview now finishes an overdue frame instead of repeatedly cancelling it during a scrub, then reads the newest queued request.

  • The paused preview keeps showing frames while you scrub.

The previous-review ancestry checks did not pass, so this review covered the full PR diff. No new actionable issues were found. Tests were inspected, not run.

Reviews (2) · Last reviewed commit: "test(editor): cover when a newer scrub r..." · Reviewed by Greptile

@vercel

vercel Bot commented Oct 14, 2026

Copy link
Copy Markdown
Contributor

Someone is attempting to deploy a commit to the Cap Software Inc Team on Vercel.

A member of the Team first needs to authorize it.

Comment thread crates/editor/examples/scrub-benchmark.rs Outdated
Comment thread crates/editor/examples/scrub-benchmark.rs Outdated
Comment thread crates/editor/examples/scrub-benchmark.rs Outdated
Comment thread crates/editor/examples/scrub-benchmark.rs
Comment thread crates/editor/src/editor_instance.rs
Scott Newton added 4 commits October 14, 2026 12:10
A newer preview request cancelled the in-flight frame every time, so a scrub faster than one seek never presented a frame until the mouse stopped. A newer request now only wins while a frame was presented in the last 33 ms; otherwise the in-flight frame is shown and the renderer moves on to the latest request.
@richiemcilroy

Copy link
Copy Markdown
Member

@greptileai review

@richiemcilroy

Copy link
Copy Markdown
Member

Verified hands-on in the real editor before merging (M4 Max, release build of this branch merged with current main, vs main at fc57ffd). A local harness drove the editor's real paused ruler-scrub path (scrub = Ruler + seek_to_time per move, 120 Hz moves, 3% to 97% of the timeline) and logged every frame display_frame presented. Two real recordings, 4 drags each, 3 interleaved runs per variant.

Recording, drag Frames shown during the drag, main → this PR Longest freeze, main → this PR
cam60 (4K screen + camera), fast reverse (2 s) 0 → 11–27 2.0 s → 0.11–0.86 s
web60, fast reverse (2 s) 0 → 4–28 2.0 s → 0.14–0.86 s
cam60, slow reverse (8 s) 0–202 → 17–255 up to 8.0 s → 0.09–1.1 s
web60, slow reverse (8 s) 2–21 → 34–158 1.7–5.2 s → 0.17–0.53 s
forward drags (2 s and 8 s, both recordings) unchanged (M4 Max seeks forward fast enough on main too) similar
  • The final frame was exactly the frame under the playhead in all 24 PR runs. At most one in-flight frame lands after release and is immediately replaced by the target, as described.
  • Time to the final frame after release is in the same range on both (main 19–450 ms, PR 8–410 ms on reverse drags).
  • I dumped every presented frame from a reverse and a forward drag. None were black or torn, and they followed the drag direction in order.
  • CI is green on the branch merged with current main (CI, Engine verify, Perf budget, Verify recording, A/V Sync).

@richiemcilroy
richiemcilroy merged commit 94a99fc into CapSoftware:main Oct 20, 2026
44 of 46 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants