Skip to content

perf(editor): smooth live appearance edits on long recordings - #2701

Open
AksharP5 wants to merge 1 commit into
CapSoftware:mainfrom
AksharP5:perf/reuse-zoom-timelines
Open

AksharP5 wants to merge 1 commit into
CapSoftware:mainfrom
AksharP5:perf/reuse-zoom-timelines

Conversation

@AksharP5

@AksharP5 AksharP5 commented Oct 18, 2026 •

Copy link
Copy Markdown
Contributor

Adjusting padding, colors, or camera rounding in a long recording still discarded every zoom timeline. The next frame then simulated the spring again from the beginning to the current playhead. Playback and paused preview now keep those samples while their zoom inputs match.

The shared check uses the existing time-map and crop-map builders. Zoom, spring, crop, hold, and clip-timing changes still invalidate the cache. Adding or removing the first transition also refreshes outgoing timelines, including when the zoom track is empty.

Native editor-playback-benchmark, one-hour synthetic recording at 58:20, 60 fps, 320x180, headless audio and llvmpipe on an i5-14400F:

Workload Main p95 frame gap This PR
No edits 17.73 ms 17.73 ms
Padding updates every 50 ms 31.41 ms 17.74 ms

Median of three alternating before/after pairs per workload, 360 submitted frames per run, all runs retained. Padding-edit frame gaps improve by 43.5%. Main drops one rendered frame in each edited run; this PR delivers all 360. These are native playback measurements with software rendering. Both builds use Rust 1.88 and O3 for cap-project, cap-rendering, and cap-editor, with the remaining repository dev settings unchanged.

Validation: 33 zoom tests and two preview-snapshot tests pass. A separate 56-process native zoom probe produces identical sampled transform bits before/after; unrelated edits retain the cache and the spring-change control still rebuilds. Strict Clippy passes for all three touched crates with --all-targets --no-deps -- -D warnings. The broader Linux dependency lint also fails on pristine main at crates/utils/src/disk_space.rs:186 for an unnecessary cast; that file is unchanged.

Checked all 273 current public open PRs for overlap. #2599 optimizes auto-zoom cluster lookup; this change avoids rebuilding the timelines during unrelated edits, and the measured fixture uses manual zoom.

Reproduce the native playback comparison

Use the repository's normal native build environment and Rust 1.88. Save the Python below as make-fixture.py, then run python3 make-fixture.py /tmp/cap-zoom-repro.cap. It creates 216,000 real H264 frames and a one-hour manual-zoom project.

Build the unchanged benchmark example in separate checkouts of 2b2bf8445158c078337c9f097892dde36bc3fe8d and this PR:

CARGO_INCREMENTAL=0 cargo build -p cap-editor --example editor-playback-benchmark \
  --config profile.dev.package.cap-project.opt-level=3 \
  --config profile.dev.package.cap-rendering.opt-level=3 \
  --config profile.dev.package.cap-editor.opt-level=3

On Linux with the lavapipe ICD installed, run each binary with the same fixture:

CAP_RENDER_FORCE_SOFTWARE_ADAPTER=1 \
VK_DRIVER_FILES=/usr/share/vulkan/icd.d/lvp_icd.json \
target/debug/examples/editor-playback-benchmark \
  --recording-path /tmp/cap-zoom-repro.cap --headless \
  --resolution 320x180 --fps 60 --frames 360 --start-time 3500 \
  --live-edit background --edit-interval-ms 50

Omit --live-edit background --edit-interval-ms 50 for the no-edit control. The reported callback gap p95 measures delivered-frame cadence. The measurement plan fixed three pairs per case and alternated execution order. One baseline no-edit 120-frame pilot verified the fixture and environment and was excluded before timed comparisons. Every paired run used the same canonical project-config hash, and live edits stayed in memory.

import json
import subprocess
import sys
from pathlib import Path

output = Path(sys.argv[1]).resolve()
output.mkdir(parents=True, exist_ok=False)
content = output / "content"
content.mkdir()
(output / "recording-meta.json").write_text(json.dumps({
    "pretty_name": "Synthetic zoom-cache verification",
    "display": {"path": "content/display.mp4", "fps": 60}
}))
(output / "project-config.json").write_text(json.dumps({
    "cursor": {"raw": True},
    "background": {"padding": 10},
    "timeline": {
        "segments": [{"recordingClip": 0, "timescale": 1, "start": 0, "end": 3600}],
        "zoomSegments": [{"start": 1, "end": 3599, "amount": 2,
                          "mode": {"manual": {"x": 0.7, "y": 0.3}}}]
    }
}))
seed = content / "seed.mp4"
subprocess.run([
    "ffmpeg", "-hide_banner", "-loglevel", "error", "-f", "lavfi", "-i",
    "testsrc2=size=320x180:rate=60", "-t", "10", "-c:v", "libx264",
    "-threads", "2", "-preset", "ultrafast", "-crf", "30", "-g", "120",
    "-pix_fmt", "yuv420p", "-n", str(seed)
], check=True)
subprocess.run([
    "ffmpeg", "-hide_banner", "-loglevel", "error", "-stream_loop", "359",
    "-i", str(seed), "-t", "3600", "-c", "copy", "-n", str(content / "display.mp4")
], check=True)
seed.unlink()

RetriggerView in GreptileConfidence Score: 5/5

The PR appears safe to merge; no actionable issues were found.

Summary

Preview and playback now keep computed zoom samples when appearance edits leave zoom inputs unchanged.

  • Playback and preview keep zoom samples when edits leave their inputs unchanged.

No actionable issues found. Review was based on source inspection; tests and benchmarks were not rerun.

Reviews (1) · Last reviewed commit: "perf(editor): keep zoom timelines during..." · Reviewed by Greptile

@vercel

vercel Bot commented Oct 18, 2026

Copy link
Copy Markdown
Contributor

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

A member of the Team first needs to authorize it.

This branch has not been deployed

No deployments
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.

1 participant