CC Animations: Redirect all compositor animation requests to AnimationHost.
1) Use AnimationHost's AnimationRegistrar instance if new system enabled.
2) Use AnimationPlayer's LayerAnimationController instance if new system enabled.
Previous episode: https://codereview.chromium.org/947033002/
Next episode:
https://codereview.chromium.org/1009233002/
In general: The compositor should not need to worry about servicing animations.
A step towards AnimationHost/AnimationPlayer/AnimationTimeline decoupling.
BUG=394777
[email protected]
[email protected]
CQ_INCLUDE_TRYBOTS=tryserver.blink:linux_blink_rel
Review URL: https://codereview.chromium.org/1010663002
Cr-Commit-Position: refs/heads/master@{#337309}
diff --git a/cc/BUILD.gn b/cc/BUILD.gn
index 2d640ea..719fcf9 100644
--- a/cc/BUILD.gn
+++ b/cc/BUILD.gn
@@ -805,6 +805,7 @@
"trees/layer_tree_host_pixeltest_tiles.cc",
"trees/layer_tree_host_unittest.cc",
"trees/layer_tree_host_unittest_animation.cc",
+ "trees/layer_tree_host_unittest_animation_timelines.cc",
"trees/layer_tree_host_unittest_context.cc",
"trees/layer_tree_host_unittest_copyrequest.cc",
"trees/layer_tree_host_unittest_damage.cc",
diff --git a/cc/animation/animation_host.cc b/cc/animation/animation_host.cc
index 99e13e60..489c6f8 100644
--- a/cc/animation/animation_host.cc
+++ b/cc/animation/animation_host.cc
@@ -10,6 +10,7 @@
#include "cc/animation/animation_registrar.h"
#include "cc/animation/animation_timeline.h"
#include "cc/animation/element_animations.h"
+#include "ui/gfx/geometry/box_f.h"
namespace cc {
@@ -202,4 +203,179 @@
: iter->second;
}
+void AnimationHost::SetSupportsScrollAnimations(
+ bool supports_scroll_animations) {
+ animation_registrar_->set_supports_scroll_animations(
+ supports_scroll_animations);
+}
+
+bool AnimationHost::SupportsScrollAnimations() const {
+ return animation_registrar_->supports_scroll_animations();
+}
+
+bool AnimationHost::NeedsAnimateLayers() const {
+ return animation_registrar_->needs_animate_layers();
+}
+
+bool AnimationHost::ActivateAnimations() {
+ return animation_registrar_->ActivateAnimations();
+}
+
+bool AnimationHost::AnimateLayers(base::TimeTicks monotonic_time) {
+ return animation_registrar_->AnimateLayers(monotonic_time);
+}
+
+bool AnimationHost::UpdateAnimationState(bool start_ready_animations,
+ AnimationEventsVector* events) {
+ return animation_registrar_->UpdateAnimationState(start_ready_animations,
+ events);
+}
+
+scoped_ptr<AnimationEventsVector> AnimationHost::CreateEvents() {
+ return animation_registrar_->CreateEvents();
+}
+
+void AnimationHost::SetAnimationEvents(
+ scoped_ptr<AnimationEventsVector> events) {
+ return animation_registrar_->SetAnimationEvents(events.Pass());
+}
+
+bool AnimationHost::ScrollOffsetAnimationWasInterrupted(int layer_id) const {
+ LayerAnimationController* controller = GetControllerForLayerId(layer_id);
+ return controller ? controller->scroll_offset_animation_was_interrupted()
+ : false;
+}
+
+bool AnimationHost::IsAnimatingFilterProperty(int layer_id) const {
+ LayerAnimationController* controller = GetControllerForLayerId(layer_id);
+ return controller ? controller->IsAnimatingProperty(Animation::FILTER)
+ : false;
+}
+
+bool AnimationHost::IsAnimatingOpacityProperty(int layer_id) const {
+ LayerAnimationController* controller = GetControllerForLayerId(layer_id);
+ return controller ? controller->IsAnimatingProperty(Animation::OPACITY)
+ : false;
+}
+
+bool AnimationHost::IsAnimatingTransformProperty(int layer_id) const {
+ LayerAnimationController* controller = GetControllerForLayerId(layer_id);
+ return controller ? controller->IsAnimatingProperty(Animation::TRANSFORM)
+ : false;
+}
+
+bool AnimationHost::HasPotentiallyRunningOpacityAnimation(int layer_id) const {
+ LayerAnimationController* controller = GetControllerForLayerId(layer_id);
+ if (!controller)
+ return false;
+
+ Animation* animation = controller->GetAnimation(Animation::OPACITY);
+ return animation && !animation->is_finished();
+}
+
+bool AnimationHost::HasPotentiallyRunningTransformAnimation(
+ int layer_id) const {
+ LayerAnimationController* controller = GetControllerForLayerId(layer_id);
+ if (!controller)
+ return false;
+
+ Animation* animation = controller->GetAnimation(Animation::TRANSFORM);
+ return animation && !animation->is_finished();
+}
+
+bool AnimationHost::FilterIsAnimatingOnImplOnly(int layer_id) const {
+ LayerAnimationController* controller = GetControllerForLayerId(layer_id);
+ if (!controller)
+ return false;
+
+ Animation* animation = controller->GetAnimation(Animation::FILTER);
+ return animation && animation->is_impl_only();
+}
+
+bool AnimationHost::OpacityIsAnimatingOnImplOnly(int layer_id) const {
+ LayerAnimationController* controller = GetControllerForLayerId(layer_id);
+ if (!controller)
+ return false;
+
+ Animation* animation = controller->GetAnimation(Animation::OPACITY);
+ return animation && animation->is_impl_only();
+}
+
+bool AnimationHost::TransformIsAnimatingOnImplOnly(int layer_id) const {
+ LayerAnimationController* controller = GetControllerForLayerId(layer_id);
+ if (!controller)
+ return false;
+
+ Animation* animation = controller->GetAnimation(Animation::TRANSFORM);
+ return animation && animation->is_impl_only();
+}
+
+bool AnimationHost::HasFilterAnimationThatInflatesBounds(int layer_id) const {
+ LayerAnimationController* controller = GetControllerForLayerId(layer_id);
+ return controller ? controller->HasFilterAnimationThatInflatesBounds()
+ : false;
+}
+
+bool AnimationHost::HasTransformAnimationThatInflatesBounds(
+ int layer_id) const {
+ LayerAnimationController* controller = GetControllerForLayerId(layer_id);
+ return controller ? controller->HasTransformAnimationThatInflatesBounds()
+ : false;
+}
+
+bool AnimationHost::HasAnimationThatInflatesBounds(int layer_id) const {
+ LayerAnimationController* controller = GetControllerForLayerId(layer_id);
+ return controller ? controller->HasAnimationThatInflatesBounds() : false;
+}
+
+bool AnimationHost::FilterAnimationBoundsForBox(int layer_id,
+ const gfx::BoxF& box,
+ gfx::BoxF* bounds) const {
+ LayerAnimationController* controller = GetControllerForLayerId(layer_id);
+ return controller ? controller->FilterAnimationBoundsForBox(box, bounds)
+ : false;
+}
+
+bool AnimationHost::TransformAnimationBoundsForBox(int layer_id,
+ const gfx::BoxF& box,
+ gfx::BoxF* bounds) const {
+ *bounds = gfx::BoxF();
+ LayerAnimationController* controller = GetControllerForLayerId(layer_id);
+ return controller ? controller->TransformAnimationBoundsForBox(box, bounds)
+ : true;
+}
+
+bool AnimationHost::HasOnlyTranslationTransforms(int layer_id) const {
+ LayerAnimationController* controller = GetControllerForLayerId(layer_id);
+ return controller ? controller->HasOnlyTranslationTransforms() : true;
+}
+
+bool AnimationHost::AnimationsPreserveAxisAlignment(int layer_id) const {
+ LayerAnimationController* controller = GetControllerForLayerId(layer_id);
+ return controller ? controller->AnimationsPreserveAxisAlignment() : true;
+}
+
+bool AnimationHost::MaximumTargetScale(int layer_id, float* max_scale) const {
+ *max_scale = 0.f;
+ LayerAnimationController* controller = GetControllerForLayerId(layer_id);
+ return controller ? controller->MaximumTargetScale(max_scale) : true;
+}
+
+bool AnimationHost::AnimationStartScale(int layer_id,
+ float* start_scale) const {
+ *start_scale = 0.f;
+ LayerAnimationController* controller = GetControllerForLayerId(layer_id);
+ return controller ? controller->AnimationStartScale(start_scale) : true;
+}
+
+bool AnimationHost::HasAnyAnimation(int layer_id) const {
+ LayerAnimationController* controller = GetControllerForLayerId(layer_id);
+ return controller ? controller->has_any_animation() : false;
+}
+
+bool AnimationHost::HasActiveAnimation(int layer_id) const {
+ LayerAnimationController* controller = GetControllerForLayerId(layer_id);
+ return controller ? controller->HasActiveAnimation() : false;
+}
+
} // namespace cc
diff --git a/cc/animation/animation_host.h b/cc/animation/animation_host.h
index 5ae46ee..2887ad8 100644
--- a/cc/animation/animation_host.h
+++ b/cc/animation/animation_host.h
@@ -10,6 +10,8 @@
#include "base/containers/scoped_ptr_hash_map.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
+#include "base/time/time.h"
+#include "cc/animation/animation_events.h"
#include "cc/base/cc_export.h"
#include "cc/trees/mutator_host_client.h"
@@ -73,6 +75,51 @@
return animation_registrar_.get();
}
+ void SetSupportsScrollAnimations(bool supports_scroll_animations);
+ bool SupportsScrollAnimations() const;
+ bool NeedsAnimateLayers() const;
+
+ bool ActivateAnimations();
+ bool AnimateLayers(base::TimeTicks monotonic_time);
+ bool UpdateAnimationState(bool start_ready_animations,
+ AnimationEventsVector* events);
+
+ scoped_ptr<AnimationEventsVector> CreateEvents();
+ void SetAnimationEvents(scoped_ptr<AnimationEventsVector> events);
+
+ bool ScrollOffsetAnimationWasInterrupted(int layer_id) const;
+
+ bool IsAnimatingFilterProperty(int layer_id) const;
+ bool IsAnimatingOpacityProperty(int layer_id) const;
+ bool IsAnimatingTransformProperty(int layer_id) const;
+
+ bool HasPotentiallyRunningOpacityAnimation(int layer_id) const;
+ bool HasPotentiallyRunningTransformAnimation(int layer_id) const;
+
+ bool FilterIsAnimatingOnImplOnly(int layer_id) const;
+ bool OpacityIsAnimatingOnImplOnly(int layer_id) const;
+ bool TransformIsAnimatingOnImplOnly(int layer_id) const;
+
+ bool HasFilterAnimationThatInflatesBounds(int layer_id) const;
+ bool HasTransformAnimationThatInflatesBounds(int layer_id) const;
+ bool HasAnimationThatInflatesBounds(int layer_id) const;
+
+ bool FilterAnimationBoundsForBox(int layer_id,
+ const gfx::BoxF& box,
+ gfx::BoxF* bounds) const;
+ bool TransformAnimationBoundsForBox(int layer_id,
+ const gfx::BoxF& box,
+ gfx::BoxF* bounds) const;
+
+ bool HasOnlyTranslationTransforms(int layer_id) const;
+ bool AnimationsPreserveAxisAlignment(int layer_id) const;
+
+ bool MaximumTargetScale(int layer_id, float* max_scale) const;
+ bool AnimationStartScale(int layer_id, float* start_scale) const;
+
+ bool HasAnyAnimation(int layer_id) const;
+ bool HasActiveAnimation(int layer_id) const;
+
private:
explicit AnimationHost(ThreadInstance thread_instance);
diff --git a/cc/cc_tests.gyp b/cc/cc_tests.gyp
index 58c71018..b115e3b 100644
--- a/cc/cc_tests.gyp
+++ b/cc/cc_tests.gyp
@@ -120,6 +120,7 @@
'trees/layer_tree_host_pixeltest_tiles.cc',
'trees/layer_tree_host_unittest.cc',
'trees/layer_tree_host_unittest_animation.cc',
+ 'trees/layer_tree_host_unittest_animation_timelines.cc',
'trees/layer_tree_host_unittest_context.cc',
'trees/layer_tree_host_unittest_copyrequest.cc',
'trees/layer_tree_host_unittest_damage.cc',
diff --git a/cc/layers/layer.cc b/cc/layers/layer.cc
index 6476b28..b9867d6 100644
--- a/cc/layers/layer.cc
+++ b/cc/layers/layer.cc
@@ -156,7 +156,13 @@
if (host)
RegisterForAnimations(host->animation_registrar());
- if (host && layer_animation_controller_->has_any_animation())
+ bool has_any_animation = false;
+ if (layer_animation_controller_)
+ has_any_animation = layer_animation_controller_->has_any_animation();
+ else if (layer_tree_host_)
+ has_any_animation = layer_tree_host_->HasAnyAnimation(this);
+
+ if (host && has_any_animation)
host->SetNeedsCommit();
}
@@ -502,7 +508,11 @@
}
bool Layer::FilterIsAnimating() const {
- return layer_animation_controller_->IsAnimatingProperty(Animation::FILTER);
+ DCHECK(layer_tree_host_);
+ return layer_animation_controller_
+ ? layer_animation_controller_->IsAnimatingProperty(
+ Animation::FILTER)
+ : layer_tree_host_->IsAnimatingFilterProperty(this);
}
void Layer::SetBackgroundFilters(const FilterOperations& filters) {
@@ -522,7 +532,24 @@
}
bool Layer::OpacityIsAnimating() const {
- return layer_animation_controller_->IsAnimatingProperty(Animation::OPACITY);
+ DCHECK(layer_tree_host_);
+ return layer_animation_controller_
+ ? layer_animation_controller_->IsAnimatingProperty(
+ Animation::OPACITY)
+ : layer_tree_host_->IsAnimatingOpacityProperty(this);
+}
+
+bool Layer::HasPotentiallyRunningOpacityAnimation() const {
+ if (layer_animation_controller_) {
+ if (Animation* animation =
+ layer_animation_controller()->GetAnimation(Animation::OPACITY)) {
+ return !animation->is_finished();
+ }
+ return false;
+ } else {
+ DCHECK(layer_tree_host_);
+ return layer_tree_host_->HasPotentiallyRunningOpacityAnimation(this);
+ }
}
bool Layer::OpacityCanAnimateOnImplThread() const {
@@ -706,11 +733,39 @@
}
bool Layer::AnimationsPreserveAxisAlignment() const {
- return layer_animation_controller_->AnimationsPreserveAxisAlignment();
+ DCHECK(layer_tree_host_);
+ return layer_animation_controller_
+ ? layer_animation_controller_->AnimationsPreserveAxisAlignment()
+ : layer_tree_host_->AnimationsPreserveAxisAlignment(this);
}
bool Layer::TransformIsAnimating() const {
- return layer_animation_controller_->IsAnimatingProperty(Animation::TRANSFORM);
+ DCHECK(layer_tree_host_);
+ return layer_animation_controller_
+ ? layer_animation_controller_->IsAnimatingProperty(
+ Animation::TRANSFORM)
+ : layer_tree_host_->IsAnimatingTransformProperty(this);
+}
+
+bool Layer::HasPotentiallyRunningTransformAnimation() const {
+ if (layer_animation_controller_) {
+ if (Animation* animation =
+ layer_animation_controller()->GetAnimation(Animation::TRANSFORM)) {
+ return !animation->is_finished();
+ }
+ return false;
+ } else {
+ DCHECK(layer_tree_host_);
+ return layer_tree_host_->HasPotentiallyRunningTransformAnimation(this);
+ }
+}
+
+bool Layer::ScrollOffsetAnimationWasInterrupted() const {
+ DCHECK(layer_tree_host_);
+ return layer_animation_controller_
+ ? layer_animation_controller_
+ ->scroll_offset_animation_was_interrupted()
+ : layer_tree_host_->ScrollOffsetAnimationWasInterrupted(this);
}
void Layer::SetScrollParent(Layer* parent) {
@@ -1220,7 +1275,7 @@
// the pending tree will clobber any impl-side scrolling occuring on the
// active tree. To do so, avoid scrolling the pending tree along with it
// instead of trying to undo that scrolling later.
- if (layer_animation_controller_->scroll_offset_animation_was_interrupted())
+ if (ScrollOffsetAnimationWasInterrupted())
layer->PushScrollOffsetFromMainThreadAndClobberActiveValue(scroll_offset_);
else
layer->PushScrollOffsetFromMainThread(scroll_offset_);
@@ -1260,8 +1315,9 @@
layer->SetStackingOrderChanged(stacking_order_changed_);
- layer_animation_controller_->PushAnimationUpdatesTo(
- layer->layer_animation_controller());
+ if (layer->layer_animation_controller() && layer_animation_controller_)
+ layer_animation_controller_->PushAnimationUpdatesTo(
+ layer->layer_animation_controller());
if (frame_timing_requests_dirty_) {
layer->SetFrameTimingRequests(frame_timing_requests_);
@@ -1476,6 +1532,7 @@
void Layer::SetLayerAnimationControllerForTest(
scoped_refptr<LayerAnimationController> controller) {
+ DCHECK(layer_animation_controller_);
layer_animation_controller_->RemoveValueObserver(this);
layer_animation_controller_ = controller;
layer_animation_controller_->AddValueObserver(this);
@@ -1483,8 +1540,10 @@
}
bool Layer::HasActiveAnimation() const {
- DCHECK(layer_animation_controller_);
- return layer_animation_controller_->HasActiveAnimation();
+ DCHECK(layer_tree_host_);
+ return layer_animation_controller_
+ ? layer_animation_controller_->HasActiveAnimation()
+ : layer_tree_host_->HasActiveAnimation(this);
}
void Layer::RegisterForAnimations(AnimationRegistrar* registrar) {
diff --git a/cc/layers/layer.h b/cc/layers/layer.h
index 7452ba9..3ff2810c 100644
--- a/cc/layers/layer.h
+++ b/cc/layers/layer.h
@@ -135,6 +135,7 @@
void SetOpacity(float opacity);
float opacity() const { return opacity_; }
bool OpacityIsAnimating() const;
+ bool HasPotentiallyRunningOpacityAnimation() const;
virtual bool OpacityCanAnimateOnImplThread() const;
void SetBlendMode(SkXfermode::Mode blend_mode);
@@ -188,12 +189,15 @@
void SetTransform(const gfx::Transform& transform);
const gfx::Transform& transform() const { return transform_; }
bool TransformIsAnimating() const;
+ bool HasPotentiallyRunningTransformAnimation() const;
bool AnimationsPreserveAxisAlignment() const;
bool transform_is_invertible() const { return transform_is_invertible_; }
void SetTransformOrigin(const gfx::Point3F&);
gfx::Point3F transform_origin() const { return transform_origin_; }
+ bool ScrollOffsetAnimationWasInterrupted() const;
+
void SetScrollParent(Layer* parent);
Layer* scroll_parent() { return scroll_parent_; }
@@ -400,8 +404,7 @@
void PauseAnimation(int animation_id, double time_offset);
void RemoveAnimation(int animation_id);
void RemoveAnimation(int animation_id, Animation::TargetProperty property);
-
- LayerAnimationController* layer_animation_controller() {
+ LayerAnimationController* layer_animation_controller() const {
return layer_animation_controller_.get();
}
void SetLayerAnimationControllerForTest(
diff --git a/cc/layers/layer_impl.cc b/cc/layers/layer_impl.cc
index b1778ea..3b9a7555 100644
--- a/cc/layers/layer_impl.cc
+++ b/cc/layers/layer_impl.cc
@@ -90,13 +90,16 @@
DCHECK_GT(layer_id_, 0);
DCHECK(layer_tree_impl_);
layer_tree_impl_->RegisterLayer(this);
- AnimationRegistrar* registrar = layer_tree_impl_->GetAnimationRegistrar();
- layer_animation_controller_ =
- registrar->GetAnimationControllerForId(layer_id_);
- layer_animation_controller_->AddValueObserver(this);
- if (IsActive()) {
- layer_animation_controller_->set_value_provider(this);
- layer_animation_controller_->set_layer_animation_delegate(this);
+
+ if (!layer_tree_impl_->settings().use_compositor_animation_timelines) {
+ AnimationRegistrar* registrar = layer_tree_impl_->GetAnimationRegistrar();
+ layer_animation_controller_ =
+ registrar->GetAnimationControllerForId(layer_id_);
+ layer_animation_controller_->AddValueObserver(this);
+ if (IsActive()) {
+ layer_animation_controller_->set_value_provider(this);
+ layer_animation_controller_->set_layer_animation_delegate(this);
+ }
}
SetNeedsPushProperties();
}
@@ -104,9 +107,11 @@
LayerImpl::~LayerImpl() {
DCHECK_EQ(DRAW_MODE_NONE, current_draw_mode_);
- layer_animation_controller_->RemoveValueObserver(this);
- layer_animation_controller_->remove_value_provider(this);
- layer_animation_controller_->remove_layer_animation_delegate(this);
+ if (layer_animation_controller_) {
+ layer_animation_controller_->RemoveValueObserver(this);
+ layer_animation_controller_->remove_value_provider(this);
+ layer_animation_controller_->remove_layer_animation_delegate(this);
+ }
if (!copy_requests_.empty() && layer_tree_impl_->IsActiveTree())
layer_tree_impl()->RemoveLayerWithCopyOutputRequest(this);
@@ -1008,10 +1013,16 @@
}
bool LayerImpl::FilterIsAnimating() const {
- return layer_animation_controller_->IsAnimatingProperty(Animation::FILTER);
+ return layer_animation_controller_
+ ? layer_animation_controller_->IsAnimatingProperty(
+ Animation::FILTER)
+ : layer_tree_impl_->IsAnimatingFilterProperty(this);
}
bool LayerImpl::FilterIsAnimatingOnImplOnly() const {
+ if (!layer_animation_controller_)
+ return layer_tree_impl_->FilterIsAnimatingOnImplOnly(this);
+
Animation* filter_animation =
layer_animation_controller_->GetAnimation(Animation::FILTER);
return filter_animation && filter_animation->is_impl_only();
@@ -1051,10 +1062,28 @@
}
bool LayerImpl::OpacityIsAnimating() const {
- return layer_animation_controller_->IsAnimatingProperty(Animation::OPACITY);
+ return layer_animation_controller_
+ ? layer_animation_controller_->IsAnimatingProperty(
+ Animation::OPACITY)
+ : layer_tree_impl_->IsAnimatingOpacityProperty(this);
+}
+
+bool LayerImpl::HasPotentiallyRunningOpacityAnimation() const {
+ if (layer_animation_controller_) {
+ if (Animation* animation =
+ layer_animation_controller()->GetAnimation(Animation::OPACITY)) {
+ return !animation->is_finished();
+ }
+ return false;
+ } else {
+ return layer_tree_impl_->HasPotentiallyRunningOpacityAnimation(this);
+ }
}
bool LayerImpl::OpacityIsAnimatingOnImplOnly() const {
+ if (!layer_animation_controller_)
+ return layer_tree_impl_->OpacityIsAnimatingOnImplOnly(this);
+
Animation* opacity_animation =
layer_animation_controller_->GetAnimation(Animation::OPACITY);
return opacity_animation && opacity_animation->is_impl_only();
@@ -1133,15 +1162,92 @@
}
bool LayerImpl::TransformIsAnimating() const {
- return layer_animation_controller_->IsAnimatingProperty(Animation::TRANSFORM);
+ return layer_animation_controller_
+ ? layer_animation_controller_->IsAnimatingProperty(
+ Animation::TRANSFORM)
+ : layer_tree_impl_->IsAnimatingTransformProperty(this);
+}
+
+bool LayerImpl::HasPotentiallyRunningTransformAnimation() const {
+ if (layer_animation_controller_) {
+ if (Animation* animation =
+ layer_animation_controller()->GetAnimation(Animation::TRANSFORM)) {
+ return !animation->is_finished();
+ }
+ return false;
+ } else {
+ return layer_tree_impl_->HasPotentiallyRunningTransformAnimation(this);
+ }
}
bool LayerImpl::TransformIsAnimatingOnImplOnly() const {
+ if (!layer_animation_controller_)
+ return layer_tree_impl_->TransformIsAnimatingOnImplOnly(this);
+
Animation* transform_animation =
layer_animation_controller_->GetAnimation(Animation::TRANSFORM);
return transform_animation && transform_animation->is_impl_only();
}
+bool LayerImpl::HasOnlyTranslationTransforms() const {
+ if (!layer_animation_controller_)
+ return layer_tree_impl_->HasOnlyTranslationTransforms(this);
+
+ return layer_animation_controller_->HasOnlyTranslationTransforms();
+}
+
+bool LayerImpl::MaximumTargetScale(float* max_scale) const {
+ if (!layer_animation_controller_)
+ return layer_tree_impl_->MaximumTargetScale(this, max_scale);
+
+ return layer_animation_controller_->MaximumTargetScale(max_scale);
+}
+
+bool LayerImpl::AnimationStartScale(float* start_scale) const {
+ if (!layer_animation_controller_)
+ return layer_tree_impl_->AnimationStartScale(this, start_scale);
+
+ return layer_animation_controller_->AnimationStartScale(start_scale);
+}
+
+bool LayerImpl::HasFilterAnimationThatInflatesBounds() const {
+ if (!layer_animation_controller_)
+ return layer_tree_impl_->HasFilterAnimationThatInflatesBounds(this);
+
+ return layer_animation_controller_->HasFilterAnimationThatInflatesBounds();
+}
+
+bool LayerImpl::HasTransformAnimationThatInflatesBounds() const {
+ if (!layer_animation_controller_)
+ return layer_tree_impl_->HasTransformAnimationThatInflatesBounds(this);
+
+ return layer_animation_controller_->HasTransformAnimationThatInflatesBounds();
+}
+
+bool LayerImpl::HasAnimationThatInflatesBounds() const {
+ if (!layer_animation_controller_)
+ return layer_tree_impl_->HasAnimationThatInflatesBounds(this);
+
+ return layer_animation_controller_->HasAnimationThatInflatesBounds();
+}
+
+bool LayerImpl::FilterAnimationBoundsForBox(const gfx::BoxF& box,
+ gfx::BoxF* bounds) const {
+ if (!layer_animation_controller_)
+ return layer_tree_impl_->FilterAnimationBoundsForBox(this, box, bounds);
+
+ return layer_animation_controller_->FilterAnimationBoundsForBox(box, bounds);
+}
+
+bool LayerImpl::TransformAnimationBoundsForBox(const gfx::BoxF& box,
+ gfx::BoxF* bounds) const {
+ if (!layer_animation_controller_)
+ return layer_tree_impl_->TransformAnimationBoundsForBox(this, box, bounds);
+
+ return layer_animation_controller_->TransformAnimationBoundsForBox(box,
+ bounds);
+}
+
void LayerImpl::SetUpdateRect(const gfx::Rect& update_rect) {
update_rect_ = update_rect;
SetNeedsPushProperties();
@@ -1594,7 +1700,9 @@
state->SetBoolean(
"has_animation_bounds",
- layer_animation_controller()->HasAnimationThatInflatesBounds());
+ layer_animation_controller_
+ ? layer_animation_controller_->HasAnimationThatInflatesBounds()
+ : layer_tree_impl_->HasAnimationThatInflatesBounds(this));
gfx::BoxF box;
if (LayerUtils::GetAnimationBounds(*this, &box))
diff --git a/cc/layers/layer_impl.h b/cc/layers/layer_impl.h
index 262dbcb..3635a3ab 100644
--- a/cc/layers/layer_impl.h
+++ b/cc/layers/layer_impl.h
@@ -300,6 +300,7 @@
void SetOpacity(float opacity);
float opacity() const { return opacity_; }
bool OpacityIsAnimating() const;
+ bool HasPotentiallyRunningOpacityAnimation() const;
bool OpacityIsAnimatingOnImplOnly() const;
void SetBlendMode(SkXfermode::Mode);
@@ -535,11 +536,25 @@
void SetTransform(const gfx::Transform& transform);
const gfx::Transform& transform() const { return transform_; }
bool TransformIsAnimating() const;
+ bool HasPotentiallyRunningTransformAnimation() const;
bool TransformIsAnimatingOnImplOnly() const;
+ bool HasOnlyTranslationTransforms() const;
void SetTransformAndInvertibility(const gfx::Transform& transform,
bool transform_is_invertible);
bool transform_is_invertible() const { return transform_is_invertible_; }
+ bool MaximumTargetScale(float* max_scale) const;
+ bool AnimationStartScale(float* start_scale) const;
+
+ bool HasFilterAnimationThatInflatesBounds() const;
+ bool HasTransformAnimationThatInflatesBounds() const;
+ bool HasAnimationThatInflatesBounds() const;
+
+ bool FilterAnimationBoundsForBox(const gfx::BoxF& box,
+ gfx::BoxF* bounds) const;
+ bool TransformAnimationBoundsForBox(const gfx::BoxF& box,
+ gfx::BoxF* bounds) const;
+
// Note this rect is in layer space (not content space).
void SetUpdateRect(const gfx::Rect& update_rect);
gfx::Rect update_rect() const { return update_rect_; }
diff --git a/cc/layers/layer_utils.cc b/cc/layers/layer_utils.cc
index 888174a..42d0e1b 100644
--- a/cc/layers/layer_utils.cc
+++ b/cc/layers/layer_utils.cc
@@ -13,17 +13,15 @@
namespace {
bool HasAnimationThatInflatesBounds(const LayerImpl& layer) {
- return layer.layer_animation_controller()->HasAnimationThatInflatesBounds();
+ return layer.HasAnimationThatInflatesBounds();
}
bool HasFilterAnimationThatInflatesBounds(const LayerImpl& layer) {
- return layer.layer_animation_controller()
- ->HasFilterAnimationThatInflatesBounds();
+ return layer.HasFilterAnimationThatInflatesBounds();
}
bool HasTransformAnimationThatInflatesBounds(const LayerImpl& layer) {
- return layer.layer_animation_controller()
- ->HasTransformAnimationThatInflatesBounds();
+ return layer.HasTransformAnimationThatInflatesBounds();
}
inline bool HasAncestorTransformAnimation(const LayerImpl& layer) {
@@ -112,16 +110,14 @@
// Perform the inflation
if (HasFilterAnimationThatInflatesBounds(*layer)) {
gfx::BoxF inflated;
- if (!layer->layer_animation_controller()->FilterAnimationBoundsForBox(
- box, &inflated))
+ if (!layer->FilterAnimationBoundsForBox(box, &inflated))
return false;
box = inflated;
}
if (HasTransformAnimationThatInflatesBounds(*layer)) {
gfx::BoxF inflated;
- if (!layer->layer_animation_controller()->TransformAnimationBoundsForBox(
- box, &inflated))
+ if (!layer->TransformAnimationBoundsForBox(box, &inflated))
return false;
box = inflated;
}
diff --git a/cc/test/layer_tree_test.cc b/cc/test/layer_tree_test.cc
index d3ee22f..95e2478 100644
--- a/cc/test/layer_tree_test.cc
+++ b/cc/test/layer_tree_test.cc
@@ -9,6 +9,7 @@
#include "base/single_thread_task_runner.h"
#include "base/thread_task_runner_handle.h"
#include "cc/animation/animation.h"
+#include "cc/animation/animation_host.h"
#include "cc/animation/animation_registrar.h"
#include "cc/animation/layer_animation_controller.h"
#include "cc/animation/timing_function.h"
@@ -358,8 +359,11 @@
void UpdateAnimationState(bool start_ready_animations) override {
LayerTreeHostImpl::UpdateAnimationState(start_ready_animations);
bool has_unfinished_animation = false;
+ AnimationRegistrar* registrar =
+ animation_registrar() ? animation_registrar()
+ : animation_host()->animation_registrar();
for (const auto& it :
- animation_registrar()->active_animation_controllers_for_testing()) {
+ registrar->active_animation_controllers_for_testing()) {
if (it.second->HasActiveAnimation()) {
has_unfinished_animation = true;
break;
@@ -596,6 +600,33 @@
1.0));
}
+void LayerTreeTest::PostAddAnimationToMainThreadPlayer(
+ AnimationPlayer* player_to_receive_animation) {
+ main_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&LayerTreeTest::DispatchAddAnimationToPlayer,
+ main_thread_weak_ptr_,
+ base::Unretained(player_to_receive_animation), 0.000004));
+}
+
+void LayerTreeTest::PostAddInstantAnimationToMainThreadPlayer(
+ AnimationPlayer* player_to_receive_animation) {
+ main_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&LayerTreeTest::DispatchAddAnimationToPlayer,
+ main_thread_weak_ptr_,
+ base::Unretained(player_to_receive_animation), 0.0));
+}
+
+void LayerTreeTest::PostAddLongAnimationToMainThreadPlayer(
+ AnimationPlayer* player_to_receive_animation) {
+ main_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&LayerTreeTest::DispatchAddAnimationToPlayer,
+ main_thread_weak_ptr_,
+ base::Unretained(player_to_receive_animation), 1.0));
+}
+
void LayerTreeTest::PostSetDeferCommitsToMainThread(bool defer_commits) {
main_task_runner_->PostTask(
FROM_HERE,
@@ -734,6 +765,17 @@
}
}
+void LayerTreeTest::DispatchAddAnimationToPlayer(
+ AnimationPlayer* player_to_receive_animation,
+ double animation_duration) {
+ DCHECK(!proxy() || proxy()->IsMainThread());
+
+ if (player_to_receive_animation) {
+ AddOpacityTransitionToPlayer(player_to_receive_animation,
+ animation_duration, 0, 0.5, true);
+ }
+}
+
void LayerTreeTest::DispatchSetDeferCommits(bool defer_commits) {
DCHECK(!proxy() || proxy()->IsMainThread());
diff --git a/cc/test/layer_tree_test.h b/cc/test/layer_tree_test.h
index 44e3915c..68d8c9a 100644
--- a/cc/test/layer_tree_test.h
+++ b/cc/test/layer_tree_test.h
@@ -13,6 +13,7 @@
#include "testing/gtest/include/gtest/gtest.h"
namespace cc {
+class AnimationPlayer;
class FakeExternalBeginFrameSource;
class FakeLayerTreeHostClient;
class FakeOutputSurface;
@@ -137,6 +138,12 @@
void PostAddAnimationToMainThread(Layer* layer_to_receive_animation);
void PostAddInstantAnimationToMainThread(Layer* layer_to_receive_animation);
void PostAddLongAnimationToMainThread(Layer* layer_to_receive_animation);
+ void PostAddAnimationToMainThreadPlayer(
+ AnimationPlayer* player_to_receive_animation);
+ void PostAddInstantAnimationToMainThreadPlayer(
+ AnimationPlayer* player_to_receive_animation);
+ void PostAddLongAnimationToMainThreadPlayer(
+ AnimationPlayer* player_to_receive_animation);
void PostSetDeferCommitsToMainThread(bool defer_commits);
void PostSetNeedsCommitToMainThread();
void PostSetNeedsUpdateLayersToMainThread();
@@ -166,6 +173,9 @@
virtual void DispatchAddAnimation(Layer* layer_to_receive_animation,
double animation_duration);
+ virtual void DispatchAddAnimationToPlayer(
+ AnimationPlayer* player_to_receive_animation,
+ double animation_duration);
void DispatchSetDeferCommits(bool defer_commits);
void DispatchSetNeedsCommit();
void DispatchSetNeedsUpdateLayers();
diff --git a/cc/trees/layer_tree_host.cc b/cc/trees/layer_tree_host.cc
index 79e6b24..62d730b5 100644
--- a/cc/trees/layer_tree_host.cc
+++ b/cc/trees/layer_tree_host.cc
@@ -121,15 +121,18 @@
surface_id_namespace_(0u),
next_surface_sequence_(1u) {
DCHECK(task_graph_runner_);
- if (settings_.accelerated_animation_enabled)
- animation_registrar_ = AnimationRegistrar::Create();
+
+ if (settings_.accelerated_animation_enabled) {
+ if (settings_.use_compositor_animation_timelines) {
+ animation_host_ = AnimationHost::Create(ThreadInstance::MAIN);
+ animation_host_->SetMutatorHostClient(this);
+ } else {
+ animation_registrar_ = AnimationRegistrar::Create();
+ }
+ }
+
rendering_stats_instrumentation_->set_record_rendering_stats(
debug_state_.RecordRenderingStats());
-
- if (settings_.use_compositor_animation_timelines) {
- animation_host_ = AnimationHost::Create(ThreadInstance::MAIN);
- animation_host_->SetMutatorHostClient(this);
- }
}
void LayerTreeHost::InitializeThreaded(
@@ -163,8 +166,12 @@
proxy_ = proxy.Pass();
proxy_->Start();
if (settings_.accelerated_animation_enabled) {
- animation_registrar_->set_supports_scroll_animations(
- proxy_->SupportsImplScrolling());
+ if (animation_host_)
+ animation_host_->SetSupportsScrollAnimations(
+ proxy_->SupportsImplScrolling());
+ else
+ animation_registrar_->set_supports_scroll_animations(
+ proxy_->SupportsImplScrolling());
}
}
@@ -513,7 +520,10 @@
void LayerTreeHost::SetAnimationEvents(
scoped_ptr<AnimationEventsVector> events) {
DCHECK(proxy_->IsMainThread());
- animation_registrar_->SetAnimationEvents(events.Pass());
+ if (animation_host_)
+ animation_host_->SetAnimationEvents(events.Pass());
+ else
+ animation_registrar_->SetAnimationEvents(events.Pass());
}
void LayerTreeHost::SetRootLayer(scoped_refptr<Layer> root_layer) {
@@ -921,11 +931,16 @@
return;
AnimationEventsVector events;
- if (animation_registrar_->AnimateLayers(monotonic_time)) {
- animation_registrar_->UpdateAnimationState(true, &events);
- if (!events.empty())
- property_trees_.needs_rebuild = true;
+ if (animation_host_) {
+ if (animation_host_->AnimateLayers(monotonic_time))
+ animation_host_->UpdateAnimationState(true, &events);
+ } else {
+ if (animation_registrar_->AnimateLayers(monotonic_time))
+ animation_registrar_->UpdateAnimationState(true, &events);
}
+
+ if (!events.empty())
+ property_trees_.needs_rebuild = true;
}
UIResourceId LayerTreeHost::CreateUIResource(UIResourceClient* client) {
@@ -1134,4 +1149,61 @@
layer->OnScrollOffsetAnimated(scroll_offset);
}
+bool LayerTreeHost::ScrollOffsetAnimationWasInterrupted(
+ const Layer* layer) const {
+ return animation_host_
+ ? animation_host_->ScrollOffsetAnimationWasInterrupted(layer->id())
+ : false;
+}
+
+bool LayerTreeHost::IsAnimatingFilterProperty(const Layer* layer) const {
+ return animation_host_
+ ? animation_host_->IsAnimatingFilterProperty(layer->id())
+ : false;
+}
+
+bool LayerTreeHost::IsAnimatingOpacityProperty(const Layer* layer) const {
+ return animation_host_
+ ? animation_host_->IsAnimatingOpacityProperty(layer->id())
+ : false;
+}
+
+bool LayerTreeHost::IsAnimatingTransformProperty(const Layer* layer) const {
+ return animation_host_
+ ? animation_host_->IsAnimatingTransformProperty(layer->id())
+ : false;
+}
+
+bool LayerTreeHost::HasPotentiallyRunningOpacityAnimation(
+ const Layer* layer) const {
+ return animation_host_
+ ? animation_host_->HasPotentiallyRunningOpacityAnimation(
+ layer->id())
+ : false;
+}
+
+bool LayerTreeHost::HasPotentiallyRunningTransformAnimation(
+ const Layer* layer) const {
+ return animation_host_
+ ? animation_host_->HasPotentiallyRunningTransformAnimation(
+ layer->id())
+ : false;
+}
+
+bool LayerTreeHost::AnimationsPreserveAxisAlignment(const Layer* layer) const {
+ return animation_host_
+ ? animation_host_->AnimationsPreserveAxisAlignment(layer->id())
+ : true;
+}
+
+bool LayerTreeHost::HasAnyAnimation(const Layer* layer) const {
+ return animation_host_ ? animation_host_->HasAnyAnimation(layer->id())
+ : false;
+}
+
+bool LayerTreeHost::HasActiveAnimation(const Layer* layer) const {
+ return animation_host_ ? animation_host_->HasActiveAnimation(layer->id())
+ : false;
+}
+
} // namespace cc
diff --git a/cc/trees/layer_tree_host.h b/cc/trees/layer_tree_host.h
index d18d7dab..8a6f270b 100644
--- a/cc/trees/layer_tree_host.h
+++ b/cc/trees/layer_tree_host.h
@@ -256,7 +256,6 @@
HeadsUpDisplayLayer* hud_layer() const { return hud_layer_.get(); }
Proxy* proxy() const { return proxy_.get(); }
-
AnimationRegistrar* animation_registrar() const {
return animation_registrar_.get();
}
@@ -341,6 +340,16 @@
LayerTreeType tree_type,
const gfx::ScrollOffset& scroll_offset) override;
+ bool ScrollOffsetAnimationWasInterrupted(const Layer* layer) const;
+ bool IsAnimatingFilterProperty(const Layer* layer) const;
+ bool IsAnimatingOpacityProperty(const Layer* layer) const;
+ bool IsAnimatingTransformProperty(const Layer* layer) const;
+ bool HasPotentiallyRunningOpacityAnimation(const Layer* layer) const;
+ bool HasPotentiallyRunningTransformAnimation(const Layer* layer) const;
+ bool AnimationsPreserveAxisAlignment(const Layer* layer) const;
+ bool HasAnyAnimation(const Layer* layer) const;
+ bool HasActiveAnimation(const Layer* layer) const;
+
protected:
explicit LayerTreeHost(InitParams* params);
void InitializeThreaded(
diff --git a/cc/trees/layer_tree_host_common.cc b/cc/trees/layer_tree_host_common.cc
index 8e80a727..ffd16907d 100644
--- a/cc/trees/layer_tree_host_common.cc
+++ b/cc/trees/layer_tree_host_common.cc
@@ -964,8 +964,7 @@
// scales and translations. We treat all non-translations as potentially
// affecting scale. Animations that include non-translation/scale components
// will cause the computation of MaximumScale below to fail.
- bool layer_is_animating_scale =
- !layer->layer_animation_controller()->HasOnlyTranslationTransforms();
+ bool layer_is_animating_scale = !layer->HasOnlyTranslationTransforms();
if (!layer_is_animating_scale && !ancestor_is_animating_scale) {
*combined_maximum_animation_contents_scale = 0.f;
@@ -1002,13 +1001,11 @@
float layer_maximum_animated_scale = 0.f;
float layer_start_animated_scale = 0.f;
- if (!layer->layer_animation_controller()->MaximumTargetScale(
- &layer_maximum_animated_scale)) {
+ if (!layer->MaximumTargetScale(&layer_maximum_animated_scale)) {
*combined_maximum_animation_contents_scale = 0.f;
return;
}
- if (!layer->layer_animation_controller()->AnimationStartScale(
- &layer_start_animated_scale)) {
+ if (!layer->AnimationStartScale(&layer_start_animated_scale)) {
*combined_starting_animation_contents_scale = 0.f;
return;
}
diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc
index 4533bab..d403004 100644
--- a/cc/trees/layer_tree_host_impl.cc
+++ b/cc/trees/layer_tree_host_impl.cc
@@ -230,7 +230,7 @@
max_memory_needed_bytes_(0),
device_scale_factor_(1.f),
resourceless_software_draw_(false),
- animation_registrar_(AnimationRegistrar::Create()),
+ animation_registrar_(),
rendering_stats_instrumentation_(rendering_stats_instrumentation),
micro_benchmark_controller_(this),
shared_bitmap_manager_(shared_bitmap_manager),
@@ -241,16 +241,22 @@
is_likely_to_require_a_draw_(false),
frame_timing_tracker_(FrameTimingTracker::Create(this)) {
if (settings.use_compositor_animation_timelines) {
- animation_host_ = AnimationHost::Create(ThreadInstance::IMPL);
- animation_host_->SetMutatorHostClient(this);
+ if (settings.accelerated_animation_enabled) {
+ animation_host_ = AnimationHost::Create(ThreadInstance::IMPL);
+ animation_host_->SetMutatorHostClient(this);
+ animation_host_->SetSupportsScrollAnimations(
+ proxy_->SupportsImplScrolling());
+ }
+ } else {
+ animation_registrar_ = AnimationRegistrar::Create();
+ animation_registrar_->set_supports_scroll_animations(
+ proxy_->SupportsImplScrolling());
}
DCHECK(proxy_->IsImplThread());
DCHECK_IMPLIES(settings.use_one_copy, !settings.use_zero_copy);
DCHECK_IMPLIES(settings.use_zero_copy, !settings.use_one_copy);
DidVisibilityChange(this, visible_);
- animation_registrar_->set_supports_scroll_animations(
- proxy_->SupportsImplScrolling());
SetDebugState(settings.initial_debug_state);
@@ -294,6 +300,12 @@
recycle_tree_ = nullptr;
pending_tree_ = nullptr;
active_tree_ = nullptr;
+
+ if (animation_host_) {
+ animation_host_->ClearTimelines();
+ animation_host_->SetMutatorHostClient(nullptr);
+ }
+
DestroyTileManager();
}
@@ -1563,7 +1575,9 @@
if (draw_mode == DRAW_MODE_RESOURCELESS_SOFTWARE) {
bool disable_picture_quad_image_filtering =
- IsActivelyScrolling() || animation_registrar_->needs_animate_layers();
+ IsActivelyScrolling() ||
+ (animation_host_ ? animation_host_->NeedsAnimateLayers()
+ : animation_registrar_->needs_animate_layers());
scoped_ptr<SoftwareRenderer> temp_software_renderer =
SoftwareRenderer::Create(this, &settings_.renderer_settings,
@@ -3103,18 +3117,31 @@
if (!settings_.accelerated_animation_enabled || !active_tree_->root_layer())
return;
- if (animation_registrar_->AnimateLayers(monotonic_time))
- SetNeedsAnimate();
+ if (animation_host_) {
+ if (animation_host_->AnimateLayers(monotonic_time))
+ SetNeedsAnimate();
+ } else {
+ if (animation_registrar_->AnimateLayers(monotonic_time))
+ SetNeedsAnimate();
+ }
}
void LayerTreeHostImpl::UpdateAnimationState(bool start_ready_animations) {
if (!settings_.accelerated_animation_enabled || !active_tree_->root_layer())
return;
- scoped_ptr<AnimationEventsVector> events =
- animation_registrar_->CreateEvents();
- const bool has_active_animations = animation_registrar_->UpdateAnimationState(
- start_ready_animations, events.get());
+ bool has_active_animations = false;
+ scoped_ptr<AnimationEventsVector> events;
+
+ if (animation_host_) {
+ events = animation_host_->CreateEvents();
+ has_active_animations = animation_host_->UpdateAnimationState(
+ start_ready_animations, events.get());
+ } else {
+ events = animation_registrar_->CreateEvents();
+ has_active_animations = animation_registrar_->UpdateAnimationState(
+ start_ready_animations, events.get());
+ }
if (!events->empty())
client_->PostAnimationEventsToMainThreadOnImplThread(events.Pass());
@@ -3127,8 +3154,13 @@
if (!settings_.accelerated_animation_enabled || !active_tree_->root_layer())
return;
- if (animation_registrar_->ActivateAnimations())
- SetNeedsAnimate();
+ if (animation_host_) {
+ if (animation_host_->ActivateAnimations())
+ SetNeedsAnimate();
+ } else {
+ if (animation_registrar_->ActivateAnimations())
+ SetNeedsAnimate();
+ }
}
std::string LayerTreeHostImpl::LayerTreeAsJson() const {
diff --git a/cc/trees/layer_tree_host_unittest_animation_timelines.cc b/cc/trees/layer_tree_host_unittest_animation_timelines.cc
new file mode 100644
index 0000000..c8513744
--- /dev/null
+++ b/cc/trees/layer_tree_host_unittest_animation_timelines.cc
@@ -0,0 +1,745 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cc/trees/layer_tree_host.h"
+
+#include "cc/animation/animation_curve.h"
+#include "cc/animation/animation_host.h"
+#include "cc/animation/animation_id_provider.h"
+#include "cc/animation/animation_player.h"
+#include "cc/animation/animation_timeline.h"
+#include "cc/animation/element_animations.h"
+#include "cc/animation/layer_animation_controller.h"
+#include "cc/animation/scroll_offset_animation_curve.h"
+#include "cc/animation/timing_function.h"
+#include "cc/base/time_util.h"
+#include "cc/layers/layer.h"
+#include "cc/layers/layer_impl.h"
+#include "cc/test/animation_test_common.h"
+#include "cc/test/fake_content_layer_client.h"
+#include "cc/test/fake_picture_layer.h"
+#include "cc/test/layer_tree_test.h"
+#include "cc/trees/layer_tree_impl.h"
+
+namespace cc {
+namespace {
+
+class LayerTreeHostTimelinesTest : public LayerTreeTest {
+ public:
+ LayerTreeHostTimelinesTest()
+ : timeline_id_(AnimationIdProvider::NextTimelineId()),
+ player_id_(AnimationIdProvider::NextPlayerId()),
+ player_child_id_(AnimationIdProvider::NextPlayerId()) {
+ timeline_ = AnimationTimeline::Create(timeline_id_);
+ player_ = AnimationPlayer::Create(player_id_);
+ player_child_ = AnimationPlayer::Create(player_child_id_);
+
+ player_->set_layer_animation_delegate(this);
+ }
+
+ void InitializeSettings(LayerTreeSettings* settings) override {
+ settings->use_compositor_animation_timelines = true;
+ }
+
+ void InitializeLayerSettings(LayerSettings* layer_settings) override {
+ layer_settings->use_compositor_animation_timelines = true;
+ }
+
+ void SetupTree() override { LayerTreeTest::SetupTree(); }
+
+ void AttachPlayersToTimeline() {
+ layer_tree_host()->animation_host()->AddAnimationTimeline(timeline_.get());
+ timeline_->AttachPlayer(player_.get());
+ timeline_->AttachPlayer(player_child_.get());
+ }
+
+ protected:
+ scoped_refptr<AnimationTimeline> timeline_;
+ scoped_refptr<AnimationPlayer> player_;
+ scoped_refptr<AnimationPlayer> player_child_;
+
+ const int timeline_id_;
+ const int player_id_;
+ const int player_child_id_;
+};
+
+// Add a layer animation and confirm that
+// LayerTreeHostImpl::UpdateAnimationState does get called.
+// Evolved frome LayerTreeHostAnimationTestAddAnimation
+class LayerTreeHostTimelinesTestAddAnimation
+ : public LayerTreeHostTimelinesTest {
+ public:
+ LayerTreeHostTimelinesTestAddAnimation()
+ : update_animation_state_was_called_(false) {}
+
+ void BeginTest() override {
+ AttachPlayersToTimeline();
+ player_->AttachLayer(layer_tree_host()->root_layer()->id());
+ PostAddInstantAnimationToMainThreadPlayer(player_.get());
+ }
+
+ void UpdateAnimationState(LayerTreeHostImpl* host_impl,
+ bool has_unfinished_animation) override {
+ EXPECT_FALSE(has_unfinished_animation);
+ update_animation_state_was_called_ = true;
+ }
+
+ void NotifyAnimationStarted(base::TimeTicks monotonic_time,
+ Animation::TargetProperty target_property,
+ int group) override {
+ EXPECT_LT(base::TimeTicks(), monotonic_time);
+
+ LayerAnimationController* controller =
+ player_->element_animations()->layer_animation_controller();
+ Animation* animation = controller->GetAnimation(Animation::OPACITY);
+ if (animation)
+ player_->RemoveAnimation(animation->id());
+
+ EndTest();
+ }
+
+ void AfterTest() override { EXPECT_TRUE(update_animation_state_was_called_); }
+
+ private:
+ bool update_animation_state_was_called_;
+};
+
+SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTimelinesTestAddAnimation);
+
+// Add a layer animation to a layer, but continually fail to draw. Confirm that
+// after a while, we do eventually force a draw.
+// Evolved from LayerTreeHostAnimationTestCheckerboardDoesNotStarveDraws.
+class LayerTreeHostTimelinesTestCheckerboardDoesNotStarveDraws
+ : public LayerTreeHostTimelinesTest {
+ public:
+ LayerTreeHostTimelinesTestCheckerboardDoesNotStarveDraws()
+ : started_animating_(false) {}
+
+ void BeginTest() override {
+ AttachPlayersToTimeline();
+ player_->AttachLayer(layer_tree_host()->root_layer()->id());
+ PostAddAnimationToMainThreadPlayer(player_.get());
+ }
+
+ void AnimateLayers(LayerTreeHostImpl* host_impl,
+ base::TimeTicks monotonic_time) override {
+ started_animating_ = true;
+ }
+
+ void DrawLayersOnThread(LayerTreeHostImpl* host_impl) override {
+ if (started_animating_)
+ EndTest();
+ }
+
+ DrawResult PrepareToDrawOnThread(LayerTreeHostImpl* host_impl,
+ LayerTreeHostImpl::FrameData* frame,
+ DrawResult draw_result) override {
+ return DRAW_ABORTED_CHECKERBOARD_ANIMATIONS;
+ }
+
+ void AfterTest() override {}
+
+ private:
+ bool started_animating_;
+};
+
+// Starvation can only be an issue with the MT compositor.
+MULTI_THREAD_TEST_F(LayerTreeHostTimelinesTestCheckerboardDoesNotStarveDraws);
+
+// Ensures that animations eventually get deleted.
+// Evolved from LayerTreeHostAnimationTestAnimationsGetDeleted.
+class LayerTreeHostTimelinesTestAnimationsGetDeleted
+ : public LayerTreeHostTimelinesTest {
+ public:
+ LayerTreeHostTimelinesTestAnimationsGetDeleted()
+ : started_animating_(false) {}
+
+ void BeginTest() override {
+ AttachPlayersToTimeline();
+ player_->AttachLayer(layer_tree_host()->root_layer()->id());
+ PostAddAnimationToMainThreadPlayer(player_.get());
+ }
+
+ void AnimateLayers(LayerTreeHostImpl* host_impl,
+ base::TimeTicks monotonic_time) override {
+ bool have_animations = !host_impl->animation_host()
+ ->animation_registrar()
+ ->active_animation_controllers_for_testing()
+ .empty();
+ if (!started_animating_ && have_animations) {
+ started_animating_ = true;
+ return;
+ }
+
+ if (started_animating_ && !have_animations)
+ EndTest();
+ }
+
+ void NotifyAnimationFinished(base::TimeTicks monotonic_time,
+ Animation::TargetProperty target_property,
+ int group) override {
+ // Animations on the impl-side controller only get deleted during a commit,
+ // so we need to schedule a commit.
+ layer_tree_host()->SetNeedsCommit();
+ }
+
+ void AfterTest() override {}
+
+ private:
+ bool started_animating_;
+};
+
+SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTimelinesTestAnimationsGetDeleted);
+
+// Ensure that an animation's timing function is respected.
+// Evolved from LayerTreeHostAnimationTestAddAnimationWithTimingFunction.
+class LayerTreeHostTimelinesTestAddAnimationWithTimingFunction
+ : public LayerTreeHostTimelinesTest {
+ public:
+ LayerTreeHostTimelinesTestAddAnimationWithTimingFunction() {}
+
+ void SetupTree() override {
+ LayerTreeHostTimelinesTest::SetupTree();
+ picture_ = FakePictureLayer::Create(layer_settings(), &client_);
+ picture_->SetBounds(gfx::Size(4, 4));
+ layer_tree_host()->root_layer()->AddChild(picture_);
+
+ AttachPlayersToTimeline();
+ player_child_->AttachLayer(picture_->id());
+ }
+
+ void BeginTest() override {
+ PostAddAnimationToMainThreadPlayer(player_child_.get());
+ }
+
+ void AnimateLayers(LayerTreeHostImpl* host_impl,
+ base::TimeTicks monotonic_time) override {
+ scoped_refptr<AnimationTimeline> timeline_impl =
+ host_impl->animation_host()->GetTimelineById(timeline_id_);
+ scoped_refptr<AnimationPlayer> player_child_impl =
+ timeline_impl->GetPlayerById(player_child_id_);
+
+ LayerAnimationController* controller_impl =
+ player_child_impl->element_animations()->layer_animation_controller();
+ if (!controller_impl)
+ return;
+
+ Animation* animation = controller_impl->GetAnimation(Animation::OPACITY);
+ if (!animation)
+ return;
+
+ const FloatAnimationCurve* curve =
+ animation->curve()->ToFloatAnimationCurve();
+ float start_opacity = curve->GetValue(base::TimeDelta());
+ float end_opacity = curve->GetValue(curve->Duration());
+ float linearly_interpolated_opacity =
+ 0.25f * end_opacity + 0.75f * start_opacity;
+ base::TimeDelta time = TimeUtil::Scale(curve->Duration(), 0.25f);
+ // If the linear timing function associated with this animation was not
+ // picked up, then the linearly interpolated opacity would be different
+ // because of the default ease timing function.
+ EXPECT_FLOAT_EQ(linearly_interpolated_opacity, curve->GetValue(time));
+
+ EndTest();
+ }
+
+ void AfterTest() override {}
+
+ FakeContentLayerClient client_;
+ scoped_refptr<FakePictureLayer> picture_;
+};
+
+SINGLE_AND_MULTI_THREAD_TEST_F(
+ LayerTreeHostTimelinesTestAddAnimationWithTimingFunction);
+
+// Ensures that main thread animations have their start times synchronized with
+// impl thread animations.
+// Evolved from LayerTreeHostAnimationTestSynchronizeAnimationStartTimes.
+class LayerTreeHostTimelinesTestSynchronizeAnimationStartTimes
+ : public LayerTreeHostTimelinesTest {
+ public:
+ LayerTreeHostTimelinesTestSynchronizeAnimationStartTimes() {}
+
+ void SetupTree() override {
+ LayerTreeHostTimelinesTest::SetupTree();
+ picture_ = FakePictureLayer::Create(layer_settings(), &client_);
+ picture_->SetBounds(gfx::Size(4, 4));
+
+ layer_tree_host()->root_layer()->AddChild(picture_);
+
+ AttachPlayersToTimeline();
+ player_child_->set_layer_animation_delegate(this);
+ player_child_->AttachLayer(picture_->id());
+ }
+
+ void BeginTest() override {
+ PostAddAnimationToMainThreadPlayer(player_child_.get());
+ }
+
+ void NotifyAnimationStarted(base::TimeTicks monotonic_time,
+ Animation::TargetProperty target_property,
+ int group) override {
+ LayerAnimationController* controller =
+ player_child_->element_animations()->layer_animation_controller();
+ Animation* animation = controller->GetAnimation(Animation::OPACITY);
+ main_start_time_ = animation->start_time();
+ controller->RemoveAnimation(animation->id());
+ EndTest();
+ }
+
+ void UpdateAnimationState(LayerTreeHostImpl* impl_host,
+ bool has_unfinished_animation) override {
+ scoped_refptr<AnimationTimeline> timeline_impl =
+ impl_host->animation_host()->GetTimelineById(timeline_id_);
+ scoped_refptr<AnimationPlayer> player_child_impl =
+ timeline_impl->GetPlayerById(player_child_id_);
+
+ LayerAnimationController* controller =
+ player_child_impl->element_animations()->layer_animation_controller();
+ Animation* animation = controller->GetAnimation(Animation::OPACITY);
+ if (!animation)
+ return;
+
+ impl_start_time_ = animation->start_time();
+ }
+
+ void AfterTest() override {
+ EXPECT_EQ(impl_start_time_, main_start_time_);
+ EXPECT_LT(base::TimeTicks(), impl_start_time_);
+ }
+
+ private:
+ base::TimeTicks main_start_time_;
+ base::TimeTicks impl_start_time_;
+ FakeContentLayerClient client_;
+ scoped_refptr<FakePictureLayer> picture_;
+};
+
+SINGLE_AND_MULTI_THREAD_TEST_F(
+ LayerTreeHostTimelinesTestSynchronizeAnimationStartTimes);
+
+// Ensures that notify animation finished is called.
+// Evolved from LayerTreeHostAnimationTestAnimationFinishedEvents.
+class LayerTreeHostTimelinesTestAnimationFinishedEvents
+ : public LayerTreeHostTimelinesTest {
+ public:
+ LayerTreeHostTimelinesTestAnimationFinishedEvents() {}
+
+ void BeginTest() override {
+ AttachPlayersToTimeline();
+ player_->AttachLayer(layer_tree_host()->root_layer()->id());
+ PostAddInstantAnimationToMainThreadPlayer(player_.get());
+ }
+
+ void NotifyAnimationFinished(base::TimeTicks monotonic_time,
+ Animation::TargetProperty target_property,
+ int group) override {
+ LayerAnimationController* controller =
+ player_->element_animations()->layer_animation_controller();
+ Animation* animation = controller->GetAnimation(Animation::OPACITY);
+ if (animation)
+ controller->RemoveAnimation(animation->id());
+ EndTest();
+ }
+
+ void AfterTest() override {}
+};
+
+SINGLE_AND_MULTI_THREAD_TEST_F(
+ LayerTreeHostTimelinesTestAnimationFinishedEvents);
+
+// Ensures that when opacity is being animated, this value does not cause the
+// subtree to be skipped.
+// Evolved from LayerTreeHostAnimationTestDoNotSkipLayersWithAnimatedOpacity.
+class LayerTreeHostTimelinesTestDoNotSkipLayersWithAnimatedOpacity
+ : public LayerTreeHostTimelinesTest {
+ public:
+ LayerTreeHostTimelinesTestDoNotSkipLayersWithAnimatedOpacity()
+ : update_check_layer_(
+ FakePictureLayer::Create(layer_settings(), &client_)) {}
+
+ void SetupTree() override {
+ update_check_layer_->SetOpacity(0.f);
+ layer_tree_host()->SetRootLayer(update_check_layer_);
+ LayerTreeHostTimelinesTest::SetupTree();
+
+ AttachPlayersToTimeline();
+ player_->AttachLayer(update_check_layer_->id());
+ }
+
+ void BeginTest() override {
+ PostAddAnimationToMainThreadPlayer(player_.get());
+ }
+
+ void DidActivateTreeOnThread(LayerTreeHostImpl* host_impl) override {
+ scoped_refptr<AnimationTimeline> timeline_impl =
+ host_impl->animation_host()->GetTimelineById(timeline_id_);
+ scoped_refptr<AnimationPlayer> player_impl =
+ timeline_impl->GetPlayerById(player_id_);
+
+ LayerAnimationController* controller_impl =
+ player_impl->element_animations()->layer_animation_controller();
+ Animation* animation_impl =
+ controller_impl->GetAnimation(Animation::OPACITY);
+ controller_impl->RemoveAnimation(animation_impl->id());
+ EndTest();
+ }
+
+ void AfterTest() override {
+ // Update() should have been called once, proving that the layer was not
+ // skipped.
+ EXPECT_EQ(1, update_check_layer_->update_count());
+
+ // clear update_check_layer_ so LayerTreeHost dies.
+ update_check_layer_ = NULL;
+ }
+
+ private:
+ FakeContentLayerClient client_;
+ scoped_refptr<FakePictureLayer> update_check_layer_;
+};
+
+SINGLE_AND_MULTI_THREAD_TEST_F(
+ LayerTreeHostTimelinesTestDoNotSkipLayersWithAnimatedOpacity);
+
+// Layers added to tree with existing active animations should have the
+// animation correctly recognized.
+// Evolved from LayerTreeHostAnimationTestLayerAddedWithAnimation.
+class LayerTreeHostTimelinesTestLayerAddedWithAnimation
+ : public LayerTreeHostTimelinesTest {
+ public:
+ LayerTreeHostTimelinesTestLayerAddedWithAnimation() {}
+
+ void BeginTest() override { PostSetNeedsCommitToMainThread(); }
+
+ void DidCommit() override {
+ if (layer_tree_host()->source_frame_number() == 1) {
+ AttachPlayersToTimeline();
+
+ scoped_refptr<Layer> layer = Layer::Create(layer_settings());
+ player_->AttachLayer(layer->id());
+ player_->set_layer_animation_delegate(this);
+
+ // Any valid AnimationCurve will do here.
+ scoped_ptr<AnimationCurve> curve(new FakeFloatAnimationCurve());
+ scoped_ptr<Animation> animation(
+ Animation::Create(curve.Pass(), 1, 1, Animation::OPACITY));
+ player_->AddAnimation(animation.Pass());
+
+ // We add the animation *before* attaching the layer to the tree.
+ layer_tree_host()->root_layer()->AddChild(layer);
+ }
+ }
+
+ void AnimateLayers(LayerTreeHostImpl* impl_host,
+ base::TimeTicks monotonic_time) override {
+ EndTest();
+ }
+
+ void AfterTest() override {}
+};
+
+SINGLE_AND_MULTI_THREAD_TEST_F(
+ LayerTreeHostTimelinesTestLayerAddedWithAnimation);
+
+// Make sure the main thread can still execute animations when CanDraw() is not
+// true.
+// Evolved from LayerTreeHostAnimationTestRunAnimationWhenNotCanDraw
+class LayerTreeHostTimelinesTestRunAnimationWhenNotCanDraw
+ : public LayerTreeHostTimelinesTest {
+ public:
+ LayerTreeHostTimelinesTestRunAnimationWhenNotCanDraw() : started_times_(0) {}
+
+ void SetupTree() override {
+ LayerTreeHostTimelinesTest::SetupTree();
+ picture_ = FakePictureLayer::Create(layer_settings(), &client_);
+ picture_->SetBounds(gfx::Size(4, 4));
+ layer_tree_host()->root_layer()->AddChild(picture_);
+
+ AttachPlayersToTimeline();
+ player_child_->AttachLayer(picture_->id());
+ player_child_->set_layer_animation_delegate(this);
+ }
+
+ void BeginTest() override {
+ layer_tree_host()->SetViewportSize(gfx::Size());
+ PostAddAnimationToMainThreadPlayer(player_child_.get());
+ }
+
+ void NotifyAnimationStarted(base::TimeTicks monotonic_time,
+ Animation::TargetProperty target_property,
+ int group) override {
+ started_times_++;
+ }
+
+ void NotifyAnimationFinished(base::TimeTicks monotonic_time,
+ Animation::TargetProperty target_property,
+ int group) override {
+ EndTest();
+ }
+
+ void AfterTest() override { EXPECT_EQ(1, started_times_); }
+
+ private:
+ int started_times_;
+ FakeContentLayerClient client_;
+ scoped_refptr<FakePictureLayer> picture_;
+};
+
+SINGLE_AND_MULTI_THREAD_TEST_F(
+ LayerTreeHostTimelinesTestRunAnimationWhenNotCanDraw);
+
+// Animations should not be started when frames are being skipped due to
+// checkerboard.
+// Evolved from LayerTreeHostAnimationTestCheckerboardDoesntStartAnimations.
+class LayerTreeHostTimelinesTestCheckerboardDoesntStartAnimations
+ : public LayerTreeHostTimelinesTest {
+ void SetupTree() override {
+ LayerTreeHostTimelinesTest::SetupTree();
+ picture_ = FakePictureLayer::Create(layer_settings(), &client_);
+ picture_->SetBounds(gfx::Size(4, 4));
+ layer_tree_host()->root_layer()->AddChild(picture_);
+
+ AttachPlayersToTimeline();
+ player_child_->AttachLayer(picture_->id());
+ player_child_->set_layer_animation_delegate(this);
+ }
+
+ void InitializeSettings(LayerTreeSettings* settings) override {
+ // Make sure that drawing many times doesn't cause a checkerboarded
+ // animation to start so we avoid flake in this test.
+ settings->timeout_and_draw_when_animation_checkerboards = false;
+ LayerTreeHostTimelinesTest::InitializeSettings(settings);
+ }
+
+ void BeginTest() override {
+ prevented_draw_ = 0;
+ added_animations_ = 0;
+ started_times_ = 0;
+
+ PostSetNeedsCommitToMainThread();
+ }
+
+ DrawResult PrepareToDrawOnThread(LayerTreeHostImpl* host_impl,
+ LayerTreeHostImpl::FrameData* frame_data,
+ DrawResult draw_result) override {
+ if (added_animations_ < 2)
+ return draw_result;
+ if (TestEnded())
+ return draw_result;
+ // Act like there is checkerboard when the second animation wants to draw.
+ ++prevented_draw_;
+ if (prevented_draw_ > 2)
+ EndTest();
+ return DRAW_ABORTED_CHECKERBOARD_ANIMATIONS;
+ }
+
+ void DidCommitAndDrawFrame() override {
+ switch (layer_tree_host()->source_frame_number()) {
+ case 1:
+ // The animation is longer than 1 BeginFrame interval.
+ AddOpacityTransitionToPlayer(player_child_.get(), 0.1, 0.2f, 0.8f,
+ false);
+ added_animations_++;
+ break;
+ case 2:
+ // This second animation will not be drawn so it should not start.
+ AddAnimatedTransformToPlayer(player_child_.get(), 0.1, 5, 5);
+ added_animations_++;
+ break;
+ }
+ }
+
+ void NotifyAnimationStarted(base::TimeTicks monotonic_time,
+ Animation::TargetProperty target_property,
+ int group) override {
+ if (TestEnded())
+ return;
+ started_times_++;
+ }
+
+ void AfterTest() override {
+ // Make sure we tried to draw the second animation but failed.
+ EXPECT_LT(0, prevented_draw_);
+ // The first animation should be started, but the second should not because
+ // of checkerboard.
+ EXPECT_EQ(1, started_times_);
+ }
+
+ int prevented_draw_;
+ int added_animations_;
+ int started_times_;
+ FakeContentLayerClient client_;
+ scoped_refptr<FakePictureLayer> picture_;
+};
+
+MULTI_THREAD_TEST_F(
+ LayerTreeHostTimelinesTestCheckerboardDoesntStartAnimations);
+
+// 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.
+// Evolved from LayerTreeHostAnimationTestAnimationsAddedToNewAndExistingLayers.
+class LayerTreeHostTimelinesTestAnimationsAddedToNewAndExistingLayers
+ : public LayerTreeHostTimelinesTest {
+ public:
+ LayerTreeHostTimelinesTestAnimationsAddedToNewAndExistingLayers()
+ : frame_count_with_pending_tree_(0) {}
+
+ void BeginTest() override {
+ AttachPlayersToTimeline();
+ PostSetNeedsCommitToMainThread();
+ }
+
+ void DidCommit() override {
+ if (layer_tree_host()->source_frame_number() == 1) {
+ player_->AttachLayer(layer_tree_host()->root_layer()->id());
+ AddAnimatedTransformToPlayer(player_.get(), 4, 1, 1);
+ } else if (layer_tree_host()->source_frame_number() == 2) {
+ AddOpacityTransitionToPlayer(player_.get(), 1, 0.f, 0.5f, true);
+
+ scoped_refptr<Layer> layer = Layer::Create(layer_settings());
+ layer_tree_host()->root_layer()->AddChild(layer);
+ layer->SetBounds(gfx::Size(4, 4));
+
+ player_child_->AttachLayer(layer->id());
+ player_child_->set_layer_animation_delegate(this);
+ AddOpacityTransitionToPlayer(player_child_.get(), 1, 0.f, 0.5f, true);
+ }
+ }
+
+ void BeginCommitOnThread(LayerTreeHostImpl* host_impl) override {
+ host_impl->BlockNotifyReadyToActivateForTesting(true);
+ }
+
+ 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->pending_tree()->source_frame_number() != 2) {
+ host_impl->BlockNotifyReadyToActivateForTesting(false);
+ }
+ }
+
+ 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);
+ }
+ }
+
+ void UpdateAnimationState(LayerTreeHostImpl* host_impl,
+ bool has_unfinished_animation) override {
+ scoped_refptr<AnimationTimeline> timeline_impl =
+ host_impl->animation_host()->GetTimelineById(timeline_id_);
+ scoped_refptr<AnimationPlayer> player_impl =
+ timeline_impl->GetPlayerById(player_id_);
+ scoped_refptr<AnimationPlayer> player_child_impl =
+ timeline_impl->GetPlayerById(player_child_id_);
+
+ // wait for tree activation.
+ if (!player_impl->element_animations())
+ return;
+
+ LayerAnimationController* root_controller_impl =
+ player_impl->element_animations()->layer_animation_controller();
+ Animation* root_animation =
+ root_controller_impl->GetAnimation(Animation::OPACITY);
+ if (!root_animation || root_animation->run_state() != Animation::RUNNING)
+ return;
+
+ LayerAnimationController* child_controller_impl =
+ player_child_impl->element_animations()->layer_animation_controller();
+ Animation* child_animation =
+ child_controller_impl->GetAnimation(Animation::OPACITY);
+ EXPECT_EQ(Animation::RUNNING, child_animation->run_state());
+ EXPECT_EQ(root_animation->start_time(), child_animation->start_time());
+ root_controller_impl->AbortAnimations(Animation::OPACITY);
+ root_controller_impl->AbortAnimations(Animation::TRANSFORM);
+ child_controller_impl->AbortAnimations(Animation::OPACITY);
+ EndTest();
+ }
+
+ void AfterTest() override {}
+
+ private:
+ int frame_count_with_pending_tree_;
+};
+
+// This test blocks activation which is not supported for single thread mode.
+MULTI_THREAD_BLOCKNOTIFY_TEST_F(
+ LayerTreeHostTimelinesTestAnimationsAddedToNewAndExistingLayers);
+
+// Evolved from LayerTreeHostAnimationTestAddAnimationAfterAnimating.
+class LayerTreeHostTimelinesTestAddAnimationAfterAnimating
+ : public LayerTreeHostTimelinesTest {
+ public:
+ void SetupTree() override {
+ LayerTreeHostTimelinesTest::SetupTree();
+ content_ = Layer::Create(layer_settings());
+ content_->SetBounds(gfx::Size(4, 4));
+ layer_tree_host()->root_layer()->AddChild(content_);
+
+ AttachPlayersToTimeline();
+
+ player_->AttachLayer(layer_tree_host()->root_layer()->id());
+ player_child_->AttachLayer(content_->id());
+ }
+
+ void BeginTest() override { PostSetNeedsCommitToMainThread(); }
+
+ void DidCommit() override {
+ switch (layer_tree_host()->source_frame_number()) {
+ case 1:
+ // First frame: add an animation to the root layer.
+ AddAnimatedTransformToPlayer(player_.get(), 0.1, 5, 5);
+ break;
+ case 2:
+ // Second frame: add an animation to the content layer. The root layer
+ // animation has caused us to animate already during this frame.
+ AddOpacityTransitionToPlayer(player_child_.get(), 0.1, 5, 5, false);
+ break;
+ }
+ }
+
+ void SwapBuffersOnThread(LayerTreeHostImpl* host_impl, bool result) override {
+ // After both animations have started, verify that they have valid
+ // start times.
+ if (host_impl->active_tree()->source_frame_number() < 2)
+ return;
+ AnimationRegistrar::AnimationControllerMap controllers_copy =
+ host_impl->animation_host()
+ ->animation_registrar()
+ ->active_animation_controllers_for_testing();
+ EXPECT_EQ(2u, controllers_copy.size());
+ for (auto& it : controllers_copy) {
+ int id = it.first;
+ if (id == host_impl->RootLayer()->id()) {
+ Animation* anim = it.second->GetAnimation(Animation::TRANSFORM);
+ EXPECT_GT((anim->start_time() - base::TimeTicks()).InSecondsF(), 0);
+ } else if (id == host_impl->RootLayer()->children()[0]->id()) {
+ Animation* anim = it.second->GetAnimation(Animation::OPACITY);
+ EXPECT_GT((anim->start_time() - base::TimeTicks()).InSecondsF(), 0);
+ }
+ EndTest();
+ }
+ }
+
+ void AfterTest() override {}
+
+ private:
+ scoped_refptr<Layer> content_;
+};
+
+SINGLE_AND_MULTI_THREAD_TEST_F(
+ LayerTreeHostTimelinesTestAddAnimationAfterAnimating);
+
+} // namespace
+} // namespace cc
diff --git a/cc/trees/layer_tree_impl.cc b/cc/trees/layer_tree_impl.cc
index 3fc54411..d4d2dee 100644
--- a/cc/trees/layer_tree_impl.cc
+++ b/cc/trees/layer_tree_impl.cc
@@ -32,6 +32,7 @@
#include "cc/trees/occlusion_tracker.h"
#include "cc/trees/property_tree.h"
#include "cc/trees/property_tree_builder.h"
+#include "ui/gfx/geometry/box_f.h"
#include "ui/gfx/geometry/point_conversions.h"
#include "ui/gfx/geometry/size_conversions.h"
#include "ui/gfx/geometry/vector2d_conversions.h"
@@ -1625,4 +1626,131 @@
return pending_page_scale_animation_.Pass();
}
+bool LayerTreeImpl::IsAnimatingFilterProperty(const LayerImpl* layer) const {
+ return layer_tree_host_impl_->animation_host()
+ ? layer_tree_host_impl_->animation_host()
+ ->IsAnimatingFilterProperty(layer->id())
+ : false;
+}
+
+bool LayerTreeImpl::IsAnimatingOpacityProperty(const LayerImpl* layer) const {
+ return layer_tree_host_impl_->animation_host()
+ ? layer_tree_host_impl_->animation_host()
+ ->IsAnimatingOpacityProperty(layer->id())
+ : false;
+}
+
+bool LayerTreeImpl::IsAnimatingTransformProperty(const LayerImpl* layer) const {
+ return layer_tree_host_impl_->animation_host()
+ ? layer_tree_host_impl_->animation_host()
+ ->IsAnimatingTransformProperty(layer->id())
+ : false;
+}
+
+bool LayerTreeImpl::HasPotentiallyRunningOpacityAnimation(
+ const LayerImpl* layer) const {
+ return layer_tree_host_impl_->animation_host()
+ ? layer_tree_host_impl_->animation_host()
+ ->HasPotentiallyRunningOpacityAnimation(layer->id())
+ : false;
+}
+
+bool LayerTreeImpl::HasPotentiallyRunningTransformAnimation(
+ const LayerImpl* layer) const {
+ return layer_tree_host_impl_->animation_host()
+ ? layer_tree_host_impl_->animation_host()
+ ->HasPotentiallyRunningTransformAnimation(layer->id())
+ : false;
+}
+
+bool LayerTreeImpl::FilterIsAnimatingOnImplOnly(const LayerImpl* layer) const {
+ return layer_tree_host_impl_->animation_host()
+ ? layer_tree_host_impl_->animation_host()
+ ->FilterIsAnimatingOnImplOnly(layer->id())
+ : false;
+}
+
+bool LayerTreeImpl::OpacityIsAnimatingOnImplOnly(const LayerImpl* layer) const {
+ return layer_tree_host_impl_->animation_host()
+ ? layer_tree_host_impl_->animation_host()
+ ->OpacityIsAnimatingOnImplOnly(layer->id())
+ : false;
+}
+
+bool LayerTreeImpl::TransformIsAnimatingOnImplOnly(
+ const LayerImpl* layer) const {
+ return layer_tree_host_impl_->animation_host()
+ ? layer_tree_host_impl_->animation_host()
+ ->TransformIsAnimatingOnImplOnly(layer->id())
+ : false;
+}
+
+bool LayerTreeImpl::HasOnlyTranslationTransforms(const LayerImpl* layer) const {
+ return layer_tree_host_impl_->animation_host()
+ ? layer_tree_host_impl_->animation_host()
+ ->HasOnlyTranslationTransforms(layer->id())
+ : true;
+}
+
+bool LayerTreeImpl::MaximumTargetScale(const LayerImpl* layer,
+ float* max_scale) const {
+ *max_scale = 0.f;
+ return layer_tree_host_impl_->animation_host()
+ ? layer_tree_host_impl_->animation_host()->MaximumTargetScale(
+ layer->id(), max_scale)
+ : true;
+}
+
+bool LayerTreeImpl::AnimationStartScale(const LayerImpl* layer,
+ float* start_scale) const {
+ *start_scale = 0.f;
+ return layer_tree_host_impl_->animation_host()
+ ? layer_tree_host_impl_->animation_host()->AnimationStartScale(
+ layer->id(), start_scale)
+ : true;
+}
+
+bool LayerTreeImpl::HasFilterAnimationThatInflatesBounds(
+ const LayerImpl* layer) const {
+ return layer_tree_host_impl_->animation_host()
+ ? layer_tree_host_impl_->animation_host()
+ ->HasFilterAnimationThatInflatesBounds(layer->id())
+ : false;
+}
+
+bool LayerTreeImpl::HasTransformAnimationThatInflatesBounds(
+ const LayerImpl* layer) const {
+ return layer_tree_host_impl_->animation_host()
+ ? layer_tree_host_impl_->animation_host()
+ ->HasTransformAnimationThatInflatesBounds(layer->id())
+ : false;
+}
+
+bool LayerTreeImpl::HasAnimationThatInflatesBounds(
+ const LayerImpl* layer) const {
+ return layer_tree_host_impl_->animation_host()
+ ? layer_tree_host_impl_->animation_host()
+ ->HasAnimationThatInflatesBounds(layer->id())
+ : false;
+}
+
+bool LayerTreeImpl::FilterAnimationBoundsForBox(const LayerImpl* layer,
+ const gfx::BoxF& box,
+ gfx::BoxF* bounds) const {
+ return layer_tree_host_impl_->animation_host()
+ ? layer_tree_host_impl_->animation_host()
+ ->FilterAnimationBoundsForBox(layer->id(), box, bounds)
+ : false;
+}
+
+bool LayerTreeImpl::TransformAnimationBoundsForBox(const LayerImpl* layer,
+ const gfx::BoxF& box,
+ gfx::BoxF* bounds) const {
+ *bounds = gfx::BoxF();
+ return layer_tree_host_impl_->animation_host()
+ ? layer_tree_host_impl_->animation_host()
+ ->TransformAnimationBoundsForBox(layer->id(), box, bounds)
+ : true;
+}
+
} // namespace cc
diff --git a/cc/trees/layer_tree_impl.h b/cc/trees/layer_tree_impl.h
index 11a59f7..973df977 100644
--- a/cc/trees/layer_tree_impl.h
+++ b/cc/trees/layer_tree_impl.h
@@ -345,6 +345,33 @@
bool IsExternalScrollActive() const;
void DidUpdateScrollOffset(int layer_id);
+ bool IsAnimatingFilterProperty(const LayerImpl* layer) const;
+ bool IsAnimatingOpacityProperty(const LayerImpl* layer) const;
+ bool IsAnimatingTransformProperty(const LayerImpl* layer) const;
+
+ bool HasPotentiallyRunningOpacityAnimation(const LayerImpl* layer) const;
+ bool HasPotentiallyRunningTransformAnimation(const LayerImpl* layer) const;
+
+ bool FilterIsAnimatingOnImplOnly(const LayerImpl* layer) const;
+ bool OpacityIsAnimatingOnImplOnly(const LayerImpl* layer) const;
+ bool TransformIsAnimatingOnImplOnly(const LayerImpl* layer) const;
+
+ bool HasOnlyTranslationTransforms(const LayerImpl* layer) const;
+
+ bool MaximumTargetScale(const LayerImpl* layer, float* max_scale) const;
+ bool AnimationStartScale(const LayerImpl* layer, float* start_scale) const;
+
+ bool HasFilterAnimationThatInflatesBounds(const LayerImpl* layer) const;
+ bool HasTransformAnimationThatInflatesBounds(const LayerImpl* layer) const;
+ bool HasAnimationThatInflatesBounds(const LayerImpl* layer) const;
+
+ bool FilterAnimationBoundsForBox(const LayerImpl* layer,
+ const gfx::BoxF& box,
+ gfx::BoxF* bounds) const;
+ bool TransformAnimationBoundsForBox(const LayerImpl* layer,
+ const gfx::BoxF& box,
+ gfx::BoxF* bounds) const;
+
protected:
explicit LayerTreeImpl(
LayerTreeHostImpl* layer_tree_host_impl,
diff --git a/cc/trees/property_tree_builder.cc b/cc/trees/property_tree_builder.cc
index 1a63331..28ad43f 100644
--- a/cc/trees/property_tree_builder.cc
+++ b/cc/trees/property_tree_builder.cc
@@ -63,16 +63,6 @@
}
template <typename LayerType>
-static bool HasPotentiallyRunningAnimation(LayerType* layer,
- Animation::TargetProperty property) {
- if (Animation* animation =
- layer->layer_animation_controller()->GetAnimation(property)) {
- return !animation->is_finished();
- }
- return false;
-}
-
-template <typename LayerType>
static bool RequiresClipNode(LayerType* layer,
const DataForRecursion<LayerType>& data,
int parent_transform_id,
@@ -174,11 +164,8 @@
!layer->transform().IsIdentityOr2DTranslation();
const bool has_potentially_animated_transform =
- HasPotentiallyRunningAnimation(layer, Animation::TRANSFORM);
-
- const bool has_animated_transform =
- layer->layer_animation_controller()->IsAnimatingProperty(
- Animation::TRANSFORM);
+ layer->HasPotentiallyRunningTransformAnimation();
+ const bool has_animated_transform = layer->TransformIsAnimating();
const bool has_surface = !!layer->render_surface();
@@ -343,12 +330,12 @@
}
bool IsAnimatingOpacity(Layer* layer) {
- return HasPotentiallyRunningAnimation(layer, Animation::OPACITY) ||
+ return layer->HasPotentiallyRunningOpacityAnimation() ||
layer->OpacityCanAnimateOnImplThread();
}
bool IsAnimatingOpacity(LayerImpl* layer) {
- return HasPotentiallyRunningAnimation(layer, Animation::OPACITY);
+ return layer->HasPotentiallyRunningOpacityAnimation();
}
template <typename LayerType>