diff options
-rw-r--r-- | cc/animation/animation.cc | 5 | ||||
-rw-r--r-- | cc/animation/animation.h | 24 | ||||
-rw-r--r-- | cc/animation/layer_animation_controller.cc | 528 | ||||
-rw-r--r-- | cc/animation/layer_animation_controller.h | 25 | ||||
-rw-r--r-- | cc/animation/layer_animation_controller_unittest.cc | 301 | ||||
-rw-r--r-- | cc/trees/layer_tree_host.cc | 1 | ||||
-rw-r--r-- | cc/trees/layer_tree_host_impl.cc | 15 | ||||
-rw-r--r-- | cc/trees/layer_tree_host_impl.h | 1 | ||||
-rw-r--r-- | cc/trees/layer_tree_host_unittest_animation.cc | 79 |
9 files changed, 743 insertions, 236 deletions
diff --git a/cc/animation/animation.cc b/cc/animation/animation.cc index 81659cb..1af35d2 100644 --- a/cc/animation/animation.cc +++ b/cc/animation/animation.cc @@ -73,7 +73,10 @@ Animation::Animation(scoped_ptr<AnimationCurve> curve, pause_time_(0), total_paused_time_(0), is_controlling_instance_(false), - is_impl_only_(false) {} + is_impl_only_(false), + affects_active_observers_(true), + affects_pending_observers_(true) { +} Animation::~Animation() { if (run_state_ == Running || run_state_ == Paused) diff --git a/cc/animation/animation.h b/cc/animation/animation.h index 3cfc8bb..ecc48b5 100644 --- a/cc/animation/animation.h +++ b/cc/animation/animation.h @@ -126,6 +126,16 @@ class CC_EXPORT Animation { void set_is_impl_only(bool is_impl_only) { is_impl_only_ = is_impl_only; } bool is_impl_only() const { return is_impl_only_; } + void set_affects_active_observers(bool affects_active_observers) { + affects_active_observers_ = affects_active_observers; + } + bool affects_active_observers() const { return affects_active_observers_; } + + void set_affects_pending_observers(bool affects_pending_observers) { + affects_pending_observers_ = affects_pending_observers; + } + bool affects_pending_observers() const { return affects_pending_observers_; } + private: Animation(scoped_ptr<AnimationCurve> curve, int animation_id, @@ -182,6 +192,20 @@ class CC_EXPORT Animation { bool is_impl_only_; + // When pushed from a main-thread controller to a compositor-thread + // controller, an animation will initially only affect pending observers + // (corresponding to layers in the pending tree). Animations that only + // affect pending observers are able to reach the Starting state and tick + // pending observers, but cannot proceed any further and do not tick active + // observers. After activation, such animations affect both kinds of observers + // and are able to proceed past the Starting state. When the removal of + // an animation is pushed from a main-thread controller to a + // compositor-thread controller, this initially only makes the animation + // stop affecting pending observers. After activation, such animations no + // longer affect any observers, and are deleted. + bool affects_active_observers_; + bool affects_pending_observers_; + DISALLOW_COPY_AND_ASSIGN(Animation); }; diff --git a/cc/animation/layer_animation_controller.cc b/cc/animation/layer_animation_controller.cc index 464801c..62b8a5a 100644 --- a/cc/animation/layer_animation_controller.cc +++ b/cc/animation/layer_animation_controller.cc @@ -40,10 +40,10 @@ scoped_refptr<LayerAnimationController> LayerAnimationController::Create( void LayerAnimationController::PauseAnimation(int animation_id, double time_offset) { - for (size_t i = 0; i < active_animations_.size(); ++i) { - if (active_animations_[i]->id() == animation_id) { - active_animations_[i]->SetRunState( - Animation::Paused, time_offset + active_animations_[i]->start_time()); + for (size_t i = 0; i < animations_.size(); ++i) { + if (animations_[i]->id() == animation_id) { + animations_[i]->SetRunState(Animation::Paused, + time_offset + animations_[i]->start_time()); } } } @@ -59,12 +59,11 @@ struct HasAnimationId { }; void LayerAnimationController::RemoveAnimation(int animation_id) { - ScopedPtrVector<Animation>& animations = active_animations_; - animations.erase(cc::remove_if(&animations, - animations.begin(), - animations.end(), - HasAnimationId(animation_id)), - animations.end()); + animations_.erase(cc::remove_if(&animations_, + animations_.begin(), + animations_.end(), + HasAnimationId(animation_id)), + animations_.end()); UpdateActivation(NormalActivation); } @@ -84,22 +83,21 @@ struct HasAnimationIdAndProperty { void LayerAnimationController::RemoveAnimation( int animation_id, Animation::TargetProperty target_property) { - ScopedPtrVector<Animation>& animations = active_animations_; - animations.erase( - cc::remove_if(&animations, - animations.begin(), - animations.end(), + animations_.erase( + cc::remove_if(&animations_, + animations_.begin(), + animations_.end(), HasAnimationIdAndProperty(animation_id, target_property)), - animations.end()); + animations_.end()); UpdateActivation(NormalActivation); } void LayerAnimationController::AbortAnimations( Animation::TargetProperty target_property) { - for (size_t i = 0; i < active_animations_.size(); ++i) { - if (active_animations_[i]->target_property() == target_property && - !active_animations_[i]->is_finished()) - active_animations_[i]->SetRunState(Animation::Aborted, last_tick_time_); + for (size_t i = 0; i < animations_.size(); ++i) { + if (animations_[i]->target_property() == target_property && + !animations_[i]->is_finished()) + animations_[i]->SetRunState(Animation::Aborted, last_tick_time_); } } @@ -139,8 +137,8 @@ void LayerAnimationController::AccumulatePropertyUpdates( if (!events) return; - for (size_t i = 0; i < active_animations_.size(); ++i) { - Animation* animation = active_animations_[i]; + for (size_t i = 0; i < animations_.size(); ++i) { + Animation* animation = animations_[i]; if (!animation->is_impl_only()) continue; @@ -221,34 +219,54 @@ void LayerAnimationController::UpdateState(bool start_ready_animations, UpdateActivation(NormalActivation); } +struct AffectsNoObservers { + bool operator()(Animation* animation) const { + return !animation->affects_active_observers() && + !animation->affects_pending_observers(); + } +}; + +void LayerAnimationController::ActivateAnimations() { + for (size_t i = 0; i < animations_.size(); ++i) { + animations_[i]->set_affects_active_observers( + animations_[i]->affects_pending_observers()); + } + animations_.erase(cc::remove_if(&animations_, + animations_.begin(), + animations_.end(), + AffectsNoObservers()), + animations_.end()); + UpdateActivation(NormalActivation); +} + void LayerAnimationController::AddAnimation(scoped_ptr<Animation> animation) { - active_animations_.push_back(animation.Pass()); + animations_.push_back(animation.Pass()); UpdateActivation(NormalActivation); } Animation* LayerAnimationController::GetAnimation( int group_id, Animation::TargetProperty target_property) const { - for (size_t i = 0; i < active_animations_.size(); ++i) - if (active_animations_[i]->group() == group_id && - active_animations_[i]->target_property() == target_property) - return active_animations_[i]; + for (size_t i = 0; i < animations_.size(); ++i) + if (animations_[i]->group() == group_id && + animations_[i]->target_property() == target_property) + return animations_[i]; return 0; } Animation* LayerAnimationController::GetAnimation( Animation::TargetProperty target_property) const { - for (size_t i = 0; i < active_animations_.size(); ++i) { - size_t index = active_animations_.size() - i - 1; - if (active_animations_[index]->target_property() == target_property) - return active_animations_[index]; + for (size_t i = 0; i < animations_.size(); ++i) { + size_t index = animations_.size() - i - 1; + if (animations_[index]->target_property() == target_property) + return animations_[index]; } return 0; } bool LayerAnimationController::HasActiveAnimation() const { - for (size_t i = 0; i < active_animations_.size(); ++i) { - if (!active_animations_[i]->is_finished()) + for (size_t i = 0; i < animations_.size(); ++i) { + if (!animations_[i]->is_finished()) return true; } return false; @@ -256,9 +274,9 @@ bool LayerAnimationController::HasActiveAnimation() const { bool LayerAnimationController::IsAnimatingProperty( Animation::TargetProperty target_property) const { - for (size_t i = 0; i < active_animations_.size(); ++i) { - if (!active_animations_[i]->is_finished() && - active_animations_[i]->target_property() == target_property) + for (size_t i = 0; i < animations_.size(); ++i) { + if (!animations_[i]->is_finished() && + animations_[i]->target_property() == target_property) return true; } return false; @@ -293,13 +311,13 @@ void LayerAnimationController::NotifyAnimationStarted( return; } - for (size_t i = 0; i < active_animations_.size(); ++i) { - if (active_animations_[i]->group() == event.group_id && - active_animations_[i]->target_property() == event.target_property && - active_animations_[i]->needs_synchronized_start_time()) { - active_animations_[i]->set_needs_synchronized_start_time(false); - if (!active_animations_[i]->has_set_start_time()) - active_animations_[i]->set_start_time(event.monotonic_time); + for (size_t i = 0; i < animations_.size(); ++i) { + if (animations_[i]->group() == event.group_id && + animations_[i]->target_property() == event.target_property && + animations_[i]->needs_synchronized_start_time()) { + animations_[i]->set_needs_synchronized_start_time(false); + if (!animations_[i]->has_set_start_time()) + animations_[i]->set_start_time(event.monotonic_time); FOR_EACH_OBSERVER(LayerAnimationEventObserver, event_observers_, OnAnimationStarted(event)); @@ -323,10 +341,10 @@ void LayerAnimationController::NotifyAnimationFinished( return; } - for (size_t i = 0; i < active_animations_.size(); ++i) { - if (active_animations_[i]->group() == event.group_id && - active_animations_[i]->target_property() == event.target_property) { - active_animations_[i]->set_received_finished_event(true); + for (size_t i = 0; i < animations_.size(); ++i) { + if (animations_[i]->group() == event.group_id && + animations_[i]->target_property() == event.target_property) { + animations_[i]->set_received_finished_event(true); if (layer_animation_delegate_) layer_animation_delegate_->NotifyAnimationFinished( monotonic_time, event.target_property); @@ -338,23 +356,26 @@ void LayerAnimationController::NotifyAnimationFinished( void LayerAnimationController::NotifyAnimationAborted( const AnimationEvent& event) { - for (size_t i = 0; i < active_animations_.size(); ++i) { - if (active_animations_[i]->group() == event.group_id && - active_animations_[i]->target_property() == event.target_property) { - active_animations_[i]->SetRunState(Animation::Aborted, - event.monotonic_time); + for (size_t i = 0; i < animations_.size(); ++i) { + if (animations_[i]->group() == event.group_id && + animations_[i]->target_property() == event.target_property) { + animations_[i]->SetRunState(Animation::Aborted, event.monotonic_time); } } } void LayerAnimationController::NotifyAnimationPropertyUpdate( const AnimationEvent& event) { + bool notify_active_observers = true; + bool notify_pending_observers = true; switch (event.target_property) { case Animation::Opacity: - NotifyObserversOpacityAnimated(event.opacity); + NotifyObserversOpacityAnimated( + event.opacity, notify_active_observers, notify_pending_observers); break; case Animation::Transform: - NotifyObserversTransformAnimated(event.transform); + NotifyObserversTransformAnimated( + event.transform, notify_active_observers, notify_pending_observers); break; default: NOTREACHED(); @@ -384,10 +405,10 @@ void LayerAnimationController::RemoveEventObserver( } bool LayerAnimationController::HasFilterAnimationThatInflatesBounds() const { - for (size_t i = 0; i < active_animations_.size(); ++i) { - if (!active_animations_[i]->is_finished() && - active_animations_[i]->target_property() == Animation::Filter && - active_animations_[i] + for (size_t i = 0; i < animations_.size(); ++i) { + if (!animations_[i]->is_finished() && + animations_[i]->target_property() == Animation::Filter && + animations_[i] ->curve() ->ToFilterAnimationCurve() ->HasFilterThatMovesPixels()) @@ -420,13 +441,13 @@ bool LayerAnimationController::TransformAnimationBoundsForBox( // that callers will take care of computing bounds based on the owning layer's // actual transform. *bounds = gfx::BoxF(); - for (size_t i = 0; i < active_animations_.size(); ++i) { - if (active_animations_[i]->is_finished() || - active_animations_[i]->target_property() != Animation::Transform) + for (size_t i = 0; i < animations_.size(); ++i) { + if (animations_[i]->is_finished() || + animations_[i]->target_property() != Animation::Transform) continue; const TransformAnimationCurve* transform_animation_curve = - active_animations_[i]->curve()->ToTransformAnimationCurve(); + animations_[i]->curve()->ToTransformAnimationCurve(); gfx::BoxF animation_bounds; bool success = transform_animation_curve->AnimatedBoundsForBox(box, &animation_bounds); @@ -439,13 +460,13 @@ bool LayerAnimationController::TransformAnimationBoundsForBox( } bool LayerAnimationController::HasAnimationThatAffectsScale() const { - for (size_t i = 0; i < active_animations_.size(); ++i) { - if (active_animations_[i]->is_finished() || - active_animations_[i]->target_property() != Animation::Transform) + for (size_t i = 0; i < animations_.size(); ++i) { + if (animations_[i]->is_finished() || + animations_[i]->target_property() != Animation::Transform) continue; const TransformAnimationCurve* transform_animation_curve = - active_animations_[i]->curve()->ToTransformAnimationCurve(); + animations_[i]->curve()->ToTransformAnimationCurve(); if (transform_animation_curve->AffectsScale()) return true; } @@ -454,13 +475,13 @@ bool LayerAnimationController::HasAnimationThatAffectsScale() const { } bool LayerAnimationController::HasOnlyTranslationTransforms() const { - for (size_t i = 0; i < active_animations_.size(); ++i) { - if (active_animations_[i]->is_finished() || - active_animations_[i]->target_property() != Animation::Transform) + for (size_t i = 0; i < animations_.size(); ++i) { + if (animations_[i]->is_finished() || + animations_[i]->target_property() != Animation::Transform) continue; const TransformAnimationCurve* transform_animation_curve = - active_animations_[i]->curve()->ToTransformAnimationCurve(); + animations_[i]->curve()->ToTransformAnimationCurve(); if (!transform_animation_curve->IsTranslation()) return false; } @@ -470,13 +491,13 @@ bool LayerAnimationController::HasOnlyTranslationTransforms() const { bool LayerAnimationController::MaximumScale(float* max_scale) const { *max_scale = 0.f; - for (size_t i = 0; i < active_animations_.size(); ++i) { - if (active_animations_[i]->is_finished() || - active_animations_[i]->target_property() != Animation::Transform) + for (size_t i = 0; i < animations_.size(); ++i) { + if (animations_[i]->is_finished() || + animations_[i]->target_property() != Animation::Transform) continue; const TransformAnimationCurve* transform_animation_curve = - active_animations_[i]->curve()->ToTransformAnimationCurve(); + animations_[i]->curve()->ToTransformAnimationCurve(); float animation_scale = 0.f; if (!transform_animation_curve->MaximumScale(&animation_scale)) return false; @@ -490,11 +511,11 @@ void LayerAnimationController::PushNewAnimationsToImplThread( LayerAnimationController* controller_impl) const { // Any new animations owned by the main thread's controller are cloned and // add to the impl thread's controller. - for (size_t i = 0; i < active_animations_.size(); ++i) { + for (size_t i = 0; i < animations_.size(); ++i) { // If the animation is already running on the impl thread, there is no // need to copy it over. - if (controller_impl->GetAnimation(active_animations_[i]->group(), - active_animations_[i]->target_property())) + if (controller_impl->GetAnimation(animations_[i]->group(), + animations_[i]->target_property())) continue; // If the animation is not running on the impl thread, it does not @@ -503,11 +524,11 @@ void LayerAnimationController::PushNewAnimationsToImplThread( // have already notified that it has started and the main thread animation // will no longer need // a synchronized start time. - if (!active_animations_[i]->needs_synchronized_start_time()) + if (!animations_[i]->needs_synchronized_start_time()) continue; // Scroll animations always start at the current scroll offset. - if (active_animations_[i]->target_property() == Animation::ScrollOffset) { + if (animations_[i]->target_property() == Animation::ScrollOffset) { gfx::Vector2dF current_scroll_offset; if (controller_impl->value_provider_) { current_scroll_offset = @@ -517,107 +538,128 @@ void LayerAnimationController::PushNewAnimationsToImplThread( // scroll offset will be up-to-date. current_scroll_offset = value_provider_->ScrollOffsetForAnimation(); } - active_animations_[i]->curve()->ToScrollOffsetAnimationCurve() - ->SetInitialValue(current_scroll_offset); + animations_[i]->curve()->ToScrollOffsetAnimationCurve()->SetInitialValue( + current_scroll_offset); } // The new animation should be set to run as soon as possible. Animation::RunState initial_run_state = Animation::WaitingForTargetAvailability; - scoped_ptr<Animation> to_add(active_animations_[i]->CloneAndInitialize( - initial_run_state)); + scoped_ptr<Animation> to_add( + animations_[i]->CloneAndInitialize(initial_run_state)); DCHECK(!to_add->needs_synchronized_start_time()); + to_add->set_affects_active_observers(false); controller_impl->AddAnimation(to_add.Pass()); } } -struct IsCompleted { - explicit IsCompleted(const LayerAnimationController& main_thread_controller) - : main_thread_controller_(main_thread_controller) {} - bool operator()(Animation* animation) const { - if (animation->is_impl_only()) { - return (animation->run_state() == Animation::WaitingForDeletion); - } else { - return !main_thread_controller_.GetAnimation( - animation->group(), - animation->target_property()); - } +static bool IsCompleted( + Animation* animation, + const LayerAnimationController* main_thread_controller) { + if (animation->is_impl_only()) { + return (animation->run_state() == Animation::WaitingForDeletion); + } else { + return !main_thread_controller->GetAnimation(animation->group(), + animation->target_property()); } +} - private: - const LayerAnimationController& main_thread_controller_; -}; +static bool AffectsActiveOnlyAndIsWaitingForDeletion(Animation* animation) { + return animation->run_state() == Animation::WaitingForDeletion && + !animation->affects_pending_observers(); +} void LayerAnimationController::RemoveAnimationsCompletedOnMainThread( LayerAnimationController* controller_impl) const { - // Delete all impl thread animations for which there is no corresponding - // main thread animation. Each iteration, - // controller->active_animations_.size() is decremented or i is incremented - // guaranteeing progress towards loop termination. - ScopedPtrVector<Animation>& animations = - controller_impl->active_animations_; + // Animations removed on the main thread should no longer affect pending + // observers, and should stop affecting active observers after the next call + // to ActivateAnimations. If already WaitingForDeletion, they can be removed + // immediately. + ScopedPtrVector<Animation>& animations = controller_impl->animations_; + for (size_t i = 0; i < animations.size(); ++i) { + if (IsCompleted(animations[i], this)) + animations[i]->set_affects_pending_observers(false); + } animations.erase(cc::remove_if(&animations, animations.begin(), animations.end(), - IsCompleted(*this)), + AffectsActiveOnlyAndIsWaitingForDeletion), animations.end()); } void LayerAnimationController::PushPropertiesToImplThread( LayerAnimationController* controller_impl) const { - for (size_t i = 0; i < active_animations_.size(); ++i) { - Animation* current_impl = - controller_impl->GetAnimation( - active_animations_[i]->group(), - active_animations_[i]->target_property()); + for (size_t i = 0; i < animations_.size(); ++i) { + Animation* current_impl = controller_impl->GetAnimation( + animations_[i]->group(), animations_[i]->target_property()); if (current_impl) - active_animations_[i]->PushPropertiesTo(current_impl); + animations_[i]->PushPropertiesTo(current_impl); } } void LayerAnimationController::StartAnimations(double monotonic_time) { - // First collect running properties. - TargetProperties blocked_properties; - for (size_t i = 0; i < active_animations_.size(); ++i) { - if (active_animations_[i]->run_state() == Animation::Starting || - active_animations_[i]->run_state() == Animation::Running) - blocked_properties.insert(active_animations_[i]->target_property()); + // First collect running properties affecting each type of observer. + TargetProperties blocked_properties_for_active_observers; + TargetProperties blocked_properties_for_pending_observers; + for (size_t i = 0; i < animations_.size(); ++i) { + if (animations_[i]->run_state() == Animation::Starting || + animations_[i]->run_state() == Animation::Running) { + if (animations_[i]->affects_active_observers()) { + blocked_properties_for_active_observers.insert( + animations_[i]->target_property()); + } + if (animations_[i]->affects_pending_observers()) { + blocked_properties_for_pending_observers.insert( + animations_[i]->target_property()); + } + } } - for (size_t i = 0; i < active_animations_.size(); ++i) { - if (active_animations_[i]->run_state() == + for (size_t i = 0; i < animations_.size(); ++i) { + if (animations_[i]->run_state() == Animation::WaitingForTargetAvailability) { // Collect all properties for animations with the same group id (they // should all also be in the list of animations). TargetProperties enqueued_properties; - enqueued_properties.insert(active_animations_[i]->target_property()); - for (size_t j = i + 1; j < active_animations_.size(); ++j) { - if (active_animations_[i]->group() == active_animations_[j]->group()) - enqueued_properties.insert(active_animations_[j]->target_property()); + bool affects_active_observers = + animations_[i]->affects_active_observers(); + bool affects_pending_observers = + animations_[i]->affects_pending_observers(); + enqueued_properties.insert(animations_[i]->target_property()); + for (size_t j = i + 1; j < animations_.size(); ++j) { + if (animations_[i]->group() == animations_[j]->group()) { + enqueued_properties.insert(animations_[j]->target_property()); + affects_active_observers |= + animations_[j]->affects_active_observers(); + affects_pending_observers |= + animations_[j]->affects_pending_observers(); + } } // Check to see if intersection of the list of properties affected by - // the group and the list of currently blocked properties is null. In - // any case, the group's target properties need to be added to the list - // of blocked properties. + // the group and the list of currently blocked properties is null, taking + // into account the type(s) of observers affected by the group. In any + // case, the group's target properties need to be added to the lists of + // blocked properties. bool null_intersection = true; for (TargetProperties::iterator p_iter = enqueued_properties.begin(); p_iter != enqueued_properties.end(); ++p_iter) { - if (!blocked_properties.insert(*p_iter).second) + if (affects_active_observers && + !blocked_properties_for_active_observers.insert(*p_iter).second) + null_intersection = false; + if (affects_pending_observers && + !blocked_properties_for_pending_observers.insert(*p_iter).second) null_intersection = false; } // If the intersection is null, then we are free to start the animations // in the group. if (null_intersection) { - active_animations_[i]->SetRunState( - Animation::Starting, monotonic_time); - for (size_t j = i + 1; j < active_animations_.size(); ++j) { - if (active_animations_[i]->group() == - active_animations_[j]->group()) { - active_animations_[j]->SetRunState( - Animation::Starting, monotonic_time); + animations_[i]->SetRunState(Animation::Starting, monotonic_time); + for (size_t j = i + 1; j < animations_.size(); ++j) { + if (animations_[i]->group() == animations_[j]->group()) { + animations_[j]->SetRunState(Animation::Starting, monotonic_time); } } } @@ -628,20 +670,20 @@ void LayerAnimationController::StartAnimations(double monotonic_time) { void LayerAnimationController::PromoteStartedAnimations( double monotonic_time, AnimationEventsVector* events) { - for (size_t i = 0; i < active_animations_.size(); ++i) { - if (active_animations_[i]->run_state() == Animation::Starting) { - active_animations_[i]->SetRunState(Animation::Running, monotonic_time); - if (!active_animations_[i]->has_set_start_time() && - !active_animations_[i]->needs_synchronized_start_time()) - active_animations_[i]->set_start_time(monotonic_time); + for (size_t i = 0; i < animations_.size(); ++i) { + if (animations_[i]->run_state() == Animation::Starting && + animations_[i]->affects_active_observers()) { + animations_[i]->SetRunState(Animation::Running, monotonic_time); + if (!animations_[i]->has_set_start_time() && + !animations_[i]->needs_synchronized_start_time()) + animations_[i]->set_start_time(monotonic_time); if (events) { - AnimationEvent started_event( - AnimationEvent::Started, - id_, - active_animations_[i]->group(), - active_animations_[i]->target_property(), - monotonic_time); - started_event.is_impl_only = active_animations_[i]->is_impl_only(); + AnimationEvent started_event(AnimationEvent::Started, + id_, + animations_[i]->group(), + animations_[i]->target_property(), + monotonic_time); + started_event.is_impl_only = animations_[i]->is_impl_only(); events->push_back(started_event); } } @@ -649,11 +691,11 @@ void LayerAnimationController::PromoteStartedAnimations( } void LayerAnimationController::MarkFinishedAnimations(double monotonic_time) { - for (size_t i = 0; i < active_animations_.size(); ++i) { - if (active_animations_[i]->IsFinishedAt(monotonic_time) && - active_animations_[i]->run_state() != Animation::Aborted && - active_animations_[i]->run_state() != Animation::WaitingForDeletion) - active_animations_[i]->SetRunState(Animation::Finished, monotonic_time); + for (size_t i = 0; i < animations_.size(); ++i) { + if (animations_[i]->IsFinishedAt(monotonic_time) && + animations_[i]->run_state() != Animation::Aborted && + animations_[i]->run_state() != Animation::WaitingForDeletion) + animations_[i]->SetRunState(Animation::Finished, monotonic_time); } } @@ -665,20 +707,19 @@ void LayerAnimationController::MarkAnimationsForDeletion( // AnimationEvent::Finished event is sent or received. This means that if // we don't have an events vector, we must ensure that non-aborted animations // have received a finished event before marking them for deletion. - for (size_t i = 0; i < active_animations_.size(); i++) { - int group_id = active_animations_[i]->group(); - if (active_animations_[i]->run_state() == Animation::Aborted) { - if (events && !active_animations_[i]->is_impl_only()) { - AnimationEvent aborted_event( - AnimationEvent::Aborted, - id_, - group_id, - active_animations_[i]->target_property(), - monotonic_time); + for (size_t i = 0; i < animations_.size(); i++) { + int group_id = animations_[i]->group(); + if (animations_[i]->run_state() == Animation::Aborted) { + if (events && !animations_[i]->is_impl_only()) { + AnimationEvent aborted_event(AnimationEvent::Aborted, + id_, + group_id, + animations_[i]->target_property(), + monotonic_time); events->push_back(aborted_event); } - active_animations_[i]->SetRunState(Animation::WaitingForDeletion, - monotonic_time); + animations_[i]->SetRunState(Animation::WaitingForDeletion, + monotonic_time); marked_animations_for_deletions = true; continue; } @@ -689,18 +730,18 @@ void LayerAnimationController::MarkAnimationsForDeletion( // on the impl thread, we only mark a Finished main thread animation for // deletion once it has received a Finished event from the impl thread. bool animation_i_will_send_or_has_received_finish_event = - events || active_animations_[i]->received_finished_event(); + events || animations_[i]->received_finished_event(); // If an animation is finished, and not already marked for deletion, // find out if all other animations in the same group are also finished. - if (active_animations_[i]->run_state() == Animation::Finished && + if (animations_[i]->run_state() == Animation::Finished && animation_i_will_send_or_has_received_finish_event) { all_anims_with_same_id_are_finished = true; - for (size_t j = 0; j < active_animations_.size(); ++j) { + for (size_t j = 0; j < animations_.size(); ++j) { bool animation_j_will_send_or_has_received_finish_event = - events || active_animations_[j]->received_finished_event(); - if (group_id == active_animations_[j]->group() && - (!active_animations_[j]->is_finished() || - (active_animations_[j]->run_state() == Animation::Finished && + events || animations_[j]->received_finished_event(); + if (group_id == animations_[j]->group() && + (!animations_[j]->is_finished() || + (animations_[j]->run_state() == Animation::Finished && !animation_j_will_send_or_has_received_finish_event))) { all_anims_with_same_id_are_finished = false; break; @@ -711,21 +752,20 @@ void LayerAnimationController::MarkAnimationsForDeletion( // We now need to remove all animations with the same group id as // group_id (and send along animation finished notifications, if // necessary). - for (size_t j = i; j < active_animations_.size(); j++) { - if (active_animations_[j]->group() == group_id && - active_animations_[j]->run_state() != Animation::Aborted) { + for (size_t j = i; j < animations_.size(); j++) { + if (animations_[j]->group() == group_id && + animations_[j]->run_state() != Animation::Aborted) { if (events) { - AnimationEvent finished_event( - AnimationEvent::Finished, - id_, - active_animations_[j]->group(), - active_animations_[j]->target_property(), - monotonic_time); - finished_event.is_impl_only = active_animations_[j]->is_impl_only(); + AnimationEvent finished_event(AnimationEvent::Finished, + id_, + animations_[j]->group(), + animations_[j]->target_property(), + monotonic_time); + finished_event.is_impl_only = animations_[j]->is_impl_only(); events->push_back(finished_event); } - active_animations_[j]->SetRunState(Animation::WaitingForDeletion, - monotonic_time); + animations_[j]->SetRunState(Animation::WaitingForDeletion, + monotonic_time); } } marked_animations_for_deletions = true; @@ -740,46 +780,54 @@ static bool IsWaitingForDeletion(Animation* animation) { } void LayerAnimationController::PurgeAnimationsMarkedForDeletion() { - ScopedPtrVector<Animation>& animations = active_animations_; - animations.erase(cc::remove_if(&animations, - animations.begin(), - animations.end(), - IsWaitingForDeletion), - animations.end()); + animations_.erase(cc::remove_if(&animations_, + animations_.begin(), + animations_.end(), + IsWaitingForDeletion), + animations_.end()); } void LayerAnimationController::TickAnimations(double monotonic_time) { - for (size_t i = 0; i < active_animations_.size(); ++i) { - if (active_animations_[i]->run_state() == Animation::Starting || - active_animations_[i]->run_state() == Animation::Running || - active_animations_[i]->run_state() == Animation::Paused) { + for (size_t i = 0; i < animations_.size(); ++i) { + if (animations_[i]->run_state() == Animation::Starting || + animations_[i]->run_state() == Animation::Running || + animations_[i]->run_state() == Animation::Paused) { double trimmed = - active_animations_[i]->TrimTimeToCurrentIteration(monotonic_time); + animations_[i]->TrimTimeToCurrentIteration(monotonic_time); - switch (active_animations_[i]->target_property()) { + switch (animations_[i]->target_property()) { case Animation::Transform: { const TransformAnimationCurve* transform_animation_curve = - active_animations_[i]->curve()->ToTransformAnimationCurve(); + animations_[i]->curve()->ToTransformAnimationCurve(); const gfx::Transform transform = transform_animation_curve->GetValue(trimmed); - NotifyObserversTransformAnimated(transform); + NotifyObserversTransformAnimated( + transform, + animations_[i]->affects_active_observers(), + animations_[i]->affects_pending_observers()); break; } case Animation::Opacity: { const FloatAnimationCurve* float_animation_curve = - active_animations_[i]->curve()->ToFloatAnimationCurve(); + animations_[i]->curve()->ToFloatAnimationCurve(); const float opacity = float_animation_curve->GetValue(trimmed); - NotifyObserversOpacityAnimated(opacity); + NotifyObserversOpacityAnimated( + opacity, + animations_[i]->affects_active_observers(), + animations_[i]->affects_pending_observers()); break; } case Animation::Filter: { const FilterAnimationCurve* filter_animation_curve = - active_animations_[i]->curve()->ToFilterAnimationCurve(); + animations_[i]->curve()->ToFilterAnimationCurve(); const FilterOperations filter = filter_animation_curve->GetValue(trimmed); - NotifyObserversFilterAnimated(filter); + NotifyObserversFilterAnimated( + filter, + animations_[i]->affects_active_observers(), + animations_[i]->affects_pending_observers()); break; } @@ -790,10 +838,13 @@ void LayerAnimationController::TickAnimations(double monotonic_time) { case Animation::ScrollOffset: { const ScrollOffsetAnimationCurve* scroll_offset_animation_curve = - active_animations_[i]->curve()->ToScrollOffsetAnimationCurve(); + animations_[i]->curve()->ToScrollOffsetAnimationCurve(); const gfx::Vector2dF scroll_offset = scroll_offset_animation_curve->GetValue(trimmed); - NotifyObserversScrollOffsetAnimated(scroll_offset); + NotifyObserversScrollOffsetAnimated( + scroll_offset, + animations_[i]->affects_active_observers(), + animations_[i]->affects_pending_observers()); break; } @@ -810,8 +861,8 @@ void LayerAnimationController::UpdateActivation(UpdateActivationType type) { if (registrar_) { bool was_active = is_active_; is_active_ = false; - for (size_t i = 0; i < active_animations_.size(); ++i) { - if (active_animations_[i]->run_state() != Animation::WaitingForDeletion) { + for (size_t i = 0; i < animations_.size(); ++i) { + if (animations_[i]->run_state() != Animation::WaitingForDeletion) { is_active_ = true; break; } @@ -824,31 +875,68 @@ void LayerAnimationController::UpdateActivation(UpdateActivationType type) { } } -void LayerAnimationController::NotifyObserversOpacityAnimated(float opacity) { - FOR_EACH_OBSERVER(LayerAnimationValueObserver, - value_observers_, - OnOpacityAnimated(opacity)); +void LayerAnimationController::NotifyObserversOpacityAnimated( + float opacity, + bool notify_active_observers, + bool notify_pending_observers) { + if (value_observers_.might_have_observers()) { + ObserverListBase<LayerAnimationValueObserver>::Iterator it( + value_observers_); + LayerAnimationValueObserver* obs; + while ((obs = it.GetNext()) != NULL) { + if ((notify_active_observers && obs->IsActive()) || + (notify_pending_observers && !obs->IsActive())) + obs->OnOpacityAnimated(opacity); + } + } } void LayerAnimationController::NotifyObserversTransformAnimated( - const gfx::Transform& transform) { - FOR_EACH_OBSERVER(LayerAnimationValueObserver, - value_observers_, - OnTransformAnimated(transform)); + const gfx::Transform& transform, + bool notify_active_observers, + bool notify_pending_observers) { + if (value_observers_.might_have_observers()) { + ObserverListBase<LayerAnimationValueObserver>::Iterator it( + value_observers_); + LayerAnimationValueObserver* obs; + while ((obs = it.GetNext()) != NULL) { + if ((notify_active_observers && obs->IsActive()) || + (notify_pending_observers && !obs->IsActive())) + obs->OnTransformAnimated(transform); + } + } } void LayerAnimationController::NotifyObserversFilterAnimated( - const FilterOperations& filters) { - FOR_EACH_OBSERVER(LayerAnimationValueObserver, - value_observers_, - OnFilterAnimated(filters)); + const FilterOperations& filters, + bool notify_active_observers, + bool notify_pending_observers) { + if (value_observers_.might_have_observers()) { + ObserverListBase<LayerAnimationValueObserver>::Iterator it( + value_observers_); + LayerAnimationValueObserver* obs; + while ((obs = it.GetNext()) != NULL) { + if ((notify_active_observers && obs->IsActive()) || + (notify_pending_observers && !obs->IsActive())) + obs->OnFilterAnimated(filters); + } + } } void LayerAnimationController::NotifyObserversScrollOffsetAnimated( - const gfx::Vector2dF& scroll_offset) { - FOR_EACH_OBSERVER(LayerAnimationValueObserver, - value_observers_, - OnScrollOffsetAnimated(scroll_offset)); + const gfx::Vector2dF& scroll_offset, + bool notify_active_observers, + bool notify_pending_observers) { + if (value_observers_.might_have_observers()) { + ObserverListBase<LayerAnimationValueObserver>::Iterator it( + value_observers_); + LayerAnimationValueObserver* obs; + while ((obs = it.GetNext()) != NULL) { + if ((notify_active_observers && obs->IsActive()) || + (notify_pending_observers && !obs->IsActive())) + obs->OnScrollOffsetAnimated(scroll_offset); + } + } } void LayerAnimationController::NotifyObserversAnimationWaitingForDeletion() { diff --git a/cc/animation/layer_animation_controller.h b/cc/animation/layer_animation_controller.h index c5f0a31..c98a879 100644 --- a/cc/animation/layer_animation_controller.h +++ b/cc/animation/layer_animation_controller.h @@ -59,6 +59,11 @@ class CC_EXPORT LayerAnimationController void UpdateState(bool start_ready_animations, AnimationEventsVector* events); + // Make animations affect active observers if and only if they affect + // pending observers. Any animations that no longer affect any observers + // are deleted. + void ActivateAnimations(); + // Returns the active animation in the given group, animating the given // property, if such an animation exists. Animation* GetAnimation(int group_id, @@ -73,7 +78,7 @@ class CC_EXPORT LayerAnimationController bool HasActiveAnimation() const; // Returns true if there are any animations at all to process. - bool has_any_animation() const { return !active_animations_.empty(); } + bool has_any_animation() const { return !animations_.empty(); } // Returns true if there is an animation currently animating the given // property, or if there is an animation scheduled to animate this property in @@ -159,10 +164,18 @@ class CC_EXPORT LayerAnimationController }; void UpdateActivation(UpdateActivationType type); - void NotifyObserversOpacityAnimated(float opacity); - void NotifyObserversTransformAnimated(const gfx::Transform& transform); - void NotifyObserversFilterAnimated(const FilterOperations& filter); - void NotifyObserversScrollOffsetAnimated(const gfx::Vector2dF& scroll_offset); + void NotifyObserversOpacityAnimated(float opacity, + bool notify_active_observers, + bool notify_pending_observers); + void NotifyObserversTransformAnimated(const gfx::Transform& transform, + bool notify_active_observers, + bool notify_pending_observers); + void NotifyObserversFilterAnimated(const FilterOperations& filter, + bool notify_active_observers, + bool notify_pending_observers); + void NotifyObserversScrollOffsetAnimated(const gfx::Vector2dF& scroll_offset, + bool notify_active_observers, + bool notify_pending_observers); void NotifyObserversAnimationWaitingForDeletion(); @@ -171,7 +184,7 @@ class CC_EXPORT LayerAnimationController AnimationRegistrar* registrar_; int id_; - ScopedPtrVector<Animation> active_animations_; + ScopedPtrVector<Animation> animations_; // This is used to ensure that we don't spam the registrar. bool is_active_; diff --git a/cc/animation/layer_animation_controller_unittest.cc b/cc/animation/layer_animation_controller_unittest.cc index 4dca3f6..a6a5363 100644 --- a/cc/animation/layer_animation_controller_unittest.cc +++ b/cc/animation/layer_animation_controller_unittest.cc @@ -47,6 +47,7 @@ TEST(LayerAnimationControllerTest, SyncNewAnimation) { int group_id = controller->GetAnimation(Animation::Opacity)->group(); controller->PushAnimationUpdatesTo(controller_impl.get()); + controller_impl->ActivateAnimations(); EXPECT_TRUE(controller_impl->GetAnimation(group_id, Animation::Opacity)); EXPECT_EQ(Animation::WaitingForTargetAvailability, @@ -72,6 +73,7 @@ TEST(LayerAnimationControllerTest, DoNotClobberStartTimes) { int group_id = controller->GetAnimation(Animation::Opacity)->group(); controller->PushAnimationUpdatesTo(controller_impl.get()); + controller_impl->ActivateAnimations(); EXPECT_TRUE(controller_impl->GetAnimation(group_id, Animation::Opacity)); EXPECT_EQ(Animation::WaitingForTargetAvailability, @@ -116,6 +118,7 @@ TEST(LayerAnimationControllerTest, UseSpecifiedStartTimes) { controller->GetAnimation(Animation::Opacity)->set_start_time(start_time); controller->PushAnimationUpdatesTo(controller_impl.get()); + controller_impl->ActivateAnimations(); EXPECT_TRUE(controller_impl->GetAnimation(group_id, Animation::Opacity)); EXPECT_EQ(Animation::WaitingForTargetAvailability, @@ -180,6 +183,7 @@ TEST(LayerAnimationControllerTest, Activation) { EXPECT_EQ(1u, registrar->active_animation_controllers().size()); controller->PushAnimationUpdatesTo(controller_impl.get()); + controller_impl->ActivateAnimations(); // Both controllers should now be active. EXPECT_EQ(1u, registrar->active_animation_controllers().size()); EXPECT_EQ(1u, registrar_impl->active_animation_controllers().size()); @@ -222,6 +226,7 @@ TEST(LayerAnimationControllerTest, Activation) { EXPECT_EQ(0u, registrar->active_animation_controllers().size()); controller->PushAnimationUpdatesTo(controller_impl.get()); + controller_impl->ActivateAnimations(); EXPECT_FALSE(controller->has_any_animation()); EXPECT_FALSE(controller_impl->has_any_animation()); EXPECT_EQ(0u, registrar->active_animation_controllers().size()); @@ -248,6 +253,7 @@ TEST(LayerAnimationControllerTest, SyncPause) { int animation_id = controller->GetAnimation(Animation::Opacity)->id(); controller->PushAnimationUpdatesTo(controller_impl.get()); + controller_impl->ActivateAnimations(); EXPECT_TRUE(controller_impl->GetAnimation(group_id, Animation::Opacity)); EXPECT_EQ(Animation::WaitingForTargetAvailability, @@ -275,6 +281,7 @@ TEST(LayerAnimationControllerTest, SyncPause) { // The pause run state change should make it to the impl thread controller. controller->PushAnimationUpdatesTo(controller_impl.get()); + controller_impl->ActivateAnimations(); EXPECT_EQ(Animation::Paused, controller_impl->GetAnimation(group_id, Animation::Opacity)->run_state()); @@ -297,6 +304,7 @@ TEST(LayerAnimationControllerTest, DoNotSyncFinishedAnimation) { int group_id = controller->GetAnimation(Animation::Opacity)->group(); controller->PushAnimationUpdatesTo(controller_impl.get()); + controller_impl->ActivateAnimations(); EXPECT_TRUE(controller_impl->GetAnimation(group_id, Animation::Opacity)); EXPECT_EQ(Animation::WaitingForTargetAvailability, @@ -317,6 +325,7 @@ TEST(LayerAnimationControllerTest, DoNotSyncFinishedAnimation) { EXPECT_FALSE(controller_impl->GetAnimation(group_id, Animation::Opacity)); controller->PushAnimationUpdatesTo(controller_impl.get()); + controller_impl->ActivateAnimations(); // Even though the main thread has a 'new' animation, it should not be pushed // because the animation has already completed on the impl thread. @@ -341,6 +350,7 @@ TEST(LayerAnimationControllerTest, AnimationsAreDeleted) { controller->Animate(kInitialTickTime); controller->UpdateState(true, NULL); controller->PushAnimationUpdatesTo(controller_impl.get()); + controller_impl->ActivateAnimations(); controller_impl->Animate(kInitialTickTime + 0.5); controller_impl->UpdateState(true, events.get()); @@ -378,7 +388,10 @@ TEST(LayerAnimationControllerTest, AnimationsAreDeleted) { controller->PushAnimationUpdatesTo(controller_impl.get()); - // Both controllers should now have deleted the animation. + // Both controllers should now have deleted the animation. The impl controller + // should have deleted the animation even though activation has not occurred, + // since the animation was already waiting for deletion when + // PushAnimationUpdatesTo was called. EXPECT_FALSE(controller->has_any_animation()); EXPECT_FALSE(controller_impl->has_any_animation()); } @@ -641,6 +654,7 @@ TEST(LayerAnimationControllerTest, ScrollOffsetTransition) { dummy_provider_impl.set_scroll_offset(initial_value); controller->PushAnimationUpdatesTo(controller_impl.get()); + controller_impl->ActivateAnimations(); EXPECT_TRUE(controller_impl->GetAnimation(Animation::ScrollOffset)); double duration = controller_impl->GetAnimation( Animation::ScrollOffset)->curve()->Duration(); @@ -719,6 +733,7 @@ TEST(LayerAnimationControllerTest, ScrollOffsetTransitionNoImplProvider) { dummy_provider.set_scroll_offset(initial_value); controller->PushAnimationUpdatesTo(controller_impl.get()); + controller_impl->ActivateAnimations(); EXPECT_TRUE(controller_impl->GetAnimation(Animation::ScrollOffset)); double duration = controller_impl->GetAnimation( Animation::ScrollOffset)->curve()->Duration(); @@ -1306,6 +1321,7 @@ TEST(LayerAnimationControllerTest, PushUpdatesWhenSynchronizedStartTimeNeeded) { EXPECT_TRUE(active_animation->needs_synchronized_start_time()); controller->PushAnimationUpdatesTo(controller_impl.get()); + controller_impl->ActivateAnimations(); active_animation = controller_impl->GetAnimation(0, Animation::Opacity); EXPECT_TRUE(active_animation); @@ -1358,13 +1374,13 @@ TEST(LayerAnimationControllerTest, SkipUpdateState) { EXPECT_FALSE(controller->HasActiveAnimation()); } -// Tests that an animation controller with only an inactive observer gets ticked +// Tests that an animation controller with only a pending observer gets ticked // but doesn't progress animations past the Starting state. TEST(LayerAnimationControllerTest, InactiveObserverGetsTicked) { scoped_ptr<AnimationEventsVector> events( make_scoped_ptr(new AnimationEventsVector)); FakeLayerAnimationValueObserver dummy; - FakeInactiveLayerAnimationValueObserver inactive_dummy; + FakeInactiveLayerAnimationValueObserver pending_dummy; scoped_refptr<LayerAnimationController> controller( LayerAnimationController::Create(0)); @@ -1382,9 +1398,9 @@ TEST(LayerAnimationControllerTest, InactiveObserverGetsTicked) { EXPECT_EQ(Animation::WaitingForTargetAvailability, controller->GetAnimation(id, Animation::Opacity)->run_state()); - controller->AddValueObserver(&inactive_dummy); + controller->AddValueObserver(&pending_dummy); - // With only an inactive observer, the animation should progress to the + // With only a pending observer, the animation should progress to the // Starting state and get ticked at its starting point, but should not // progress to Running. controller->Animate(kInitialTickTime + 1.0); @@ -1392,7 +1408,7 @@ TEST(LayerAnimationControllerTest, InactiveObserverGetsTicked) { EXPECT_EQ(0u, events->size()); EXPECT_EQ(Animation::Starting, controller->GetAnimation(id, Animation::Opacity)->run_state()); - EXPECT_EQ(0.5f, inactive_dummy.opacity()); + EXPECT_EQ(0.5f, pending_dummy.opacity()); // Even when already in the Starting state, the animation should stay // there, and shouldn't be ticked past its starting point. @@ -1401,7 +1417,7 @@ TEST(LayerAnimationControllerTest, InactiveObserverGetsTicked) { EXPECT_EQ(0u, events->size()); EXPECT_EQ(Animation::Starting, controller->GetAnimation(id, Animation::Opacity)->run_state()); - EXPECT_EQ(0.5f, inactive_dummy.opacity()); + EXPECT_EQ(0.5f, pending_dummy.opacity()); controller->AddValueObserver(&dummy); @@ -1412,12 +1428,12 @@ TEST(LayerAnimationControllerTest, InactiveObserverGetsTicked) { EXPECT_EQ(1u, events->size()); EXPECT_EQ(Animation::Running, controller->GetAnimation(id, Animation::Opacity)->run_state()); - EXPECT_EQ(0.5f, inactive_dummy.opacity()); + EXPECT_EQ(0.5f, pending_dummy.opacity()); EXPECT_EQ(0.5f, dummy.opacity()); // The animation should now tick past its starting point. controller->Animate(kInitialTickTime + 3.5); - EXPECT_NE(0.5f, inactive_dummy.opacity()); + EXPECT_NE(0.5f, pending_dummy.opacity()); EXPECT_NE(0.5f, dummy.opacity()); } @@ -1568,6 +1584,7 @@ TEST(LayerAnimationControllerTest, MainThreadAbortedAnimationGetsDeleted) { int group_id = controller->GetAnimation(Animation::Opacity)->group(); controller->PushAnimationUpdatesTo(controller_impl.get()); + controller_impl->ActivateAnimations(); EXPECT_TRUE(controller_impl->GetAnimation(group_id, Animation::Opacity)); controller->AbortAnimations(Animation::Opacity); @@ -1583,6 +1600,7 @@ TEST(LayerAnimationControllerTest, MainThreadAbortedAnimationGetsDeleted) { controller->GetAnimation(Animation::Opacity)->run_state()); controller->PushAnimationUpdatesTo(controller_impl.get()); + controller_impl->ActivateAnimations(); EXPECT_FALSE(controller->GetAnimation(group_id, Animation::Opacity)); EXPECT_FALSE(controller_impl->GetAnimation(group_id, Animation::Opacity)); } @@ -1602,6 +1620,7 @@ TEST(LayerAnimationControllerTest, ImplThreadAbortedAnimationGetsDeleted) { int group_id = controller->GetAnimation(Animation::Opacity)->group(); controller->PushAnimationUpdatesTo(controller_impl.get()); + controller_impl->ActivateAnimations(); EXPECT_TRUE(controller_impl->GetAnimation(group_id, Animation::Opacity)); controller_impl->AbortAnimations(Animation::Opacity); @@ -1630,6 +1649,7 @@ TEST(LayerAnimationControllerTest, ImplThreadAbortedAnimationGetsDeleted) { controller->GetAnimation(Animation::Opacity)->run_state()); controller->PushAnimationUpdatesTo(controller_impl.get()); + controller_impl->ActivateAnimations(); EXPECT_FALSE(controller->GetAnimation(group_id, Animation::Opacity)); EXPECT_FALSE(controller_impl->GetAnimation(group_id, Animation::Opacity)); } @@ -1908,5 +1928,268 @@ TEST(LayerAnimationControllerTest, MaximumScale) { EXPECT_EQ(4.f, max_scale); } +TEST(LayerAnimationControllerTest, NewlyPushedAnimationWaitsForActivation) { + scoped_ptr<AnimationEventsVector> events( + make_scoped_ptr(new AnimationEventsVector)); + FakeLayerAnimationValueObserver dummy_impl; + FakeInactiveLayerAnimationValueObserver pending_dummy_impl; + scoped_refptr<LayerAnimationController> controller_impl( + LayerAnimationController::Create(0)); + controller_impl->AddValueObserver(&dummy_impl); + controller_impl->AddValueObserver(&pending_dummy_impl); + FakeLayerAnimationValueObserver dummy; + scoped_refptr<LayerAnimationController> controller( + LayerAnimationController::Create(0)); + controller->AddValueObserver(&dummy); + + AddOpacityTransitionToController(controller.get(), 1, 0.5f, 1.f, false); + int group_id = controller->GetAnimation(Animation::Opacity)->group(); + + controller->PushAnimationUpdatesTo(controller_impl.get()); + + EXPECT_TRUE(controller_impl->GetAnimation(group_id, Animation::Opacity)); + EXPECT_EQ( + Animation::WaitingForTargetAvailability, + controller_impl->GetAnimation(group_id, Animation::Opacity)->run_state()); + EXPECT_TRUE(controller_impl->GetAnimation(group_id, Animation::Opacity) + ->affects_pending_observers()); + EXPECT_FALSE(controller_impl->GetAnimation(group_id, Animation::Opacity) + ->affects_active_observers()); + + controller_impl->Animate(kInitialTickTime); + controller_impl->UpdateState(true, events.get()); + + // Since the animation hasn't been activated, it should still be Starting + // rather than Running. + EXPECT_EQ( + Animation::Starting, + controller_impl->GetAnimation(group_id, Animation::Opacity)->run_state()); + + // Since the animation hasn't been activated, only the pending observer + // should have been ticked. + EXPECT_EQ(0.5f, pending_dummy_impl.opacity()); + EXPECT_EQ(0.f, dummy_impl.opacity()); + + controller_impl->ActivateAnimations(); + EXPECT_TRUE(controller_impl->GetAnimation(group_id, Animation::Opacity) + ->affects_pending_observers()); + EXPECT_TRUE(controller_impl->GetAnimation(group_id, Animation::Opacity) + ->affects_active_observers()); + + controller_impl->Animate(kInitialTickTime + 1.0); + controller_impl->UpdateState(true, events.get()); + + // Since the animation has been activated, it should have reached the + // Running state and the active observer should start to get ticked. + EXPECT_EQ( + Animation::Running, + controller_impl->GetAnimation(group_id, Animation::Opacity)->run_state()); + EXPECT_EQ(0.5f, pending_dummy_impl.opacity()); + EXPECT_EQ(0.5f, dummy_impl.opacity()); +} + +TEST(LayerAnimationControllerTest, ActivationBetweenAnimateAndUpdateState) { + scoped_ptr<AnimationEventsVector> events( + make_scoped_ptr(new AnimationEventsVector)); + FakeLayerAnimationValueObserver dummy_impl; + FakeInactiveLayerAnimationValueObserver pending_dummy_impl; + scoped_refptr<LayerAnimationController> controller_impl( + LayerAnimationController::Create(0)); + controller_impl->AddValueObserver(&dummy_impl); + controller_impl->AddValueObserver(&pending_dummy_impl); + FakeLayerAnimationValueObserver dummy; + scoped_refptr<LayerAnimationController> controller( + LayerAnimationController::Create(0)); + controller->AddValueObserver(&dummy); + + AddOpacityTransitionToController(controller.get(), 1, 0.5f, 1.f, true); + int group_id = controller->GetAnimation(Animation::Opacity)->group(); + + controller->PushAnimationUpdatesTo(controller_impl.get()); + + EXPECT_TRUE(controller_impl->GetAnimation(group_id, Animation::Opacity)); + EXPECT_EQ( + Animation::WaitingForTargetAvailability, + controller_impl->GetAnimation(group_id, Animation::Opacity)->run_state()); + EXPECT_TRUE(controller_impl->GetAnimation(group_id, Animation::Opacity) + ->affects_pending_observers()); + EXPECT_FALSE(controller_impl->GetAnimation(group_id, Animation::Opacity) + ->affects_active_observers()); + + controller_impl->Animate(kInitialTickTime); + + // Since the animation hasn't been activated, only the pending observer + // should have been ticked. + EXPECT_EQ(0.5f, pending_dummy_impl.opacity()); + EXPECT_EQ(0.f, dummy_impl.opacity()); + + controller_impl->ActivateAnimations(); + EXPECT_TRUE(controller_impl->GetAnimation(group_id, Animation::Opacity) + ->affects_pending_observers()); + EXPECT_TRUE(controller_impl->GetAnimation(group_id, Animation::Opacity) + ->affects_active_observers()); + + controller_impl->UpdateState(true, events.get()); + + // Since the animation has been activated, it should have reached the + // Running state. + EXPECT_EQ( + Animation::Running, + controller_impl->GetAnimation(group_id, Animation::Opacity)->run_state()); + + controller_impl->Animate(kInitialTickTime + 0.5); + + // Both observers should have been ticked. + EXPECT_EQ(0.75f, pending_dummy_impl.opacity()); + EXPECT_EQ(0.75f, dummy_impl.opacity()); +} + +TEST(LayerAnimationControllerTest, PushedDeletedAnimationWaitsForActivation) { + scoped_ptr<AnimationEventsVector> events( + make_scoped_ptr(new AnimationEventsVector)); + FakeLayerAnimationValueObserver dummy_impl; + FakeInactiveLayerAnimationValueObserver pending_dummy_impl; + scoped_refptr<LayerAnimationController> controller_impl( + LayerAnimationController::Create(0)); + controller_impl->AddValueObserver(&dummy_impl); + controller_impl->AddValueObserver(&pending_dummy_impl); + FakeLayerAnimationValueObserver dummy; + scoped_refptr<LayerAnimationController> controller( + LayerAnimationController::Create(0)); + controller->AddValueObserver(&dummy); + + AddOpacityTransitionToController(controller.get(), 1, 0.5f, 1.f, true); + int group_id = controller->GetAnimation(Animation::Opacity)->group(); + + controller->PushAnimationUpdatesTo(controller_impl.get()); + controller_impl->ActivateAnimations(); + controller_impl->Animate(kInitialTickTime); + controller_impl->UpdateState(true, events.get()); + EXPECT_EQ( + Animation::Running, + controller_impl->GetAnimation(group_id, Animation::Opacity)->run_state()); + EXPECT_EQ(0.5f, pending_dummy_impl.opacity()); + EXPECT_EQ(0.5f, dummy_impl.opacity()); + + EXPECT_TRUE(controller_impl->GetAnimation(group_id, Animation::Opacity) + ->affects_pending_observers()); + EXPECT_TRUE(controller_impl->GetAnimation(group_id, Animation::Opacity) + ->affects_active_observers()); + + // Delete the animation on the main-thread controller. + controller->RemoveAnimation( + controller->GetAnimation(Animation::Opacity)->id()); + controller->PushAnimationUpdatesTo(controller_impl.get()); + + // The animation should no longer affect pending observers. + EXPECT_FALSE(controller_impl->GetAnimation(group_id, Animation::Opacity) + ->affects_pending_observers()); + EXPECT_TRUE(controller_impl->GetAnimation(group_id, Animation::Opacity) + ->affects_active_observers()); + + controller_impl->Animate(kInitialTickTime + 0.5); + controller_impl->UpdateState(true, events.get()); + + // Only the active observer should have been ticked. + EXPECT_EQ(0.5f, pending_dummy_impl.opacity()); + EXPECT_EQ(0.75f, dummy_impl.opacity()); + + controller_impl->ActivateAnimations(); + + // Activation should cause the animation to be deleted. + EXPECT_FALSE(controller_impl->has_any_animation()); +} + +// Tests that an animation that affects only active observers won't block +// an animation that affects only pending observers from starting. +TEST(LayerAnimationControllerTest, StartAnimationsAffectingDifferentObservers) { + scoped_ptr<AnimationEventsVector> events( + make_scoped_ptr(new AnimationEventsVector)); + FakeLayerAnimationValueObserver dummy_impl; + FakeInactiveLayerAnimationValueObserver pending_dummy_impl; + scoped_refptr<LayerAnimationController> controller_impl( + LayerAnimationController::Create(0)); + controller_impl->AddValueObserver(&dummy_impl); + controller_impl->AddValueObserver(&pending_dummy_impl); + FakeLayerAnimationValueObserver dummy; + scoped_refptr<LayerAnimationController> controller( + LayerAnimationController::Create(0)); + controller->AddValueObserver(&dummy); + + AddOpacityTransitionToController(controller.get(), 1, 0.f, 1.f, true); + int first_animation_group_id = + controller->GetAnimation(Animation::Opacity)->group(); + + controller->PushAnimationUpdatesTo(controller_impl.get()); + controller_impl->ActivateAnimations(); + controller_impl->Animate(kInitialTickTime); + controller_impl->UpdateState(true, events.get()); + + // Remove the first animation from the main-thread controller, and add a + // new animation affecting the same property. + controller->RemoveAnimation( + controller->GetAnimation(Animation::Opacity)->id()); + AddOpacityTransitionToController(controller.get(), 1, 1.f, 0.5f, true); + int second_animation_group_id = + controller->GetAnimation(Animation::Opacity)->group(); + controller->PushAnimationUpdatesTo(controller_impl.get()); + + // The original animation should only affect active observers, and the new + // animation should only affect pending observers. + EXPECT_FALSE(controller_impl->GetAnimation(first_animation_group_id, + Animation::Opacity) + ->affects_pending_observers()); + EXPECT_TRUE(controller_impl->GetAnimation(first_animation_group_id, + Animation::Opacity) + ->affects_active_observers()); + EXPECT_TRUE(controller_impl->GetAnimation(second_animation_group_id, + Animation::Opacity) + ->affects_pending_observers()); + EXPECT_FALSE(controller_impl->GetAnimation(second_animation_group_id, + Animation::Opacity) + ->affects_active_observers()); + + controller_impl->Animate(kInitialTickTime + 0.5); + controller_impl->UpdateState(true, events.get()); + + // The original animation should still be running, and the new animation + // should be starting. + EXPECT_EQ(Animation::Running, + controller_impl->GetAnimation(first_animation_group_id, + Animation::Opacity)->run_state()); + EXPECT_EQ(Animation::Starting, + controller_impl->GetAnimation(second_animation_group_id, + Animation::Opacity)->run_state()); + + // The active observer should have been ticked by the original animation, + // and the pending observer should have been ticked by the new animation. + EXPECT_EQ(1.f, pending_dummy_impl.opacity()); + EXPECT_EQ(0.5f, dummy_impl.opacity()); + + controller_impl->ActivateAnimations(); + + // The original animation should have been deleted, and the new animation + // should now affect both observers. + EXPECT_FALSE(controller_impl->GetAnimation(first_animation_group_id, + Animation::Opacity)); + EXPECT_TRUE(controller_impl->GetAnimation(second_animation_group_id, + Animation::Opacity) + ->affects_pending_observers()); + EXPECT_TRUE(controller_impl->GetAnimation(second_animation_group_id, + Animation::Opacity) + ->affects_active_observers()); + + controller_impl->Animate(kInitialTickTime + 1.0); + controller_impl->UpdateState(true, events.get()); + + // The new animation should be running, and the active observer should have + // been ticked at the new animation's starting point. + EXPECT_EQ(Animation::Running, + controller_impl->GetAnimation(second_animation_group_id, + Animation::Opacity)->run_state()); + EXPECT_EQ(1.f, pending_dummy_impl.opacity()); + EXPECT_EQ(1.f, dummy_impl.opacity()); +} + } // namespace } // namespace cc diff --git a/cc/trees/layer_tree_host.cc b/cc/trees/layer_tree_host.cc index dcf124e..65d5dd6 100644 --- a/cc/trees/layer_tree_host.cc +++ b/cc/trees/layer_tree_host.cc @@ -387,6 +387,7 @@ void LayerTreeHost::FinishCommitOnImplThread(LayerTreeHostImpl* host_impl) { // If we're not in impl-side painting, the tree is immediately // considered active. sync_tree->DidBecomeActive(); + host_impl->ActivateAnimations(); devtools_instrumentation::DidActivateLayerTree(id_, source_frame_number_); } diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc index 1ec6d33..306fd79 100644 --- a/cc/trees/layer_tree_host_impl.cc +++ b/cc/trees/layer_tree_host_impl.cc @@ -1693,6 +1693,7 @@ void LayerTreeHostImpl::ActivatePendingTree() { active_tree_->DidBecomeActive(); active_tree_->SetRootLayerScrollOffsetDelegate( root_layer_scroll_offset_delegate_); + ActivateAnimations(); client_->OnCanDrawStateChanged(CanDraw()); SetNeedsRedraw(); @@ -2799,6 +2800,20 @@ void LayerTreeHostImpl::UpdateAnimationState(bool start_ready_animations) { } } +void LayerTreeHostImpl::ActivateAnimations() { + if (!settings_.accelerated_animation_enabled || !needs_animate_layers() || + !active_tree_->root_layer()) + return; + + TRACE_EVENT0("cc", "LayerTreeHostImpl::ActivateAnimations"); + AnimationRegistrar::AnimationControllerMap copy = + animation_registrar_->active_animation_controllers(); + for (AnimationRegistrar::AnimationControllerMap::iterator iter = copy.begin(); + iter != copy.end(); + ++iter) + (*iter).second->ActivateAnimations(); +} + base::TimeDelta LayerTreeHostImpl::LowFrequencyAnimationInterval() const { return base::TimeDelta::FromSeconds(1); } diff --git a/cc/trees/layer_tree_host_impl.h b/cc/trees/layer_tree_host_impl.h index aa02278..28eddb8 100644 --- a/cc/trees/layer_tree_host_impl.h +++ b/cc/trees/layer_tree_host_impl.h @@ -173,6 +173,7 @@ class CC_EXPORT LayerTreeHostImpl virtual void CommitComplete(); virtual void Animate(base::TimeTicks monotonic_time); virtual void UpdateAnimationState(bool start_ready_animations); + void ActivateAnimations(); void MainThreadHasStoppedFlinging(); void UpdateBackgroundAnimateTicking(bool should_background_tick); void DidAnimateScrollOffset(); diff --git a/cc/trees/layer_tree_host_unittest_animation.cc b/cc/trees/layer_tree_host_unittest_animation.cc index aeeb4f5..636c7b1 100644 --- a/cc/trees/layer_tree_host_unittest_animation.cc +++ b/cc/trees/layer_tree_host_unittest_animation.cc @@ -1244,5 +1244,84 @@ class LayerTreeHostAnimationTestFrozenAnimationTickTime // Only the non-impl-paint multi-threaded compositor freezes animations. MULTI_THREAD_NOIMPL_TEST_F(LayerTreeHostAnimationTestFrozenAnimationTickTime); +// When animations are simultaneously added to an existing layer and to a new +// layer, they should start at the same time, even when there's already a +// running animation on the existing layer. +class LayerTreeHostAnimationTestAnimationsAddedToNewAndExistingLayers + : public LayerTreeHostAnimationTest { + public: + LayerTreeHostAnimationTestAnimationsAddedToNewAndExistingLayers() + : frame_count_with_pending_tree_(0) {} + + virtual void BeginTest() OVERRIDE { PostSetNeedsCommitToMainThread(); } + + virtual void DidCommit() OVERRIDE { + if (layer_tree_host()->source_frame_number() == 1) { + AddAnimatedTransformToLayer(layer_tree_host()->root_layer(), 4, 1, 1); + } else if (layer_tree_host()->source_frame_number() == 2) { + AddOpacityTransitionToLayer( + layer_tree_host()->root_layer(), 1, 0.f, 0.5f, true); + + scoped_refptr<Layer> layer = Layer::Create(); + layer_tree_host()->root_layer()->AddChild(layer); + layer->set_layer_animation_delegate(this); + layer->SetBounds(gfx::Size(4, 4)); + AddOpacityTransitionToLayer(layer, 1, 0.f, 0.5f, true); + } + } + + virtual void BeginCommitOnThread(LayerTreeHostImpl* host_impl) OVERRIDE { + host_impl->BlockNotifyReadyToActivateForTesting(true); + } + + virtual void CommitCompleteOnThread(LayerTreeHostImpl* host_impl) OVERRIDE { + // For the commit that added animations to new and existing layers, keep + // blocking activation. We want to verify that even with activation blocked, + // the animation on the layer that's already in the active tree won't get a + // head start. + if (!host_impl->settings().impl_side_painting || + host_impl->pending_tree()->source_frame_number() != 2) + host_impl->BlockNotifyReadyToActivateForTesting(false); + } + + virtual void WillBeginImplFrameOnThread(LayerTreeHostImpl* host_impl, + const BeginFrameArgs& args) OVERRIDE { + if (!host_impl->pending_tree() || + host_impl->pending_tree()->source_frame_number() != 2) + return; + + frame_count_with_pending_tree_++; + if (frame_count_with_pending_tree_ == 2) + host_impl->BlockNotifyReadyToActivateForTesting(false); + } + + virtual void UpdateAnimationState(LayerTreeHostImpl* host_impl, + bool has_unfinished_animation) OVERRIDE { + Animation* root_animation = host_impl->active_tree() + ->root_layer() + ->layer_animation_controller() + ->GetAnimation(Animation::Opacity); + if (!root_animation || root_animation->run_state() != Animation::Running) + return; + + Animation* child_animation = host_impl->active_tree() + ->root_layer() + ->children()[0] + ->layer_animation_controller() + ->GetAnimation(Animation::Opacity); + EXPECT_EQ(Animation::Running, child_animation->run_state()); + EXPECT_EQ(root_animation->start_time(), child_animation->start_time()); + EndTest(); + } + + virtual void AfterTest() OVERRIDE {} + + private: + int frame_count_with_pending_tree_; +}; + +SINGLE_AND_MULTI_THREAD_TEST_F( + LayerTreeHostAnimationTestAnimationsAddedToNewAndExistingLayers); + } // namespace } // namespace cc |