iris: drive animation from painter frame time
This commit is contained in:
1 parent
5f4975285d
commit
eb30a01f2d
14 files changed
+148
-116
No files matched your search
+12
-26
@@ -26,7 +26,6 @@ pub struct UiData {
|
||||
pub text: Rc<RefCell<TextResources>>,
|
||||
pub masks: TrackedArena<Mask, u32>,
|
||||
pub move_offsets: TrackedArena<MoveOffset, u32>,
|
||||
animating: Vec<WidgetId>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
@@ -169,40 +168,27 @@ impl DerefMut for Ui {
|
||||
}
|
||||
}
|
||||
|
||||
impl UiData {
|
||||
/// Ask for `id`'s [`crate::Widget::tick`] to run every frame until it
|
||||
/// says it is done. Idempotent -- registering an already-animating
|
||||
/// widget is the ordinary case (a second fling before the first
|
||||
/// settled) and must not tick it twice per frame.
|
||||
pub fn animate(&mut self, id: WidgetId) {
|
||||
if !self.animating.contains(&id) {
|
||||
self.animating.push(id);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn tick_animations(&mut self, now: std::time::Instant) -> bool {
|
||||
let mut registered = std::mem::take(&mut self.animating);
|
||||
registered.retain(|&id| match self.widgets.get_dyn_mut(id) {
|
||||
Some(widget) => widget.tick(now),
|
||||
None => false,
|
||||
});
|
||||
for id in registered {
|
||||
self.animate(id);
|
||||
}
|
||||
!self.animating.is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
pub trait UiRsc {
|
||||
fn ui(&self) -> &Ui;
|
||||
fn ui_mut(&mut self) -> &mut Ui;
|
||||
|
||||
fn draw<'a>(&mut self, root: impl Into<Option<&'a crate::StrongWidget>>)
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
self.draw_at(root, std::time::Instant::now());
|
||||
}
|
||||
|
||||
fn draw_at<'a>(
|
||||
&mut self,
|
||||
root: impl Into<Option<&'a crate::StrongWidget>>,
|
||||
frame_time: std::time::Instant,
|
||||
) -> bool
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
let render_state = self.ui().render_state.clone();
|
||||
render_state.get_mut().update(root, self);
|
||||
render_state.get_mut().update_at(root, self, frame_time)
|
||||
}
|
||||
|
||||
#[allow(unused_variables)]
|
||||
|
||||
+16
-1
@@ -9,7 +9,7 @@ use crate::{
|
||||
ui::render_state::Retained,
|
||||
util::Vec2,
|
||||
};
|
||||
use std::{cell::RefCell, rc::Rc};
|
||||
use std::{cell::RefCell, rc::Rc, time::Instant};
|
||||
|
||||
pub struct Painter<'a> {
|
||||
pub(super) render_state: &'a mut UiRenderState,
|
||||
@@ -52,6 +52,21 @@ impl DrawResult<'_, '_> {
|
||||
}
|
||||
|
||||
impl<'a> Painter<'a> {
|
||||
/// The presentation time of this frame, supplied by the platform. On a
|
||||
/// backend with a display clock this is the vsync timestamp, not the time
|
||||
/// at which this widget happened to be drawn.
|
||||
pub fn frame_time(&self) -> Instant {
|
||||
self.render_state.frame_time
|
||||
}
|
||||
|
||||
/// Redraw this widget on the following frame and ask the platform to
|
||||
/// produce that frame. The invalidation is staged until the current
|
||||
/// retained redraw has finished, so it cannot be consumed again in this
|
||||
/// frame.
|
||||
pub fn request_next_frame(&mut self) {
|
||||
self.render_state.next_frame.insert(self.id);
|
||||
}
|
||||
|
||||
pub fn set_size(&mut self, size: Size) {
|
||||
assert!(
|
||||
self.size.replace(size).is_none(),
|
||||
|
||||
@@ -45,6 +45,11 @@ pub struct UiRenderState {
|
||||
/// guard could never fire and the set grew by one entry per widget
|
||||
/// ever drawn and was never emptied.
|
||||
draw_started: HashSet<WidgetId>,
|
||||
/// Widgets which asked from inside `draw` to be drawn on the following
|
||||
/// frame. Kept separate from `Widgets::needs_redraw` until `update_at`
|
||||
/// finishes because `redraw_updates` drains that set to completion.
|
||||
pub(super) next_frame: HashSet<WidgetId>,
|
||||
pub(super) frame_time: Instant,
|
||||
|
||||
/// `Widget::draw` calls and `Primitives::region_mut` rewrites since the
|
||||
/// last `take_counters`. LAYOUT.md section 8's pass conditions are
|
||||
@@ -111,6 +116,8 @@ impl UiRenderState {
|
||||
old_root: None,
|
||||
resized: false,
|
||||
draw_started: Default::default(),
|
||||
next_frame: Default::default(),
|
||||
frame_time: Instant::now(),
|
||||
draw_count: 0,
|
||||
region_mut_count: 0,
|
||||
mov_count: 0,
|
||||
@@ -225,6 +232,17 @@ impl UiRenderState {
|
||||
}
|
||||
|
||||
pub fn update<'a>(&mut self, root: impl Into<Option<&'a StrongWidget>>, rsc: &mut dyn UiRsc) {
|
||||
self.update_at(root, rsc, Instant::now());
|
||||
}
|
||||
|
||||
/// Draw a frame dated on the platform's presentation clock. Returns
|
||||
/// whether a widget requested another frame.
|
||||
pub fn update_at<'a>(
|
||||
&mut self,
|
||||
root: impl Into<Option<&'a StrongWidget>>,
|
||||
rsc: &mut dyn UiRsc,
|
||||
frame_time: Instant,
|
||||
) -> bool {
|
||||
// safety mechanism for memory leaks; might wanna return a result instead so user can
|
||||
// decide whether to panic or not
|
||||
if !rsc.widgets().waiting.is_empty() {
|
||||
@@ -241,6 +259,7 @@ impl UiRenderState {
|
||||
weak widgets: {all:#?}"
|
||||
);
|
||||
}
|
||||
self.frame_time = frame_time;
|
||||
let root = root.into();
|
||||
debug_assert!(
|
||||
self.draw_started.is_empty(),
|
||||
@@ -270,6 +289,13 @@ impl UiRenderState {
|
||||
self.apply_free();
|
||||
#[cfg(debug_assertions)]
|
||||
debug_assert!(self.primitive_counts_agree(), "{}", self.orphan_report(rsc),);
|
||||
let next: Vec<_> = std::mem::take(&mut self.next_frame)
|
||||
.into_iter()
|
||||
.filter(|id| self.active.contains_key(id))
|
||||
.collect();
|
||||
let requested = !next.is_empty();
|
||||
rsc.widgets_mut().needs_redraw.extend(next);
|
||||
requested
|
||||
}
|
||||
|
||||
pub fn epoch(&self) -> Instant {
|
||||
|
||||
@@ -47,11 +47,6 @@ pub trait Widget: Any {
|
||||
fn child_order(&self) -> ChildOrder {
|
||||
ChildOrder::Draw
|
||||
}
|
||||
|
||||
#[allow(unused_variables)]
|
||||
fn tick(&mut self, now: std::time::Instant) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl Widget for () {
|
||||
|
||||
Reference in new issue
Block a user