Skip to content

Commit

Permalink
Fixed animation frame-skip by only using the time since the last rere…
Browse files Browse the repository at this point in the history
…nder request

Also cleaned up a few things
  • Loading branch information
Philipp-M committed Feb 21, 2024
1 parent e9a2b07 commit 626796a
Show file tree
Hide file tree
Showing 4 changed files with 27 additions and 22 deletions.
13 changes: 9 additions & 4 deletions src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -420,7 +420,7 @@ impl<T: Send + 'static, V: View<T> + 'static> App<T, V> {

let main_loop_tracing_span = tracing::debug_span!("main loop");
let mut time_of_last_render = Instant::now();
let mut time_since_last_render = Duration::ZERO;
let mut time_since_last_render_request = Duration::ZERO;
while let Some(event) = self.event_chan.blocking_recv() {
let mut events = vec![event];
// batch events
Expand All @@ -439,7 +439,7 @@ impl<T: Send + 'static, V: View<T> + 'static> App<T, V> {
}

if let Some(root_pod) = self.root_pod.as_mut() {
let cx_state = &mut CxState::new(&mut self.events, time_since_last_render);
let cx_state = &mut CxState::new(&mut self.events, time_since_last_render_request);

let mut cx = EventCx {
is_handled: false,
Expand All @@ -453,12 +453,17 @@ impl<T: Send + 'static, V: View<T> + 'static> App<T, V> {
}
self.send_events();

let rerender_requested = self.render(time_since_last_render)?;
let rerender_requested = self.render(time_since_last_render_request)?;
// TODO this is a workaround (I consider this at least as that) for getting animations right
// There's likely a cleaner solution
if rerender_requested {
self.request_render_notifier.notify_one();
time_since_last_render_request = time_of_last_render.elapsed();
} else {
time_since_last_render_request = Duration::ZERO;
}
time_since_last_render = time_of_last_render.elapsed();
time_of_last_render = Instant::now();

if quit {
break;
}
Expand Down
17 changes: 8 additions & 9 deletions src/view/animatables.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ use crate::{

// This is basically a View trait without <T, A> (but this may be subject to change, to allow animations based on the AppState (via e.g. event callbacks))
pub trait Animatable<V>: Send + Sync {
/// Associated state for the view.
/// Associated state for the animatable.
type State: Send;

/// Associated state for the view.
/// Associated state for the animatable.
type Element: AnimatableElement<V>;

/// Build the associated widget and initialize state.
Expand All @@ -33,7 +33,7 @@ pub trait Animatable<V>: Send + Sync {
/// Propagate a message.
///
/// Handle a message, propagating to children if needed. Here, `id_path` is a slice
/// of ids beginning at a child of this view.
/// of ids beginning at a child of this animatable.
fn message(
&self,
id_path: &[Id],
Expand All @@ -58,16 +58,15 @@ impl<V, T: Tweenable<V>, R: Animatable<f64>> Animatable<V> for Lerp<T, R> {
type Element = widget::animatables::Lerp<T::Element, R::Element>;

fn build(&self, cx: &mut Cx) -> (Id, Self::State, Self::Element) {
let (id, (state, el)) = cx.with_new_id(|cx| {
let (id, (state, element)) = cx.with_new_id(|cx| {
let (ratio_id, ratio_state, ratio_element) = self.ratio.build(cx);
let (tweenable_id, tweenable_state, tweenable_element) = self.tweenable.build(cx);
let element = widget::animatables::Lerp::new(tweenable_element, ratio_element);
(
(tweenable_id, tweenable_state, ratio_id, ratio_state),
element,
widget::animatables::Lerp::new(tweenable_element, ratio_element),
)
});
(id, state, el)
(id, state, element)
}

fn rebuild(
Expand Down Expand Up @@ -236,7 +235,7 @@ pub trait Tweenable<V>: Send + Sync {
/// Build the associated widget and initialize state.
fn build(&self, cx: &mut Cx) -> (Id, Self::State, Self::Element);

/// Update the associated value.
/// Update the associated element.
///
/// Returns an indication of what, if anything, has changed.
fn rebuild(
Expand All @@ -245,7 +244,7 @@ pub trait Tweenable<V>: Send + Sync {
prev: &Self,
id: &mut Id,
state: &mut Self::State,
value: &mut Self::Element,
element: &mut Self::Element,
) -> ChangeFlags;

/// Propagate a message.
Expand Down
9 changes: 5 additions & 4 deletions src/widget/animatables.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,6 @@ macro_rules! impl_animatable_for_primitive {
}

// All builtin number types
impl_animatable_for_primitive!(f64);
impl_animatable_for_primitive!(f32);
impl_animatable_for_primitive!(i8);
impl_animatable_for_primitive!(u8);
impl_animatable_for_primitive!(i16);
Expand All @@ -60,6 +58,9 @@ impl_animatable_for_primitive!(u128);
impl_animatable_for_primitive!(isize);
impl_animatable_for_primitive!(usize);

impl_animatable_for_primitive!(f32);
impl_animatable_for_primitive!(f64);

#[derive(Clone, Debug)]
pub struct LowPassIIR<AT, V> {
pub(crate) target: AT,
Expand Down Expand Up @@ -91,7 +92,7 @@ impl<AT: AnimatableElement<f64>> AnimatableElement<f64> for LowPassIIR<AT, f64>
let target_value = self.target.animate(cx);
if let Some(value) = &mut self.value {
if (*target_value - *value).abs() > 0.0001 {
let delta_time = cx.time_since_last_render().as_secs_f64() * 100.0; // could be a different factor, and maybe more precisely a frequency based cutoff or something like that
let delta_time = cx.time_since_last_render_request().as_secs_f64() * 100.0; // could be a different factor, and maybe more precisely a frequency based cutoff or something like that
let time_adjusted_decay =
1.0 - ((1.0 - self.decay.clamp(0.0, 1.0)).powf(delta_time));
*value += time_adjusted_decay * (*target_value - *value);
Expand Down Expand Up @@ -154,7 +155,7 @@ impl<V: 'static, PS: AnimatableElement<f64>, TW: TweenableElement<V>> Animatable
let duration_as_secs = self.duration.as_secs_f64();
let current_time_as_secs = self.current_time.as_secs_f64();
let new_time = (current_time_as_secs
+ *play_speed * cx.time_since_last_render().as_secs_f64())
+ *play_speed * cx.time_since_last_render_request().as_secs_f64())
.clamp(0.0, duration_as_secs);
// avoid division by zero
let ratio = if duration_as_secs == 0.0 {
Expand Down
10 changes: 5 additions & 5 deletions src/widget/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,14 @@ message!(Send);
/// Static state that is shared between most contexts.
pub struct CxState<'a> {
messages: &'a mut Vec<Message>,
pub(crate) time_since_last_render: Duration, // in seconds TODO Duration instead of f64?
pub(crate) time_since_last_render_request: Duration, // in seconds TODO Duration instead of f64?
}

impl<'a> CxState<'a> {
pub fn new(messages: &'a mut Vec<Message>, time_since_last_render: Duration) -> Self {
pub fn new(messages: &'a mut Vec<Message>, time_since_last_render_request: Duration) -> Self {
Self {
messages,
time_since_last_render,
time_since_last_render_request,
}
}
}
Expand Down Expand Up @@ -144,8 +144,8 @@ impl_context_method!(
self.widget_state.flags |= PodFlags::REQUEST_ANIMATION;
}

pub fn time_since_last_render(&self) -> Duration {
self.cx_state.time_since_last_render
pub fn time_since_last_render_request(&self) -> Duration {
self.cx_state.time_since_last_render_request
}

/// Notify Trui that this widgets view context changed.
Expand Down

0 comments on commit 626796a

Please sign in to comment.