From a495c8cb58b214e25bff87a747809207f0c2ab97 Mon Sep 17 00:00:00 2001 From: Glenn Watson Date: Thu, 28 Jan 2016 10:49:24 +0501 Subject: [PATCH] servo: Merge #9401 - Fixes additional calls to rAF (from glennw:raf-timing); r=jdm Often, a rAF callback will request another rAF from the callback itself. Previously, the constellation would quickly receive two messages saying that there were no animations, and then there are animations again in the situation above. This would make the compositor tick the new animation straight away, causing strange fluctuations and timings in rAF callbacks. Instead, only send the NoAnimationCallbacks message if the animation callback queue is still empty after invoking the callbacks. This fixes rAF timing, which now runs at the correct (vsync) framerate. Source-Repo: https://github.com/servo/servo Source-Revision: bc44ae679f0d4a01194777c56e09a48fbebea1ad --- servo/components/compositing/compositor.rs | 55 +++++++++++++--------- servo/components/script/dom/document.rs | 16 +++++-- 2 files changed, 45 insertions(+), 26 deletions(-) diff --git a/servo/components/compositing/compositor.rs b/servo/components/compositing/compositor.rs index 3ada78486cd0..f75126cab28a 100644 --- a/servo/components/compositing/compositor.rs +++ b/servo/components/compositing/compositor.rs @@ -65,8 +65,7 @@ pub enum NotReadyToPaint { LayerHasOutstandingPaintMessages, MissingRoot, PendingSubpages(usize), - AnimationsRunning, - AnimationCallbacksRunning, + AnimationsActive, JustNotifiedConstellation, WaitingOnConstellation, } @@ -1608,6 +1607,22 @@ impl IOCompositor { false } + // Check if any pipelines currently have active animations or animation callbacks. + fn animations_active(&self) -> bool { + for (_, details) in &self.pipeline_details { + // If animations are currently running, then don't bother checking + // with the constellation if the output image is stable. + if details.animations_running { + return true; + } + if details.animation_callbacks_running { + return true; + } + } + + false + } + /// Query the constellation to see if the current compositor /// output matches the current frame tree output, and if the /// associated script threads are idle. @@ -1641,15 +1656,6 @@ impl IOCompositor { // frame tree. let mut pipeline_epochs = HashMap::new(); for (id, details) in &self.pipeline_details { - // If animations are currently running, then don't bother checking - // with the constellation if the output image is stable. - if details.animations_running { - return Err(NotReadyToPaint::AnimationsRunning); - } - if details.animation_callbacks_running { - return Err(NotReadyToPaint::AnimationCallbacksRunning); - } - pipeline_epochs.insert(*id, details.current_epoch); } @@ -1710,18 +1716,25 @@ impl IOCompositor { return Err(UnableToComposite::WindowUnprepared) } - match target { - CompositeTarget::WindowAndPng | CompositeTarget::PngFile => { - if let Err(result) = self.is_ready_to_paint_image_output() { - return Err(UnableToComposite::NotReadyToPaintImage(result)) - } - } - CompositeTarget::Window => { - if opts::get().exit_after_load { - if let Err(result) = self.is_ready_to_paint_image_output() { - return Err(UnableToComposite::NotReadyToPaintImage(result)) + let wait_for_stable_image = match target { + CompositeTarget::WindowAndPng | CompositeTarget::PngFile => true, + CompositeTarget::Window => opts::get().exit_after_load, + }; + + if wait_for_stable_image { + match self.is_ready_to_paint_image_output() { + Ok(()) => { + // The current image is ready to output. However, if there are animations active, + // tick those instead and continue waiting for the image output to be stable AND + // all active animations to complete. + if self.animations_active() { + self.process_animations(); + return Err(UnableToComposite::NotReadyToPaintImage(NotReadyToPaint::AnimationsActive)); } } + Err(result) => { + return Err(UnableToComposite::NotReadyToPaintImage(result)) + } } } diff --git a/servo/components/script/dom/document.rs b/servo/components/script/dom/document.rs index b6e1ce8bde0b..b5672319837f 100644 --- a/servo/components/script/dom/document.rs +++ b/servo/components/script/dom/document.rs @@ -1199,11 +1199,6 @@ impl Document { { let mut list = self.animation_frame_list.borrow_mut(); animation_frame_list = Vec::from_iter(list.drain()); - - let ConstellationChan(ref chan) = self.window.constellation_chan(); - let event = ConstellationMsg::ChangeRunningAnimationsState(self.window.pipeline(), - AnimationState::NoAnimationCallbacksPresent); - chan.send(event).unwrap(); } let performance = self.window.Performance(); let performance = performance.r(); @@ -1213,6 +1208,17 @@ impl Document { callback(*timing); } + // Only send the animation change state message after running any callbacks. + // This means that if the animation callback adds a new callback for + // the next frame (which is the common case), we won't send a NoAnimationCallbacksPresent + // message quickly followed by an AnimationCallbacksPresent message. + if self.animation_frame_list.borrow().is_empty() { + let ConstellationChan(ref chan) = self.window.constellation_chan(); + let event = ConstellationMsg::ChangeRunningAnimationsState(self.window.pipeline(), + AnimationState::NoAnimationCallbacksPresent); + chan.send(event).unwrap(); + } + self.window.reflow(ReflowGoal::ForDisplay, ReflowQueryType::NoQuery, ReflowReason::RequestAnimationFrame);