From 8a83261b61704b7146a44237271ec5cafa1091d1 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Thu, 12 May 2016 16:29:44 -0700 Subject: [PATCH] script: Avoid needless `ChangeRunningAnimationsState` messages during typical `requestAnimationFrame()` animations. This skips useless message traffic when `requestAnimationFrame()` is called during an animation frame callback. It reduces CPU usage of the following snippet by 49%: Partially addresses #9844. --- components/script/dom/document.rs | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs index b7e7cf47cfbc..866b698687dc 100644 --- a/components/script/dom/document.rs +++ b/components/script/dom/document.rs @@ -201,6 +201,11 @@ pub struct Document { /// List of animation frame callbacks #[ignore_heap_size_of = "closures are hard"] animation_frame_list: DOMRefCell>>, + /// Whether we're in the process of running animation callbacks. + /// + /// Tracking this is not necessary for correctness. Instead, it is an optimization to avoid + /// sending needless `ChangeRunningAnimationsState` messages to the compositor. + running_animation_callbacks: Cell, /// Tracks all outstanding loads related to this document. loader: DOMRefCell, /// The current active HTML parser, to allow resuming after interruptions. @@ -1281,11 +1286,20 @@ impl Document { self.animation_frame_ident.set(ident); self.animation_frame_list.borrow_mut().insert(ident, callback); + // No need to send a `ChangeRunningAnimationsState` if we're running animation callbacks: + // we're guaranteed to already be in the "animation callbacks present" state. + // + // This reduces CPU usage by avoiding needless thread wakeups in the common case of + // repeated rAF. + // // TODO: Should tick animation only when document is visible - let ConstellationChan(ref chan) = *self.window.constellation_chan(); - let event = ConstellationMsg::ChangeRunningAnimationsState(self.window.pipeline(), - AnimationState::AnimationCallbacksPresent); - chan.send(event).unwrap(); + if !self.running_animation_callbacks.get() { + let ConstellationChan(ref chan) = *self.window.constellation_chan(); + let event = ConstellationMsg::ChangeRunningAnimationsState( + self.window.pipeline(), + AnimationState::AnimationCallbacksPresent); + chan.send(event).unwrap(); + } ident } @@ -1305,6 +1319,7 @@ impl Document { pub fn run_the_animation_frame_callbacks(&self) { let animation_frame_list = mem::replace(&mut *self.animation_frame_list.borrow_mut(), BTreeMap::new()); + self.running_animation_callbacks.set(true); let performance = self.window.Performance(); let performance = performance.r(); let timing = performance.Now(); @@ -1324,6 +1339,8 @@ impl Document { chan.send(event).unwrap(); } + self.running_animation_callbacks.set(false); + self.window.reflow(ReflowGoal::ForDisplay, ReflowQueryType::NoQuery, ReflowReason::RequestAnimationFrame); @@ -1676,6 +1693,7 @@ impl Document { scripting_enabled: Cell::new(browsing_context.is_some()), animation_frame_ident: Cell::new(0), animation_frame_list: DOMRefCell::new(BTreeMap::new()), + running_animation_callbacks: Cell::new(false), loader: DOMRefCell::new(doc_loader), current_parser: Default::default(), reflow_timeout: Cell::new(None),