Skip to content

Commit

Permalink
Fix animation smoothness when using requestAnimationFrame.
Browse files Browse the repository at this point in the history
Previously, the flow for ticking animations was:

Compositor -> Constellation -> Layout -> Script

However, this means that the compositor <-> layout messages can thrash, meaning layout thread is very rarely idle.

This means that the script thread (which joins on the layout thread during reflow) was unable to execute and run rAF callbacks.

With this change, the flow is now:

Compositor -> Constellation -> Script (when rAF is active).
Compositor -> Constellation -> Layout (when transitions / animations are active and no rAF is present).

This makes rAF based animation *much* smoother.
  • Loading branch information
gw3583 committed Mar 3, 2016
1 parent 46256b3 commit 9206113
Show file tree
Hide file tree
Showing 5 changed files with 37 additions and 15 deletions.
9 changes: 8 additions & 1 deletion components/compositing/compositor.rs
Expand Up @@ -2,6 +2,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use AnimationTickType;
use CompositorMsg as ConstellationMsg;
use app_units::Au;
use compositor_layer::{CompositorData, CompositorLayer, RcCompositorLayer, WantsScrollEventsFlag};
Expand Down Expand Up @@ -1550,7 +1551,13 @@ impl<Window: WindowMethods> IOCompositor<Window> {

fn tick_animations_for_pipeline(&mut self, pipeline_id: PipelineId) {
self.schedule_delayed_composite_if_necessary();
self.constellation_chan.send(ConstellationMsg::TickAnimation(pipeline_id)).unwrap()
let animation_callbacks_running = self.pipeline_details(pipeline_id).animation_callbacks_running;
let animation_type = if animation_callbacks_running {
AnimationTickType::Script
} else {
AnimationTickType::Layout
};
self.constellation_chan.send(ConstellationMsg::TickAnimation(pipeline_id, animation_type)).unwrap()
}

fn constrain_viewport(&mut self, pipeline_id: PipelineId, constraints: ViewportConstraints) {
Expand Down
27 changes: 19 additions & 8 deletions components/compositing/constellation.rs
Expand Up @@ -9,6 +9,7 @@
//! navigation context, each `Pipeline` encompassing a `ScriptThread`,
//! `LayoutThread`, and `PaintThread`.

use AnimationTickType;
use CompositorMsg as FromCompositorMsg;
use canvas::canvas_paint_thread::CanvasPaintThread;
use canvas::webgl_paint_thread::WebGLPaintThread;
Expand Down Expand Up @@ -585,8 +586,8 @@ impl<LTF: LayoutThreadFactory, STF: ScriptThreadFactory> Constellation<LTF, STF>
debug!("constellation got window resize message");
self.handle_resized_window_msg(new_size);
}
Request::Compositor(FromCompositorMsg::TickAnimation(pipeline_id)) => {
self.handle_tick_animation(pipeline_id)
Request::Compositor(FromCompositorMsg::TickAnimation(pipeline_id, tick_type)) => {
self.handle_tick_animation(pipeline_id, tick_type)
}
Request::Compositor(FromCompositorMsg::WebDriverCommand(command)) => {
debug!("constellation got webdriver command message");
Expand Down Expand Up @@ -912,12 +913,22 @@ impl<LTF: LayoutThreadFactory, STF: ScriptThreadFactory> Constellation<LTF, STF>
animation_state))
}

fn handle_tick_animation(&mut self, pipeline_id: PipelineId) {
self.pipeline(pipeline_id)
.layout_chan
.0
.send(LayoutControlMsg::TickAnimations)
.unwrap();
fn handle_tick_animation(&mut self, pipeline_id: PipelineId, tick_type: AnimationTickType) {
match tick_type {
AnimationTickType::Script => {
self.pipeline(pipeline_id)
.script_chan
.send(ConstellationControlMsg::TickAllAnimations(pipeline_id))
.unwrap();
}
AnimationTickType::Layout => {
self.pipeline(pipeline_id)
.layout_chan
.0
.send(LayoutControlMsg::TickAnimations)
.unwrap();
}
}
}

fn handle_load_url_msg(&mut self, source_id: PipelineId, load_data: LoadData) {
Expand Down
3 changes: 2 additions & 1 deletion components/compositing/headless.rs
Expand Up @@ -2,6 +2,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use AnimationTickType;
use CompositorMsg as ConstellationMsg;
use compositor_thread::{CompositorEventListener, CompositorReceiver};
use compositor_thread::{InitialCompositorState, Msg};
Expand Down Expand Up @@ -98,7 +99,7 @@ impl CompositorEventListener for NullCompositor {
AnimationState::NoAnimationsPresent |
AnimationState::NoAnimationCallbacksPresent => {}
AnimationState::AnimationCallbacksPresent => {
let msg = ConstellationMsg::TickAnimation(pipeline_id);
let msg = ConstellationMsg::TickAnimation(pipeline_id, AnimationTickType::Script);
self.constellation_chan.send(msg).unwrap()
}
}
Expand Down
9 changes: 8 additions & 1 deletion components/compositing/lib.rs
Expand Up @@ -76,6 +76,13 @@ mod timer_scheduler;
mod touch;
pub mod windowing;

/// Specifies whether the script or layout thread needs to be ticked for animation.
#[derive(Deserialize, Serialize)]
pub enum AnimationTickType {
Script,
Layout,
}

/// Messages from the compositor to the constellation.
#[derive(Deserialize, Serialize)]
pub enum CompositorMsg {
Expand All @@ -98,7 +105,7 @@ pub enum CompositorMsg {
Navigate(Option<(PipelineId, SubpageId)>, NavigationDirection),
ResizedWindow(WindowSizeData),
/// Requests that the constellation instruct layout to begin a new tick of the animation.
TickAnimation(PipelineId),
TickAnimation(PipelineId, AnimationTickType),
/// Dispatch a webdriver command
WebDriverCommand(WebDriverCommandMsg),
}
4 changes: 0 additions & 4 deletions components/layout/layout_thread.rs
Expand Up @@ -1202,10 +1202,6 @@ impl LayoutThread {
fn tick_all_animations<'a, 'b>(&mut self, possibly_locked_rw_data: &mut RwData<'a, 'b>) {
let mut rw_data = possibly_locked_rw_data.lock();
self.tick_animations(&mut rw_data);

self.script_chan
.send(ConstellationControlMsg::TickAllAnimations(self.id))
.unwrap();
}

pub fn tick_animations(&mut self, rw_data: &mut LayoutThreadData) {
Expand Down

0 comments on commit 9206113

Please sign in to comment.