Repository navigation
perf(rendering): speed up zoom timeline rebuilds after live edits - #2557
Merged
richiemcilroy merged 3 commits intoOct 14, 2026
Merged
Conversation
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. |
JeffreyLin1
force-pushed
the
perf/zoom-timeline-rebuild
branch
from
October 14, 2026 06:20
9ad2297 to
39e6f94
Compare
The editor drops its zoom timelines on every project change and simulates them again from 0:00 to the playhead. A paused live edit pays for the whole recording up to the playhead, and so does pressing play or editing during playback. Most of that time was repeated work. targets_at scanned every zoom segment twice per 8 ms step, and now uses a small index with binary searches. Each auto segment filtered every cursor move to build its clusters; time-ordered moves are now narrowed by binary search first and the same filters run on what is left. The spring solver also recomputed exp, sin and cos terms on every step even though they only depend on the step length and the spring constants, so each simulation now caches them. Samples are bit-identical to the previous implementation. On a 30 minute recording with 60 auto zooms, a paused edit at 29:24 goes from 28.7 ms to 8.4 ms.
JeffreyLin1
force-pushed
the
perf/zoom-timeline-rebuild
branch
from
October 14, 2026 06:22
39e6f94 to
3156dd2
Compare
JeffreyLin1
marked this pull request as ready for review
October 14, 2026 06:22
Comment on lines
+281
to
+289
| let mut fresh = SpringMassDamperSimulation::new(config); | ||
| fresh.set_position(position); | ||
| fresh.set_velocity(velocity); | ||
| fresh.set_target_position(target); | ||
| let expected = fresh.run(dt_ms); | ||
|
|
||
| cached.set_config(config); | ||
| cached.set_target_position(target); | ||
| let actual = cached.run(dt_ms); |
Contributor
There was a problem hiding this comment.
cached_spring_steps_match_fresh_simulations compares two instances that both use the new solver. It checks cache reuse, but a mistake in the moved spring formulas would affect both sides and still pass. Keep the original solver as a test-only reference, or assert saved baseline results, so the promised bit-identical output has a lasting regression check.
Prompt To Fix With AI
This is a comment left during a code review.
Path: crates/rendering/src/spring_mass_damper.rs
Line: 281-289
Comment:
**Spring test shares its solver**
`cached_spring_steps_match_fresh_simulations` compares two instances that both use the new solver. It checks cache reuse, but a mistake in the moved spring formulas would affect both sides and still pass. Keep the original solver as a test-only reference, or assert saved baseline results, so the promised bit-identical output has a lasting regression check.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
The spring test compared the cached simulation with fresh instances of the same new solver, so a mistake in the moved formulas would pass on both sides. Compare against a test-only copy of the original solver instead, and add a config that reaches the degenerate overdamped branch.
Contributor
Author
|
@greptile |
Member
|
Thanks so much! |
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.
Editing a long recording that has zoom segments gets slower the further the playhead is into it. On every slider tick, GPUI's
publish_projectsends the edited project and asks for the paused frame again, and the preview renderer throws away its zoom timelines and simulates them from 0:00 up to the playhead. Playback does the same when you press play or edit while playing. #2476 fixed this for scrubbing, but edits still pay for a full rebuild, and any edit to the zooms themselves has to rebuild anyway. #2507 skips the cursor rebuild on unrelated edits but still rebuilds zoom timelines, so it doesn't overlap with this.Most of the rebuild was repeated work, so this PR makes it cheaper without changing what it computes:
targets_atscanned every zoom segment twice on each 8 ms step, and a 30 minute recording has 225k steps. A small index now finds the active segment with binary search, using the same first-match(start, end]rule.exp/sin/costerms on every step, even though they only change with the step length and the spring constants. Each simulation now caches them.Output is bit-identical. The benchmark hashes every sample and all scenarios match
main, and the new tests compare each fast path against the old scan. I also ran 20,000 random projects through both versions (the old copy isn't committed) and every sample matched.Results
The new
live-edit-benchmarkexample drivesEditorInstancewith the GPUI app's preview settings and drags the padding slider while paused, 60 edits at each position. The project is a synthetic 30 minute 1080p recording with 60 Hz cursor data and 60 auto zooms. Apple M3 Pro, release build, before and after runs interleaved, median of 3.Near the end of that recording a slider drag now fits in a 60 Hz frame. On a 10 minute recording with 20 zooms the median edit goes from 6.6 to 4.3 ms, and recordings of a few minutes only save a millisecond or two.
Timing just the rebuild (
benchmark_zoom_timeline_rebuild) with the playhead at the end gives 30.4 -> 6.4 ms for 30 minutes with 60 zooms and 104 -> 13.7 ms for 60 minutes with 120. The existingbenchmark_long_recording_zoom_startupalso runs about twice as fast.Reproduce
Point the example at a copy, since the editor can write
project-config.jsonback into the project.cargo test -p cap-rendering, clippy and fmt all pass.
Confidence Score: 5/5
The PR appears safe to merge, with the previous test concern addressed and no new actionable issues found.Summary
Speeds up zoom timeline rebuilds with a segment index, narrower cursor searches, and cached spring calculations. Adds a live-edit benchmark.
Reviews (2) · Last reviewed commit: "test(rendering): check cached spring ste..." · Reviewed by Greptile