Skip to content

Commit 00b070b

Browse files
committed
perf(windows): playback timing and prefetch tuning
1 parent 79a76ff commit 00b070b

File tree

2 files changed

+123
-34
lines changed

2 files changed

+123
-34
lines changed

crates/editor/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,3 +36,6 @@ ringbuf = "0.4.8"
3636
lru = "0.12"
3737
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
3838
workspace-hack = { version = "0.1", path = "../workspace-hack" }
39+
40+
[target.'cfg(target_os = "windows")'.dependencies]
41+
windows = { workspace = true, features = ["Win32_Media"] }

crates/editor/src/playback.rs

Lines changed: 120 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,63 @@ use crate::{
3535
};
3636

3737
const PREFETCH_BUFFER_SIZE: usize = 90;
38+
#[cfg(not(target_os = "windows"))]
3839
const PARALLEL_DECODE_TASKS: usize = 6;
40+
#[cfg(not(target_os = "windows"))]
3941
const INITIAL_PARALLEL_DECODE_TASKS: usize = 8;
4042
const MAX_PREFETCH_AHEAD: u32 = 90;
43+
#[cfg(not(target_os = "windows"))]
4144
const PREFETCH_BEHIND: u32 = 10;
4245
const FRAME_CACHE_SIZE: usize = 90;
4346
const RAMP_UP_FRAME_COUNT: u32 = 15;
4447

48+
#[cfg(target_os = "windows")]
49+
struct WindowsTimerResolution;
50+
51+
#[cfg(target_os = "windows")]
52+
impl WindowsTimerResolution {
53+
fn set_high_precision() -> Self {
54+
unsafe {
55+
windows::Win32::Media::timeBeginPeriod(1);
56+
}
57+
Self
58+
}
59+
}
60+
61+
#[cfg(target_os = "windows")]
62+
impl Drop for WindowsTimerResolution {
63+
fn drop(&mut self) {
64+
unsafe {
65+
windows::Win32::Media::timeEndPeriod(1);
66+
}
67+
}
68+
}
69+
70+
#[cfg(target_os = "windows")]
71+
async fn precision_sleep_until(deadline: Instant, stop_rx: &mut watch::Receiver<bool>) -> bool {
72+
let spin_threshold = Duration::from_millis(2);
73+
74+
let now = Instant::now();
75+
if now >= deadline {
76+
return false;
77+
}
78+
79+
let remaining = deadline - now;
80+
if remaining > spin_threshold {
81+
let sleep_target = deadline - spin_threshold;
82+
tokio::select! {
83+
_ = stop_rx.changed() => return true,
84+
_ = tokio::time::sleep_until(sleep_target) => {}
85+
}
86+
}
87+
88+
while Instant::now() < deadline {
89+
tokio::task::yield_now().await;
90+
}
91+
92+
false
93+
}
94+
4595
#[derive(Debug)]
4696
pub enum PlaybackStartError {
4797
InvalidFps,
@@ -181,6 +231,12 @@ impl Playback {
181231
let mut in_flight: FuturesUnordered<PrefetchFuture> = FuturesUnordered::new();
182232
let mut frames_decoded: u32 = 0;
183233
let mut prefetched_behind: HashSet<u32> = HashSet::new();
234+
#[cfg(target_os = "windows")]
235+
let prefetch_behind = 0u32;
236+
#[cfg(not(target_os = "windows"))]
237+
let prefetch_behind = PREFETCH_BEHIND;
238+
#[cfg(target_os = "windows")]
239+
let prefetch_start = Instant::now();
184240

185241
let mut cached_project = prefetch_project.borrow().clone();
186242

@@ -219,12 +275,29 @@ impl Playback {
219275
}
220276

221277
let current_playback_frame = *playback_position_rx.borrow();
222-
let max_prefetch_frame = current_playback_frame + MAX_PREFETCH_AHEAD;
278+
#[cfg(target_os = "windows")]
279+
let max_prefetch_ahead = if prefetch_start.elapsed() < Duration::from_secs(6) {
280+
45u32
281+
} else {
282+
MAX_PREFETCH_AHEAD
283+
};
284+
#[cfg(not(target_os = "windows"))]
285+
let max_prefetch_ahead = MAX_PREFETCH_AHEAD;
286+
let max_prefetch_frame = current_playback_frame + max_prefetch_ahead;
287+
288+
#[cfg(target_os = "windows")]
289+
let initial_parallel_decode_tasks = 3usize;
290+
#[cfg(not(target_os = "windows"))]
291+
let initial_parallel_decode_tasks = INITIAL_PARALLEL_DECODE_TASKS;
292+
#[cfg(target_os = "windows")]
293+
let parallel_decode_tasks = 3usize;
294+
#[cfg(not(target_os = "windows"))]
295+
let parallel_decode_tasks = PARALLEL_DECODE_TASKS;
223296

224297
let effective_parallel = if frames_decoded < RAMP_UP_FRAME_COUNT {
225-
INITIAL_PARALLEL_DECODE_TASKS
298+
initial_parallel_decode_tasks
226299
} else {
227-
PARALLEL_DECODE_TASKS
300+
parallel_decode_tasks
228301
};
229302

230303
while in_flight.len() < effective_parallel {
@@ -292,7 +365,7 @@ impl Playback {
292365
}
293366

294367
if in_flight.len() < effective_parallel {
295-
for behind_offset in 1..=PREFETCH_BEHIND {
368+
for behind_offset in 1..=prefetch_behind {
296369
if in_flight.len() >= effective_parallel {
297370
break;
298371
}
@@ -400,7 +473,6 @@ impl Playback {
400473
let mut prefetch_buffer: VecDeque<PrefetchedFrame> =
401474
VecDeque::with_capacity(PREFETCH_BUFFER_SIZE);
402475
let mut frame_cache = FrameCache::new(FRAME_CACHE_SIZE);
403-
let aggressive_skip_threshold = 6u32;
404476

405477
let mut total_frames_rendered = 0u64;
406478
let mut total_frames_skipped = 0u64;
@@ -410,6 +482,9 @@ impl Playback {
410482
let mut last_stats_time = Instant::now();
411483
let stats_interval = Duration::from_secs(2);
412484

485+
#[cfg(target_os = "windows")]
486+
let warmup_target_frames = 45usize;
487+
#[cfg(not(target_os = "windows"))]
413488
let warmup_target_frames = 10usize;
414489
let warmup_after_first_timeout = Duration::from_millis(500);
415490
let warmup_no_frames_timeout = Duration::from_secs(5);
@@ -460,6 +535,9 @@ impl Playback {
460535
.make_contiguous()
461536
.sort_by_key(|p| p.frame_number);
462537

538+
#[cfg(target_os = "windows")]
539+
let _timer_guard = WindowsTimerResolution::set_high_precision();
540+
463541
let start = Instant::now();
464542
let mut cached_project = self.project.borrow().clone();
465543

@@ -491,9 +569,18 @@ impl Playback {
491569
let frame_offset = frame_number.saturating_sub(self.start_frame_number) as f64;
492570
let next_deadline = start + frame_duration.mul_f64(frame_offset);
493571

494-
tokio::select! {
495-
_ = stop_rx.changed() => break 'playback,
496-
_ = tokio::time::sleep_until(next_deadline) => {}
572+
#[cfg(target_os = "windows")]
573+
{
574+
if precision_sleep_until(next_deadline, &mut stop_rx).await {
575+
break 'playback;
576+
}
577+
}
578+
#[cfg(not(target_os = "windows"))]
579+
{
580+
tokio::select! {
581+
_ = stop_rx.changed() => break 'playback,
582+
_ = tokio::time::sleep_until(next_deadline) => {}
583+
}
497584
}
498585

499586
if *stop_rx.borrow() {
@@ -505,6 +592,8 @@ impl Playback {
505592
break;
506593
}
507594

595+
let decode_wait_timeout = Duration::from_millis(100);
596+
508597
let mut was_cached = false;
509598

510599
let segment_frames_opt = if let Some(cached) = frame_cache.get(frame_number) {
@@ -531,7 +620,7 @@ impl Playback {
531620

532621
if is_in_flight {
533622
let wait_start = Instant::now();
534-
let max_wait = Duration::from_millis(100);
623+
let max_wait = decode_wait_timeout;
535624
let mut found_frame = None;
536625

537626
while wait_start.elapsed() < max_wait {
@@ -629,7 +718,7 @@ impl Playback {
629718
guard.insert(frame_number);
630719
}
631720

632-
let max_wait = Duration::from_millis(100);
721+
let max_wait = decode_wait_timeout;
633722
let data = tokio::select! {
634723
_ = stop_rx.changed() => {
635724
if let Ok(mut guard) = main_in_flight.write() {
@@ -702,13 +791,11 @@ impl Playback {
702791
&zoom_focus_interpolator,
703792
);
704793

705-
self.renderer
706-
.render_frame(
707-
Arc::unwrap_or_clone(segment_frames),
708-
uniforms,
709-
segment_media.cursor.clone(),
710-
)
711-
.await;
794+
self.renderer.render_frame(
795+
Arc::unwrap_or_clone(segment_frames),
796+
uniforms,
797+
segment_media.cursor.clone(),
798+
);
712799

713800
total_frames_rendered += 1;
714801
}
@@ -748,26 +835,25 @@ impl Playback {
748835
if frame_number < expected_frame {
749836
let frames_behind = expected_frame - frame_number;
750837

751-
if frames_behind <= aggressive_skip_threshold {
838+
if frames_behind <= 2 {
752839
continue;
753840
}
754841

755-
let skipped = frames_behind.saturating_sub(1);
756-
if skipped > 0 {
757-
frame_number += skipped;
758-
total_frames_skipped += skipped as u64;
759-
760-
prefetch_buffer.retain(|p| p.frame_number >= frame_number);
761-
frame_cache.evict_far_from(frame_number, MAX_PREFETCH_AHEAD);
762-
let _ = frame_request_tx.send(frame_number);
763-
let _ = playback_position_tx.send(frame_number);
764-
if has_audio
765-
&& audio_playhead_tx
766-
.send(frame_number as f64 / fps_f64)
767-
.is_err()
768-
{
769-
break 'playback;
770-
}
842+
let max_skip = 3u32;
843+
let skipped = frames_behind.min(max_skip);
844+
frame_number += skipped;
845+
total_frames_skipped += skipped as u64;
846+
847+
prefetch_buffer.retain(|p| p.frame_number >= frame_number);
848+
frame_cache.evict_far_from(frame_number, MAX_PREFETCH_AHEAD);
849+
let _ = frame_request_tx.send(frame_number);
850+
let _ = playback_position_tx.send(frame_number);
851+
if has_audio
852+
&& audio_playhead_tx
853+
.send(frame_number as f64 / fps_f64)
854+
.is_err()
855+
{
856+
break 'playback;
771857
}
772858
}
773859
}

0 commit comments

Comments
 (0)