diff --git a/apps/desktop-gpui/src/editor_export.rs b/apps/desktop-gpui/src/editor_export.rs index 7ffa4fc171..77498a4103 100644 --- a/apps/desktop-gpui/src/editor_export.rs +++ b/apps/desktop-gpui/src/editor_export.rs @@ -943,7 +943,11 @@ impl EditorWindow { }); loop { + let mut latest: Option<(u32, u32)> = None; while let Ok((rendered, total)) = progress_rx.try_recv() { + latest = Some((rendered, total)); + } + if let Some((rendered, total)) = latest { let _ = this.update(cx, |this, cx| { if let Some(ui) = this.export.as_mut() { ui.rendered = rendered; @@ -1184,7 +1188,11 @@ impl EditorWindow { }); loop { + let mut latest: Option<(u32, u32)> = None; while let Ok((rendered, total)) = progress_rx.try_recv() { + latest = Some((rendered, total)); + } + if let Some((rendered, total)) = latest { let _ = this.update(cx, |this, cx| { if let Some(ui) = this.export.as_mut() { ui.rendered = rendered; @@ -1229,7 +1237,11 @@ impl EditorWindow { }); loop { + let mut latest: Option = None; while let Ok(progress) = upload_rx.try_recv() { + latest = Some(progress); + } + if let Some(progress) = latest { let _ = this.update(cx, |this, cx| { if let Some(ui) = this.export.as_mut() { ui.upload_progress = progress as f32; diff --git a/crates/editor/src/editor_instance.rs b/crates/editor/src/editor_instance.rs index cecbe1db9c..4a726bc66d 100644 --- a/crates/editor/src/editor_instance.rs +++ b/crates/editor/src/editor_instance.rs @@ -8,8 +8,8 @@ use cap_project::{ }; use cap_rendering::{ PrecomputedCursorTimeline, ProjectRecordingsMeta, ProjectUniforms, RecordingSegmentDecoders, - RenderVideoConstants, SegmentVideoPaths, SharedWgpuDevice, Video, ZoomTransformTimeline, - get_duration, spring_mass_damper::SpringMassDamperSimulationConfig, + RenderVideoConstants, RendererLayers, SegmentVideoPaths, SharedWgpuDevice, Video, + ZoomTransformTimeline, get_duration, spring_mass_damper::SpringMassDamperSimulationConfig, }; use std::{ collections::HashMap, @@ -248,6 +248,7 @@ pub struct EditorInstance { meta: RecordingMeta, pub export_preview_active: AtomicBool, pub export_active: AtomicBool, + pub preview_layers: std::sync::Mutex>, runtime_handle: tokio::runtime::Handle, audio_output: Arc, } @@ -655,6 +656,7 @@ impl EditorInstance { playback_active_rx, export_preview_active: AtomicBool::new(false), export_active: AtomicBool::new(false), + preview_layers: std::sync::Mutex::new(None), playback_epoch: AtomicU64::new(0), runtime_handle: tokio::runtime::Handle::current(), audio_output, @@ -781,6 +783,7 @@ impl EditorInstance { meta: self.meta.clone(), export_preview_active: AtomicBool::new(false), export_active: AtomicBool::new(false), + preview_layers: std::sync::Mutex::new(None), runtime_handle: self.runtime_handle.clone(), audio_output: self.audio_output.clone(), }); diff --git a/crates/export/src/preview.rs b/crates/export/src/preview.rs index c092ec0fdf..09e1172af4 100644 --- a/crates/export/src/preview.rs +++ b/crates/export/src/preview.rs @@ -100,6 +100,7 @@ struct PreviewSource<'a> { render_constants: &'a RenderVideoConstants, segments: Vec>, total_duration: f64, + layers_cache: Option<&'a std::sync::Mutex>>, } struct PreviewSegment<'a> { @@ -130,6 +131,7 @@ async fn render_preview_with_base( }) .collect(), total_duration, + layers_cache: None, }; render_preview_frame(source, frame_time, settings).await } @@ -184,6 +186,7 @@ pub async fn render_preview_with_editor( }) .collect(), total_duration, + layers_cache: Some(&editor.preview_layers), }; render_preview_frame(source, frame_time, settings).await } @@ -219,6 +222,7 @@ async fn render_preview_frame( render_constants, segments, total_duration, + layers_cache, } = source; let project_config = &Arc::new(project_config.clone()); let transition_mapping = project_config.timeline.as_ref().and_then(|timeline| { @@ -286,11 +290,22 @@ async fn render_preview_frame( ); let mut frame_renderer = FrameRenderer::new(render_constants); - let mut layers = RendererLayers::new_with_options( - &render_constants.device, - &render_constants.queue, - render_constants.is_software_adapter, - ); + let mut layers = layers_cache + .and_then(|cache| { + let mut guard = cache.lock().ok()?; + guard.take() + }) + .map(|mut cached| { + cached.reset_frame_state(); + cached + }) + .unwrap_or_else(|| { + RendererLayers::new_with_options( + &render_constants.device, + &render_constants.queue, + render_constants.is_software_adapter, + ) + }); let frame = if let Some((outgoing, kind, progress)) = transition_mapping { let outgoing_media = segments @@ -392,6 +407,12 @@ async fn render_preview_frame( (total_bitrate * encoder_efficiency * duration_seconds) / (8.0 * 1024.0 * 1024.0) }; + if let Some(cache) = layers_cache + && let Ok(mut guard) = cache.lock() + { + *guard = Some(layers); + } + Ok(ExportPreviewResult { jpeg_base64: STANDARD.encode(&jpeg_buffer), estimated_size_mb, diff --git a/crates/rendering/src/layers/cursor.rs b/crates/rendering/src/layers/cursor.rs index b093954378..10c06b9997 100644 --- a/crates/rendering/src/layers/cursor.rs +++ b/crates/rendering/src/layers/cursor.rs @@ -455,6 +455,7 @@ impl CursorLayer { let time_s = segment_frames.recording_time; let Some(interpolated_cursor) = &uniforms.interpolated_cursor else { + self.bind_group = None; return; }; @@ -565,6 +566,7 @@ impl CursorLayer { } let Some(tex) = self.cursors.get(&interpolated_cursor.cursor_id) else { error!("Cursor {:?} not found!", interpolated_cursor.cursor_id); + self.bind_group = None; return; }; tex diff --git a/crates/rendering/src/lib.rs b/crates/rendering/src/lib.rs index 77974d9833..15ddef1bfb 100644 --- a/crates/rendering/src/lib.rs +++ b/crates/rendering/src/lib.rs @@ -8116,7 +8116,7 @@ fn join_layer(handle: std::thread::ScopedJoinHandle<'_, T>) -> T { } impl RendererLayers { - fn reset_frame_state(&mut self) { + pub fn reset_frame_state(&mut self) { self.display.reset_frame_state(); self.camera.reset_frame_state(); self.camera_only.reset_frame_state();