diff options
-rw-r--r-- | components/layout/animation.rs | 90 | ||||
-rw-r--r-- | components/layout/context.rs | 7 | ||||
-rw-r--r-- | components/layout/css/matching.rs | 74 | ||||
-rw-r--r-- | components/layout/layout_task.rs | 18 | ||||
-rw-r--r-- | components/layout/lib.rs | 1 |
5 files changed, 134 insertions, 56 deletions
diff --git a/components/layout/animation.rs b/components/layout/animation.rs index f3e01c20b8c..b214910c4f6 100644 --- a/components/layout/animation.rs +++ b/components/layout/animation.rs @@ -19,11 +19,14 @@ use style::animation::{GetMod, PropertyAnimation}; use style::properties::ComputedValues; /// Inserts transitions into the queue of running animations as applicable for the given style -/// difference. This is called from the layout worker threads. +/// difference. This is called from the layout worker threads. Returns true if any animations were +/// kicked off and false otherwise. pub fn start_transitions_if_applicable(new_animations_sender: &Mutex<Sender<Animation>>, node: OpaqueNode, old_style: &ComputedValues, - new_style: &mut ComputedValues) { + new_style: &mut ComputedValues) + -> bool { + let mut had_animations = false; for i in 0..new_style.get_animation().transition_property.0.len() { // Create any property animations, if applicable. let property_animations = PropertyAnimation::from_transition(i, old_style, new_style); @@ -42,15 +45,20 @@ pub fn start_transitions_if_applicable(new_animations_sender: &Mutex<Sender<Anim start_time: start_time, end_time: start_time + (animation_style.transition_duration.0.get_mod(i).seconds() as f64), - }).unwrap() + }).unwrap(); + + had_animations = true } } + + had_animations } /// Processes any new animations that were discovered after style recalculation. -/// Also expire any old animations that have completed. +/// Also expire any old animations that have completed, inserting them into `expired_animations`. pub fn update_animation_state(constellation_chan: &ConstellationChan<ConstellationMsg>, - running_animations: &mut Arc<HashMap<OpaqueNode, Vec<Animation>>>, + running_animations: &mut HashMap<OpaqueNode, Vec<Animation>>, + expired_animations: &mut HashMap<OpaqueNode, Vec<Animation>>, new_animations_receiver: &Receiver<Animation>, pipeline_id: PipelineId) { let mut new_running_animations = Vec::new(); @@ -58,9 +66,7 @@ pub fn update_animation_state(constellation_chan: &ConstellationChan<Constellati new_running_animations.push(animation) } - let mut running_animations_hash = (**running_animations).clone(); - - if running_animations_hash.is_empty() && new_running_animations.is_empty() { + if running_animations.is_empty() && new_running_animations.is_empty() { // Nothing to do. Return early so we don't flood the compositor with // `ChangeRunningAnimationsState` messages. return @@ -69,21 +75,33 @@ pub fn update_animation_state(constellation_chan: &ConstellationChan<Constellati // Expire old running animations. let now = clock_ticks::precise_time_s(); let mut keys_to_remove = Vec::new(); - for (key, running_animations) in &mut running_animations_hash { - running_animations.retain(|running_animation| { - now < running_animation.end_time - }); - if running_animations.len() == 0 { + for (key, running_animations) in running_animations.iter_mut() { + let mut animations_still_running = vec![]; + for running_animation in running_animations.drain(..) { + if now < running_animation.end_time { + animations_still_running.push(running_animation); + continue + } + match expired_animations.entry(*key) { + Entry::Vacant(entry) => { + entry.insert(vec![running_animation]); + } + Entry::Occupied(mut entry) => entry.get_mut().push(running_animation), + } + } + if animations_still_running.len() == 0 { keys_to_remove.push(*key); + } else { + *running_animations = animations_still_running } } for key in keys_to_remove { - running_animations_hash.remove(&key).unwrap(); + running_animations.remove(&key).unwrap(); } // Add new running animations. for new_running_animation in new_running_animations { - match running_animations_hash.entry(OpaqueNode(new_running_animation.node)) { + match running_animations.entry(OpaqueNode(new_running_animation.node)) { Entry::Vacant(entry) => { entry.insert(vec![new_running_animation]); } @@ -91,8 +109,6 @@ pub fn update_animation_state(constellation_chan: &ConstellationChan<Constellati } } - *running_animations = Arc::new(running_animations_hash); - let animation_state; if running_animations.is_empty() { animation_state = AnimationState::NoAnimationsPresent; @@ -112,21 +128,7 @@ pub fn recalc_style_for_animations(flow: &mut Flow, flow.mutate_fragments(&mut |fragment| { if let Some(ref animations) = animations.get(&OpaqueNode(fragment.node.id())) { for animation in *animations { - 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 mut new_style = fragment.style.clone(); - animation.property_animation.update(&mut *Arc::make_mut(&mut new_style), - progress); - damage.insert(incremental::compute_damage(&Some(fragment.style.clone()), - &new_style)); - fragment.style = new_style + update_style_for_animation(animation, &mut fragment.style, Some(&mut damage)); } } }); @@ -137,3 +139,27 @@ pub fn recalc_style_for_animations(flow: &mut Flow, recalc_style_for_animations(kid, animations) } } + +/// Updates a single animation and associated style based on the current time. If `damage` is +/// provided, inserts the appropriate restyle damage. +pub fn update_style_for_animation(animation: &Animation, + style: &mut Arc<ComputedValues>, + damage: Option<&mut RestyleDamage>) { + 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 { + return + } + + let mut new_style = (*style).clone(); + animation.property_animation.update(&mut *Arc::make_mut(&mut new_style), progress); + if let Some(damage) = damage { + damage.insert(incremental::compute_damage(&Some((*style).clone()), &new_style)); + } + + *style = new_style +} + diff --git a/components/layout/context.rs b/components/layout/context.rs index f6058eaaf44..624fb01a127 100644 --- a/components/layout/context.rs +++ b/components/layout/context.rs @@ -25,7 +25,7 @@ use std::collections::HashMap; use std::collections::hash_state::DefaultState; use std::rc::Rc; use std::sync::mpsc::{Sender, channel}; -use std::sync::{Arc, Mutex}; +use std::sync::{Arc, Mutex, RwLock}; use style::selector_matching::Stylist; use url::Url; use util::mem::HeapSizeOf; @@ -120,7 +120,10 @@ pub struct SharedLayoutContext { pub visible_rects: Arc<HashMap<LayerId, Rect<Au>, DefaultState<FnvHasher>>>, /// The animations that are currently running. - pub running_animations: Arc<HashMap<OpaqueNode, Vec<Animation>>>, + pub running_animations: Arc<RwLock<HashMap<OpaqueNode, Vec<Animation>>>>, + + /// The list of animations that have expired since the last style recalculation. + pub expired_animations: Arc<RwLock<HashMap<OpaqueNode, Vec<Animation>>>>, /// Why is this reflow occurring pub goal: ReflowGoal, diff --git a/components/layout/css/matching.rs b/components/layout/css/matching.rs index fc5e7aadb14..4d969e00fd8 100644 --- a/components/layout/css/matching.rs +++ b/components/layout/css/matching.rs @@ -414,6 +414,10 @@ trait PrivateMatchMethods { shareable: bool, animate_properties: bool) -> RestyleDamage; + fn update_animations_for_cascade(&self, + layout_context: &SharedLayoutContext, + style: &mut Option<Arc<ComputedValues>>) + -> bool; } trait PrivateElementMatchMethods { @@ -435,20 +439,12 @@ impl<'ln> PrivateMatchMethods for ServoLayoutNode<'ln> { shareable: bool, animate_properties: bool) -> RestyleDamage { - // Finish any transitions. + let mut cacheable = true; 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 { - animation.property_animation.update(&mut *Arc::make_mut(style), 1.0); - } - } - } + cacheable = !self.update_animations_for_cascade(layout_context, style) && cacheable; } let mut this_style; - let cacheable; match parent_style { Some(ref parent_style) => { let cache_entry = applicable_declarations_cache.find(applicable_declarations); @@ -461,7 +457,7 @@ impl<'ln> PrivateMatchMethods for ServoLayoutNode<'ln> { shareable, Some(&***parent_style), cached_computed_values); - cacheable = is_cacheable; + cacheable = cacheable && is_cacheable; this_style = the_style } None => { @@ -470,7 +466,7 @@ impl<'ln> PrivateMatchMethods for ServoLayoutNode<'ln> { shareable, None, None); - cacheable = is_cacheable; + cacheable = cacheable && is_cacheable; this_style = the_style } }; @@ -479,10 +475,12 @@ impl<'ln> PrivateMatchMethods for ServoLayoutNode<'ln> { // it did trigger a transition. if animate_properties { if let Some(ref style) = *style { - animation::start_transitions_if_applicable(new_animations_sender, - self.opaque(), - &**style, - &mut this_style); + let animations_started = + animation::start_transitions_if_applicable(new_animations_sender, + self.opaque(), + &**style, + &mut this_style); + cacheable = cacheable && !animations_started } } @@ -500,6 +498,50 @@ impl<'ln> PrivateMatchMethods for ServoLayoutNode<'ln> { *style = Some(this_style); damage } + + fn update_animations_for_cascade(&self, + layout_context: &SharedLayoutContext, + style: &mut Option<Arc<ComputedValues>>) + -> bool { + let style = match *style { + None => return false, + Some(ref mut style) => style, + }; + + // Finish any expired transitions. + let this_opaque = self.opaque(); + let had_animations_to_expire; + { + let all_expired_animations = layout_context.expired_animations.read().unwrap(); + let animations_to_expire = all_expired_animations.get(&this_opaque); + had_animations_to_expire = animations_to_expire.is_some(); + if let Some(ref animations) = animations_to_expire { + for animation in *animations { + animation.property_animation.update(&mut *Arc::make_mut(style), 1.0); + } + } + } + + if had_animations_to_expire { + layout_context.expired_animations.write().unwrap().remove(&this_opaque); + } + + // Merge any running transitions into the current style, and cancel them. + let had_running_animations = layout_context.running_animations + .read() + .unwrap() + .get(&this_opaque) + .is_some(); + if had_running_animations { + let mut all_running_animations = layout_context.running_animations.write().unwrap(); + for running_animation in all_running_animations.get(&this_opaque).unwrap() { + animation::update_style_for_animation(running_animation, style, None); + } + all_running_animations.remove(&this_opaque); + } + + had_animations_to_expire || had_running_animations + } } impl<'ln> PrivateElementMatchMethods for ServoLayoutElement<'ln> { diff --git a/components/layout/layout_task.rs b/components/layout/layout_task.rs index b8903a6363b..411611d6a0a 100644 --- a/components/layout/layout_task.rs +++ b/components/layout/layout_task.rs @@ -59,7 +59,7 @@ use std::mem::transmute; use std::ops::{Deref, DerefMut}; use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::mpsc::{channel, Sender, Receiver}; -use std::sync::{Arc, Mutex, MutexGuard}; +use std::sync::{Arc, Mutex, MutexGuard, RwLock}; use style::computed_values::{filter, mix_blend_mode}; use style::media_queries::{Device, MediaType}; use style::selector_matching::{Stylist, USER_OR_USER_AGENT_STYLESHEETS}; @@ -194,7 +194,10 @@ pub struct LayoutTask { visible_rects: Arc<HashMap<LayerId, Rect<Au>, DefaultState<FnvHasher>>>, /// The list of currently-running animations. - running_animations: Arc<HashMap<OpaqueNode, Vec<Animation>>>, + running_animations: Arc<RwLock<HashMap<OpaqueNode, Vec<Animation>>>>, + + /// The list of animations that have expired since the last style recalculation. + expired_animations: Arc<RwLock<HashMap<OpaqueNode, Vec<Animation>>>>, /// A counter for epoch messages epoch: Epoch, @@ -420,7 +423,8 @@ impl LayoutTask { outstanding_web_fonts: outstanding_web_fonts_counter, root_flow: None, visible_rects: Arc::new(HashMap::with_hash_state(Default::default())), - running_animations: Arc::new(HashMap::new()), + running_animations: Arc::new(RwLock::new(HashMap::new())), + expired_animations: Arc::new(RwLock::new(HashMap::new())), epoch: Epoch(0), viewport_size: Size2D::new(Au(0), Au(0)), rw_data: Arc::new(Mutex::new( @@ -471,6 +475,7 @@ impl LayoutTask { new_animations_sender: Mutex::new(self.new_animations_sender.clone()), goal: goal, running_animations: self.running_animations.clone(), + expired_animations: self.expired_animations.clone(), } } @@ -1137,13 +1142,13 @@ impl LayoutTask { if let Some(mut root_flow) = self.root_flow.clone() { // Perform an abbreviated style recalc that operates without access to the DOM. - let animations = &*self.running_animations; + let animations = self.running_animations.read().unwrap(); profile(time::ProfilerCategory::LayoutStyleRecalc, self.profiler_metadata(), self.time_profiler_chan.clone(), || { animation::recalc_style_for_animations(flow_ref::deref_mut(&mut root_flow), - animations) + &*animations) }); } @@ -1182,7 +1187,8 @@ impl LayoutTask { if let Some(mut root_flow) = self.root_flow.clone() { // Kick off animations if any were triggered, expire completed ones. animation::update_animation_state(&self.constellation_chan, - &mut self.running_animations, + &mut *self.running_animations.write().unwrap(), + &mut *self.expired_animations.write().unwrap(), &self.new_animations_receiver, self.id); diff --git a/components/layout/lib.rs b/components/layout/lib.rs index 85c6e506e92..2727000ef24 100644 --- a/components/layout/lib.rs +++ b/components/layout/lib.rs @@ -5,6 +5,7 @@ #![feature(box_syntax)] #![feature(cell_extras)] #![feature(custom_derive)] +#![feature(drain)] #![feature(hashmap_hasher)] #![feature(mpsc_select)] #![feature(plugin)] |