blob: f6bc4d9011b9db51a03597d52fcce8e377252d78 [file] [log] [blame]
// Copyright 2012 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/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.h"
#include "cc/test/fake_content_layer_client.h"
#include "cc/test/layer_tree_test.h"
#include "cc/trees/layer_tree_impl.h"
namespace cc {
namespace {
class LayerTreeHostAnimationTest : public LayerTreeTest {
public:
void SetupTree() override {
LayerTreeTest::SetupTree();
layer_tree_host()->root_layer()->set_layer_animation_delegate(this);
}
};
// Makes sure that SetNeedsAnimate does not cause the CommitRequested() state to
// be set.
class LayerTreeHostAnimationTestSetNeedsAnimateShouldNotSetCommitRequested
: public LayerTreeHostAnimationTest {
public:
LayerTreeHostAnimationTestSetNeedsAnimateShouldNotSetCommitRequested()
: num_commits_(0) {}
void BeginTest() override { PostSetNeedsCommitToMainThread(); }
void BeginMainFrame(const BeginFrameArgs& args) override {
// We skip the first commit because its the commit that populates the
// impl thread with a tree. After the second commit, the test is done.
if (num_commits_ != 1)
return;
layer_tree_host()->SetNeedsAnimate();
// Right now, CommitRequested is going to be true, because during
// BeginFrame, we force CommitRequested to true to prevent requests from
// hitting the impl thread. But, when the next DidCommit happens, we should
// verify that CommitRequested has gone back to false.
}
void DidCommit() override {
if (!num_commits_) {
EXPECT_FALSE(layer_tree_host()->CommitRequested());
layer_tree_host()->SetNeedsAnimate();
EXPECT_FALSE(layer_tree_host()->CommitRequested());
}
// Verifies that the SetNeedsAnimate we made in ::Animate did not
// trigger CommitRequested.
EXPECT_FALSE(layer_tree_host()->CommitRequested());
EndTest();
num_commits_++;
}
void AfterTest() override {}
private:
int num_commits_;
};
MULTI_THREAD_TEST_F(
LayerTreeHostAnimationTestSetNeedsAnimateShouldNotSetCommitRequested);
// Trigger a frame with SetNeedsCommit. Then, inside the resulting animate
// callback, request another frame using SetNeedsAnimate. End the test when
// animate gets called yet-again, indicating that the proxy is correctly
// handling the case where SetNeedsAnimate() is called inside the BeginFrame
// flow.
class LayerTreeHostAnimationTestSetNeedsAnimateInsideAnimationCallback
: public LayerTreeHostAnimationTest {
public:
LayerTreeHostAnimationTestSetNeedsAnimateInsideAnimationCallback()
: num_begin_frames_(0) {}
void BeginTest() override { PostSetNeedsCommitToMainThread(); }
void BeginMainFrame(const BeginFrameArgs& args) override {
if (!num_begin_frames_) {
layer_tree_host()->SetNeedsAnimate();
num_begin_frames_++;
return;
}
EndTest();
}
void AfterTest() override {}
private:
int num_begin_frames_;
};
MULTI_THREAD_TEST_F(
LayerTreeHostAnimationTestSetNeedsAnimateInsideAnimationCallback);
// Add a layer animation and confirm that
// LayerTreeHostImpl::UpdateAnimationState does get called.
class LayerTreeHostAnimationTestAddAnimation
: public LayerTreeHostAnimationTest {
public:
LayerTreeHostAnimationTestAddAnimation()
: update_animation_state_was_called_(false) {}
void BeginTest() override {
PostAddInstantAnimationToMainThread(layer_tree_host()->root_layer());
}
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 =
layer_tree_host()->root_layer()->layer_animation_controller();
Animation* animation = controller->GetAnimation(Animation::Opacity);
if (animation)
controller->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(LayerTreeHostAnimationTestAddAnimation);
// Add a layer animation to a layer, but continually fail to draw. Confirm that
// after a while, we do eventually force a draw.
class LayerTreeHostAnimationTestCheckerboardDoesNotStarveDraws
: public LayerTreeHostAnimationTest {
public:
LayerTreeHostAnimationTestCheckerboardDoesNotStarveDraws()
: started_animating_(false) {}
void BeginTest() override {
PostAddAnimationToMainThread(layer_tree_host()->root_layer());
}
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(LayerTreeHostAnimationTestCheckerboardDoesNotStarveDraws);
// Ensures that animations eventually get deleted.
class LayerTreeHostAnimationTestAnimationsGetDeleted
: public LayerTreeHostAnimationTest {
public:
LayerTreeHostAnimationTestAnimationsGetDeleted()
: started_animating_(false) {}
void BeginTest() override {
PostAddAnimationToMainThread(layer_tree_host()->root_layer());
}
void AnimateLayers(LayerTreeHostImpl* host_impl,
base::TimeTicks monotonic_time) override {
bool have_animations = !host_impl->animation_registrar()->
active_animation_controllers().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(LayerTreeHostAnimationTestAnimationsGetDeleted);
// Ensures that animations continue to be ticked when we are backgrounded.
class LayerTreeHostAnimationTestTickAnimationWhileBackgrounded
: public LayerTreeHostAnimationTest {
public:
LayerTreeHostAnimationTestTickAnimationWhileBackgrounded()
: num_begin_frames_(0) {}
void BeginTest() override {
PostAddLongAnimationToMainThread(layer_tree_host()->root_layer());
}
// Use WillAnimateLayers to set visible false before the animation runs and
// causes a commit, so we block the second visible animate in single-thread
// mode.
void WillAnimateLayers(LayerTreeHostImpl* host_impl,
base::TimeTicks monotonic_time) override {
// Verify that the host can draw, it's just not visible.
EXPECT_TRUE(host_impl->CanDraw());
if (num_begin_frames_ < 2) {
if (!num_begin_frames_) {
// We have a long animation running. It should continue to tick even
// if we are not visible.
PostSetVisibleToMainThread(false);
}
num_begin_frames_++;
return;
}
EndTest();
}
void AfterTest() override {}
private:
int num_begin_frames_;
};
SINGLE_AND_MULTI_THREAD_TEST_F(
LayerTreeHostAnimationTestTickAnimationWhileBackgrounded);
// Ensures that animation time remains monotonic when we switch from foreground
// to background ticking and back, even if we're skipping draws due to
// checkerboarding when in the foreground.
class LayerTreeHostAnimationTestAnimationTickTimeIsMonotonic
: public LayerTreeHostAnimationTest {
public:
LayerTreeHostAnimationTestAnimationTickTimeIsMonotonic()
: has_background_ticked_(false), num_foreground_animates_(0) {}
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;
}
void BeginTest() override {
PostAddLongAnimationToMainThread(layer_tree_host()->root_layer());
}
void AnimateLayers(LayerTreeHostImpl* host_impl,
base::TimeTicks monotonic_time) override {
EXPECT_GE(monotonic_time, last_tick_time_);
last_tick_time_ = monotonic_time;
if (host_impl->visible()) {
num_foreground_animates_++;
if (num_foreground_animates_ > 1 && !has_background_ticked_)
PostSetVisibleToMainThread(false);
else if (has_background_ticked_)
EndTest();
} else {
has_background_ticked_ = true;
PostSetVisibleToMainThread(true);
}
}
DrawResult PrepareToDrawOnThread(LayerTreeHostImpl* host_impl,
LayerTreeHostImpl::FrameData* frame,
DrawResult draw_result) override {
if (TestEnded())
return draw_result;
return DRAW_ABORTED_CHECKERBOARD_ANIMATIONS;
}
void AfterTest() override {}
private:
bool has_background_ticked_;
int num_foreground_animates_;
base::TimeTicks last_tick_time_;
};
SINGLE_AND_MULTI_THREAD_TEST_F(
LayerTreeHostAnimationTestAnimationTickTimeIsMonotonic);
// Ensures that animations do not tick when we are backgrounded and
// and we have an empty active tree.
class LayerTreeHostAnimationTestNoBackgroundTickingWithoutActiveTree
: public LayerTreeHostAnimationTest {
protected:
LayerTreeHostAnimationTestNoBackgroundTickingWithoutActiveTree()
: active_tree_was_animated_(false) {}
base::TimeDelta BackgroundAnimationInterval(LayerTreeHostImpl* host_impl) {
return base::TimeDelta::FromSecondsD(
1.0 / host_impl->settings().background_animation_rate);
}
void BeginTest() override {
PostAddAnimationToMainThread(layer_tree_host()->root_layer());
}
void NotifyAnimationFinished(base::TimeTicks monotonic_time,
Animation::TargetProperty target_property,
int group) override {
// Replace animated commits with an empty tree.
layer_tree_host()->SetRootLayer(make_scoped_refptr<Layer>(NULL));
}
void DidCommit() override {
// This alternates setting an empty tree and a non-empty tree with an
// animation.
switch (layer_tree_host()->source_frame_number()) {
case 1:
// Wait for NotifyAnimationFinished to commit an empty tree.
break;
case 2:
SetupTree();
AddOpacityTransitionToLayer(
layer_tree_host()->root_layer(), 0.000001, 0, 0.5, true);
break;
case 3:
// Wait for NotifyAnimationFinished to commit an empty tree.
break;
case 4:
EndTest();
break;
}
}
void BeginCommitOnThread(LayerTreeHostImpl* host_impl) override {
// At the start of every commit, block activations and make sure
// we are backgrounded.
if (host_impl->settings().impl_side_painting)
host_impl->BlockNotifyReadyToActivateForTesting(true);
PostSetVisibleToMainThread(false);
}
void CommitCompleteOnThread(LayerTreeHostImpl* host_impl) override {
if (!host_impl->settings().impl_side_painting) {
// There are no activations to block if we're not impl-side-painting,
// so just advance the test immediately.
if (host_impl->active_tree()->source_frame_number() < 3)
UnblockActivations(host_impl);
return;
}
// We block activation for several ticks to make sure that, even though
// there is a pending tree with animations, we still do not background
// tick if the active tree is empty.
if (host_impl->pending_tree()->source_frame_number() < 3) {
base::MessageLoopProxy::current()->PostDelayedTask(
FROM_HERE,
base::Bind(
&LayerTreeHostAnimationTestNoBackgroundTickingWithoutActiveTree::
UnblockActivations,
base::Unretained(this), host_impl),
4 * BackgroundAnimationInterval(host_impl));
}
}
virtual void UnblockActivations(LayerTreeHostImpl* host_impl) {
if (host_impl->settings().impl_side_painting)
host_impl->BlockNotifyReadyToActivateForTesting(false);
}
void DidActivateTreeOnThread(LayerTreeHostImpl* host_impl) override {
active_tree_was_animated_ = false;
// Verify that commits are actually alternating with empty / non-empty
// trees.
int frame_number = host_impl->active_tree()->source_frame_number();
switch (frame_number) {
case 0:
case 2:
EXPECT_TRUE(host_impl->active_tree()->root_layer())
<< "frame: " << frame_number;
break;
case 1:
case 3:
EXPECT_FALSE(host_impl->active_tree()->root_layer())
<< "frame: " << frame_number;
break;
}
if (host_impl->active_tree()->source_frame_number() < 3) {
// Initiate the next commit after a delay to give us a chance to
// background tick if the active tree isn't empty.
base::MessageLoopProxy::current()->PostDelayedTask(
FROM_HERE,
base::Bind(
&LayerTreeHostAnimationTestNoBackgroundTickingWithoutActiveTree::
InitiateNextCommit,
base::Unretained(this), host_impl),
4 * BackgroundAnimationInterval(host_impl));
}
}
void WillAnimateLayers(LayerTreeHostImpl* host_impl,
base::TimeTicks monotonic_time) override {
EXPECT_TRUE(host_impl->active_tree()->root_layer());
active_tree_was_animated_ = true;
}
void InitiateNextCommit(LayerTreeHostImpl* host_impl) {
// Verify that we actually animated when we should have.
bool has_active_tree = host_impl->active_tree()->root_layer();
EXPECT_EQ(has_active_tree, active_tree_was_animated_);
// The next commit is blocked until we become visible again.
PostSetVisibleToMainThread(true);
}
void AfterTest() override {}
bool active_tree_was_animated_;
};
SINGLE_AND_MULTI_THREAD_BLOCKNOTIFY_TEST_F(
LayerTreeHostAnimationTestNoBackgroundTickingWithoutActiveTree);
// Ensure that an animation's timing function is respected.
class LayerTreeHostAnimationTestAddAnimationWithTimingFunction
: public LayerTreeHostAnimationTest {
public:
LayerTreeHostAnimationTestAddAnimationWithTimingFunction() {}
void SetupTree() override {
LayerTreeHostAnimationTest::SetupTree();
content_ = FakeContentLayer::Create(&client_);
content_->SetBounds(gfx::Size(4, 4));
layer_tree_host()->root_layer()->AddChild(content_);
}
void BeginTest() override { PostAddAnimationToMainThread(content_.get()); }
void AnimateLayers(LayerTreeHostImpl* host_impl,
base::TimeTicks monotonic_time) override {
LayerAnimationController* controller_impl =
host_impl->active_tree()->root_layer()->children()[0]->
layer_animation_controller();
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<FakeContentLayer> content_;
};
SINGLE_AND_MULTI_THREAD_TEST_F(
LayerTreeHostAnimationTestAddAnimationWithTimingFunction);
// Ensures that main thread animations have their start times synchronized with
// impl thread animations.
class LayerTreeHostAnimationTestSynchronizeAnimationStartTimes
: public LayerTreeHostAnimationTest {
public:
LayerTreeHostAnimationTestSynchronizeAnimationStartTimes() {}
void SetupTree() override {
LayerTreeHostAnimationTest::SetupTree();
content_ = FakeContentLayer::Create(&client_);
content_->SetBounds(gfx::Size(4, 4));
content_->set_layer_animation_delegate(this);
layer_tree_host()->root_layer()->AddChild(content_);
}
void BeginTest() override { PostAddAnimationToMainThread(content_.get()); }
void NotifyAnimationStarted(base::TimeTicks monotonic_time,
Animation::TargetProperty target_property,
int group) override {
LayerAnimationController* controller =
layer_tree_host()->root_layer()->children()[0]->
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 {
LayerAnimationController* controller =
impl_host->active_tree()->root_layer()->children()[0]->
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<FakeContentLayer> content_;
};
SINGLE_AND_MULTI_THREAD_TEST_F(
LayerTreeHostAnimationTestSynchronizeAnimationStartTimes);
// Ensures that notify animation finished is called.
class LayerTreeHostAnimationTestAnimationFinishedEvents
: public LayerTreeHostAnimationTest {
public:
LayerTreeHostAnimationTestAnimationFinishedEvents() {}
void BeginTest() override {
PostAddInstantAnimationToMainThread(layer_tree_host()->root_layer());
}
void NotifyAnimationFinished(base::TimeTicks monotonic_time,
Animation::TargetProperty target_property,
int group) override {
LayerAnimationController* controller =
layer_tree_host()->root_layer()->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(
LayerTreeHostAnimationTestAnimationFinishedEvents);
// Ensures that when opacity is being animated, this value does not cause the
// subtree to be skipped.
class LayerTreeHostAnimationTestDoNotSkipLayersWithAnimatedOpacity
: public LayerTreeHostAnimationTest {
public:
LayerTreeHostAnimationTestDoNotSkipLayersWithAnimatedOpacity()
: update_check_layer_(FakeContentLayer::Create(&client_)) {
}
void SetupTree() override {
update_check_layer_->SetOpacity(0.f);
layer_tree_host()->SetRootLayer(update_check_layer_);
LayerTreeHostAnimationTest::SetupTree();
}
void BeginTest() override {
PostAddAnimationToMainThread(update_check_layer_.get());
}
void DidActivateTreeOnThread(LayerTreeHostImpl* host_impl) override {
LayerAnimationController* controller_impl =
host_impl->active_tree()->root_layer()->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(1u, update_check_layer_->update_count());
// clear update_check_layer_ so LayerTreeHost dies.
update_check_layer_ = NULL;
}
private:
FakeContentLayerClient client_;
scoped_refptr<FakeContentLayer> update_check_layer_;
};
SINGLE_AND_MULTI_THREAD_TEST_F(
LayerTreeHostAnimationTestDoNotSkipLayersWithAnimatedOpacity);
// Layers added to tree with existing active animations should have the
// animation correctly recognized.
class LayerTreeHostAnimationTestLayerAddedWithAnimation
: public LayerTreeHostAnimationTest {
public:
LayerTreeHostAnimationTestLayerAddedWithAnimation() {}
void BeginTest() override { PostSetNeedsCommitToMainThread(); }
void DidCommit() override {
if (layer_tree_host()->source_frame_number() == 1) {
scoped_refptr<Layer> layer = Layer::Create();
layer->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));
layer->layer_animation_controller()->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(
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 BeginFrameArgs& args) override {
num_begin_frames_++;
// No-op animate will cancel the commit.
if (layer_tree_host()->source_frame_number() == 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_);
}
private:
int num_begin_frames_;
int num_commit_calls_;
int num_draw_calls_;
FakeContentLayerClient client_;
scoped_refptr<FakeContentLayer> content_;
};
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 BeginFrameArgs& args) override {
if (++num_animate_ < 2)
layer_tree_host()->SetNeedsAnimate();
}
void Layout() override { layer_tree_host()->SetNextCommitForcesRedraw(); }
void DrawLayersOnThread(LayerTreeHostImpl* impl) override {
if (++num_draw_layers_ == 2)
EndTest();
}
void AfterTest() override {
// The first commit will always draw; make sure the second draw triggered
// by the animation was not cancelled.
EXPECT_EQ(2, num_draw_layers_);
EXPECT_EQ(2, num_animate_);
}
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 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 {
// The first commit will always draw; make sure the second draw triggered
// by the SetNeedsCommit was not cancelled.
EXPECT_EQ(2, num_draw_layers_);
EXPECT_GE(num_animate_, 2);
}
private:
int num_animate_;
int num_draw_layers_;
};
MULTI_THREAD_TEST_F(LayerTreeHostAnimationTestAnimateAfterSetNeedsCommit);
// Make sure the main thread can still execute animations when CanDraw() is not
// true.
class LayerTreeHostAnimationTestRunAnimationWhenNotCanDraw
: public LayerTreeHostAnimationTest {
public:
LayerTreeHostAnimationTestRunAnimationWhenNotCanDraw() : started_times_(0) {}
void SetupTree() override {
LayerTreeHostAnimationTest::SetupTree();
content_ = FakeContentLayer::Create(&client_);
content_->SetBounds(gfx::Size(4, 4));
content_->set_layer_animation_delegate(this);
layer_tree_host()->root_layer()->AddChild(content_);
}
void BeginTest() override {
layer_tree_host()->SetViewportSize(gfx::Size());
PostAddAnimationToMainThread(content_.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<FakeContentLayer> content_;
};
SINGLE_AND_MULTI_THREAD_TEST_F(
LayerTreeHostAnimationTestRunAnimationWhenNotCanDraw);
// Make sure the main thread can still execute animations when the renderer is
// backgrounded.
class LayerTreeHostAnimationTestRunAnimationWhenNotVisible
: public LayerTreeHostAnimationTest {
public:
LayerTreeHostAnimationTestRunAnimationWhenNotVisible() : started_times_(0) {}
void SetupTree() override {
LayerTreeHostAnimationTest::SetupTree();
content_ = FakeContentLayer::Create(&client_);
content_->SetBounds(gfx::Size(4, 4));
content_->set_layer_animation_delegate(this);
layer_tree_host()->root_layer()->AddChild(content_);
}
void BeginTest() override {
visible_ = true;
PostAddAnimationToMainThread(content_.get());
}
void DidCommit() override {
visible_ = false;
layer_tree_host()->SetVisible(false);
}
void NotifyAnimationStarted(base::TimeTicks monotonic_time,
Animation::TargetProperty target_property,
int group) override {
EXPECT_FALSE(visible_);
started_times_++;
}
void NotifyAnimationFinished(base::TimeTicks monotonic_time,
Animation::TargetProperty target_property,
int group) override {
EXPECT_FALSE(visible_);
EXPECT_EQ(1, started_times_);
EndTest();
}
void AfterTest() override {}
private:
bool visible_;
int started_times_;
FakeContentLayerClient client_;
scoped_refptr<FakeContentLayer> content_;
};
SINGLE_AND_MULTI_THREAD_TEST_F(
LayerTreeHostAnimationTestRunAnimationWhenNotVisible);
// Animations should not be started when frames are being skipped due to
// checkerboard.
class LayerTreeHostAnimationTestCheckerboardDoesntStartAnimations
: public LayerTreeHostAnimationTest {
void SetupTree() override {
LayerTreeHostAnimationTest::SetupTree();
content_ = FakeContentLayer::Create(&client_);
content_->SetBounds(gfx::Size(4, 4));
content_->set_layer_animation_delegate(this);
layer_tree_host()->root_layer()->AddChild(content_);
}
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;
}
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.
AddOpacityTransitionToLayer(content_.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.
AddAnimatedTransformToLayer(content_.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<FakeContentLayer> content_;
};
MULTI_THREAD_TEST_F(
LayerTreeHostAnimationTestCheckerboardDoesntStartAnimations);
// Verifies that scroll offset animations are only accepted when impl-scrolling
// is supported, and that when scroll offset animations are accepted,
// scroll offset updates are sent back to the main thread.
class LayerTreeHostAnimationTestScrollOffsetChangesArePropagated
: public LayerTreeHostAnimationTest {
public:
LayerTreeHostAnimationTestScrollOffsetChangesArePropagated() {}
void SetupTree() override {
LayerTreeHostAnimationTest::SetupTree();
scroll_layer_ = FakeContentLayer::Create(&client_);
scroll_layer_->SetScrollClipLayerId(layer_tree_host()->root_layer()->id());
scroll_layer_->SetBounds(gfx::Size(1000, 1000));
scroll_layer_->SetScrollOffset(gfx::ScrollOffset(10, 20));
layer_tree_host()->root_layer()->AddChild(scroll_layer_);
}
void BeginTest() override { PostSetNeedsCommitToMainThread(); }
void DidCommit() override {
switch (layer_tree_host()->source_frame_number()) {
case 1: {
scoped_ptr<ScrollOffsetAnimationCurve> curve(
ScrollOffsetAnimationCurve::Create(
gfx::ScrollOffset(500.f, 550.f),
EaseInOutTimingFunction::Create()));
scoped_ptr<Animation> animation(
Animation::Create(curve.Pass(), 1, 0, Animation::ScrollOffset));
animation->set_needs_synchronized_start_time(true);
bool animation_added = scroll_layer_->AddAnimation(animation.Pass());
bool impl_scrolling_supported =
layer_tree_host()->proxy()->SupportsImplScrolling();
EXPECT_EQ(impl_scrolling_supported, animation_added);
if (!impl_scrolling_supported)
EndTest();
break;
}
default:
if (scroll_layer_->scroll_offset().x() > 10 &&
scroll_layer_->scroll_offset().y() > 20)
EndTest();
}
}
void AfterTest() override {}
private:
FakeContentLayerClient client_;
scoped_refptr<FakeContentLayer> scroll_layer_;
};
SINGLE_AND_MULTI_THREAD_TEST_F(
LayerTreeHostAnimationTestScrollOffsetChangesArePropagated);
// When animations are simultaneously added to an existing layer and to a new
// layer, they should start at the same time, even when there's already a
// running animation on the existing layer.
class LayerTreeHostAnimationTestAnimationsAddedToNewAndExistingLayers
: public LayerTreeHostAnimationTest {
public:
LayerTreeHostAnimationTestAnimationsAddedToNewAndExistingLayers()
: frame_count_with_pending_tree_(0) {}
void BeginTest() override { PostSetNeedsCommitToMainThread(); }
void DidCommit() override {
if (layer_tree_host()->source_frame_number() == 1) {
AddAnimatedTransformToLayer(layer_tree_host()->root_layer(), 4, 1, 1);
} else if (layer_tree_host()->source_frame_number() == 2) {
AddOpacityTransitionToLayer(
layer_tree_host()->root_layer(), 1, 0.f, 0.5f, true);
scoped_refptr<Layer> layer = Layer::Create();
layer_tree_host()->root_layer()->AddChild(layer);
layer->set_layer_animation_delegate(this);
layer->SetBounds(gfx::Size(4, 4));
AddOpacityTransitionToLayer(layer.get(), 1, 0.f, 0.5f, true);
}
}
void BeginCommitOnThread(LayerTreeHostImpl* host_impl) override {
if (host_impl->settings().impl_side_painting)
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->settings().impl_side_painting &&
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->settings().impl_side_painting) {
host_impl->BlockNotifyReadyToActivateForTesting(false);
}
}
void UpdateAnimationState(LayerTreeHostImpl* host_impl,
bool has_unfinished_animation) override {
LayerAnimationController* root_controller_impl =
host_impl->active_tree()->root_layer()->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 =
host_impl->active_tree()->root_layer()->children()
[0]->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_;
};
SINGLE_AND_MULTI_THREAD_BLOCKNOTIFY_TEST_F(
LayerTreeHostAnimationTestAnimationsAddedToNewAndExistingLayers);
class LayerTreeHostAnimationTestAddAnimationAfterAnimating
: public LayerTreeHostAnimationTest {
public:
LayerTreeHostAnimationTestAddAnimationAfterAnimating()
: num_swap_buffers_(0) {}
void SetupTree() override {
LayerTreeHostAnimationTest::SetupTree();
content_ = Layer::Create();
content_->SetBounds(gfx::Size(4, 4));
layer_tree_host()->root_layer()->AddChild(content_);
}
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.
AddAnimatedTransformToLayer(layer_tree_host()->root_layer(), 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.
AddOpacityTransitionToLayer(content_.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.
num_swap_buffers_++;
AnimationRegistrar::AnimationControllerMap copy =
host_impl->animation_registrar()->active_animation_controllers();
if (copy.size() == 2u) {
EndTest();
EXPECT_GE(num_swap_buffers_, 3);
for (AnimationRegistrar::AnimationControllerMap::iterator iter =
copy.begin();
iter != copy.end();
++iter) {
int id = ((*iter).second->id());
if (id == host_impl->RootLayer()->id()) {
Animation* anim = (*iter).second->GetAnimation(Animation::Transform);
EXPECT_GT((anim->start_time() - base::TimeTicks()).InSecondsF(), 0);
} else if (id == host_impl->RootLayer()->children()[0]->id()) {
Animation* anim = (*iter).second->GetAnimation(Animation::Opacity);
EXPECT_GT((anim->start_time() - base::TimeTicks()).InSecondsF(), 0);
}
}
}
}
void AfterTest() override {}
private:
scoped_refptr<Layer> content_;
int num_swap_buffers_;
};
SINGLE_AND_MULTI_THREAD_TEST_F(
LayerTreeHostAnimationTestAddAnimationAfterAnimating);
} // namespace
} // namespace cc