From 2fca0fe936dfb4832002655ff49f795b9c965769 Mon Sep 17 00:00:00 2001
From: Richie McIlroy <33632126+richiemcilroy@users.noreply.github.com>
Date: Thu, 10 Sep 2026 23:35:52 +0100
Subject: [PATCH 1/4] improve: desktop recording and editor responsiveness
---
Cargo.lock | 12 +
apps/desktop-gpui/Cargo.lock | 6 +
apps/desktop-gpui/Cargo.toml | 6 +
apps/desktop-gpui/dev.sh | 9 +-
.../examples/audio-readiness-benchmark.rs | 302 ++++
.../examples/media-readiness-benchmark.rs | 71 +
apps/desktop-gpui/src/app_windows.rs | 466 ++++-
apps/desktop-gpui/src/editor_crop.rs | 392 ++++-
.../desktop-gpui/src/editor_crop/alignment.rs | 257 +++
apps/desktop-gpui/src/editor_export.rs | 114 +-
apps/desktop-gpui/src/editor_preparing.rs | 988 +++++++++++
.../src/editor_preparing/audio.rs | 362 ++++
.../src/editor_preparing/presentation.rs | 275 +++
apps/desktop-gpui/src/editor_timeline.rs | 199 ++-
.../src/editor_timeline/playback_follow.rs | 103 +-
apps/desktop-gpui/src/editor_window.rs | 637 ++++++-
apps/desktop-gpui/src/main.rs | 12 +-
apps/desktop-gpui/src/platform/windows.rs | 5 +
.../src/platform/windows/hidden_frame.rs | 134 ++
apps/desktop-gpui/src/recording.rs | 585 +++++-
apps/desktop-gpui/src/session.rs | 178 +-
apps/desktop-gpui/src/upload.rs | 25 +-
.../scripts/stop-editor-performance.md | 712 ++++++++
apps/desktop/src-tauri/Cargo.toml | 1 +
apps/desktop/src-tauri/src/api.rs | 3 +
apps/desktop/src-tauri/src/audio.rs | 33 +-
.../desktop/src-tauri/src/editor_preparing.rs | 1395 +++++++++++++++
.../src-tauri/src/editor_preparing/audio.rs | 226 +++
apps/desktop/src-tauri/src/editor_window.rs | 256 ++-
apps/desktop/src-tauri/src/frame_ws.rs | 224 ++-
apps/desktop/src-tauri/src/gpui_app.rs | 4 +
apps/desktop/src-tauri/src/lib.rs | 236 ++-
apps/desktop/src-tauri/src/main.rs | 153 ++
.../src-tauri/src/preparing_finalization.rs | 588 +++++++
apps/desktop/src-tauri/src/recording.rs | 425 ++++-
.../src-tauri/src/stop-editor-benchmark.js | 30 +
.../src-tauri/src/stop-editor-parity-dom.js | 214 +++
.../src-tauri/src/stop_editor_benchmark.rs | 472 +++++
apps/desktop/src-tauri/src/upload.rs | 101 +-
.../desktop/src-tauri/src/upload/lifecycle.rs | 3 +-
apps/desktop/src-tauri/src/windows.rs | 18 +-
apps/desktop/src/components/Cropper.tsx | 107 +-
.../src/components/crop-alignment.test.ts | 74 +
apps/desktop/src/components/crop-alignment.ts | 76 +
apps/desktop/src/components/cropper.css | 62 +
.../src/routes/editor/ClipsSidebar.tsx | 33 +-
apps/desktop/src/routes/editor/Editor.tsx | 85 +-
apps/desktop/src/routes/editor/ExportPage.tsx | 68 +-
apps/desktop/src/routes/editor/Header.tsx | 5 +-
apps/desktop/src/routes/editor/Player.tsx | 158 +-
.../desktop/src/routes/editor/ShareButton.tsx | 286 +--
.../src/routes/editor/Timeline/index.tsx | 11 +-
.../editor/Timeline/playback-follow.test.ts | 44 +
.../routes/editor/Timeline/playback-follow.ts | 16 +-
.../src/routes/editor/TranscriptPage.tsx | 81 +-
apps/desktop/src/routes/editor/context.ts | 204 ++-
.../src/routes/editor/editor-skeleton.tsx | 306 ++--
apps/desktop/src/routes/editor/index.tsx | 9 +-
.../editor/playback-intent-routing.test.ts | 163 ++
.../routes/editor/playback-intent-routing.ts | 14 +
.../editor/preparing-editor-context.tsx | 264 +++
.../editor/preparing-editor-model.test.ts | 294 ++++
.../routes/editor/preparing-editor-model.ts | 250 +++
.../preparing-editor-subscription.test.ts | 198 +++
.../editor/preparing-editor-subscription.ts | 90 +
.../editor/preparing-frame-lease.test.ts | 131 ++
.../routes/editor/preparing-frame-lease.ts | 60 +
.../editor/preparing-frame-transport.test.ts | 285 +++
.../editor/preparing-frame-transport.ts | 47 +
.../src/routes/editor/preparing-frame.tsx | 35 +
.../routes/editor/preparing-handoff.test.ts | 494 ++++++
.../src/routes/editor/preparing-handoff.ts | 180 ++
.../editor/preparing-playback-handoff.test.ts | 184 ++
.../editor/preparing-playback-handoff.ts | 119 ++
.../src/routes/editor/preparing-timeline.tsx | 123 ++
.../src/routes/editor/preview-bounds.test.ts | 264 +++
.../src/routes/editor/preview-bounds.ts | 94 +
apps/desktop/src/utils/frame-identity.ts | 11 +
apps/desktop/src/utils/frame-worker.ts | 6 +
apps/desktop/src/utils/socket.ts | 60 +-
.../src/utils/stride-correction-worker.ts | 11 +-
apps/desktop/src/utils/tauri.ts | 33 +-
...sktop-recording-output-replacement.test.ts | 40 +
.../__tests__/unit/multipart-presign.test.ts | 197 ++-
.../app/api/upload/[...route]/multipart.ts | 91 +-
apps/web/lib/desktop-reupload.ts | 85 +
crates/audio/Cargo.toml | 3 +
.../audio/examples/relocatable-audio-probe.rs | 139 ++
crates/audio/src/audio_data.rs | 199 ++-
crates/audio/src/lib.rs | 2 +
crates/audio/src/progressive.rs | 1162 ++++++++++++
crates/audio/src/progressive/managed.rs | 758 ++++++++
crates/audio/src/progressive/test_support.rs | 91 +
crates/audio/src/renderer.rs | 221 ++-
crates/audio/src/streaming.rs | 402 ++++-
crates/cap-muxer/src/main.rs | 65 +
crates/editor/Cargo.toml | 5 +-
.../examples/editor-startup-benchmark.rs | 26 +-
crates/editor/src/audio.rs | 84 +-
crates/editor/src/audio_output.rs | 626 ++++++-
.../editor/src/audio_output/native_tests.rs | 714 ++++++++
crates/editor/src/completed_audio.rs | 302 ++++
crates/editor/src/editor_instance.rs | 717 ++++++--
crates/editor/src/lib.rs | 34 +-
crates/editor/src/playback.rs | 161 +-
crates/editor/src/preparing_audio/mixer.rs | 669 +++++++
crates/editor/src/preparing_audio/mod.rs | 7 +
crates/editor/src/preparing_audio/output.rs | 590 +++++++
.../src/preparing_audio/output_tests.rs | 624 +++++++
crates/editor/src/preparing_editor.rs | 29 +
crates/editor/src/preparing_handoff.rs | 478 +++++
crates/editor/src/preparing_handoff_tests.rs | 261 +++
crates/editor/src/preparing_playback.rs | 1498 ++++++++++++++++
crates/editor/src/preparing_preview.rs | 800 +++++++++
.../src/preparing_preview/native_tests.rs | 1423 +++++++++++++++
crates/editor/src/preparing_preview/tests.rs | 269 +++
crates/editor/src/segments.rs | 120 +-
crates/enc-ffmpeg/Cargo.toml | 1 +
crates/enc-ffmpeg/src/lib.rs | 4 +
crates/enc-ffmpeg/src/relocatable_source.rs | 1172 ++++++++++++
crates/enc-ffmpeg/src/remux.rs | 34 +
crates/enc-ffmpeg/src/segmented_input.rs | 1028 +++++++++++
crates/project/src/cursor.rs | 56 +-
crates/project/src/keyboard.rs | 61 +-
.../examples/studio-finalization-benchmark.rs | 46 +
crates/recording/src/recovery.rs | 1471 +++++++++++++++-
.../src/recovery/preparing_canonical_probe.rs | 1259 +++++++++++++
.../src/recovery/preparing_observer.rs | 738 ++++++++
.../src/recovery/preparing_projection.rs | 1391 +++++++++++++++
crates/recording/src/studio_recording.rs | 424 ++++-
crates/recording/tests/oop_muxer.rs | 195 +-
crates/rendering/Cargo.toml | 3 +
crates/rendering/src/decoder/ffmpeg.rs | 1566 ++++++++++-------
crates/rendering/src/decoder/mod.rs | 1396 ++++++++++++++-
crates/rendering/src/frame_pipeline.rs | 52 +-
crates/rendering/src/layers/captions.rs | 2 +
crates/rendering/src/layers/cursor.rs | 89 +-
crates/rendering/src/layers/keyboard.rs | 2 +
crates/rendering/src/layers/mod.rs | 16 +-
crates/rendering/src/layers/text.rs | 2 +
crates/rendering/src/lib.rs | 457 +++--
crates/rendering/src/managed_segment.rs | 587 ++++++
crates/rendering/src/overlay_layers.rs | 152 ++
.../src/overlay_layers/native_tests.rs | 368 ++++
crates/rendering/src/project_recordings.rs | 156 +-
crates/rendering/src/readiness.rs | 70 +
.../rendering/src/recorded_cursor_assets.rs | 307 ++++
crates/rendering/src/segment_timing.rs | 132 ++
crates/video-decode/Cargo.toml | 6 +
.../examples/fragmented-readiness.rs | 95 +
.../examples/relocatable-video-probe.rs | 310 ++++
crates/video-decode/src/ffmpeg.rs | 478 ++++-
152 files changed, 39950 insertions(+), 2335 deletions(-)
create mode 100644 apps/desktop-gpui/examples/audio-readiness-benchmark.rs
create mode 100644 apps/desktop-gpui/examples/media-readiness-benchmark.rs
create mode 100644 apps/desktop-gpui/src/editor_crop/alignment.rs
create mode 100644 apps/desktop-gpui/src/editor_preparing.rs
create mode 100644 apps/desktop-gpui/src/editor_preparing/audio.rs
create mode 100644 apps/desktop-gpui/src/editor_preparing/presentation.rs
create mode 100644 apps/desktop-gpui/src/platform/windows/hidden_frame.rs
create mode 100644 apps/desktop/scripts/stop-editor-performance.md
create mode 100644 apps/desktop/src-tauri/src/editor_preparing.rs
create mode 100644 apps/desktop/src-tauri/src/editor_preparing/audio.rs
create mode 100644 apps/desktop/src-tauri/src/preparing_finalization.rs
create mode 100644 apps/desktop/src-tauri/src/stop-editor-benchmark.js
create mode 100644 apps/desktop/src-tauri/src/stop-editor-parity-dom.js
create mode 100644 apps/desktop/src-tauri/src/stop_editor_benchmark.rs
create mode 100644 apps/desktop/src/components/crop-alignment.test.ts
create mode 100644 apps/desktop/src/components/crop-alignment.ts
create mode 100644 apps/desktop/src/components/cropper.css
create mode 100644 apps/desktop/src/routes/editor/playback-intent-routing.test.ts
create mode 100644 apps/desktop/src/routes/editor/playback-intent-routing.ts
create mode 100644 apps/desktop/src/routes/editor/preparing-editor-context.tsx
create mode 100644 apps/desktop/src/routes/editor/preparing-editor-model.test.ts
create mode 100644 apps/desktop/src/routes/editor/preparing-editor-model.ts
create mode 100644 apps/desktop/src/routes/editor/preparing-editor-subscription.test.ts
create mode 100644 apps/desktop/src/routes/editor/preparing-editor-subscription.ts
create mode 100644 apps/desktop/src/routes/editor/preparing-frame-lease.test.ts
create mode 100644 apps/desktop/src/routes/editor/preparing-frame-lease.ts
create mode 100644 apps/desktop/src/routes/editor/preparing-frame-transport.test.ts
create mode 100644 apps/desktop/src/routes/editor/preparing-frame-transport.ts
create mode 100644 apps/desktop/src/routes/editor/preparing-frame.tsx
create mode 100644 apps/desktop/src/routes/editor/preparing-handoff.test.ts
create mode 100644 apps/desktop/src/routes/editor/preparing-handoff.ts
create mode 100644 apps/desktop/src/routes/editor/preparing-playback-handoff.test.ts
create mode 100644 apps/desktop/src/routes/editor/preparing-playback-handoff.ts
create mode 100644 apps/desktop/src/routes/editor/preparing-timeline.tsx
create mode 100644 apps/desktop/src/routes/editor/preview-bounds.test.ts
create mode 100644 apps/desktop/src/routes/editor/preview-bounds.ts
create mode 100644 apps/desktop/src/utils/frame-identity.ts
create mode 100644 apps/web/lib/desktop-reupload.ts
create mode 100644 crates/audio/examples/relocatable-audio-probe.rs
create mode 100644 crates/audio/src/progressive.rs
create mode 100644 crates/audio/src/progressive/managed.rs
create mode 100644 crates/audio/src/progressive/test_support.rs
create mode 100644 crates/editor/src/audio_output/native_tests.rs
create mode 100644 crates/editor/src/completed_audio.rs
create mode 100644 crates/editor/src/preparing_audio/mixer.rs
create mode 100644 crates/editor/src/preparing_audio/mod.rs
create mode 100644 crates/editor/src/preparing_audio/output.rs
create mode 100644 crates/editor/src/preparing_audio/output_tests.rs
create mode 100644 crates/editor/src/preparing_editor.rs
create mode 100644 crates/editor/src/preparing_handoff.rs
create mode 100644 crates/editor/src/preparing_handoff_tests.rs
create mode 100644 crates/editor/src/preparing_playback.rs
create mode 100644 crates/editor/src/preparing_preview.rs
create mode 100644 crates/editor/src/preparing_preview/native_tests.rs
create mode 100644 crates/editor/src/preparing_preview/tests.rs
create mode 100644 crates/enc-ffmpeg/src/relocatable_source.rs
create mode 100644 crates/enc-ffmpeg/src/segmented_input.rs
create mode 100644 crates/recording/examples/studio-finalization-benchmark.rs
create mode 100644 crates/recording/src/recovery/preparing_canonical_probe.rs
create mode 100644 crates/recording/src/recovery/preparing_observer.rs
create mode 100644 crates/recording/src/recovery/preparing_projection.rs
create mode 100644 crates/rendering/src/managed_segment.rs
create mode 100644 crates/rendering/src/overlay_layers.rs
create mode 100644 crates/rendering/src/overlay_layers/native_tests.rs
create mode 100644 crates/rendering/src/readiness.rs
create mode 100644 crates/rendering/src/recorded_cursor_assets.rs
create mode 100644 crates/rendering/src/segment_timing.rs
create mode 100644 crates/video-decode/examples/fragmented-readiness.rs
create mode 100644 crates/video-decode/examples/relocatable-video-probe.rs
diff --git a/Cargo.lock b/Cargo.lock
index c395bf64716..9197d093840 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1268,6 +1268,7 @@ dependencies = [
name = "cap-audio"
version = "0.1.0"
dependencies = [
+ "cap-enc-ffmpeg",
"cidre",
"cpal 0.15.3 (git+https://github.com/CapSoftware/cpal?rev=6013cb5f8bd3)",
"ffmpeg-next",
@@ -1560,6 +1561,7 @@ dependencies = [
"thiserror 1.0.69",
"tokio",
"tokio-stream",
+ "tokio-tungstenite",
"tokio-util",
"tracing",
"tracing-appender",
@@ -1584,6 +1586,7 @@ version = "0.1.0"
dependencies = [
"axum",
"cap-audio",
+ "cap-enc-ffmpeg",
"cap-media",
"cap-media-info",
"cap-project",
@@ -1593,6 +1596,7 @@ dependencies = [
"ffmpeg-next",
"flume",
"futures",
+ "image 0.25.8",
"lru",
"ringbuf",
"sentry",
@@ -1635,6 +1639,7 @@ dependencies = [
"tempfile",
"thiserror 1.0.69",
"tracing",
+ "windows 0.60.0",
"workspace-hack",
]
@@ -1926,7 +1931,9 @@ dependencies = [
"bytemuck",
"cap-camera-effects",
"cap-cursor-info",
+ "cap-enc-ffmpeg",
"cap-flags",
+ "cap-media-info",
"cap-project",
"cap-video-decode",
"cidre",
@@ -1950,6 +1957,7 @@ dependencies = [
"serde",
"serde_json",
"specta",
+ "tempfile",
"thiserror 1.0.69",
"tiny-skia",
"tokio",
@@ -2054,10 +2062,14 @@ name = "cap-video-decode"
version = "0.1.0"
dependencies = [
"cap-d3d-adapter",
+ "cap-enc-ffmpeg",
+ "cap-media-info",
"cidre",
"ffmpeg-hw-device",
"ffmpeg-next",
"num_cpus",
+ "serde_json",
+ "sha2",
"tempfile",
"tokio",
"tracing",
diff --git a/apps/desktop-gpui/Cargo.lock b/apps/desktop-gpui/Cargo.lock
index d36888cc560..686399628c5 100644
--- a/apps/desktop-gpui/Cargo.lock
+++ b/apps/desktop-gpui/Cargo.lock
@@ -1396,6 +1396,7 @@ dependencies = [
name = "cap-audio"
version = "0.1.0"
dependencies = [
+ "cap-enc-ffmpeg",
"cidre",
"cpal 0.15.3 (git+https://github.com/CapSoftware/cpal?rev=6013cb5f8bd3)",
"ffmpeg-next",
@@ -1538,6 +1539,7 @@ dependencies = [
"anyhow",
"ashpd 0.11.1",
"base64 0.22.1",
+ "cap-audio",
"cap-camera",
"cap-camera-effects",
"cap-cursor-info",
@@ -1619,6 +1621,7 @@ dependencies = [
"ringbuf",
"sentry",
"serde",
+ "serde_json",
"specta",
"tokio",
"tokio-util",
@@ -1654,6 +1657,7 @@ dependencies = [
"sysinfo 0.32.1",
"thiserror 1.0.69",
"tracing",
+ "windows 0.60.0",
"workspace-hack",
]
@@ -1909,6 +1913,7 @@ dependencies = [
"bytemuck",
"cap-camera-effects",
"cap-cursor-info",
+ "cap-enc-ffmpeg",
"cap-flags",
"cap-project",
"cap-video-decode",
@@ -1979,6 +1984,7 @@ name = "cap-video-decode"
version = "0.1.0"
dependencies = [
"cap-d3d-adapter",
+ "cap-enc-ffmpeg",
"cidre",
"ffmpeg-hw-device",
"ffmpeg-next",
diff --git a/apps/desktop-gpui/Cargo.toml b/apps/desktop-gpui/Cargo.toml
index 83aa68e187b..b2411ea5a9f 100644
--- a/apps/desktop-gpui/Cargo.toml
+++ b/apps/desktop-gpui/Cargo.toml
@@ -18,6 +18,10 @@ publish = false
name = "cap-gpui"
path = "src/main.rs"
+[[example]]
+name = "studio-finalization-benchmark"
+path = "../../crates/recording/examples/studio-finalization-benchmark.rs"
+
[package.metadata.bundle]
name = "Cap GPUI"
identifier = "so.cap.desktop.gpui"
@@ -28,6 +32,7 @@ osx_info_plist_exts = ["resources/Info.plist"]
osx_url_schemes = ["cap-desktop"]
[dependencies]
+cap-audio = { path = "../../crates/audio" }
# gpui — the wingleeio/zed fork Comet ships. The pinned rev carries f596cde
# ("destination alpha on transparent windows: Porter-Duff OVER, not additive"),
# which our transparent rounded main-window shell depends on.
@@ -183,6 +188,7 @@ windows-sys = { version = "0.59", features = [
"Win32_Security",
"Win32_System_SystemInformation",
"Win32_System_Threading",
+ "Win32_UI_Shell",
"Win32_UI_WindowsAndMessaging",
] }
diff --git a/apps/desktop-gpui/dev.sh b/apps/desktop-gpui/dev.sh
index 8dff6251270..cd92c3edd60 100755
--- a/apps/desktop-gpui/dev.sh
+++ b/apps/desktop-gpui/dev.sh
@@ -4,17 +4,12 @@
# app persists its window state through CAP_GPUI_DEV_RESTORE and reopens where
# it was, and the swap waits out an in-flight recording (see dev_restore.rs).
set -uo pipefail
-cd "$(dirname "$0")"
+cd "$(dirname "$0")" || exit 1
STATE_FILE="$PWD/target/dev-restore.json"
BIN="$PWD/target/debug/cap-gpui"
BUILD=cargo
-# Cargo.toml turns incremental off to keep the dep tree's caches off a full
-# disk, but deps never rebuild in this loop -- the cache this creates is the
-# app crate's only, and it takes a warm rebuild from ~41s to 3-23s.
-export CARGO_INCREMENTAL=1
-
WATCH_PATHS=(src assets Cargo.toml)
[ -d resources ] && WATCH_PATHS+=(resources)
for crate in camera scap-targets recording timestamp utils project rendering editor export; do
@@ -84,7 +79,7 @@ while true; do
if [ "$CURRENT" != "$LAST" ]; then
LAST="$CURRENT"
echo "[dev] building..."
- if "$BUILD" build; then
+ if "$BUILD" build --config profile.dev.package.cap-desktop-gpui.incremental=true; then
if [ -n "$APP_PID" ] || gpui_owns_session || instance_live; then
stop_app
start_app
diff --git a/apps/desktop-gpui/examples/audio-readiness-benchmark.rs b/apps/desktop-gpui/examples/audio-readiness-benchmark.rs
new file mode 100644
index 00000000000..e0fcf623125
--- /dev/null
+++ b/apps/desktop-gpui/examples/audio-readiness-benchmark.rs
@@ -0,0 +1,302 @@
+use std::{
+ path::PathBuf,
+ sync::{Arc, atomic::AtomicBool},
+ time::Instant,
+};
+
+use cap_audio::{AudioData, AudioStream, ChunkRead};
+use sha2::{Digest, Sha256};
+use tokio::sync::watch;
+
+fn main() -> anyhow::Result<()> {
+ let path = std::env::args_os()
+ .nth(1)
+ .map(PathBuf::from)
+ .ok_or_else(|| anyhow::anyhow!("Pass an audio path"))?;
+ if std::env::var_os("CAP_BENCH_PROGRESSIVE_AUDIO").is_some() {
+ return progressive_audio(path);
+ }
+ background_audio(path)
+}
+
+#[derive(Clone)]
+struct PreviousAudioLoader {
+ rx: watch::Receiver