From b50e8d5683dbf36170be3c52c61d39c83d90081b Mon Sep 17 00:00:00 2001 From: atagen Date: Thu, 21 May 2026 18:27:01 +1000 Subject: [PATCH] Batch layout animation candidates --- src/compositor.rs | 1 + src/state.rs | 53 +++++++++++++++++++++++++++++++++++++++---- src/tree/container.rs | 6 +++++ 3 files changed, 55 insertions(+), 5 deletions(-) diff --git a/src/compositor.rs b/src/compositor.rs index 93600f27..fdb0f282 100644 --- a/src/compositor.rs +++ b/src/compositor.rs @@ -364,6 +364,7 @@ fn start_compositor2( layout_animations_requested: Default::default(), layout_animations_active: Default::default(), layout_animation_curve_override: Default::default(), + layout_animation_batch: Default::default(), suppress_animations_for_next_layout: Default::default(), toplevels: Default::default(), const_40hz_latch: Default::default(), diff --git a/src/state.rs b/src/state.rs index adf7b2ca..303a63c5 100644 --- a/src/state.rs +++ b/src/state.rs @@ -157,6 +157,14 @@ use { uapi::{OwnedFd, c}, }; +pub(crate) struct LayoutAnimationCandidate { + node_id: NodeId, + old: Rect, + new: Rect, + retained: Option>, + curve: AnimationCurve, +} + pub struct State { pub pid: c::pid_t, pub kb_ctx: KbvmContext, @@ -271,6 +279,7 @@ pub struct State { pub layout_animations_requested: Cell, pub layout_animations_active: Cell, pub layout_animation_curve_override: Cell>, + pub(crate) layout_animation_batch: RefCell>>, pub suppress_animations_for_next_layout: Cell, pub toplevels: CopyHashMap>, pub const_40hz_latch: EventSource, @@ -1526,25 +1535,59 @@ impl State { if old_output != new_output || old_scale != new_scale { return; } - let now = self.now_nsec(); - let started = self.animations.set_target( + let candidate = LayoutAnimationCandidate { node_id, old, new, retained, - now, - self.animations.duration_ms.get(), curve, + }; + if let Some(batch) = self.layout_animation_batch.borrow_mut().as_mut() { + batch.push(candidate); + return; + } + self.start_layout_animation_candidate(candidate, self.now_nsec()); + } + + fn start_layout_animation_candidate( + self: &Rc, + candidate: LayoutAnimationCandidate, + now_nsec: u64, + ) { + let started = self.animations.set_target( + candidate.node_id, + candidate.old, + candidate.new, + candidate.retained, + now_nsec, + self.animations.duration_ms.get(), + candidate.curve, ); if started { self.damage(expand_damage_rect( - old.union(new), + candidate.old.union(candidate.new), self.theme.sizes.border_width.get().max(0), )); self.ensure_animation_tick(); } } + pub fn begin_layout_animation_batch(&self) { + self.layout_animation_batch + .borrow_mut() + .get_or_insert_with(Vec::new); + } + + pub fn finish_layout_animation_batch(self: &Rc) { + let Some(candidates) = self.layout_animation_batch.borrow_mut().take() else { + return; + }; + let now = self.now_nsec(); + for candidate in candidates { + self.start_layout_animation_candidate(candidate, now); + } + } + pub fn queue_spawn_in_animation( self: &Rc, node_id: NodeId, diff --git a/src/tree/container.rs b/src/tree/container.rs index 3a6db3a2..8670125c 100644 --- a/src/tree/container.rs +++ b/src/tree/container.rs @@ -1771,7 +1771,13 @@ pub async fn container_layout(state: Rc) { let animate = container.animate_next_layout.replace(false) && !state.suppress_animations_for_next_layout.get(); let prev_active = state.layout_animations_active.replace(animate); + if animate { + state.begin_layout_animation_batch(); + } container.perform_layout(); + if animate { + state.finish_layout_animation_batch(); + } state.layout_animations_active.set(prev_active); } state.suppress_animations_for_next_layout.set(false);