#include <stdint.h>
#include <climits>
#include <memory>
#include "base/functional/bind.h"
#include "base/metrics/statistics_recorder.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "cc/animation/animation.h"
#include "cc/animation/animation_host.h"
#include "cc/animation/animation_id_provider.h"
#include "cc/animation/animation_timeline.h"
#include "cc/animation/element_animations.h"
#include "cc/animation/keyframe_effect.h"
#include "cc/animation/scroll_offset_animation_curve.h"
#include "cc/animation/scroll_offset_animation_curve_factory.h"
#include "cc/animation/scroll_offset_animations.h"
#include "cc/base/completion_event.h"
#include "cc/base/features.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/effect_node.h"
#include "cc/trees/layer_tree_host.h"
#include "cc/trees/layer_tree_impl.h"
#include "cc/trees/target_property.h"
#include "cc/trees/transform_node.h"
#include "components/viz/common/quads/compositor_frame.h"
#include "ui/gfx/animation/keyframe/animation_curve.h"
#include "ui/gfx/animation/keyframe/timing_function.h"
#include "ui/gfx/geometry/test/geometry_util.h"
#include "ui/gfx/geometry/transform_operations.h"
namespace cc {
namespace {
#define SAMPLE(curve, time) \
curve->GetTransformedValue(time, \
time < base::TimeDelta() \
? gfx::TimingFunction::LimitDirection::LEFT \
: gfx::TimingFunction::LimitDirection::RIGHT)
class LayerTreeHostAnimationTest : public LayerTreeTest {
public:
LayerTreeHostAnimationTest()
: timeline_id_(AnimationIdProvider::NextTimelineId()),
animation_id_(AnimationIdProvider::NextAnimationId()),
animation_child_id_(AnimationIdProvider::NextAnimationId()) {
timeline_ = AnimationTimeline::Create(timeline_id_);
animation_ = Animation::Create(animation_id_);
animation_child_ = Animation::Create(animation_child_id_);
animation_->set_animation_delegate(this);
}
void AttachAnimationsToTimeline() {
animation_host()->AddAnimationTimeline(timeline_.get());
layer_tree_host()->SetElementIdsForTesting();
timeline_->AttachAnimation(animation_.get());
timeline_->AttachAnimation(animation_child_.get());
}
void DetachAnimationsFromTimeline() {
if (animation_)
timeline_->DetachAnimation(animation_.get());
if (animation_child_)
timeline_->DetachAnimation(animation_child_.get());
animation_host()->RemoveAnimationTimeline(timeline_.get());
}
void GetImplTimelineAndAnimationByID(const LayerTreeHostImpl& host_impl) {
AnimationHost* animation_host_impl = GetImplAnimationHost(&host_impl);
timeline_impl_ = animation_host_impl->GetTimelineById(timeline_id_);
EXPECT_TRUE(timeline_impl_);
animation_impl_ = timeline_impl_->GetAnimationById(animation_id_);
EXPECT_TRUE(animation_impl_);
animation_child_impl_ =
timeline_impl_->GetAnimationById(animation_child_id_);
EXPECT_TRUE(animation_child_impl_);
}
void AfterTest() override {
DetachAnimationsFromTimeline();
LayerTreeTest::AfterTest();
}
AnimationHost* GetImplAnimationHost(
const LayerTreeHostImpl* host_impl) const {
return static_cast<AnimationHost*>(host_impl->mutator_host());
}
protected:
scoped_refptr<AnimationTimeline> timeline_;
scoped_refptr<Animation> animation_;
scoped_refptr<Animation> animation_child_;
scoped_refptr<AnimationTimeline> timeline_impl_;
scoped_refptr<Animation> animation_impl_;
scoped_refptr<Animation> animation_child_impl_;
const int timeline_id_;
const int animation_id_;
const int animation_child_id_;
};
class LayerTreeHostAnimationTestSetNeedsAnimateShouldNotSetCommitRequested
: public LayerTreeHostAnimationTest {
public:
LayerTreeHostAnimationTestSetNeedsAnimateShouldNotSetCommitRequested()
: num_commits_(0) {}
void BeginTest() override { PostSetNeedsCommitToMainThread(); }
void BeginMainFrame(const viz::BeginFrameArgs& args) override {
if (num_commits_ != 1)
return;
layer_tree_host()->SetNeedsAnimate();
}
void DidCommit() override {
if (!num_commits_) {
EXPECT_FALSE(layer_tree_host()->CommitRequested());
layer_tree_host()->SetNeedsAnimate();
EXPECT_FALSE(layer_tree_host()->CommitRequested());
}
EXPECT_FALSE(layer_tree_host()->CommitRequested());
EndTest();
num_commits_++;
}
private:
int num_commits_;
};
MULTI_THREAD_TEST_F(
LayerTreeHostAnimationTestSetNeedsAnimateShouldNotSetCommitRequested);
class LayerTreeHostAnimationTestSetNeedsAnimateInsideAnimationCallback
: public LayerTreeHostAnimationTest {
public:
LayerTreeHostAnimationTestSetNeedsAnimateInsideAnimationCallback()
: num_begin_frames_(0) {}
void BeginTest() override { PostSetNeedsCommitToMainThread(); }
void BeginMainFrame(const viz::BeginFrameArgs& args) override {
if (!num_begin_frames_) {
layer_tree_host()->SetNeedsAnimate();
num_begin_frames_++;
return;
}
EndTest();
}
private:
int num_begin_frames_;
};
SINGLE_AND_MULTI_THREAD_TEST_F(
LayerTreeHostAnimationTestSetNeedsAnimateInsideAnimationCallback);
class LayerTreeHostAnimationTestAddKeyframeModel
: public LayerTreeHostAnimationTest {
public:
LayerTreeHostAnimationTestAddKeyframeModel()
: update_animation_state_was_called_(false) {}
void BeginTest() override {
AttachAnimationsToTimeline();
animation_->AttachElement(layer_tree_host()->root_layer()->element_id());
PostAddOpacityAnimationToMainThreadInstantly(animation_.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,
int target_property,
int group) override {
EXPECT_LT(base::TimeTicks(), monotonic_time);
gfx::KeyframeModel* keyframe_model =
animation_->GetKeyframeModel(TargetProperty::OPACITY);
if (keyframe_model)
animation_->RemoveKeyframeModel(keyframe_model->id());
EndTest();
}
void AfterTest() override {
EXPECT_TRUE(update_animation_state_was_called_);
LayerTreeHostAnimationTest::AfterTest();
}
private:
bool update_animation_state_was_called_;
};
SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostAnimationTestAddKeyframeModel);
class LayerTreeHostAnimationTestNoDamageAnimation
: public LayerTreeHostAnimationTest {
void InitializeSettings(LayerTreeSettings* settings) override {
settings->using_synchronous_renderer_compositor = true;
settings->enable_early_damage_check = true;
}
public:
LayerTreeHostAnimationTestNoDamageAnimation()
: started_animating_(false), finished_animating_(false) {}
void BeginTest() override {
AttachAnimationsToTimeline();
animation_->AttachElement(layer_tree_host()->root_layer()->element_id());
PostAddNoDamageAnimationToMainThread(animation_.get());
PostAddOpacityAnimationToMainThread(animation_.get());
}
void AnimateLayers(LayerTreeHostImpl* host_impl,
base::TimeTicks monotonic_time) override {
base::AutoLock auto_lock(lock_);
started_animating_ = true;
}
void DrawLayersOnThread(LayerTreeHostImpl* host_impl) override {
base::AutoLock auto_lock(lock_);
if (started_animating_ && finished_animating_)
EndTest();
}
void NotifyAnimationFinished(base::TimeTicks monotonic_time,
int target_property,
int group) override {
base::AutoLock auto_lock(lock_);
finished_animating_ = true;
}
private:
bool started_animating_;
bool finished_animating_;
base::Lock lock_;
};
MULTI_THREAD_TEST_F(LayerTreeHostAnimationTestNoDamageAnimation);
class LayerTreeHostAnimationTestCheckerboardDoesNotStarveDraws
: public LayerTreeHostAnimationTest {
public:
LayerTreeHostAnimationTestCheckerboardDoesNotStarveDraws()
: started_animating_(false) {}
void BeginTest() override {
AttachAnimationsToTimeline();
animation_->AttachElement(layer_tree_host()->root_layer()->element_id());
PostAddOpacityAnimationToMainThread(animation_.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,
FrameData* frame,
DrawResult draw_result) override {
return DrawResult::kAbortedCheckerboardAnimations;
}
private:
bool started_animating_;
};
MULTI_THREAD_TEST_F(LayerTreeHostAnimationTestCheckerboardDoesNotStarveDraws);
class LayerTreeHostAnimationTestAnimationsGetDeleted
: public LayerTreeHostAnimationTest {
public:
LayerTreeHostAnimationTestAnimationsGetDeleted()
: started_animating_(false) {}
void BeginTest() override {
AttachAnimationsToTimeline();
animation_->AttachElement(layer_tree_host()->root_layer()->element_id());
PostAddOpacityAnimationToMainThread(animation_.get());
}
void AnimateLayers(LayerTreeHostImpl* host_impl,
base::TimeTicks monotonic_time) override {
bool have_animations = !GetImplAnimationHost(host_impl)
->ticking_animations_for_testing()
.empty();
if (!started_animating_ && have_animations) {
started_animating_ = true;
return;
}
if (started_animating_ && !have_animations)
EndTest();
}
void NotifyAnimationFinished(base::TimeTicks monotonic_time,
int target_property,
int group) override {
layer_tree_host()->SetNeedsCommit();
}
private:
bool started_animating_;
};
SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostAnimationTestAnimationsGetDeleted);
class LayerTreeHostAnimationTestAddKeyframeModelWithTimingFunction
: public LayerTreeHostAnimationTest {
public:
void SetupTree() override {
LayerTreeHostAnimationTest::SetupTree();
picture_ = FakePictureLayer::Create(&client_);
picture_->SetBounds(gfx::Size(4, 4));
client_.set_bounds(picture_->bounds());
layer_tree_host()->root_layer()->AddChild(picture_);
AttachAnimationsToTimeline();
animation_child_->AttachElement(picture_->element_id());
}
void BeginTest() override {
PostAddOpacityAnimationToMainThread(animation_child_.get());
}
void AnimateLayers(LayerTreeHostImpl* host_impl,
base::TimeTicks monotonic_time) override {
if (!host_impl->active_tree()->root_layer())
return;
if (host_impl->active_tree()->source_frame_number() != 0)
return;
if (!first_animation_frame_)
return;
first_animation_frame_ = false;
scoped_refptr<AnimationTimeline> timeline_impl =
GetImplAnimationHost(host_impl)->GetTimelineById(timeline_id_);
scoped_refptr<Animation> animation_child_impl =
timeline_impl->GetAnimationById(animation_child_id_);
KeyframeModel* keyframe_model =
animation_child_impl->GetKeyframeModel(TargetProperty::OPACITY);
const gfx::FloatAnimationCurve* curve =
gfx::FloatAnimationCurve::ToFloatAnimationCurve(
keyframe_model->curve());
float start_opacity = SAMPLE(curve, base::TimeDelta());
float end_opacity = SAMPLE(curve, curve->Duration());
float linearly_interpolated_opacity =
0.25f * end_opacity + 0.75f * start_opacity;
base::TimeDelta time = curve->Duration() * 0.25f;
EXPECT_FLOAT_EQ(linearly_interpolated_opacity, SAMPLE(curve, time));
EndTest();
}
FakeContentLayerClient client_;
scoped_refptr<FakePictureLayer> picture_;
bool first_animation_frame_ = true;
};
SINGLE_AND_MULTI_THREAD_TEST_F(
LayerTreeHostAnimationTestAddKeyframeModelWithTimingFunction);
class LayerTreeHostAnimationTestSynchronizeAnimationStartTimes
: public LayerTreeHostAnimationTest {
public:
void SetupTree() override {
LayerTreeHostAnimationTest::SetupTree();
picture_ = FakePictureLayer::Create(&client_);
picture_->SetBounds(gfx::Size(4, 4));
client_.set_bounds(picture_->bounds());
layer_tree_host()->root_layer()->AddChild(picture_);
AttachAnimationsToTimeline();
animation_child_->set_animation_delegate(this);
animation_child_->AttachElement(picture_->element_id());
}
void BeginTest() override {
PostAddOpacityAnimationToMainThread(animation_child_.get());
}
void NotifyAnimationStarted(base::TimeTicks monotonic_time,
int target_property,
int group) override {
KeyframeModel* keyframe_model =
animation_child_->GetKeyframeModel(TargetProperty::OPACITY);
main_start_time_ = keyframe_model->start_time();
animation_child_->RemoveKeyframeModel(keyframe_model->id());
EndTest();
}
void UpdateAnimationState(LayerTreeHostImpl* impl_host,
bool has_unfinished_animation) override {
scoped_refptr<const AnimationTimeline> timeline_impl =
GetImplAnimationHost(impl_host)->GetTimelineById(timeline_id_);
scoped_refptr<Animation> animation_child_impl =
timeline_impl->GetAnimationById(animation_child_id_);
KeyframeModel* keyframe_model =
animation_child_impl->GetKeyframeModel(TargetProperty::OPACITY);
if (!keyframe_model)
return;
impl_start_time_ = keyframe_model->start_time();
}
void AfterTest() override {
EXPECT_EQ(impl_start_time_, main_start_time_);
EXPECT_LT(base::TimeTicks(), impl_start_time_);
LayerTreeHostAnimationTest::AfterTest();
}
private:
base::TimeTicks main_start_time_;
base::TimeTicks impl_start_time_;
FakeContentLayerClient client_;
scoped_refptr<FakePictureLayer> picture_;
};
SINGLE_AND_MULTI_THREAD_TEST_F(
LayerTreeHostAnimationTestSynchronizeAnimationStartTimes);
class LayerTreeHostAnimationTestAnimationFinishedEvents
: public LayerTreeHostAnimationTest {
public:
void BeginTest() override {
AttachAnimationsToTimeline();
animation_->AttachElement(layer_tree_host()->root_layer()->element_id());
PostAddOpacityAnimationToMainThreadInstantly(animation_.get());
}
void NotifyAnimationFinished(base::TimeTicks monotonic_time,
int target_property,
int group) override {
KeyframeModel* keyframe_model =
animation_->GetKeyframeModel(TargetProperty::OPACITY);
if (keyframe_model)
animation_->RemoveKeyframeModel(keyframe_model->id());
EndTest();
}
};
SINGLE_AND_MULTI_THREAD_TEST_F(
LayerTreeHostAnimationTestAnimationFinishedEvents);
class LayerTreeHostAnimationTestDoNotSkipLayersWithAnimatedOpacity
: public LayerTreeHostAnimationTest {
public:
LayerTreeHostAnimationTestDoNotSkipLayersWithAnimatedOpacity()
: update_check_layer_() {}
void SetupTree() override {
update_check_layer_ = FakePictureLayer::Create(&client_);
update_check_layer_->SetOpacity(0.f);
layer_tree_host()->SetRootLayer(update_check_layer_);
client_.set_bounds(update_check_layer_->bounds());
LayerTreeHostAnimationTest::SetupTree();
AttachAnimationsToTimeline();
animation_->AttachElement(update_check_layer_->element_id());
}
void BeginTest() override {
PostAddOpacityAnimationToMainThread(animation_.get());
}
void DidActivateTreeOnThread(LayerTreeHostImpl* host_impl) override {
scoped_refptr<const AnimationTimeline> timeline_impl =
GetImplAnimationHost(host_impl)->GetTimelineById(timeline_id_);
scoped_refptr<Animation> animation_impl =
timeline_impl->GetAnimationById(animation_id_);
KeyframeModel* keyframe_model_impl =
animation_impl->GetKeyframeModel(TargetProperty::OPACITY);
animation_impl->RemoveKeyframeModel(keyframe_model_impl->id());
EndTest();
}
void AfterTest() override {
EXPECT_EQ(1, update_check_layer_->update_count());
update_check_layer_ = nullptr;
LayerTreeHostAnimationTest::AfterTest();
}
private:
FakeContentLayerClient client_;
scoped_refptr<FakePictureLayer> update_check_layer_;
};
SINGLE_AND_MULTI_THREAD_TEST_F(
LayerTreeHostAnimationTestDoNotSkipLayersWithAnimatedOpacity);
class LayerTreeHostAnimationTestLayerAddedWithAnimation
: public LayerTreeHostAnimationTest {
public:
void BeginTest() override { PostSetNeedsCommitToMainThread(); }
void DidCommit() override {
if (layer_tree_host()->SourceFrameNumber() == 1) {
AttachAnimationsToTimeline();
scoped_refptr<Layer> layer = Layer::Create();
layer->SetElementId(ElementId(42));
animation_->AttachElement(layer->element_id());
animation_->set_animation_delegate(this);
auto curve = std::make_unique<FakeFloatAnimationCurve>();
std::unique_ptr<KeyframeModel> keyframe_model(KeyframeModel::Create(
std::move(curve), 1, 1,
KeyframeModel::TargetPropertyId(TargetProperty::OPACITY)));
animation_->AddKeyframeModel(std::move(keyframe_model));
layer_tree_host()->root_layer()->AddChild(layer);
}
}
void AnimateLayers(LayerTreeHostImpl* impl_host,
base::TimeTicks monotonic_time) override {
EndTest();
}
};
SINGLE_AND_MULTI_THREAD_TEST_F(
LayerTreeHostAnimationTestLayerAddedWithAnimation);
class LayerTreeHostAnimationTestCancelAnimateCommit
: public LayerTreeHostAnimationTest {
public:
LayerTreeHostAnimationTestCancelAnimateCommit()
: num_begin_frames_(0), num_commit_calls_(0), num_draw_calls_(0) {}
void BeginTest() override { PostSetNeedsCommitToMainThread(); }
void BeginMainFrame(const viz::BeginFrameArgs& args) override {
num_begin_frames_++;
if (layer_tree_host()->SourceFrameNumber() == 1) {
EndTest();
return;
}
layer_tree_host()->SetNeedsAnimate();
}
void CommitCompleteOnThread(LayerTreeHostImpl* impl) override {
num_commit_calls_++;
if (impl->active_tree()->source_frame_number() > 1)
FAIL() << "Commit should have been canceled.";
}
void DrawLayersOnThread(LayerTreeHostImpl* impl) override {
num_draw_calls_++;
if (impl->active_tree()->source_frame_number() > 1)
FAIL() << "Draw should have been canceled.";
}
void AfterTest() override {
EXPECT_EQ(2, num_begin_frames_);
EXPECT_EQ(1, num_commit_calls_);
EXPECT_EQ(1, num_draw_calls_);
LayerTreeHostAnimationTest::AfterTest();
}
private:
int num_begin_frames_;
int num_commit_calls_;
int num_draw_calls_;
};
MULTI_THREAD_TEST_F(LayerTreeHostAnimationTestCancelAnimateCommit);
class LayerTreeHostAnimationTestForceRedraw
: public LayerTreeHostAnimationTest {
public:
LayerTreeHostAnimationTestForceRedraw()
: num_animate_(0), num_draw_layers_(0) {}
void BeginTest() override { PostSetNeedsCommitToMainThread(); }
void BeginMainFrame(const viz::BeginFrameArgs& args) override {
if (++num_animate_ < 2)
layer_tree_host()->SetNeedsAnimate();
}
void UpdateLayerTreeHost() override {
layer_tree_host()->SetNeedsCommitWithForcedRedraw();
}
void DrawLayersOnThread(LayerTreeHostImpl* impl) override {
if (++num_draw_layers_ == 2)
EndTest();
}
void AfterTest() override {
EXPECT_EQ(2, num_draw_layers_);
EXPECT_EQ(2, num_animate_);
LayerTreeHostAnimationTest::AfterTest();
}
private:
int num_animate_;
int num_draw_layers_;
};
MULTI_THREAD_TEST_F(LayerTreeHostAnimationTestForceRedraw);
class LayerTreeHostAnimationTestAnimateAfterSetNeedsCommit
: public LayerTreeHostAnimationTest {
public:
LayerTreeHostAnimationTestAnimateAfterSetNeedsCommit()
: num_animate_(0), num_draw_layers_(0) {}
void BeginTest() override { PostSetNeedsCommitToMainThread(); }
void BeginMainFrame(const viz::BeginFrameArgs& args) override {
if (++num_animate_ <= 2) {
layer_tree_host()->SetNeedsCommit();
layer_tree_host()->SetNeedsAnimate();
}
}
void DrawLayersOnThread(LayerTreeHostImpl* impl) override {
if (++num_draw_layers_ == 2)
EndTest();
}
void AfterTest() override {
EXPECT_EQ(2, num_draw_layers_);
EXPECT_GE(num_animate_, 2);
LayerTreeHostAnimationTest::AfterTest();
}
private:
int num_animate_;
int num_draw_layers_;
};
MULTI_THREAD_TEST_F(LayerTreeHostAnimationTestAnimateAfterSetNeedsCommit);
class LayerTreeHostAnimationTestCheckerboardDoesntStartAnimations
: public LayerTreeHostAnimationTest {
void SetupTree() override {
LayerTreeHostAnimationTest::SetupTree();
picture_ = FakePictureLayer::Create(&client_);
picture_->SetBounds(gfx::Size(4, 4));
client_.set_bounds(picture_->bounds());
layer_tree_host()->root_layer()->AddChild(picture_);
AttachAnimationsToTimeline();
animation_child_->AttachElement(picture_->element_id());
animation_child_->set_animation_delegate(this);
}
void BeginTest() override {
prevented_draw_ = 0;
started_times_ = 0;
PostSetNeedsCommitToMainThread();
}
DrawResult PrepareToDrawOnThread(LayerTreeHostImpl* host_impl,
FrameData* frame_data,
DrawResult draw_result) override {
if (host_impl->active_tree()->source_frame_number() < 2)
return draw_result;
if (TestEnded())
return draw_result;
++prevented_draw_;
if (prevented_draw_ > 2)
EndTest();
return DrawResult::kAbortedCheckerboardAnimations;
}
void DidCommitAndDrawFrame() override {
switch (layer_tree_host()->SourceFrameNumber()) {
case 1:
AddOpacityTransitionToAnimation(animation_child_.get(), 0.1, 0.2f, 0.8f,
false);
break;
case 2:
AddAnimatedTransformToAnimation(animation_child_.get(), 0.1, 5, 5);
break;
}
}
void NotifyAnimationStarted(base::TimeTicks monotonic_time,
int target_property,
int group) override {
if (TestEnded())
return;
started_times_++;
}
void AfterTest() override {
EXPECT_LT(0, prevented_draw_);
EXPECT_EQ(1, started_times_);
LayerTreeHostAnimationTest::AfterTest();
}
int prevented_draw_;
int started_times_;
FakeContentLayerClient client_;
scoped_refptr<FakePictureLayer> picture_;
};
MULTI_THREAD_TEST_F(
LayerTreeHostAnimationTestCheckerboardDoesntStartAnimations);
class LayerTreeHostAnimationTestScrollOffsetChangesArePropagated
: public LayerTreeHostAnimationTest {
public:
void SetupTree() override {
LayerTreeHostAnimationTest::SetupTree();
scroll_layer_ = FakePictureLayer::Create(&client_);
scroll_layer_->SetScrollable(gfx::Size(100, 100));
scroll_layer_->SetBounds(gfx::Size(1000, 1000));
client_.set_bounds(scroll_layer_->bounds());
scroll_layer_->SetScrollOffset(gfx::PointF(10, 20));
layer_tree_host()->root_layer()->AddChild(scroll_layer_);
AttachAnimationsToTimeline();
animation_child_->AttachElement(scroll_layer_->element_id());
}
void BeginTest() override { PostSetNeedsCommitToMainThread(); }
void DidCommit() override {
switch (layer_tree_host()->SourceFrameNumber()) {
case 1: {
std::unique_ptr<ScrollOffsetAnimationCurve> curve(
ScrollOffsetAnimationCurveFactory::
CreateEaseInOutAnimationForTesting(gfx::PointF(500.f, 550.f)));
std::unique_ptr<KeyframeModel> keyframe_model(KeyframeModel::Create(
std::move(curve), 1, 0,
KeyframeModel::TargetPropertyId(TargetProperty::SCROLL_OFFSET)));
keyframe_model->set_needs_synchronized_start_time(true);
animation_child_->AddKeyframeModel(std::move(keyframe_model));
break;
}
default:
EXPECT_GE(scroll_layer_->scroll_offset().x(), 10);
EXPECT_GE(scroll_layer_->scroll_offset().y(), 20);
if (scroll_layer_->scroll_offset().x() > 10 &&
scroll_layer_->scroll_offset().y() > 20)
EndTest();
}
}
private:
FakeContentLayerClient client_;
scoped_refptr<FakePictureLayer> scroll_layer_;
};
SINGLE_AND_MULTI_THREAD_TEST_F(
LayerTreeHostAnimationTestScrollOffsetChangesArePropagated);
class LayerTreeHostAnimationTestScrollOffsetAnimationTakeover
: public LayerTreeHostAnimationTest {
public:
LayerTreeHostAnimationTestScrollOffsetAnimationTakeover() = default;
void SetupTree() override {
LayerTreeHostAnimationTest::SetupTree();
scroll_layer_ = FakePictureLayer::Create(&client_);
scroll_layer_->SetBounds(gfx::Size(10000, 10000));
client_.set_bounds(scroll_layer_->bounds());
scroll_layer_->SetScrollOffset(gfx::PointF(10, 20));
scroll_layer_->SetScrollable(gfx::Size(10, 10));
layer_tree_host()->root_layer()->AddChild(scroll_layer_);
AttachAnimationsToTimeline();
animation_child_->AttachElement(scroll_layer_->element_id());
animation_child_->set_animation_delegate(this);
}
void BeginTest() override { PostSetNeedsCommitToMainThread(); }
void DidCommit() override {
if (layer_tree_host()->SourceFrameNumber() == 1) {
animation_host()->scroll_offset_animations().AddTakeoverUpdate(
scroll_layer_->element_id());
EXPECT_TRUE(
animation_host()->scroll_offset_animations().HasUpdatesForTesting());
}
}
void WillCommitCompleteOnThread(LayerTreeHostImpl* host_impl) override {
if (host_impl->sync_tree()->source_frame_number() == 0) {
GetImplAnimationHost(host_impl)->ImplOnlyScrollAnimationCreate(
scroll_layer_->element_id(), gfx::PointF(650.f, 750.f),
gfx::PointF(10, 20), base::TimeDelta(), base::TimeDelta());
}
}
void NotifyAnimationTakeover(
base::TimeTicks monotonic_time,
int target_property,
base::TimeTicks animation_start_time,
std::unique_ptr<gfx::AnimationCurve> curve) override {
EndTest();
}
private:
FakeContentLayerClient client_;
scoped_refptr<FakePictureLayer> scroll_layer_;
};
class LayerTreeHostAnimationTestScrollOffsetAnimationAdjusted
: public LayerTreeHostAnimationTest {
public:
LayerTreeHostAnimationTestScrollOffsetAnimationAdjusted() = default;
void SetupTree() override {
LayerTreeHostAnimationTest::SetupTree();
scroll_layer_ = FakePictureLayer::Create(&client_);
scroll_layer_->SetBounds(gfx::Size(10000, 10000));
client_.set_bounds(scroll_layer_->bounds());
scroll_layer_->SetScrollOffset(gfx::PointF(10, 20));
scroll_layer_->SetScrollable(gfx::Size(10, 10));
layer_tree_host()->root_layer()->AddChild(scroll_layer_);
AttachAnimationsToTimeline();
scroll_layer_element_id_ = scroll_layer_->element_id();
}
const KeyframeEffect& ScrollOffsetKeyframeEffect(
const LayerTreeHostImpl& host_impl,
ElementId element_id) const {
scoped_refptr<const ElementAnimations> element_animations =
GetImplAnimationHost(&host_impl)
->GetElementAnimationsForElementIdForTesting(element_id);
DCHECK(element_animations);
const KeyframeEffect* keyframe_effect =
&*element_animations->FirstKeyframeEffectForTesting();
DCHECK(keyframe_effect);
return *keyframe_effect;
}
void BeginTest() override { PostSetNeedsCommitToMainThread(); }
void DidCommit() override {
if (layer_tree_host()->SourceFrameNumber() == 2) {
animation_host()->scroll_offset_animations().AddAdjustmentUpdate(
scroll_layer_element_id_, gfx::Vector2dF(100.f, 100.f));
EXPECT_TRUE(
animation_host()->scroll_offset_animations().HasUpdatesForTesting());
} else if (layer_tree_host()->SourceFrameNumber() == 3) {
EXPECT_FALSE(
animation_host()->scroll_offset_animations().HasUpdatesForTesting());
}
}
void BeginCommitOnThread(LayerTreeHostImpl* host_impl) override {
if (host_impl->sync_tree()->source_frame_number() == 2) {
GetImplTimelineAndAnimationByID(*host_impl);
gfx::KeyframeModel* keyframe_model =
ScrollOffsetKeyframeEffect(*host_impl, scroll_layer_element_id_)
.GetKeyframeModel(TargetProperty::SCROLL_OFFSET);
DCHECK(keyframe_model);
const ScrollOffsetAnimationCurve* curve =
ScrollOffsetAnimationCurve::ToScrollOffsetAnimationCurve(
keyframe_model->curve());
EXPECT_EQ(gfx::PointF(10.f, 20.f), curve->GetValue(base::TimeDelta()));
EXPECT_EQ(gfx::PointF(650.f, 750.f), curve->target_value());
}
}
void WillActivateTreeOnThread(LayerTreeHostImpl* host_impl) override {
if (host_impl->sync_tree()->source_frame_number() == 0) {
GetImplAnimationHost(host_impl)->ImplOnlyScrollAnimationCreate(
scroll_layer_element_id_, gfx::PointF(650.f, 750.f),
gfx::PointF(10, 20), base::TimeDelta(), base::TimeDelta());
}
}
void CommitCompleteOnThread(LayerTreeHostImpl* host_impl) override {
if (host_impl->sync_tree()->source_frame_number() == 2) {
gfx::KeyframeModel* keyframe_model =
ScrollOffsetKeyframeEffect(*host_impl, scroll_layer_element_id_)
.GetKeyframeModel(TargetProperty::SCROLL_OFFSET);
DCHECK(keyframe_model);
const ScrollOffsetAnimationCurve* curve =
ScrollOffsetAnimationCurve::ToScrollOffsetAnimationCurve(
keyframe_model->curve());
EXPECT_EQ(KeyframeModel::RunState::STARTING, keyframe_model->run_state());
EXPECT_EQ(gfx::PointF(110.f, 120.f), curve->GetValue(base::TimeDelta()));
EXPECT_EQ(gfx::PointF(750.f, 850.f), curve->target_value());
EndTest();
}
}
private:
FakeContentLayerClient client_;
scoped_refptr<FakePictureLayer> scroll_layer_;
ElementId scroll_layer_element_id_;
};
MULTI_THREAD_TEST_F(LayerTreeHostAnimationTestScrollOffsetAnimationAdjusted);
class LayerTreeHostPresentationDuringAnimation
: public LayerTreeHostAnimationTest {
public:
void SetupTree() override {
LayerTreeHostAnimationTest::SetupTree();
scroll_layer_ = FakePictureLayer::Create(&client_);
scroll_layer_->SetScrollable(gfx::Size(100, 100));
scroll_layer_->SetBounds(gfx::Size(10000, 10000));
client_.set_bounds(scroll_layer_->bounds());
scroll_layer_->SetScrollOffset(gfx::PointF(100.0, 200.0));
layer_tree_host()->root_layer()->AddChild(scroll_layer_);
std::unique_ptr<ScrollOffsetAnimationCurve> curve(
ScrollOffsetAnimationCurveFactory::CreateEaseInOutAnimationForTesting(
gfx::PointF(6500.f, 7500.f)));
std::unique_ptr<KeyframeModel> keyframe_model(KeyframeModel::Create(
std::move(curve), 1, 0,
KeyframeModel::TargetPropertyId(TargetProperty::SCROLL_OFFSET)));
keyframe_model->set_needs_synchronized_start_time(true);
AttachAnimationsToTimeline();
animation_child_->AttachElement(scroll_layer_->element_id());
animation_child_->AddKeyframeModel(std::move(keyframe_model));
}
void BeginTest() override { PostSetNeedsCommitToMainThread(); }
void BeginMainFrame(const viz::BeginFrameArgs& args) override {
PostSetNeedsCommitToMainThread();
if (const_cast<const LayerTreeHost*>(layer_tree_host())
->pending_commit_state()
->source_frame_number == 2) {
layer_tree_host()->RequestSuccessfulPresentationTimeForNextFrame(
base::BindOnce(
&LayerTreeHostPresentationDuringAnimation::OnPresentation,
base::Unretained(this)));
}
}
void BeginCommitOnThread(LayerTreeHostImpl* host_impl) override {
if (host_impl->active_tree()->source_frame_number() == 1) {
request_token_ = host_impl->next_frame_token();
host_impl->BlockNotifyReadyToActivateForTesting(true);
}
}
void WillBeginImplFrameOnThread(LayerTreeHostImpl* host_impl,
const viz::BeginFrameArgs& args,
bool has_damage) override {
if (host_impl->next_frame_token() >= 5)
host_impl->BlockNotifyReadyToActivateForTesting(false);
}
void DisplayReceivedCompositorFrameOnThread(
const viz::CompositorFrame& frame) override {
received_token_ = frame.metadata.frame_token;
}
void AfterTest() override {
EXPECT_GT(request_token_, 0u);
EXPECT_GT(received_token_, request_token_);
EXPECT_GE(received_token_, 5u);
EXPECT_TRUE(base::StatisticsRecorder::FindHistogram(
"CompositorLatency2.TotalLatency"));
LayerTreeHostAnimationTest::AfterTest();
}
private:
void OnPresentation(const viz::FrameTimingDetails& details) { EndTest(); }
base::MetricsSubSampler::ScopedAlwaysSampleForTesting no_subsampling_;
FakeContentLayerClient client_;
scoped_refptr<FakePictureLayer> scroll_layer_;
uint32_t request_token_ = 0;
uint32_t received_token_ = 0;
};
#if !BUILDFLAG(IS_CHROMEOS)
MULTI_THREAD_TEST_F(LayerTreeHostPresentationDuringAnimation);
#endif
class LayerTreeHostAnimationTestScrollOffsetAnimationRemoval
: public LayerTreeHostAnimationTest {
public:
LayerTreeHostAnimationTestScrollOffsetAnimationRemoval()
: final_postion_(50.0, 100.0) {}
void SetupTree() override {
LayerTreeHostAnimationTest::SetupTree();
scroll_layer_ = FakePictureLayer::Create(&client_);
scroll_layer_->SetScrollable(gfx::Size(100, 100));
scroll_layer_->SetBounds(gfx::Size(10000, 10000));
client_.set_bounds(scroll_layer_->bounds());
scroll_layer_->SetScrollOffset(gfx::PointF(100.0, 200.0));
layer_tree_host()->root_layer()->AddChild(scroll_layer_);
std::unique_ptr<ScrollOffsetAnimationCurve> curve(
ScrollOffsetAnimationCurveFactory::CreateEaseInOutAnimationForTesting(
gfx::PointF(6500.f, 7500.f)));
std::unique_ptr<KeyframeModel> keyframe_model(KeyframeModel::Create(
std::move(curve), 1, 0,
KeyframeModel::TargetPropertyId(TargetProperty::SCROLL_OFFSET)));
keyframe_model->set_needs_synchronized_start_time(true);
AttachAnimationsToTimeline();
animation_child_->AttachElement(scroll_layer_->element_id());
animation_child_->AddKeyframeModel(std::move(keyframe_model));
}
void BeginTest() override { PostSetNeedsCommitToMainThread(); }
void BeginMainFrame(const viz::BeginFrameArgs& args) override {
switch (layer_tree_host()->SourceFrameNumber()) {
case 0:
EXPECT_EQ(scroll_layer_->scroll_offset().x(), 100);
EXPECT_EQ(scroll_layer_->scroll_offset().y(), 200);
break;
case 1: {
EXPECT_GE(scroll_layer_->scroll_offset().x(), 100);
EXPECT_GE(scroll_layer_->scroll_offset().y(), 200);
KeyframeModel* keyframe_model =
animation_child_->GetKeyframeModel(TargetProperty::SCROLL_OFFSET);
animation_child_->RemoveKeyframeModel(keyframe_model->id());
scroll_layer_->SetScrollOffset(final_postion_);
break;
}
default:
EXPECT_EQ(final_postion_, scroll_layer_->scroll_offset());
}
}
void BeginCommitOnThread(LayerTreeHostImpl* host_impl) override {
host_impl->BlockNotifyReadyToActivateForTesting(
ShouldBlockActivation(host_impl));
}
void WillBeginImplFrameOnThread(LayerTreeHostImpl* host_impl,
const viz::BeginFrameArgs& args,
bool has_damage) override {
host_impl->BlockNotifyReadyToActivateForTesting(
ShouldBlockActivation(host_impl));
}
void WillActivateTreeOnThread(LayerTreeHostImpl* host_impl) override {
if (host_impl->pending_tree()->source_frame_number() != 1)
return;
LayerImpl* scroll_layer_impl =
host_impl->pending_tree()->LayerById(scroll_layer_->id());
EXPECT_EQ(final_postion_, CurrentScrollOffset(scroll_layer_impl));
}
void DidActivateTreeOnThread(LayerTreeHostImpl* host_impl) override {
if (host_impl->active_tree()->source_frame_number() != 1)
return;
LayerImpl* scroll_layer_impl =
host_impl->active_tree()->LayerById(scroll_layer_->id());
EXPECT_EQ(final_postion_, CurrentScrollOffset(scroll_layer_impl));
EndTest();
}
void AfterTest() override {
EXPECT_EQ(final_postion_, scroll_layer_->scroll_offset());
LayerTreeHostAnimationTest::AfterTest();
}
private:
bool ShouldBlockActivation(LayerTreeHostImpl* host_impl) {
if (!host_impl->pending_tree())
return false;
if (!host_impl->active_tree()->root_layer())
return false;
scoped_refptr<const AnimationTimeline> timeline_impl =
GetImplAnimationHost(host_impl)->GetTimelineById(timeline_id_);
scoped_refptr<Animation> animation_impl =
timeline_impl->GetAnimationById(animation_child_id_);
LayerImpl* scroll_layer_impl =
host_impl->active_tree()->LayerById(scroll_layer_->id());
KeyframeModel* keyframe_model =
animation_impl->GetKeyframeModel(TargetProperty::SCROLL_OFFSET);
if (!keyframe_model ||
keyframe_model->run_state() != KeyframeModel::RUNNING)
return false;
gfx::Vector2dF scroll_delta = ScrollDelta(scroll_layer_impl);
if (scroll_delta.x() > 0.f || scroll_delta.y() > 0.f)
return false;
return true;
}
FakeContentLayerClient client_;
scoped_refptr<FakePictureLayer> scroll_layer_;
const gfx::PointF final_postion_;
};
MULTI_THREAD_TEST_F(LayerTreeHostAnimationTestScrollOffsetAnimationRemoval);
class LayerTreeHostAnimationTestScrollOffsetAnimationCompletion
: public LayerTreeHostAnimationTest {
public:
LayerTreeHostAnimationTestScrollOffsetAnimationCompletion()
: final_position_(80.0, 180.0) {}
void SetupTree() override {
LayerTreeHostAnimationTest::SetupTree();
scroll_layer_ = FakePictureLayer::Create(&client_);
scroll_layer_->SetScrollable(gfx::Size(100, 100));
scroll_layer_->SetBounds(gfx::Size(10000, 10000));
client_.set_bounds(scroll_layer_->bounds());
scroll_layer_->SetScrollOffset(gfx::PointF(100.0, 200.0));
layer_tree_host()->root_layer()->AddChild(scroll_layer_);
std::unique_ptr<ScrollOffsetAnimationCurve> curve(
ScrollOffsetAnimationCurveFactory::CreateEaseInOutAnimationForTesting(
final_position_));
std::unique_ptr<KeyframeModel> keyframe_model(KeyframeModel::Create(
std::move(curve), 1, 0,
KeyframeModel::TargetPropertyId(TargetProperty::SCROLL_OFFSET)));
keyframe_model->set_needs_synchronized_start_time(true);
AttachAnimationsToTimeline();
animation_child_->AttachElement(scroll_layer_->element_id());
animation_child_->AddKeyframeModel(std::move(keyframe_model));
}
void BeginTest() override { PostSetNeedsCommitToMainThread(); }
void BeginMainFrame(const viz::BeginFrameArgs& args) override {
KeyframeModel* keyframe_model =
animation_child_->GetKeyframeModel(TargetProperty::SCROLL_OFFSET);
switch (layer_tree_host()->SourceFrameNumber()) {
case 0:
EXPECT_EQ(scroll_layer_->scroll_offset().x(), 100);
EXPECT_EQ(scroll_layer_->scroll_offset().y(), 200);
EXPECT_EQ(KeyframeModel::RunState::WAITING_FOR_TARGET_AVAILABILITY,
keyframe_model->run_state());
break;
case 1:
EXPECT_EQ(KeyframeModel::RunState::RUNNING,
keyframe_model->run_state());
break;
default:
break;
}
}
void CommitCompleteOnThread(LayerTreeHostImpl* host_impl) override {
if (host_impl->sync_tree()->source_frame_number() == 0) {
GetImplTimelineAndAnimationByID(*host_impl);
return;
}
KeyframeModel* keyframe_model =
animation_child_impl_->GetKeyframeModel(TargetProperty::SCROLL_OFFSET);
if (!keyframe_model) {
impl_animation_cleaned_up_ = true;
EndTest();
}
}
void DidFinishImplFrameOnThread(LayerTreeHostImpl* host_impl) override {
if (!animation_child_impl_)
return;
if (KeyframeModel* keyframe_model = animation_child_impl_->GetKeyframeModel(
TargetProperty::SCROLL_OFFSET)) {
if (keyframe_model->run_state() == KeyframeModel::RunState::RUNNING) {
ran_animation_ = true;
}
}
}
void AfterTest() override {
EXPECT_TRUE(ran_animation_);
EXPECT_EQ(nullptr, animation_child_->GetKeyframeModel(
TargetProperty::SCROLL_OFFSET));
EXPECT_TRUE(impl_animation_cleaned_up_);
EXPECT_EQ(final_position_, scroll_layer_->scroll_offset());
LayerTreeHostAnimationTest::AfterTest();
}
private:
FakeContentLayerClient client_;
scoped_refptr<FakePictureLayer> scroll_layer_;
const gfx::PointF final_position_;
bool ran_animation_ = false;
bool impl_animation_cleaned_up_ = false;
};
MULTI_THREAD_TEST_F(LayerTreeHostAnimationTestScrollOffsetAnimationCompletion);
class LayerTreeHostAnimationTestAnimationsAddedToNewAndExistingLayers
: public LayerTreeHostAnimationTest {
public:
LayerTreeHostAnimationTestAnimationsAddedToNewAndExistingLayers()
: frame_count_with_pending_tree_(0) {}
void SetupTree() override {
LayerTreeHostAnimationTest::SetupTree();
layer_ = Layer::Create();
layer_->SetBounds(gfx::Size(4, 4));
layer_tree_host()->root_layer()->AddChild(layer_);
layer_tree_host()->SetElementIdsForTesting();
}
void BeginTest() override {
AttachAnimationsToTimeline();
PostSetNeedsCommitToMainThread();
}
void DidCommit() override {
if (layer_tree_host()->SourceFrameNumber() == 1) {
animation_->AttachElement(layer_->element_id());
AddAnimatedTransformToAnimation(animation_.get(), 4, 1, 1);
} else if (layer_tree_host()->SourceFrameNumber() == 2) {
AddOpacityTransitionToAnimation(animation_.get(), 1, 0.f, 0.5f, true);
scoped_refptr<Layer> child_layer = Layer::Create();
layer_->AddChild(child_layer);
layer_tree_host()->SetElementIdsForTesting();
child_layer->SetBounds(gfx::Size(4, 4));
animation_child_->AttachElement(child_layer->element_id());
animation_child_->set_animation_delegate(this);
AddOpacityTransitionToAnimation(animation_child_.get(), 1, 0.f, 0.5f,
true);
}
}
void BeginCommitOnThread(LayerTreeHostImpl* host_impl) override {
host_impl->BlockNotifyReadyToActivateForTesting(true);
}
void CommitCompleteOnThread(LayerTreeHostImpl* host_impl) override {
if (host_impl->pending_tree()->source_frame_number() != 2) {
host_impl->BlockNotifyReadyToActivateForTesting(false);
}
}
void WillBeginImplFrameOnThread(LayerTreeHostImpl* host_impl,
const viz::BeginFrameArgs& args,
bool has_damage) 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 =
GetImplAnimationHost(host_impl)->GetTimelineById(timeline_id_);
scoped_refptr<Animation> animation_impl =
timeline_impl->GetAnimationById(animation_id_);
scoped_refptr<Animation> animation_child_impl =
timeline_impl->GetAnimationById(animation_child_id_);
if (!animation_impl->element_animations())
return;
KeyframeModel* root_keyframe_model =
animation_impl->GetKeyframeModel(TargetProperty::OPACITY);
if (!root_keyframe_model ||
root_keyframe_model->run_state() != KeyframeModel::RUNNING)
return;
KeyframeModel* child_keyframe_model =
animation_child_impl->GetKeyframeModel(TargetProperty::OPACITY);
EXPECT_EQ(KeyframeModel::RUNNING, child_keyframe_model->run_state());
EXPECT_EQ(root_keyframe_model->start_time(),
child_keyframe_model->start_time());
animation_impl->AbortKeyframeModelsWithProperty(TargetProperty::OPACITY,
false);
animation_impl->AbortKeyframeModelsWithProperty(TargetProperty::TRANSFORM,
false);
animation_child_impl->AbortKeyframeModelsWithProperty(
TargetProperty::OPACITY, false);
EndTest();
}
private:
scoped_refptr<Layer> layer_;
int frame_count_with_pending_tree_;
};
MULTI_THREAD_BLOCKNOTIFY_TEST_F(
LayerTreeHostAnimationTestAnimationsAddedToNewAndExistingLayers);
class LayerTreeHostAnimationTestAddKeyframeModelAfterAnimating
: public LayerTreeHostAnimationTest {
public:
void SetupTree() override {
LayerTreeHostAnimationTest::SetupTree();
layer_ = Layer::Create();
layer_->SetBounds(gfx::Size(4, 4));
layer_tree_host()->root_layer()->AddChild(layer_);
child_layer_ = Layer::Create();
child_layer_->SetBounds(gfx::Size(4, 4));
layer_->AddChild(child_layer_);
AttachAnimationsToTimeline();
animation_->AttachElement(layer_->element_id());
animation_child_->AttachElement(child_layer_->element_id());
}
void BeginTest() override { PostSetNeedsCommitToMainThread(); }
void DidCommit() override {
switch (layer_tree_host()->SourceFrameNumber()) {
case 1:
AddAnimatedTransformToAnimation(animation_.get(), 0.1, 5, 5);
break;
case 2:
AddOpacityTransitionToAnimation(animation_child_.get(), 0.1, 5, 5,
false);
break;
}
}
void DrawLayersOnThread(LayerTreeHostImpl* host_impl) override {
if (host_impl->active_tree()->source_frame_number() < 2)
return;
if (!TestEnded()) {
ImplThreadTaskRunner()->PostTask(
FROM_HERE,
base::BindOnce(
&LayerTreeHostAnimationTestAddKeyframeModelAfterAnimating::
CheckAnimations,
base::Unretained(this), host_impl));
}
}
void CheckAnimations(LayerTreeHostImpl* host_impl) {
GetImplTimelineAndAnimationByID(*host_impl);
EXPECT_EQ(2u, GetImplAnimationHost(host_impl)
->ticking_animations_for_testing()
.size());
KeyframeModel* root_anim =
animation_impl_->GetKeyframeModel(TargetProperty::TRANSFORM);
EXPECT_LT(base::TimeTicks(), root_anim->start_time());
KeyframeModel* anim =
animation_child_impl_->GetKeyframeModel(TargetProperty::OPACITY);
EXPECT_LT(base::TimeTicks(), anim->start_time());
EndTest();
}
private:
scoped_refptr<Layer> layer_;
scoped_refptr<Layer> child_layer_;
};
SINGLE_AND_MULTI_THREAD_TEST_F(
LayerTreeHostAnimationTestAddKeyframeModelAfterAnimating);
class LayerTreeHostAnimationTestRemoveKeyframeModel
: public LayerTreeHostAnimationTest {
public:
void SetupTree() override {
LayerTreeHostAnimationTest::SetupTree();
layer_ = FakePictureLayer::Create(&client_);
layer_->SetBounds(gfx::Size(4, 4));
client_.set_bounds(layer_->bounds());
layer_tree_host()->root_layer()->AddChild(layer_);
AttachAnimationsToTimeline();
animation_->AttachElement(layer_tree_host()->root_layer()->element_id());
animation_child_->AttachElement(layer_->element_id());
}
void BeginTest() override {
animation_stopped_ = false;
last_frame_number_ = INT_MAX;
PostSetNeedsCommitToMainThread();
}
void DidCommit() override {
switch (layer_tree_host()->SourceFrameNumber()) {
case 1:
AddAnimatedTransformToAnimation(animation_child_.get(), 1.0, 5, 5);
break;
case 2:
KeyframeModel* keyframe_model =
animation_child_->GetKeyframeModel(TargetProperty::TRANSFORM);
animation_child_->RemoveKeyframeModel(keyframe_model->id());
gfx::Transform transform;
transform.Translate(10.f, 10.f);
layer_->SetTransform(transform);
layer_->AddChild(Layer::Create());
break;
}
}
void DrawLayersOnThread(LayerTreeHostImpl* host_impl) override {
GetImplTimelineAndAnimationByID(*host_impl);
LayerImpl* child = host_impl->active_tree()->LayerById(layer_->id());
switch (host_impl->active_tree()->source_frame_number()) {
case 0:
break;
case 1:
EXPECT_TRUE(child->screen_space_transform_is_animating());
break;
case 2: {
gfx::Transform expected_transform;
expected_transform.Translate(10.f, 10.f);
EXPECT_TRANSFORM_EQ(expected_transform, child->DrawTransform());
EXPECT_FALSE(child->screen_space_transform_is_animating());
animation_stopped_ = true;
PostSetNeedsCommitToMainThread();
break;
}
}
}
void CommitCompleteOnThread(LayerTreeHostImpl* host_impl) override {
if (host_impl->sync_tree()->source_frame_number() >= last_frame_number_) {
EXPECT_FALSE(
animation_child_impl_->keyframe_effect()->has_any_keyframe_model());
EndTest();
}
}
void UpdateAnimationState(LayerTreeHostImpl* host_impl,
bool has_unfinished_animation) override {
if (animation_stopped_ && !has_unfinished_animation) {
last_frame_number_ =
std::min(last_frame_number_,
host_impl->active_tree()->source_frame_number() + 1);
PostSetNeedsCommitToMainThread();
}
}
private:
scoped_refptr<Layer> layer_;
FakeContentLayerClient client_;
int last_frame_number_;
bool animation_stopped_;
};
#if !BUILDFLAG(IS_CHROMEOS)
SINGLE_THREAD_TEST_F(LayerTreeHostAnimationTestRemoveKeyframeModel);
#endif
MULTI_THREAD_TEST_F(LayerTreeHostAnimationTestRemoveKeyframeModel);
class LayerTreeHostAnimationTestIsAnimating
: public LayerTreeHostAnimationTest {
public:
void SetupTree() override {
LayerTreeHostAnimationTest::SetupTree();
layer_ = FakePictureLayer::Create(&client_);
layer_->SetBounds(gfx::Size(4, 4));
client_.set_bounds(layer_->bounds());
layer_tree_host()->root_layer()->AddChild(layer_);
AttachAnimationsToTimeline();
animation_->AttachElement(layer_->element_id());
}
void BeginTest() override { PostSetNeedsCommitToMainThread(); }
void DidCommit() override {
switch (layer_tree_host()->SourceFrameNumber()) {
case 1:
AddAnimatedTransformToAnimation(animation_.get(), 1.0, 5, 5);
break;
case 2:
KeyframeModel* keyframe_model =
animation_->GetKeyframeModel(TargetProperty::TRANSFORM);
EXPECT_EQ(KeyframeModel::RunState::RUNNING,
keyframe_model->run_state());
animation_->RemoveKeyframeModel(keyframe_model->id());
break;
}
}
void CommitCompleteOnThread(LayerTreeHostImpl* host_impl) override {
LayerImpl* child = host_impl->sync_tree()->LayerById(layer_->id());
switch (host_impl->sync_tree()->source_frame_number()) {
case 0:
break;
case 1:
EXPECT_TRUE(child->screen_space_transform_is_animating());
break;
case 2:
EXPECT_FALSE(child->screen_space_transform_is_animating());
break;
case 3:
break;
default:
NOTREACHED();
}
}
void DidActivateTreeOnThread(LayerTreeHostImpl* host_impl) override {
GetImplTimelineAndAnimationByID(*host_impl);
switch (host_impl->active_tree()->source_frame_number()) {
case 0:
break;
case 1:
EXPECT_EQ(KeyframeModel::RunState::STARTING,
animation_impl_->GetKeyframeModel(TargetProperty::TRANSFORM)
->run_state());
break;
case 2:
EXPECT_EQ(KeyframeModel::RunState::WAITING_FOR_DELETION,
animation_impl_->GetKeyframeModel(TargetProperty::TRANSFORM)
->run_state());
break;
case 3:
EXPECT_EQ(nullptr,
animation_impl_->GetKeyframeModel(TargetProperty::TRANSFORM));
EndTest();
break;
default:
NOTREACHED();
}
}
void DrawLayersOnThread(LayerTreeHostImpl* host_impl) override {
LayerImpl* child = host_impl->active_tree()->LayerById(layer_->id());
switch (host_impl->active_tree()->source_frame_number()) {
case 0:
break;
case 1:
EXPECT_TRUE(child->screen_space_transform_is_animating());
break;
case 2:
case 3:
EXPECT_FALSE(child->screen_space_transform_is_animating());
break;
default:
NOTREACHED();
}
}
private:
scoped_refptr<Layer> layer_;
FakeContentLayerClient client_;
};
#if BUILDFLAG(IS_LINUX) || defined(ADDRESS_SANITIZER) || !defined(NDEBUG) || \
BUILDFLAG(IS_ANDROID)
SINGLE_THREAD_TEST_F(LayerTreeHostAnimationTestIsAnimating);
#else
SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostAnimationTestIsAnimating);
#endif
class LayerTreeHostAnimationTestAnimationFinishesDuringCommit
: public LayerTreeHostAnimationTest {
public:
LayerTreeHostAnimationTestAnimationFinishesDuringCommit() = default;
void SetupTree() override {
LayerTreeHostAnimationTest::SetupTree();
layer_ = FakePictureLayer::Create(&client_);
layer_->SetBounds(gfx::Size(4, 4));
client_.set_bounds(layer_->bounds());
layer_tree_host()->root_layer()->AddChild(layer_);
AttachAnimationsToTimeline();
animation_->AttachElement(layer_tree_host()->root_layer()->element_id());
animation_child_->AttachElement(layer_->element_id());
}
void BeginTest() override { PostSetNeedsCommitToMainThread(); }
void DidCommit() override {
if (layer_tree_host()->SourceFrameNumber() == 1) {
AddAnimatedTransformToAnimation(animation_child_.get(), 0.04, 5, 5);
KeyframeModel* keyframe_model =
animation_child_->GetKeyframeModel(TargetProperty::TRANSFORM);
keyframe_model->set_fill_mode(KeyframeModel::FillMode::FORWARDS);
}
}
void WillCommit(const CommitState& commit_state) override {
if (commit_state.source_frame_number == 2) {
completion_.Wait();
}
}
void CommitCompleteOnThread(LayerTreeHostImpl* host_impl) override {
switch (host_impl->sync_tree()->source_frame_number()) {
case 1:
PostSetNeedsCommitToMainThread();
break;
case 2:
gfx::Transform expected_transform;
expected_transform.Translate(5.f, 5.f);
LayerImpl* layer_impl = host_impl->sync_tree()->LayerById(layer_->id());
EXPECT_TRANSFORM_EQ(expected_transform, layer_impl->DrawTransform());
EndTest();
break;
}
}
void UpdateAnimationState(LayerTreeHostImpl* host_impl,
bool has_unfinished_animation) override {
if (host_impl->active_tree()->source_frame_number() == 1 &&
!has_unfinished_animation && !signalled_) {
completion_.Signal();
signalled_ = true;
}
}
private:
scoped_refptr<Layer> layer_;
FakeContentLayerClient client_;
CompletionEvent completion_;
bool signalled_ = false;
};
MULTI_THREAD_TEST_F(LayerTreeHostAnimationTestAnimationFinishesDuringCommit);
class LayerTreeHostAnimationTestImplSideInvalidation
: public LayerTreeHostAnimationTest {
public:
void SetupTree() override {
LayerTreeHostAnimationTest::SetupTree();
layer_ = FakePictureLayer::Create(&client_);
layer_->SetBounds(gfx::Size(4, 4));
client_.set_bounds(layer_->bounds());
layer_tree_host()->root_layer()->AddChild(layer_);
AttachAnimationsToTimeline();
animation_->AttachElement(layer_tree_host()->root_layer()->element_id());
animation_child_->AttachElement(layer_->element_id());
}
void BeginTest() override { PostSetNeedsCommitToMainThread(); }
void DidCommit() override {
if (layer_tree_host()->SourceFrameNumber() == 1) {
AddAnimatedTransformToAnimation(animation_child_.get(), 0.04, 5, 5);
KeyframeModel* keyframe_model =
animation_child_->GetKeyframeModel(TargetProperty::TRANSFORM);
keyframe_model->set_fill_mode(KeyframeModel::FillMode::BOTH);
}
}
void WillCommit(const CommitState& commit_state) override {
if (commit_state.source_frame_number == 2) {
completion_.Wait();
}
}
void DidInvalidateContentOnImplSide(LayerTreeHostImpl* host_impl) override {
DCHECK(did_request_impl_side_invalidation_);
completion_.Signal();
}
void UpdateAnimationState(LayerTreeHostImpl* host_impl,
bool has_unfinished_animation) override {
if (host_impl->active_tree()->source_frame_number() == 1 &&
!has_unfinished_animation && !did_request_impl_side_invalidation_) {
did_request_impl_side_invalidation_ = true;
host_impl->RequestImplSideInvalidationForCheckerImagedTiles();
}
}
void CommitCompleteOnThread(LayerTreeHostImpl* host_impl) override {
switch (host_impl->sync_tree()->source_frame_number()) {
case 1:
PostSetNeedsCommitToMainThread();
break;
case 2:
gfx::Transform expected_transform;
expected_transform.Translate(5.f, 5.f);
LayerImpl* layer_impl = host_impl->sync_tree()->LayerById(layer_->id());
EXPECT_TRANSFORM_EQ(expected_transform, layer_impl->DrawTransform());
EndTest();
break;
}
}
private:
scoped_refptr<Layer> layer_;
FakeContentLayerClient client_;
CompletionEvent completion_;
bool did_request_impl_side_invalidation_ = false;
};
MULTI_THREAD_TEST_F(LayerTreeHostAnimationTestImplSideInvalidation);
class LayerTreeHostAnimationTestImplSideInvalidationWithoutCommit
: public LayerTreeHostAnimationTest {
public:
void SetupTree() override {
LayerTreeHostAnimationTest::SetupTree();
layer_ = FakePictureLayer::Create(&client_);
layer_->SetBounds(gfx::Size(4, 4));
client_.set_bounds(layer_->bounds());
layer_tree_host()->root_layer()->AddChild(layer_);
AttachAnimationsToTimeline();
layer_element_id_ = layer_->element_id();
animation_child_->AttachElement(layer_element_id_);
num_draws_ = 0;
}
void UpdateAnimationState(LayerTreeHostImpl* host_impl,
bool has_unfinished_animation) override {
if (!has_unfinished_animation && !did_request_impl_side_invalidation_) {
did_request_impl_side_invalidation_ = true;
host_impl->RequestImplSideInvalidationForCheckerImagedTiles();
}
}
void DidInvalidateContentOnImplSide(LayerTreeHostImpl* host_impl) override {
completion_.Signal();
EndTest();
}
void WillCommit(const CommitState& commit_state) override {
if (commit_state.source_frame_number == 1) {
completion_.Wait();
}
}
protected:
scoped_refptr<Layer> layer_;
FakeContentLayerClient client_;
bool did_request_impl_side_invalidation_ = false;
int num_draws_;
ElementId layer_element_id_;
private:
CompletionEvent completion_;
};
class ImplSideInvalidationWithoutCommitTestOpacity
: public LayerTreeHostAnimationTestImplSideInvalidationWithoutCommit {
public:
void BeginTest() override {
AddOpacityTransitionToAnimation(animation_child_.get(), 0.04, 0.2f, 0.8f,
false);
PostSetNeedsCommitToMainThread();
}
void DrawLayersOnThread(LayerTreeHostImpl* host_impl) override {
if (num_draws_++ > 0)
return;
EXPECT_EQ(0, host_impl->active_tree()->source_frame_number());
LayerImpl* layer_impl = host_impl->active_tree()->LayerById(layer_->id());
EXPECT_FLOAT_EQ(0.2f, layer_impl->Opacity());
}
void DidInvalidateContentOnImplSide(LayerTreeHostImpl* host_impl) override {
ASSERT_TRUE(did_request_impl_side_invalidation_);
EXPECT_EQ(0, host_impl->sync_tree()->source_frame_number());
const float expected_opacity = 0.8f;
LayerImpl* layer_impl = host_impl->sync_tree()->LayerById(layer_->id());
EXPECT_FLOAT_EQ(expected_opacity, layer_impl->Opacity());
LayerTreeHostAnimationTestImplSideInvalidationWithoutCommit::
DidInvalidateContentOnImplSide(host_impl);
}
};
MULTI_THREAD_TEST_F(ImplSideInvalidationWithoutCommitTestOpacity);
class ImplSideInvalidationWithoutCommitTestTransform
: public LayerTreeHostAnimationTestImplSideInvalidationWithoutCommit {
public:
void BeginTest() override {
AddAnimatedTransformToAnimation(animation_child_.get(), 0.04, 5, 5);
PostSetNeedsCommitToMainThread();
}
void DrawLayersOnThread(LayerTreeHostImpl* host_impl) override {
if (num_draws_++ > 0)
return;
EXPECT_EQ(0, host_impl->active_tree()->source_frame_number());
LayerImpl* layer_impl = host_impl->active_tree()->LayerById(layer_->id());
EXPECT_TRANSFORM_EQ(gfx::Transform(), layer_impl->DrawTransform());
}
void DidInvalidateContentOnImplSide(LayerTreeHostImpl* host_impl) override {
ASSERT_TRUE(did_request_impl_side_invalidation_);
EXPECT_EQ(0, host_impl->sync_tree()->source_frame_number());
gfx::Transform expected_transform;
expected_transform.Translate(5.f, 5.f);
LayerImpl* layer_impl = host_impl->sync_tree()->LayerById(layer_->id());
EXPECT_TRANSFORM_EQ(expected_transform, layer_impl->DrawTransform());
LayerTreeHostAnimationTestImplSideInvalidationWithoutCommit::
DidInvalidateContentOnImplSide(host_impl);
}
};
MULTI_THREAD_TEST_F(ImplSideInvalidationWithoutCommitTestTransform);
class ImplSideInvalidationWithoutCommitTestFilter
: public LayerTreeHostAnimationTestImplSideInvalidationWithoutCommit {
public:
void BeginTest() override {
AddAnimatedFilterToAnimation(animation_child_.get(), 0.04, 0.f, 1.f);
PostSetNeedsCommitToMainThread();
}
void DrawLayersOnThread(LayerTreeHostImpl* host_impl) override {
if (num_draws_++ > 0)
return;
EXPECT_EQ(0, host_impl->active_tree()->source_frame_number());
EXPECT_EQ(
std::string("{\"FilterOperations\":[{\"type\":5,\"amount\":0.0}]}"),
host_impl->active_tree()
->property_trees()
->effect_tree()
.FindNodeFromElementId(layer_element_id_)
->filters.ToString());
}
void DidInvalidateContentOnImplSide(LayerTreeHostImpl* host_impl) override {
ASSERT_TRUE(did_request_impl_side_invalidation_);
EXPECT_EQ(0, host_impl->sync_tree()->source_frame_number());
EXPECT_EQ(
std::string("{\"FilterOperations\":[{\"type\":5,\"amount\":1.0}]}"),
host_impl->sync_tree()
->property_trees()
->effect_tree()
.FindNodeFromElementId(layer_element_id_)
->filters.ToString());
LayerTreeHostAnimationTestImplSideInvalidationWithoutCommit::
DidInvalidateContentOnImplSide(host_impl);
}
};
MULTI_THREAD_TEST_F(ImplSideInvalidationWithoutCommitTestFilter);
class ImplSideInvalidationWithoutCommitTestScroll
: public LayerTreeHostAnimationTestImplSideInvalidationWithoutCommit {
public:
void SetupTree() override {
LayerTreeHostAnimationTestImplSideInvalidationWithoutCommit::SetupTree();
layer_->SetScrollable(gfx::Size(100, 100));
layer_->SetBounds(gfx::Size(1000, 1000));
client_.set_bounds(layer_->bounds());
layer_->SetScrollOffset(gfx::PointF(10.f, 20.f));
}
void BeginTest() override {
std::unique_ptr<ScrollOffsetAnimationCurve> curve(
ScrollOffsetAnimationCurveFactory::CreateEaseInOutAnimationForTesting(
gfx::PointF(500.f, 550.f)));
std::unique_ptr<KeyframeModel> keyframe_model(KeyframeModel::Create(
std::move(curve), 1, 0,
KeyframeModel::TargetPropertyId(TargetProperty::SCROLL_OFFSET)));
keyframe_model->set_needs_synchronized_start_time(true);
animation_child_->AddKeyframeModel(std::move(keyframe_model));
PostSetNeedsCommitToMainThread();
}
void DrawLayersOnThread(LayerTreeHostImpl* host_impl) override {
if (num_draws_++ > 0)
return;
EXPECT_EQ(0, host_impl->active_tree()->source_frame_number());
LayerImpl* layer_impl = host_impl->active_tree()->LayerById(layer_->id());
EXPECT_EQ(gfx::PointF(10.f, 20.f), CurrentScrollOffset(layer_impl));
}
void DidInvalidateContentOnImplSide(LayerTreeHostImpl* host_impl) override {
ASSERT_TRUE(did_request_impl_side_invalidation_);
EXPECT_EQ(0, host_impl->sync_tree()->source_frame_number());
LayerImpl* layer_impl = host_impl->pending_tree()->LayerById(layer_->id());
EXPECT_EQ(gfx::PointF(500.f, 550.f), CurrentScrollOffset(layer_impl));
LayerTreeHostAnimationTestImplSideInvalidationWithoutCommit::
DidInvalidateContentOnImplSide(host_impl);
}
};
MULTI_THREAD_TEST_F(ImplSideInvalidationWithoutCommitTestScroll);
class LayerTreeHostAnimationTestNotifyAnimationFinished
: public LayerTreeHostAnimationTest {
public:
LayerTreeHostAnimationTestNotifyAnimationFinished()
: called_animation_started_(false), called_animation_finished_(false) {}
void SetupTree() override {
LayerTreeHostAnimationTest::SetupTree();
picture_ = FakePictureLayer::Create(&client_);
picture_->SetBounds(gfx::Size(4, 4));
client_.set_bounds(picture_->bounds());
layer_tree_host()->root_layer()->AddChild(picture_);
AttachAnimationsToTimeline();
animation_->AttachElement(picture_->element_id());
animation_->set_animation_delegate(this);
}
void BeginTest() override {
PostAddOpacityAnimationToMainThreadDelayed(animation_.get());
}
void NotifyAnimationStarted(base::TimeTicks monotonic_time,
int target_property,
int group) override {
called_animation_started_ = true;
layer_tree_host()->AnimateLayers(base::TimeTicks::Max());
PostSetNeedsCommitToMainThread();
}
void NotifyAnimationFinished(base::TimeTicks monotonic_time,
int target_property,
int group) override {
called_animation_finished_ = true;
EndTest();
}
void AfterTest() override {
EXPECT_TRUE(called_animation_started_);
EXPECT_TRUE(called_animation_finished_);
LayerTreeHostAnimationTest::AfterTest();
}
private:
bool called_animation_started_;
bool called_animation_finished_;
FakeContentLayerClient client_;
scoped_refptr<FakePictureLayer> picture_;
};
SINGLE_AND_MULTI_THREAD_TEST_F(
LayerTreeHostAnimationTestNotifyAnimationFinished);
class LayerTreeHostAnimationTestSetPotentiallyAnimatingOnLacDestruction
: public LayerTreeHostAnimationTest {
public:
void SetupTree() override {
prev_screen_space_transform_is_animating_ = true;
screen_space_transform_animation_stopped_ = false;
LayerTreeHostAnimationTest::SetupTree();
AttachAnimationsToTimeline();
animation_->AttachElement(layer_tree_host()->root_layer()->element_id());
AddAnimatedTransformToAnimation(animation_.get(), 1.0, 5, 5);
}
void BeginTest() override { PostSetNeedsCommitToMainThread(); }
void CommitCompleteOnThread(LayerTreeHostImpl* host_impl) override {
if (host_impl->pending_tree()->source_frame_number() <= 1) {
EXPECT_TRUE(host_impl->pending_tree()
->root_layer()
->screen_space_transform_is_animating());
} else {
EXPECT_FALSE(host_impl->pending_tree()
->root_layer()
->screen_space_transform_is_animating());
}
}
void DidCommit() override { PostSetNeedsCommitToMainThread(); }
void UpdateLayerTreeHost() override {
if (layer_tree_host()->SourceFrameNumber() == 2) {
timeline_->DetachAnimation(animation_.get());
animation_ = nullptr;
}
}
DrawResult PrepareToDrawOnThread(LayerTreeHostImpl* host_impl,
FrameData* frame_data,
DrawResult draw_result) override {
const bool screen_space_transform_is_animating =
host_impl->active_tree()
->root_layer()
->screen_space_transform_is_animating();
if (screen_space_transform_is_animating &&
prev_screen_space_transform_is_animating_)
EXPECT_FALSE(screen_space_transform_animation_stopped_);
if (!screen_space_transform_is_animating &&
prev_screen_space_transform_is_animating_) {
EXPECT_FALSE(screen_space_transform_animation_stopped_);
screen_space_transform_animation_stopped_ = true;
}
if (!screen_space_transform_is_animating &&
!prev_screen_space_transform_is_animating_)
EXPECT_TRUE(screen_space_transform_animation_stopped_);
prev_screen_space_transform_is_animating_ =
screen_space_transform_is_animating;
return draw_result;
}
void DrawLayersOnThread(LayerTreeHostImpl* host_impl) override {
if (host_impl->active_tree()->source_frame_number() >= 2)
EndTest();
}
void AfterTest() override {
EXPECT_TRUE(screen_space_transform_animation_stopped_);
LayerTreeHostAnimationTest::AfterTest();
}
bool prev_screen_space_transform_is_animating_;
bool screen_space_transform_animation_stopped_;
};
MULTI_THREAD_TEST_F(
LayerTreeHostAnimationTestSetPotentiallyAnimatingOnLacDestruction);
class LayerTreeHostAnimationTestRebuildPropertyTreesOnAnimationSetNeedsCommit
: public LayerTreeHostAnimationTest {
public:
void SetupTree() override {
LayerTreeHostAnimationTest::SetupTree();
layer_ = FakePictureLayer::Create(&client_);
layer_->SetBounds(gfx::Size(4, 4));
client_.set_bounds(layer_->bounds());
layer_tree_host()->root_layer()->AddChild(layer_);
AttachAnimationsToTimeline();
animation_->AttachElement(layer_tree_host()->root_layer()->element_id());
animation_child_->AttachElement(layer_->element_id());
}
void BeginTest() override { PostSetNeedsCommitToMainThread(); }
void DidCommit() override {
if (layer_tree_host()->SourceFrameNumber() == 1 ||
layer_tree_host()->SourceFrameNumber() == 2)
PostSetNeedsCommitToMainThread();
}
void UpdateLayerTreeHost() override {
if (layer_tree_host()->SourceFrameNumber() == 1)
AddAnimatedTransformToAnimation(animation_child_.get(), 1.0, 5, 5);
EXPECT_TRUE(layer_tree_host()->proxy()->CommitRequested());
}
void DrawLayersOnThread(LayerTreeHostImpl* host_impl) override {
if (host_impl->active_tree()->source_frame_number() >= 2)
EndTest();
}
private:
scoped_refptr<Layer> layer_;
FakeContentLayerClient client_;
};
MULTI_THREAD_TEST_F(
LayerTreeHostAnimationTestRebuildPropertyTreesOnAnimationSetNeedsCommit);
class LayerTreeHostTestPauseRendering : public LayerTreeHostAnimationTest {
public:
void SetupTree() override {
LayerTreeHostAnimationTest::SetupTree();
layer_ = Layer::Create();
layer_->SetBounds(gfx::Size(4, 4));
layer_tree_host()->root_layer()->AddChild(layer_);
layer_tree_host()->SetElementIdsForTesting();
}
void BeginTest() override {
AttachAnimationsToTimeline();
animation_->AttachElement(layer_->element_id());
AddAnimatedTransformToAnimation(animation_.get(), 4, 1, 1);
PostSetNeedsCommitToMainThread();
}
void WillCommit(const CommitState& state) override {
if (layer_tree_host()->SourceFrameNumber() == 0) {
EXPECT_FALSE(rendering_paused_);
rendering_paused_ = layer_tree_host()->PauseRendering();
}
}
void DidCommitAndDrawFrame() override {
if (layer_tree_host()->SourceFrameNumber() == 1) {
rendering_paused_.reset();
}
}
void WillCommitCompleteOnThread(LayerTreeHostImpl* host_impl) override {
if (host_impl->pending_tree()->source_frame_number() == 1) {
host_impl->BlockNotifyReadyToActivateForTesting(true, true);
has_pending_tree_which_resumes_draws_ = true;
}
}
void WillActivateTreeOnThread(LayerTreeHostImpl* host_impl) override {
EXPECT_FALSE(has_pending_tree_which_resumes_draws_);
}
void WillBeginImplFrameOnThread(LayerTreeHostImpl* host_impl,
const viz::BeginFrameArgs& args,
bool has_damage) override {
if (!has_pending_tree_which_resumes_draws_) {
return;
}
EXPECT_EQ(host_impl->pending_tree()->source_frame_number(), 1);
constexpr size_t kNumOfFramesToDelayActivation = 5;
if (++impl_frames_while_activation_delayed_ ==
kNumOfFramesToDelayActivation) {
has_pending_tree_which_resumes_draws_ = false;
waiting_for_draw_after_rendering_resumes_ = true;
host_impl->BlockNotifyReadyToActivateForTesting(false, true);
}
}
void WillPrepareToDrawOnThread(LayerTreeHostImpl* host_impl) override {
EXPECT_FALSE(has_pending_tree_which_resumes_draws_);
if (waiting_for_draw_after_rendering_resumes_) {
EXPECT_EQ(host_impl->active_tree()->source_frame_number(), 1);
EndTest();
}
}
private:
scoped_refptr<Layer> layer_;
std::unique_ptr<ScopedPauseRendering> rendering_paused_;
bool has_pending_tree_which_resumes_draws_ = false;
bool waiting_for_draw_after_rendering_resumes_ = false;
size_t impl_frames_while_activation_delayed_ = 0;
};
MULTI_THREAD_TEST_F(LayerTreeHostTestPauseRendering);
}
}