Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 52 additions & 6 deletions crates/editor/src/editor_instance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,16 @@ use cap_project::{
TimelineFrameMapping, TimelineSegment, XY,
};
use cap_rendering::{
PrecomputedCursorTimeline, ProjectRecordingsMeta, ProjectUniforms, RecordingSegmentDecoders,
RenderVideoConstants, RendererLayers, SegmentVideoPaths, SharedWgpuDevice, Video,
ZoomTransformTimeline, get_duration, spring_mass_damper::SpringMassDamperSimulationConfig,
CursorEventOrder, PrecomputedCursorTimeline, ProjectRecordingsMeta, ProjectUniforms,
RecordingSegmentDecoders, RenderVideoConstants, RendererLayers, SegmentVideoPaths,
SharedWgpuDevice, Video, ZoomTransformTimeline, get_duration,
spring_mass_damper::SpringMassDamperSimulationConfig,
};
use std::{
collections::HashMap,
path::{Path, PathBuf},
sync::{
Arc,
Arc, Weak,
atomic::{AtomicBool, AtomicU64, Ordering},
},
time::{Duration, Instant},
Expand Down Expand Up @@ -70,6 +71,12 @@ pub(super) fn cursor_timeline_settings(cursor: &cap_project::CursorConfiguration
#[derive(Default)]
pub(super) struct PreviewCursorCache {
entries: Vec<PreviewCursorCacheEntry>,
event_orders: Vec<PreviewCursorOrderEntry>,
}

struct PreviewCursorOrderEntry {
cursor: Weak<CursorEvents>,
order: CursorEventOrder,
}

struct PreviewCursorCacheEntry {
Expand All @@ -80,6 +87,29 @@ struct PreviewCursorCacheEntry {
}

impl PreviewCursorCache {
pub(super) fn event_order(&mut self, cursor: &Arc<CursorEvents>) -> CursorEventOrder {
if let Some(index) = self
.event_orders
.iter()
.position(|entry| std::ptr::eq(entry.cursor.as_ptr(), Arc::as_ptr(cursor)))
{
let entry = self.event_orders.remove(index);
let order = entry.order;
self.event_orders.push(entry);
return order;
}

if self.event_orders.len() == PREVIEW_CURSOR_CACHE_CAPACITY {
self.event_orders.remove(0);
}
let order = CursorEventOrder::new(cursor);
self.event_orders.push(PreviewCursorOrderEntry {
cursor: Arc::downgrade(cursor),
order,
});
order
}

pub(super) fn get(
&mut self,
recording_clip: u32,
Expand Down Expand Up @@ -1254,7 +1284,7 @@ impl EditorInstance {
cursor_timeline,
)
} else {
ProjectUniforms::new(
ProjectUniforms::new_with_cursor_event_order(
&self.render_constants,
&project,
frame_number,
Expand All @@ -1264,6 +1294,7 @@ impl EditorInstance {
&outgoing_frames,
total_duration,
outgoing_zoom,
cursor_cache.event_order(&outgoing_media.cursor),
)
};
Some((
Expand Down Expand Up @@ -1315,7 +1346,7 @@ impl EditorInstance {
cursor_timeline,
)
} else {
ProjectUniforms::new(
ProjectUniforms::new_with_cursor_event_order(
&self.render_constants,
&project,
frame_number,
Expand All @@ -1325,6 +1356,7 @@ impl EditorInstance {
&segment_frames,
total_duration,
zoom_timeline,
cursor_cache.event_order(&segment_medias.cursor),
)
};

Expand Down Expand Up @@ -2175,6 +2207,20 @@ mod tests {
assert!(cache.entries.is_empty());
assert!(smoothed_weak.upgrade().is_none());
assert_eq!(Arc::strong_count(&cursor), 1);
let order = cache.event_order(&cursor);
assert_eq!(order, CursorEventOrder::new(&cursor));
assert_ne!(order, CursorEventOrder::default());
assert_eq!(cache.event_order(&cursor), order);
assert_eq!(cache.event_orders.len(), 1);
assert_eq!(Arc::strong_count(&cursor), 1);

let mut reversed = CursorEvents::clone(&cursor);
reversed.moves.reverse();
reversed.clicks.reverse();
let replaced = Arc::new(reversed);
assert_eq!(cache.event_order(&replaced), CursorEventOrder::default());
assert_eq!(cache.event_orders.len(), 2);
assert_eq!(Arc::strong_count(&replaced), 1);

project.cursor.tension += 1.0;
project.cursor.click_spring = Some(cap_project::ClickSpringConfig {
Expand Down
3 changes: 2 additions & 1 deletion crates/editor/src/preparing_preview.rs
Original file line number Diff line number Diff line change
Expand Up @@ -672,7 +672,7 @@ async fn run_preview(
cursor_timeline,
)
} else {
ProjectUniforms::new(
ProjectUniforms::new_with_cursor_event_order(
&constants,
&project,
request.frame_number,
Expand All @@ -682,6 +682,7 @@ async fn run_preview(
&frames,
duration,
zoom,
cursor_cache.event_order(&cursor),
)
};
let layout = uniforms.frame_layout();
Expand Down
60 changes: 33 additions & 27 deletions crates/rendering/src/cursor_interpolation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -343,17 +343,39 @@ struct CursorTimelineVariant {
}

#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub(crate) struct CursorEventOrder {
pub moves: bool,
pub clicks: bool,
pub struct CursorEventOrder {
pub(crate) moves: bool,
pub(crate) clicks: bool,
}

impl CursorEventOrder {
pub fn new(cursor: &CursorEvents) -> Self {
Self {
moves: cursor.moves.is_sorted_by(|a, b| a.time_ms <= b.time_ms),
clicks: cursor
.clicks
.iter()
.try_fold(f64::NEG_INFINITY, |previous, click| {
(previous <= click.time_ms).then_some(click.time_ms)
})
.is_some(),
}
}

pub(crate) fn interpolate_raw(
self,
cursor: &CursorEvents,
time_secs: f32,
) -> Option<InterpolatedCursorPosition> {
interpolate_raw_cursor(cursor, (time_secs * 1000.0) as f64, self.moves)
}
}

pub struct PrecomputedCursorTimeline {
timeline: Vec<SmoothedCursorEvent>,
cursor_ids: Vec<String>,
raw_cursor: CursorEvents,
raw_moves_are_ordered: bool,
clicks_are_ordered: bool,
event_order: CursorEventOrder,
has_smoothing: bool,
variants: Mutex<Vec<CursorTimelineVariant>>,
}
Expand All @@ -364,21 +386,13 @@ impl PrecomputedCursorTimeline {
smoothing: Option<SpringMassDamperSimulationConfig>,
click_spring: Option<ClickSpringConfig>,
) -> Self {
let raw_moves_are_ordered = cursor.moves.is_sorted_by(|a, b| a.time_ms <= b.time_ms);
let clicks_are_ordered = cursor
.clicks
.iter()
.try_fold(f64::NEG_INFINITY, |previous, click| {
(previous <= click.time_ms).then_some(click.time_ms)
})
.is_some();
let event_order = CursorEventOrder::new(cursor);
if cursor.moves.is_empty() || smoothing.is_none() {
return Self {
timeline: vec![],
cursor_ids: Vec::new(),
raw_cursor: cursor.clone(),
raw_moves_are_ordered,
clicks_are_ordered,
event_order,
has_smoothing: false,
variants: Mutex::new(Vec::new()),
};
Expand All @@ -398,8 +412,7 @@ impl PrecomputedCursorTimeline {
timeline: built.events,
cursor_ids: built.cursor_ids,
raw_cursor: CursorEvents::default(),
raw_moves_are_ordered,
clicks_are_ordered,
event_order,
has_smoothing: true,
variants: Mutex::new(Vec::new()),
}
Expand Down Expand Up @@ -454,18 +467,15 @@ impl PrecomputedCursorTimeline {
}

pub(crate) fn event_order(&self) -> CursorEventOrder {
CursorEventOrder {
moves: self.raw_moves_are_ordered,
clicks: self.clicks_are_ordered,
}
self.event_order
}

pub fn interpolate(&self, time_secs: f32) -> Option<InterpolatedCursorPosition> {
let time_ms = (time_secs * 1000.0) as f64;
if self.has_smoothing {
interpolate_timeline(&self.timeline, &self.cursor_ids, time_ms)
} else {
interpolate_raw_cursor(&self.raw_cursor, time_ms, self.raw_moves_are_ordered)
interpolate_raw_cursor(&self.raw_cursor, time_ms, self.event_order.moves)
}
}

Expand All @@ -475,11 +485,7 @@ impl PrecomputedCursorTimeline {
cursor: &CursorEvents,
time_secs: f32,
) -> Option<InterpolatedCursorPosition> {
interpolate_raw_cursor(
cursor,
(time_secs * 1000.0) as f64,
self.raw_moves_are_ordered,
)
self.event_order.interpolate_raw(cursor, time_secs)
}
}

Expand Down
Loading
Loading