зеркало из https://github.com/mozilla/gecko-dev.git
servo: Merge #6895 - layout: Tie transitions to the DOM node and finish them instantly when new styles are set (from pcwalton:better-transitions); r=glennw
Tying transitions to the DOM node avoids quadratic complexity when updating them. Finishing transitions instantly when styles are updated makes our behavior more correct. r? @glennw Source-Repo: https://github.com/servo/servo Source-Revision: ffe4bd25a495efd672986f090150b165811b6708
This commit is contained in:
Родитель
34d2f0c3a8
Коммит
f08bfd6445
|
@ -65,11 +65,12 @@ const MIN_INDENTATION_LENGTH: usize = 4;
|
|||
/// Because the script task's GC does not trace layout, node data cannot be safely stored in layout
|
||||
/// data structures. Also, layout code tends to be faster when the DOM is not being accessed, for
|
||||
/// locality reasons. Using `OpaqueNode` enforces this invariant.
|
||||
#[derive(Clone, PartialEq, Copy, Debug, HeapSizeOf, Deserialize, Serialize)]
|
||||
#[derive(Clone, PartialEq, Copy, Debug, HeapSizeOf, Hash, Eq, Deserialize, Serialize)]
|
||||
pub struct OpaqueNode(pub uintptr_t);
|
||||
|
||||
impl OpaqueNode {
|
||||
/// Returns the address of this node, for debugging purposes.
|
||||
#[inline]
|
||||
pub fn id(&self) -> uintptr_t {
|
||||
let OpaqueNode(pointer) = *self;
|
||||
pointer
|
||||
|
|
|
@ -13,7 +13,8 @@ use layout_task::{LayoutTask, LayoutTaskData};
|
|||
use msg::constellation_msg::{AnimationState, Msg, PipelineId};
|
||||
use script::layout_interface::Animation;
|
||||
use script_traits::{ConstellationControlMsg, ScriptControlChan};
|
||||
use std::mem;
|
||||
use std::collections::HashMap;
|
||||
use std::collections::hash_map::Entry;
|
||||
use std::sync::Arc;
|
||||
use std::sync::mpsc::Sender;
|
||||
use style::animation::{GetMod, PropertyAnimation};
|
||||
|
@ -50,8 +51,30 @@ pub fn start_transitions_if_applicable(new_animations_sender: &Sender<Animation>
|
|||
|
||||
/// Processes any new animations that were discovered after style recalculation.
|
||||
pub fn process_new_animations(rw_data: &mut LayoutTaskData, pipeline_id: PipelineId) {
|
||||
let mut new_running_animations = Vec::new();
|
||||
while let Ok(animation) = rw_data.new_animations_receiver.try_recv() {
|
||||
rw_data.running_animations.push(animation)
|
||||
new_running_animations.push(animation)
|
||||
}
|
||||
if !new_running_animations.is_empty() {
|
||||
let mut running_animations = (*rw_data.running_animations).clone();
|
||||
|
||||
// Expire old running animations.
|
||||
let now = clock_ticks::precise_time_s();
|
||||
for (_, running_animations) in running_animations.iter_mut() {
|
||||
running_animations.retain(|running_animation| now < running_animation.end_time);
|
||||
}
|
||||
|
||||
// Add new running animations.
|
||||
for new_running_animation in new_running_animations.into_iter() {
|
||||
match running_animations.entry(OpaqueNode(new_running_animation.node)) {
|
||||
Entry::Vacant(entry) => {
|
||||
entry.insert(vec![new_running_animation]);
|
||||
}
|
||||
Entry::Occupied(mut entry) => entry.get_mut().push(new_running_animation),
|
||||
}
|
||||
}
|
||||
|
||||
rw_data.running_animations = Arc::new(running_animations);
|
||||
}
|
||||
|
||||
let animation_state;
|
||||
|
@ -68,48 +91,42 @@ pub fn process_new_animations(rw_data: &mut LayoutTaskData, pipeline_id: Pipelin
|
|||
|
||||
}
|
||||
|
||||
/// Recalculates style for an animation. This does *not* run with the DOM lock held.
|
||||
pub fn recalc_style_for_animation(flow: &mut Flow, animation: &Animation) {
|
||||
/// Recalculates style for a set of animations. This does *not* run with the DOM lock held.
|
||||
pub fn recalc_style_for_animations(flow: &mut Flow,
|
||||
animations: &HashMap<OpaqueNode,Vec<Animation>>) {
|
||||
let mut damage = RestyleDamage::empty();
|
||||
flow.mutate_fragments(&mut |fragment| {
|
||||
if fragment.node.id() != animation.node {
|
||||
return
|
||||
}
|
||||
if let Some(ref animations) = animations.get(&OpaqueNode(fragment.node.id())) {
|
||||
for animation in animations.iter() {
|
||||
let now = clock_ticks::precise_time_s();
|
||||
let mut progress = (now - animation.start_time) / animation.duration();
|
||||
if progress > 1.0 {
|
||||
progress = 1.0
|
||||
}
|
||||
if progress <= 0.0 {
|
||||
continue
|
||||
}
|
||||
|
||||
let now = clock_ticks::precise_time_s() as f64;
|
||||
let mut progress = (now - animation.start_time) / animation.duration();
|
||||
if progress > 1.0 {
|
||||
progress = 1.0
|
||||
let mut new_style = fragment.style.clone();
|
||||
animation.property_animation.update(&mut *Arc::make_unique(&mut new_style),
|
||||
progress);
|
||||
damage.insert(incremental::compute_damage(&Some(fragment.style.clone()),
|
||||
&new_style));
|
||||
fragment.style = new_style
|
||||
}
|
||||
}
|
||||
if progress <= 0.0 {
|
||||
return
|
||||
}
|
||||
|
||||
let mut new_style = fragment.style.clone();
|
||||
animation.property_animation.update(&mut *Arc::make_unique(&mut new_style), progress);
|
||||
damage.insert(incremental::compute_damage(&Some(fragment.style.clone()), &new_style));
|
||||
fragment.style = new_style
|
||||
});
|
||||
|
||||
let base = flow::mut_base(flow);
|
||||
base.restyle_damage.insert(damage);
|
||||
for kid in base.children.iter_mut() {
|
||||
recalc_style_for_animation(kid, animation)
|
||||
recalc_style_for_animations(kid, animations)
|
||||
}
|
||||
}
|
||||
|
||||
/// Handles animation updates.
|
||||
pub fn tick_all_animations(layout_task: &LayoutTask, rw_data: &mut LayoutTaskData) {
|
||||
let running_animations = mem::replace(&mut rw_data.running_animations, Vec::new());
|
||||
let now = clock_ticks::precise_time_s() as f64;
|
||||
for running_animation in running_animations.into_iter() {
|
||||
layout_task.tick_animation(&running_animation, rw_data);
|
||||
|
||||
if now < running_animation.end_time {
|
||||
// Keep running the animation if it hasn't expired.
|
||||
rw_data.running_animations.push(running_animation)
|
||||
}
|
||||
}
|
||||
layout_task.tick_animations(rw_data);
|
||||
|
||||
let ScriptControlChan(ref chan) = layout_task.script_chan;
|
||||
chan.send(ConstellationControlMsg::TickAllAnimations(layout_task.id)).unwrap();
|
||||
|
|
|
@ -126,6 +126,9 @@ pub struct SharedLayoutContext {
|
|||
/// The visible rects for each layer, as reported to us by the compositor.
|
||||
pub visible_rects: Arc<HashMap<LayerId, Rect<Au>, DefaultState<FnvHasher>>>,
|
||||
|
||||
/// The animations that are currently running.
|
||||
pub running_animations: Arc<HashMap<OpaqueNode,Vec<Animation>>>,
|
||||
|
||||
/// Why is this reflow occurring
|
||||
pub goal: ReflowGoal,
|
||||
}
|
||||
|
|
|
@ -420,7 +420,8 @@ trait PrivateMatchMethods {
|
|||
applicable_declarations_cache:
|
||||
&mut ApplicableDeclarationsCache,
|
||||
new_animations_sender: &Sender<Animation>,
|
||||
shareable: bool)
|
||||
shareable: bool,
|
||||
animate_properties: bool)
|
||||
-> RestyleDamage;
|
||||
|
||||
fn share_style_with_candidate_if_possible(&self,
|
||||
|
@ -438,8 +439,21 @@ impl<'ln> PrivateMatchMethods for LayoutNode<'ln> {
|
|||
applicable_declarations_cache:
|
||||
&mut ApplicableDeclarationsCache,
|
||||
new_animations_sender: &Sender<Animation>,
|
||||
shareable: bool)
|
||||
shareable: bool,
|
||||
animate_properties: bool)
|
||||
-> RestyleDamage {
|
||||
// Finish any transitions.
|
||||
if animate_properties {
|
||||
if let Some(ref mut style) = *style {
|
||||
let this_opaque = self.opaque();
|
||||
if let Some(ref animations) = layout_context.running_animations.get(&this_opaque) {
|
||||
for animation in animations.iter() {
|
||||
animation.property_animation.update(&mut *Arc::make_unique(style), 1.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut this_style;
|
||||
let cacheable;
|
||||
match parent_style {
|
||||
|
@ -470,11 +484,8 @@ impl<'ln> PrivateMatchMethods for LayoutNode<'ln> {
|
|||
|
||||
// Trigger transitions if necessary. This will reset `this_style` back to its old value if
|
||||
// it did trigger a transition.
|
||||
match *style {
|
||||
None => {
|
||||
// This is a newly-created node; we've nothing to transition from!
|
||||
}
|
||||
Some(ref style) => {
|
||||
if animate_properties {
|
||||
if let Some(ref style) = *style {
|
||||
animation::start_transitions_if_applicable(new_animations_sender,
|
||||
self.opaque(),
|
||||
&**style,
|
||||
|
@ -488,7 +499,8 @@ impl<'ln> PrivateMatchMethods for LayoutNode<'ln> {
|
|||
|
||||
// Cache the resolved style if it was cacheable.
|
||||
if cacheable {
|
||||
applicable_declarations_cache.insert(applicable_declarations.to_vec(), this_style.clone());
|
||||
applicable_declarations_cache.insert(applicable_declarations.to_vec(),
|
||||
this_style.clone());
|
||||
}
|
||||
|
||||
// Write in the final style and return the damage done to our caller.
|
||||
|
@ -686,7 +698,8 @@ impl<'ln> MatchMethods for LayoutNode<'ln> {
|
|||
&mut layout_data.shared_data.style,
|
||||
applicable_declarations_cache,
|
||||
new_animations_sender,
|
||||
applicable_declarations.normal_shareable);
|
||||
applicable_declarations.normal_shareable,
|
||||
true);
|
||||
if applicable_declarations.before.len() > 0 {
|
||||
damage = damage | self.cascade_node_pseudo_element(
|
||||
layout_context,
|
||||
|
@ -695,6 +708,7 @@ impl<'ln> MatchMethods for LayoutNode<'ln> {
|
|||
&mut layout_data.data.before_style,
|
||||
applicable_declarations_cache,
|
||||
new_animations_sender,
|
||||
false,
|
||||
false);
|
||||
}
|
||||
if applicable_declarations.after.len() > 0 {
|
||||
|
@ -705,6 +719,7 @@ impl<'ln> MatchMethods for LayoutNode<'ln> {
|
|||
&mut layout_data.data.after_style,
|
||||
applicable_declarations_cache,
|
||||
new_animations_sender,
|
||||
false,
|
||||
false);
|
||||
}
|
||||
layout_data.data.restyle_damage = damage;
|
||||
|
|
|
@ -138,7 +138,7 @@ pub struct LayoutTaskData {
|
|||
pub resolved_style_response: Option<String>,
|
||||
|
||||
/// The list of currently-running animations.
|
||||
pub running_animations: Vec<Animation>,
|
||||
pub running_animations: Arc<HashMap<OpaqueNode,Vec<Animation>>>,
|
||||
|
||||
/// Receives newly-discovered animations.
|
||||
pub new_animations_receiver: Receiver<Animation>,
|
||||
|
@ -381,7 +381,7 @@ impl LayoutTask {
|
|||
content_boxes_response: Vec::new(),
|
||||
client_rect_response: Rect::zero(),
|
||||
resolved_style_response: None,
|
||||
running_animations: Vec::new(),
|
||||
running_animations: Arc::new(HashMap::new()),
|
||||
visible_rects: Arc::new(HashMap::with_hash_state(Default::default())),
|
||||
new_animations_receiver: new_animations_receiver,
|
||||
new_animations_sender: new_animations_sender,
|
||||
|
@ -423,6 +423,7 @@ impl LayoutTask {
|
|||
generation: rw_data.generation,
|
||||
new_animations_sender: rw_data.new_animations_sender.clone(),
|
||||
goal: goal,
|
||||
running_animations: rw_data.running_animations.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1275,23 +1276,27 @@ impl LayoutTask {
|
|||
animation::tick_all_animations(self, &mut rw_data)
|
||||
}
|
||||
|
||||
pub fn tick_animation<'a>(&'a self, animation: &Animation, rw_data: &mut LayoutTaskData) {
|
||||
pub fn tick_animations<'a>(&'a self, rw_data: &mut LayoutTaskData) {
|
||||
let reflow_info = Reflow {
|
||||
goal: ReflowGoal::ForDisplay,
|
||||
page_clip_rect: MAX_RECT,
|
||||
};
|
||||
|
||||
// Perform an abbreviated style recalc that operates without access to the DOM.
|
||||
let mut layout_context = self.build_shared_layout_context(&*rw_data,
|
||||
false,
|
||||
None,
|
||||
&self.url,
|
||||
reflow_info.goal);
|
||||
let mut root_flow = (*rw_data.root_flow.as_ref().unwrap()).clone();
|
||||
profile(time::ProfilerCategory::LayoutStyleRecalc,
|
||||
self.profiler_metadata(),
|
||||
self.time_profiler_chan.clone(),
|
||||
|| animation::recalc_style_for_animation(root_flow.deref_mut(), &animation));
|
||||
|
||||
{
|
||||
// Perform an abbreviated style recalc that operates without access to the DOM.
|
||||
let mut root_flow = (*rw_data.root_flow.as_ref().unwrap()).clone();
|
||||
let animations = &*rw_data.running_animations;
|
||||
profile(time::ProfilerCategory::LayoutStyleRecalc,
|
||||
self.profiler_metadata(),
|
||||
self.time_profiler_chan.clone(),
|
||||
|| animation::recalc_style_for_animations(root_flow.deref_mut(), animations));
|
||||
}
|
||||
|
||||
self.perform_post_style_recalc_layout_passes(&reflow_info,
|
||||
&mut *rw_data,
|
||||
|
|
Загрузка…
Ссылка в новой задаче