Commit 3d9f7431 authored by danakj@chromium.org's avatar danakj@chromium.org

cc: Make animations tick regardless of drawing.

The compositor should tick and finish animations even if it is not
drawing anything. It can not draw for various reasons:
1) The tab is backgrounded.
2) CanDraw() is false for any of its reasons.
3) PrepareToDraw() fails due to checkerboarding.
4) There is no damage on the screen to draw.

Currently the problems are:
- When backgrounded, the animations are ticked but their states are not updated.
- When CanDraw() is false, we don't draw, and animations just stop ticking.
- If we stop drawing when we damage the screen, we tick animations, but don't
  update animation state since this happens in DrawLayers().

To solve these problems, I've moved the animation control more explicitly out
of LayerTreeHostImpl. The proxy already calls Animate(). Now it will also call
UpdateAnimationState(). It always does this after calling Animate() regardless
if drawing or not.

Secondly, the missing UpdateAnimationState() call is added to the OnTimerTick
for background animation ticking.

We enable background ticking only when we change visibility, currently. But
when CanDraw() is false, we don't draw and thus don't tick animations. So
instead we add to LayerTreeHostImpl a UpdateBackgroundAnimateTicking() method.
We call this always after calling Animate() since that can remove animations -
it's something Animate() used to do. And we also call this:
a) After a commit - this could add new animations, or change visibility.
b) After changing CanDraw()'s state.

However, when PrepareToDraw() is false, we do not want to start new animations
so we let animations finish without starting new ones. This is verified by 
the LayerTreeHostAnimationTestCheckerboardDoesntStartAnimations test.

This is covered by adding single-thread mode to all the animation unit tests
(except those that call SetNeedsAnimate() which is not legal in single thread
mode). Also by new tests:
LayerTreeHostAnimationTestRunAnimationWhenNotCanDraw.RunSingleThread
LayerTreeHostAnimationTestRunAnimationWhenNotCanDraw.RunMultiThread
LayerTreeHostAnimationTestRunAnimationWhenNotVisible.RunSingleThread
LayerTreeHostAnimationTestRunAnimationWhenNotVisible.RunMultiThread
LayerTreeHostAnimationTestCheckerboardDoesntStartAnimations.RunMultiThread

Added scheduler tests:
SchedulerStateMachineTest.ReportIfNotDrawing
SchedulerStateMachineTest.ReportIfNotDrawingFromAcquiredTextures

R=ajuma@chromium.org
BUG=222915

Review URL: https://chromiumcodereview.appspot.com/13613003

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@192671 0039d316-1c4b-4281-b951-d872f2087c98
parent 13ba1dae
......@@ -174,15 +174,21 @@ void LayerAnimationController::AccumulatePropertyUpdates(
}
}
void LayerAnimationController::UpdateState(AnimationEventsVector* events) {
void LayerAnimationController::UpdateState(bool start_ready_animations,
AnimationEventsVector* events) {
if (!HasActiveObserver())
return;
PromoteStartedAnimations(last_tick_time_, events);
if (start_ready_animations)
PromoteStartedAnimations(last_tick_time_, events);
MarkFinishedAnimations(last_tick_time_);
MarkAnimationsForDeletion(last_tick_time_, events);
StartAnimationsWaitingForTargetAvailability(last_tick_time_);
PromoteStartedAnimations(last_tick_time_, events);
if (start_ready_animations) {
StartAnimationsWaitingForTargetAvailability(last_tick_time_);
PromoteStartedAnimations(last_tick_time_, events);
}
AccumulatePropertyUpdates(last_tick_time_, events);
......
......@@ -52,7 +52,9 @@ class CC_EXPORT LayerAnimationController
void Animate(double monotonic_time);
void AccumulatePropertyUpdates(double monotonic_time,
AnimationEventsVector* events);
void UpdateState(AnimationEventsVector* events);
void UpdateState(bool start_ready_animations,
AnimationEventsVector* events);
// Returns the active animation in the given group, animating the given
// property, if such an animation exists.
......
......@@ -199,4 +199,8 @@ void Scheduler::ProcessScheduledActions() {
client_->DidAnticipatedDrawTimeChange(frame_rate_controller_->NextTickTime());
}
bool Scheduler::WillDrawIfNeeded() const {
return !state_machine_.DrawSuspendedUntilCommit();
}
} // namespace cc
......@@ -96,6 +96,8 @@ class CC_EXPORT Scheduler : FrameRateControllerClient {
bool CommitPending() const { return state_machine_.CommitPending(); }
bool RedrawPending() const { return state_machine_.RedrawPending(); }
bool WillDrawIfNeeded() const;
void SetTimebaseAndInterval(base::TimeTicks timebase,
base::TimeDelta interval);
......
......@@ -144,11 +144,14 @@ class CC_EXPORT SchedulerStateMachine {
// Exposed for testing purposes.
void SetMaximumNumberOfFailedDrawsBeforeDrawIsForced(int num_draws);
// False if drawing is not being prevented, true if drawing won't happen
// for some reason, such as not being visible.
bool DrawSuspendedUntilCommit() const;
std::string ToString();
protected:
bool ShouldDrawForced() const;
bool DrawSuspendedUntilCommit() const;
bool ScheduledToDraw() const;
bool ShouldDraw() const;
bool ShouldAttemptTreeActivation() const;
......
......@@ -1077,5 +1077,61 @@ TEST(SchedulerStateMachineTest, ImmediateBeginFrameWhileCantDraw) {
state.DidLeaveVSync();
}
TEST(SchedulerStateMachineTest, ReportIfNotDrawing) {
SchedulerSettings default_scheduler_settings;
SchedulerStateMachine state(default_scheduler_settings);
state.SetCanDraw(true);
state.SetVisible(true);
EXPECT_FALSE(state.DrawSuspendedUntilCommit());
state.SetCanDraw(false);
state.SetVisible(true);
EXPECT_TRUE(state.DrawSuspendedUntilCommit());
state.SetCanDraw(true);
state.SetVisible(false);
EXPECT_TRUE(state.DrawSuspendedUntilCommit());
state.SetCanDraw(false);
state.SetVisible(false);
EXPECT_TRUE(state.DrawSuspendedUntilCommit());
state.SetCanDraw(true);
state.SetVisible(true);
EXPECT_FALSE(state.DrawSuspendedUntilCommit());
}
TEST(SchedulerStateMachineTest, ReportIfNotDrawingFromAcquiredTextures) {
SchedulerSettings default_scheduler_settings;
SchedulerStateMachine state(default_scheduler_settings);
state.SetCanBeginFrame(true);
state.SetCanDraw(true);
state.SetVisible(true);
EXPECT_FALSE(state.DrawSuspendedUntilCommit());
state.SetMainThreadNeedsLayerTextures();
EXPECT_EQ(
SchedulerStateMachine::ACTION_ACQUIRE_LAYER_TEXTURES_FOR_MAIN_THREAD,
state.NextAction());
state.UpdateState(state.NextAction());
EXPECT_TRUE(state.DrawSuspendedUntilCommit());
EXPECT_EQ(SchedulerStateMachine::ACTION_BEGIN_FRAME, state.NextAction());
state.UpdateState(state.NextAction());
EXPECT_TRUE(state.DrawSuspendedUntilCommit());
EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction());
state.BeginFrameComplete();
EXPECT_TRUE(state.DrawSuspendedUntilCommit());
EXPECT_EQ(SchedulerStateMachine::ACTION_COMMIT, state.NextAction());
state.UpdateState(state.NextAction());
EXPECT_FALSE(state.DrawSuspendedUntilCommit());
}
} // namespace
} // namespace cc
......@@ -151,8 +151,8 @@ class LayerTreeHostImplForTesting : public LayerTreeHostImpl {
test_hooks_->AnimateLayers(this, monotonic_time);
}
virtual void UpdateAnimationState() OVERRIDE {
LayerTreeHostImpl::UpdateAnimationState();
virtual void UpdateAnimationState(bool start_ready_animations) OVERRIDE {
LayerTreeHostImpl::UpdateAnimationState(start_ready_animations);
bool has_unfinished_animation = false;
AnimationRegistrar::AnimationControllerMap::const_iterator iter =
active_animation_controllers().begin();
......@@ -346,10 +346,12 @@ void LayerTreeTest::PostAddAnimationToMainThread(
base::Unretained(layer_to_receive_animation)));
}
void LayerTreeTest::PostAddInstantAnimationToMainThread() {
void LayerTreeTest::PostAddInstantAnimationToMainThread(
Layer* layer_to_receive_animation) {
proxy()->MainThread()->PostTask(
base::Bind(&LayerTreeTest::DispatchAddInstantAnimation,
main_thread_weak_ptr_));
main_thread_weak_ptr_,
base::Unretained(layer_to_receive_animation)));
}
void LayerTreeTest::PostSetNeedsCommitToMainThread() {
......@@ -446,11 +448,12 @@ void LayerTreeTest::RealEndTest() {
MessageLoop::current()->Quit();
}
void LayerTreeTest::DispatchAddInstantAnimation() {
void LayerTreeTest::DispatchAddInstantAnimation(
Layer* layer_to_receive_animation) {
DCHECK(!proxy() || proxy()->IsMainThread());
if (layer_tree_host_.get() && layer_tree_host_->root_layer()) {
AddOpacityTransitionToLayer(layer_tree_host_->root_layer(),
if (layer_to_receive_animation) {
AddOpacityTransitionToLayer(layer_to_receive_animation,
0,
0,
0.5,
......@@ -461,8 +464,13 @@ void LayerTreeTest::DispatchAddInstantAnimation() {
void LayerTreeTest::DispatchAddAnimation(Layer* layer_to_receive_animation) {
DCHECK(!proxy() || proxy()->IsMainThread());
if (layer_to_receive_animation)
AddOpacityTransitionToLayer(layer_to_receive_animation, 10, 0, 0.5, true);
if (layer_to_receive_animation) {
AddOpacityTransitionToLayer(layer_to_receive_animation,
0.000001,
0,
0.5,
true);
}
}
void LayerTreeTest::DispatchSetNeedsCommit() {
......@@ -514,7 +522,9 @@ void LayerTreeTest::DispatchComposite() {
}
schedule_when_set_visible_true_ = false;
layer_tree_host_->Composite(base::TimeTicks::Now());
base::TimeTicks now = base::TimeTicks::Now();
layer_tree_host_->UpdateAnimations(now);
layer_tree_host_->Composite(now);
}
void LayerTreeTest::RunTest(bool threaded) {
......
......@@ -105,7 +105,7 @@ class LayerTreeTest : public testing::Test, public TestHooks {
void EndTestAfterDelay(int delay_milliseconds);
void PostAddAnimationToMainThread(Layer* layer_to_receive_animation);
void PostAddInstantAnimationToMainThread();
void PostAddInstantAnimationToMainThread(Layer* layer_to_receive_animation);
void PostSetNeedsCommitToMainThread();
void PostAcquireLayerTextures();
void PostSetNeedsRedrawToMainThread();
......@@ -123,8 +123,8 @@ class LayerTreeTest : public testing::Test, public TestHooks {
void RealEndTest();
void DispatchAddInstantAnimation();
void DispatchAddAnimation(Layer* layer_to_receive_animation);
virtual void DispatchAddInstantAnimation(Layer* layer_to_receive_animation);
virtual void DispatchAddAnimation(Layer* layer_to_receive_animation);
void DispatchSetNeedsCommit();
void DispatchAcquireLayerTextures();
void DispatchSetNeedsRedraw();
......
......@@ -1040,7 +1040,8 @@ void LayerTreeHost::AnimateLayers(base::TimeTicks time) {
iter != copy.end();
++iter) {
(*iter).second->Animate(monotonic_time);
(*iter).second->UpdateState(NULL);
bool start_ready_animations = true;
(*iter).second->UpdateState(start_ready_animations, NULL);
}
}
......
......@@ -112,6 +112,9 @@ class LayerTreeHostImplTimeSourceAdapter : public TimeSourceClient {
layer_tree_host_impl_->ActivatePendingTreeIfNeeded();
layer_tree_host_impl_->Animate(base::TimeTicks::Now(), base::Time::Now());
layer_tree_host_impl_->UpdateBackgroundAnimateTicking(true);
bool start_ready_animations = true;
layer_tree_host_impl_->UpdateAnimationState(start_ready_animations);
layer_tree_host_impl_->BeginNextFrame();
}
......@@ -675,7 +678,11 @@ bool LayerTreeHostImpl::CalculateRenderPasses(FrameData* frame) {
return draw_frame;
}
void LayerTreeHostImpl::SetBackgroundTickingEnabled(bool enabled) {
void LayerTreeHostImpl::UpdateBackgroundAnimateTicking(
bool should_background_tick) {
bool enabled = should_background_tick &&
!animation_registrar_->active_animation_controllers().empty();
// Lazily create the time_source adapter so that we can vary the interval for
// testing.
if (!time_source_client_adapter_) {
......@@ -1042,7 +1049,6 @@ void LayerTreeHostImpl::DrawLayers(FrameData* frame,
DidDrawDamagedArea();
}
active_tree_->root_layer()->ResetAllChangeTrackingForSubtree();
UpdateAnimationState();
}
void LayerTreeHostImpl::DidDrawAllLayers(const FrameData& frame) {
......@@ -1247,10 +1253,6 @@ void LayerTreeHostImpl::SetVisible(bool visible) {
return;
renderer_->SetVisible(visible);
SetBackgroundTickingEnabled(
!visible_ &&
!animation_registrar_->active_animation_controllers().empty());
}
bool LayerTreeHostImpl::InitializeRenderer(
......@@ -1822,12 +1824,9 @@ void LayerTreeHostImpl::AnimateLayers(base::TimeTicks monotonic_time,
(*iter).second->Animate(monotonic_seconds);
client_->SetNeedsRedrawOnImplThread();
SetBackgroundTickingEnabled(
!visible_ &&
!animation_registrar_->active_animation_controllers().empty());
}
void LayerTreeHostImpl::UpdateAnimationState() {
void LayerTreeHostImpl::UpdateAnimationState(bool start_ready_animations) {
if (!settings_.accelerated_animation_enabled ||
animation_registrar_->active_animation_controllers().empty() ||
!active_tree_->root_layer())
......@@ -1841,7 +1840,7 @@ void LayerTreeHostImpl::UpdateAnimationState() {
for (AnimationRegistrar::AnimationControllerMap::iterator iter = copy.begin();
iter != copy.end();
++iter)
(*iter).second->UpdateState(events.get());
(*iter).second->UpdateState(start_ready_animations, events.get());
if (!events->empty()) {
client_->PostAnimationEventsToMainThreadOnImplThread(events.Pass(),
......
......@@ -140,6 +140,8 @@ class CC_EXPORT LayerTreeHostImpl : public InputHandlerClient,
virtual void CommitComplete();
virtual void Animate(base::TimeTicks monotonic_time,
base::Time wall_clock_time);
virtual void UpdateAnimationState(bool start_ready_animations);
void UpdateBackgroundAnimateTicking(bool should_background_tick);
void ManageTiles();
void SetAnticipatedDrawTime(base::TimeTicks time);
......@@ -356,7 +358,6 @@ class CC_EXPORT LayerTreeHostImpl : public InputHandlerClient,
// Virtual for testing.
virtual void AnimateLayers(base::TimeTicks monotonic_time,
base::Time wall_clock_time);
virtual void UpdateAnimationState();
// Virtual for testing.
virtual base::TimeDelta LowFrequencyAnimationInterval() const;
......@@ -389,7 +390,6 @@ class CC_EXPORT LayerTreeHostImpl : public InputHandlerClient,
// only be called from PrepareToDraw, as DidDrawAllLayers must be called
// if this helper function is called.
bool CalculateRenderPasses(FrameData* frame);
void SetBackgroundTickingEnabled(bool enabled);
void SendDidLoseOutputSurfaceRecursive(LayerImpl* current);
void ClearRenderSurfaces();
......
......@@ -259,6 +259,11 @@ void SingleThreadProxy::Stop() {
layer_tree_host_ = NULL;
}
void SingleThreadProxy::OnCanDrawStateChanged(bool can_draw) {
DCHECK(Proxy::IsImplThread());
layer_tree_host_impl_->UpdateBackgroundAnimateTicking(!ShouldComposite());
}
void SingleThreadProxy::SetNeedsRedrawOnImplThread() {
layer_tree_host_->ScheduleComposite();
}
......@@ -384,6 +389,12 @@ bool SingleThreadProxy::CommitAndComposite(base::TimeTicks frame_begin_time) {
return result;
}
bool SingleThreadProxy::ShouldComposite() const {
DCHECK(Proxy::IsImplThread());
return layer_tree_host_impl_->visible() &&
layer_tree_host_impl_->CanDraw();
}
bool SingleThreadProxy::DoComposite(
scoped_refptr<cc::ContextProvider> offscreen_context_provider,
base::TimeTicks frame_begin_time) {
......@@ -395,17 +406,17 @@ bool SingleThreadProxy::DoComposite(
layer_tree_host_impl_->resource_provider()->
set_offscreen_context_provider(offscreen_context_provider);
if (!layer_tree_host_impl_->visible())
// We guard PrepareToDraw() with CanDraw() because it always returns a valid
// frame, so can only be used when such a frame is possible. Since
// DrawLayers() depends on the result of PrepareToDraw(), it is guarded on
// CanDraw() as well.
if (!ShouldComposite()) {
layer_tree_host_impl_->UpdateBackgroundAnimateTicking(true);
return false;
}
layer_tree_host_impl_->Animate(base::TimeTicks::Now(), base::Time::Now());
// We guard prepareToDraw() with canDraw() because it always returns a valid
// frame, so can only be used when such a frame is possible. Since
// drawLayers() depends on the result of prepareToDraw(), it is guarded on
// canDraw() as well.
if (!layer_tree_host_impl_->CanDraw())
return false;
layer_tree_host_impl_->UpdateBackgroundAnimateTicking(false);
LayerTreeHostImpl::FrameData frame;
layer_tree_host_impl_->PrepareToDraw(&frame);
......@@ -413,6 +424,9 @@ bool SingleThreadProxy::DoComposite(
layer_tree_host_impl_->DidDrawAllLayers(frame);
output_surface_lost_ = layer_tree_host_impl_->IsContextLost();
bool start_ready_animations = true;
layer_tree_host_impl_->UpdateAnimationState(start_ready_animations);
layer_tree_host_impl_->BeginNextFrame();
}
......
......@@ -53,7 +53,7 @@ class SingleThreadProxy : public Proxy, LayerTreeHostImplClient {
virtual void OnVSyncParametersChanged(base::TimeTicks timebase,
base::TimeDelta interval) OVERRIDE {}
virtual void DidVSync(base::TimeTicks frame_time) OVERRIDE {}
virtual void OnCanDrawStateChanged(bool can_draw) OVERRIDE {}
virtual void OnCanDrawStateChanged(bool can_draw) OVERRIDE;
virtual void OnHasPendingTreeStateChanged(bool have_pending_tree) OVERRIDE;
virtual void SetNeedsRedrawOnImplThread() OVERRIDE;
virtual void DidInitializeVisibleTileOnImplThread() OVERRIDE;
......@@ -85,6 +85,8 @@ class SingleThreadProxy : public Proxy, LayerTreeHostImplClient {
base::TimeTicks frame_begin_time);
void DidSwapFrame();
bool ShouldComposite() const;
// Accessed on main thread only.
LayerTreeHost* layer_tree_host_;
bool output_surface_lost_;
......
......@@ -348,6 +348,8 @@ void ThreadProxy::OnCanDrawStateChanged(bool can_draw) {
TRACE_EVENT1(
"cc", "ThreadProxy::OnCanDrawStateChanged", "can_draw", can_draw);
scheduler_on_impl_thread_->SetCanDraw(can_draw);
layer_tree_host_impl_->UpdateBackgroundAnimateTicking(
!scheduler_on_impl_thread_->WillDrawIfNeeded());
}
void ThreadProxy::OnHasPendingTreeStateChanged(bool has_pending_tree) {
......@@ -811,6 +813,9 @@ void ThreadProxy::ScheduledActionCommit() {
layer_tree_host_->FinishCommitOnImplThread(layer_tree_host_impl_.get());
layer_tree_host_impl_->CommitComplete();
layer_tree_host_impl_->UpdateBackgroundAnimateTicking(
!scheduler_on_impl_thread_->WillDrawIfNeeded());
next_frame_is_newly_committed_frame_on_impl_thread_ = true;
if (layer_tree_host_->settings().impl_side_painting &&
......@@ -876,30 +881,42 @@ ThreadProxy::ScheduledActionDrawAndSwapInternal(bool forced_draw) {
layer_tree_host_impl_->ActivatePendingTreeIfNeeded();
layer_tree_host_impl_->Animate(monotonic_time, wall_clock_time);
layer_tree_host_impl_->UpdateBackgroundAnimateTicking(false);
// This method is called on a forced draw, regardless of whether we are able
// to produce a frame, as the calling site on main thread is blocked until its
// request completes, and we signal completion here. If canDraw() is false, we
// request completes, and we signal completion here. If CanDraw() is false, we
// will indicate success=false to the caller, but we must still signal
// completion to avoid deadlock.
// We guard prepareToDraw() with canDraw() because it always returns a valid
// We guard PrepareToDraw() with CanDraw() because it always returns a valid
// frame, so can only be used when such a frame is possible. Since
// drawLayers() depends on the result of prepareToDraw(), it is guarded on
// canDraw() as well.
// DrawLayers() depends on the result of PrepareToDraw(), it is guarded on
// CanDraw() as well.
LayerTreeHostImpl::FrameData frame;
bool draw_frame =
layer_tree_host_impl_->CanDraw() &&
(layer_tree_host_impl_->PrepareToDraw(&frame) || forced_draw);
bool draw_frame = false;
bool start_ready_animations = true;
if (layer_tree_host_impl_->CanDraw()) {
// Do not start animations if we skip drawing the frame to avoid
// checkerboarding.
if (layer_tree_host_impl_->PrepareToDraw(&frame) || forced_draw)
draw_frame = true;
else
start_ready_animations = false;
}
if (draw_frame) {
layer_tree_host_impl_->DrawLayers(
&frame,
scheduler_on_impl_thread_->LastVSyncTime());
result.did_draw= true;
result.did_draw = true;
}
layer_tree_host_impl_->DidDrawAllLayers(frame);
layer_tree_host_impl_->UpdateAnimationState(start_ready_animations);
// Check for tree activation.
if (completion_event_for_commit_held_on_tree_activation_ &&
!layer_tree_host_impl_->pending_tree()) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment