Skip to content

Commit 8eb228e

Browse files
committed
perf(rendering): share precomputed cursor timelines via Arc and reduce decode retries
1 parent 527e998 commit 8eb228e

1 file changed

Lines changed: 62 additions & 40 deletions

File tree

crates/rendering/src/lib.rs

Lines changed: 62 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ pub mod zoom_focus_interpolation;
5050

5151
pub use coord::*;
5252
pub use decoder::{DecodedFrame, DecoderStatus, DecoderType, PixelFormat};
53-
pub use frame_pipeline::{GpuOutputFormat, Nv12RenderedFrame, RenderedFrame};
53+
pub use frame_pipeline::{GpuOutputFormat, Nv12RenderedFrame, RenderedFrame, SharedNv12Buffer};
5454
pub use project_recordings::{ProjectRecordingsMeta, SegmentRecordings, Video};
5555

5656
use mask::interpolate_masks;
@@ -461,10 +461,22 @@ pub async fn render_video_to_channel(
461461

462462
let click_spring = project.cursor.click_spring_config();
463463

464-
let mut zoom_focus_interpolators: Vec<ZoomFocusInterpolator> = render_segments
464+
let precomputed_cursor_timelines: Vec<Arc<PrecomputedCursorTimeline>> = render_segments
465465
.iter()
466466
.map(|segment| {
467-
ZoomFocusInterpolator::new(
467+
Arc::new(PrecomputedCursorTimeline::new(
468+
&segment.cursor,
469+
cursor_smoothing,
470+
Some(click_spring),
471+
))
472+
})
473+
.collect();
474+
475+
let mut zoom_focus_interpolators: Vec<ZoomFocusInterpolator> = render_segments
476+
.iter()
477+
.zip(precomputed_cursor_timelines.iter())
478+
.map(|(segment, precomputed_cursor)| {
479+
ZoomFocusInterpolator::new_with_precomputed_cursor(
468480
&segment.cursor,
469481
cursor_smoothing,
470482
click_spring,
@@ -475,17 +487,11 @@ pub async fn render_video_to_channel(
475487
.as_ref()
476488
.map(|t| t.zoom_segments.as_slice())
477489
.unwrap_or(&[]),
490+
Some(precomputed_cursor.clone()),
478491
)
479492
})
480493
.collect();
481494

482-
let precomputed_cursor_timelines: Vec<PrecomputedCursorTimeline> = render_segments
483-
.iter()
484-
.map(|segment| {
485-
PrecomputedCursorTimeline::new(&segment.cursor, cursor_smoothing, Some(click_spring))
486-
})
487-
.collect();
488-
489495
let mut frame_number = 0;
490496

491497
let mut frame_renderer = FrameRenderer::new(constants);
@@ -703,7 +709,11 @@ pub async fn render_video_to_channel(
703709
frame_number = current_frame_number,
704710
segment_time = segment_time,
705711
consecutive_failures = consecutive_failures,
706-
max_retries = DECODE_MAX_RETRIES,
712+
max_retries = if is_initial_frame {
713+
DECODE_MAX_RETRIES_INITIAL
714+
} else {
715+
DECODE_MAX_RETRIES_STEADY
716+
},
707717
"Frame decode failed after retries - using previous frame"
708718
);
709719
let mut fallback = last_frame.clone();
@@ -715,7 +725,11 @@ pub async fn render_video_to_channel(
715725
tracing::error!(
716726
frame_number = current_frame_number,
717727
segment_time = segment_time,
718-
max_retries = DECODE_MAX_RETRIES,
728+
max_retries = if is_initial_frame {
729+
DECODE_MAX_RETRIES_INITIAL
730+
} else {
731+
DECODE_MAX_RETRIES_STEADY
732+
},
719733
"First frame decode failed after retries - cannot continue"
720734
);
721735
continue;
@@ -775,11 +789,23 @@ pub async fn render_video_to_channel_nv12(
775789

776790
let click_spring = project.cursor.click_spring_config();
777791

792+
let precomputed_cursor_timelines: Vec<Arc<PrecomputedCursorTimeline>> = render_segments
793+
.iter()
794+
.map(|segment| {
795+
Arc::new(PrecomputedCursorTimeline::new(
796+
&segment.cursor,
797+
cursor_smoothing,
798+
Some(click_spring),
799+
))
800+
})
801+
.collect();
802+
778803
let zoom_build_start = Instant::now();
779804
let mut zoom_focus_interpolators: Vec<ZoomFocusInterpolator> = render_segments
780805
.iter()
781-
.map(|segment| {
782-
ZoomFocusInterpolator::new(
806+
.zip(precomputed_cursor_timelines.iter())
807+
.map(|(segment, precomputed_cursor)| {
808+
ZoomFocusInterpolator::new_with_precomputed_cursor(
783809
&segment.cursor,
784810
cursor_smoothing,
785811
click_spring,
@@ -790,6 +816,7 @@ pub async fn render_video_to_channel_nv12(
790816
.as_ref()
791817
.map(|t| t.zoom_segments.as_slice())
792818
.unwrap_or(&[]),
819+
Some(precomputed_cursor.clone()),
793820
)
794821
})
795822
.collect();
@@ -798,25 +825,6 @@ pub async fn render_video_to_channel_nv12(
798825
}
799826
let zoom_focus_interpolators_construct_ms = zoom_build_start.elapsed().as_millis() as u64;
800827

801-
let cursor_smoothing_for_precompute =
802-
(!project.cursor.raw).then_some(spring_mass_damper::SpringMassDamperSimulationConfig {
803-
tension: project.cursor.tension,
804-
mass: project.cursor.mass,
805-
friction: project.cursor.friction,
806-
});
807-
let click_spring_for_precompute = project.cursor.click_spring_config();
808-
809-
let precomputed_cursor_timelines: Vec<PrecomputedCursorTimeline> = render_segments
810-
.iter()
811-
.map(|segment| {
812-
PrecomputedCursorTimeline::new(
813-
&segment.cursor,
814-
cursor_smoothing_for_precompute,
815-
Some(click_spring_for_precompute),
816-
)
817-
})
818-
.collect();
819-
820828
let mut frame_number = 0;
821829

822830
let renderer_setup_start = Instant::now();
@@ -1149,7 +1157,11 @@ pub async fn render_video_to_channel_nv12(
11491157
frame_number = current_frame_number,
11501158
segment_time = segment_time,
11511159
consecutive_failures = consecutive_failures,
1152-
max_retries = DECODE_MAX_RETRIES,
1160+
max_retries = if is_initial_frame {
1161+
DECODE_MAX_RETRIES_INITIAL
1162+
} else {
1163+
DECODE_MAX_RETRIES_STEADY
1164+
},
11531165
"Frame decode failed after retries - using previous NV12 frame"
11541166
);
11551167
let mut fallback = last_frame.clone_metadata_with_data();
@@ -1166,7 +1178,11 @@ pub async fn render_video_to_channel_nv12(
11661178
tracing::error!(
11671179
frame_number = current_frame_number,
11681180
segment_time = segment_time,
1169-
max_retries = DECODE_MAX_RETRIES,
1181+
max_retries = if is_initial_frame {
1182+
DECODE_MAX_RETRIES_INITIAL
1183+
} else {
1184+
DECODE_MAX_RETRIES_STEADY
1185+
},
11701186
"First frame decode failed after retries - cannot continue"
11711187
);
11721188
continue;
@@ -1194,7 +1210,8 @@ pub async fn render_video_to_channel_nv12(
11941210
Ok(())
11951211
}
11961212

1197-
const DECODE_MAX_RETRIES: u32 = 5;
1213+
const DECODE_MAX_RETRIES_INITIAL: u32 = 5;
1214+
const DECODE_MAX_RETRIES_STEADY: u32 = 2;
11981215

11991216
async fn decode_segment_frames_with_retry(
12001217
decoders: &RecordingSegmentDecoders,
@@ -1206,13 +1223,18 @@ async fn decode_segment_frames_with_retry(
12061223
) -> Option<DecodedSegmentFrames> {
12071224
let mut result = None;
12081225
let mut retry_count = 0u32;
1226+
let max_retries = if is_initial_frame {
1227+
DECODE_MAX_RETRIES_INITIAL
1228+
} else {
1229+
DECODE_MAX_RETRIES_STEADY
1230+
};
12091231

1210-
while result.is_none() && retry_count < DECODE_MAX_RETRIES {
1232+
while result.is_none() && retry_count < max_retries {
12111233
if retry_count > 0 {
12121234
let delay = if is_initial_frame {
12131235
500 * (retry_count as u64 + 1)
12141236
} else {
1215-
50 * retry_count as u64
1237+
10
12161238
};
12171239
tokio::time::sleep(std::time::Duration::from_millis(delay)).await;
12181240
}
@@ -1229,7 +1251,7 @@ async fn decode_segment_frames_with_retry(
12291251

12301252
if result.is_none() {
12311253
retry_count += 1;
1232-
if retry_count < DECODE_MAX_RETRIES {
1254+
if retry_count < max_retries {
12331255
tracing::warn!(
12341256
frame_number = current_frame_number,
12351257
segment_time = segment_time,
@@ -2972,7 +2994,7 @@ impl<'a> FrameRenderer<'a> {
29722994
}
29732995

29742996
Ok(Some(frame_pipeline::Nv12RenderedFrame {
2975-
data: Arc::new(nv12_buf),
2997+
data: self.nv12_buffer_pool.wrap(nv12_buf),
29762998
width,
29772999
height,
29783000
y_stride: width,

0 commit comments

Comments
 (0)