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
This commit is contained in:
Glenn Watson 2016-01-28 10:49:24 +05:01
Родитель e6b8d8de2f
Коммит a495c8cb58
2 изменённых файлов: 45 добавлений и 26 удалений

Просмотреть файл

@ -65,8 +65,7 @@ pub enum NotReadyToPaint {
LayerHasOutstandingPaintMessages,
MissingRoot,
PendingSubpages(usize),
AnimationsRunning,
AnimationCallbacksRunning,
AnimationsActive,
JustNotifiedConstellation,
WaitingOnConstellation,
}
@ -1608,6 +1607,22 @@ impl<Window: WindowMethods> IOCompositor<Window> {
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<Window: WindowMethods> IOCompositor<Window> {
// 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<Window: WindowMethods> IOCompositor<Window> {
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))
}
}
}

Просмотреть файл

@ -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);