// Copyright 2011 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/scheduler/scheduler_state_machine.h" #include "cc/scheduler/scheduler.h" #include "testing/gtest/include/gtest/gtest.h" namespace cc { namespace { const SchedulerStateMachine::CommitState all_commit_states[] = { SchedulerStateMachine::COMMIT_STATE_IDLE, SchedulerStateMachine::COMMIT_STATE_FRAME_IN_PROGRESS, SchedulerStateMachine::COMMIT_STATE_READY_TO_COMMIT, SchedulerStateMachine::COMMIT_STATE_WAITING_FOR_FIRST_DRAW }; // Exposes the protected state fields of the SchedulerStateMachine for testing class StateMachine : public SchedulerStateMachine { public: explicit StateMachine(const SchedulerSettings& scheduler_settings) : SchedulerStateMachine(scheduler_settings) {} void SetCommitState(CommitState cs) { commit_state_ = cs; } CommitState CommitState() const { return commit_state_; } bool NeedsCommit() const { return needs_commit_; } void SetNeedsRedraw(bool b) { needs_redraw_ = b; } bool NeedsRedraw() const { return needs_redraw_; } void SetNeedsForcedRedraw(bool b) { needs_forced_redraw_ = b; } bool NeedsForcedRedraw() const { return needs_forced_redraw_; } bool CanDraw() const { return can_draw_; } bool Visible() const { return visible_; } }; TEST(SchedulerStateMachineTest, TestNextActionBeginsMainFrameIfNeeded) { SchedulerSettings default_scheduler_settings; // If no commit needed, do nothing. { StateMachine state(default_scheduler_settings); state.SetCanStart(); state.UpdateState(state.NextAction()); state.DidCreateAndInitializeOutputSurface(); state.SetCommitState(SchedulerStateMachine::COMMIT_STATE_IDLE); state.SetNeedsRedraw(false); state.SetVisible(true); EXPECT_FALSE(state.BeginFrameNeededToDrawByImplThread()); state.DidLeaveBeginFrame(); EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction()); EXPECT_FALSE(state.BeginFrameNeededToDrawByImplThread()); state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting()); EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction()); } // If commit requested but can_start is still false, do nothing. { StateMachine state(default_scheduler_settings); state.SetCommitState(SchedulerStateMachine::COMMIT_STATE_IDLE); state.SetNeedsRedraw(false); state.SetVisible(true); EXPECT_FALSE(state.BeginFrameNeededToDrawByImplThread()); state.DidLeaveBeginFrame(); EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction()); EXPECT_FALSE(state.BeginFrameNeededToDrawByImplThread()); state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting()); EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction()); } // If commit requested, begin a main frame. { StateMachine state(default_scheduler_settings); state.SetCommitState(SchedulerStateMachine::COMMIT_STATE_IDLE); state.SetCanStart(); state.SetNeedsRedraw(false); state.SetVisible(true); EXPECT_FALSE(state.BeginFrameNeededToDrawByImplThread()); } // Begin the frame, make sure needs_commit and commit_state update correctly. { StateMachine state(default_scheduler_settings); state.SetCanStart(); state.UpdateState(state.NextAction()); state.DidCreateAndInitializeOutputSurface(); state.SetVisible(true); state.UpdateState( SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD); EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_FRAME_IN_PROGRESS, state.CommitState()); EXPECT_FALSE(state.NeedsCommit()); EXPECT_FALSE(state.BeginFrameNeededToDrawByImplThread()); } } TEST(SchedulerStateMachineTest, TestSetForcedRedrawDoesNotSetsNormalRedraw) { SchedulerSettings default_scheduler_settings; SchedulerStateMachine state(default_scheduler_settings); state.SetCanDraw(true); state.SetNeedsForcedRedraw(); EXPECT_FALSE(state.RedrawPending()); EXPECT_TRUE(state.BeginFrameNeededToDrawByImplThread()); } TEST(SchedulerStateMachineTest, TestFailedDrawSetsNeedsCommitAndDoesNotDrawAgain) { SchedulerSettings default_scheduler_settings; SchedulerStateMachine state(default_scheduler_settings); state.SetCanStart(); state.UpdateState(state.NextAction()); state.DidCreateAndInitializeOutputSurface(); state.SetVisible(true); state.SetCanDraw(true); state.SetNeedsRedraw(); EXPECT_TRUE(state.RedrawPending()); EXPECT_TRUE(state.BeginFrameNeededToDrawByImplThread()); state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting()); // We're drawing now. EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.NextAction()); state.UpdateState(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE); EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction()); EXPECT_FALSE(state.RedrawPending()); EXPECT_FALSE(state.CommitPending()); // Failing the draw makes us require a commit. state.DidDrawIfPossibleCompleted(false); EXPECT_EQ(SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD, state.NextAction()); state.UpdateState( SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD); EXPECT_TRUE(state.RedrawPending()); EXPECT_TRUE(state.CommitPending()); } TEST(SchedulerStateMachineTest, TestsetNeedsRedrawDuringFailedDrawDoesNotRemoveNeedsRedraw) { SchedulerSettings default_scheduler_settings; SchedulerStateMachine state(default_scheduler_settings); state.SetCanStart(); state.UpdateState(state.NextAction()); state.DidCreateAndInitializeOutputSurface(); state.SetVisible(true); state.SetCanDraw(true); state.SetNeedsRedraw(); EXPECT_TRUE(state.RedrawPending()); EXPECT_TRUE(state.BeginFrameNeededToDrawByImplThread()); state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting()); // We're drawing now. EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.NextAction()); state.UpdateState(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE); EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction()); EXPECT_FALSE(state.RedrawPending()); EXPECT_FALSE(state.CommitPending()); // While still in the same begin frame callback on the main thread, // set needs redraw again. This should not redraw. state.SetNeedsRedraw(); EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction()); // Failing the draw makes us require a commit. state.DidDrawIfPossibleCompleted(false); EXPECT_EQ(SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD, state.NextAction()); EXPECT_TRUE(state.RedrawPending()); } TEST(SchedulerStateMachineTest, TestCommitAfterFailedDrawAllowsDrawInSameFrame) { SchedulerSettings default_scheduler_settings; SchedulerStateMachine state(default_scheduler_settings); state.SetCanStart(); state.UpdateState(state.NextAction()); state.DidCreateAndInitializeOutputSurface(); state.SetVisible(true); state.SetCanDraw(true); // Start a commit. state.SetNeedsCommit(); EXPECT_EQ(SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD, state.NextAction()); state.UpdateState( SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD); EXPECT_TRUE(state.CommitPending()); // Then initiate a draw. state.SetNeedsRedraw(); EXPECT_TRUE(state.BeginFrameNeededToDrawByImplThread()); state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting()); EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.NextAction()); EXPECT_TRUE(state.RedrawPending()); // Fail the draw. state.UpdateState(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE); EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction()); state.DidDrawIfPossibleCompleted(false); EXPECT_TRUE(state.RedrawPending()); // But the commit is ongoing. EXPECT_TRUE(state.CommitPending()); // Finish the commit. state.FinishCommit(); EXPECT_EQ(SchedulerStateMachine::ACTION_COMMIT, state.NextAction()); state.UpdateState(SchedulerStateMachine::ACTION_COMMIT); EXPECT_TRUE(state.RedrawPending()); // And we should be allowed to draw again. EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.NextAction()); } TEST(SchedulerStateMachineTest, TestCommitAfterFailedAndSuccessfulDrawDoesNotAllowDrawInSameFrame) { SchedulerSettings default_scheduler_settings; SchedulerStateMachine state(default_scheduler_settings); state.SetCanStart(); state.UpdateState(state.NextAction()); state.DidCreateAndInitializeOutputSurface(); state.SetVisible(true); state.SetCanDraw(true); // Start a commit. state.SetNeedsCommit(); EXPECT_EQ(SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD, state.NextAction()); state.UpdateState( SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD); EXPECT_TRUE(state.CommitPending()); // Then initiate a draw. state.SetNeedsRedraw(); EXPECT_TRUE(state.BeginFrameNeededToDrawByImplThread()); state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting()); EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.NextAction()); EXPECT_TRUE(state.RedrawPending()); // Fail the draw. state.UpdateState(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE); EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction()); state.DidDrawIfPossibleCompleted(false); EXPECT_TRUE(state.RedrawPending()); // But the commit is ongoing. EXPECT_TRUE(state.CommitPending()); // Force a draw. state.SetNeedsForcedRedraw(); EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_FORCED, state.NextAction()); // Do the forced draw. state.UpdateState(SchedulerStateMachine::ACTION_DRAW_FORCED); EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction()); EXPECT_FALSE(state.RedrawPending()); // And the commit is still ongoing. EXPECT_TRUE(state.CommitPending()); // Finish the commit. state.FinishCommit(); EXPECT_EQ(SchedulerStateMachine::ACTION_COMMIT, state.NextAction()); state.UpdateState(SchedulerStateMachine::ACTION_COMMIT); EXPECT_TRUE(state.RedrawPending()); // And we should not be allowed to draw again in the same frame.. EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction()); } TEST(SchedulerStateMachineTest, TestFailedDrawsWillEventuallyForceADrawAfterTheNextCommit) { SchedulerSettings default_scheduler_settings; SchedulerStateMachine state(default_scheduler_settings); state.SetCanStart(); state.UpdateState(state.NextAction()); state.DidCreateAndInitializeOutputSurface(); state.SetVisible(true); state.SetCanDraw(true); state.SetMaximumNumberOfFailedDrawsBeforeDrawIsForced(1); // Start a commit. state.SetNeedsCommit(); EXPECT_EQ(SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD, state.NextAction()); state.UpdateState( SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD); EXPECT_TRUE(state.CommitPending()); // Then initiate a draw. state.SetNeedsRedraw(); EXPECT_TRUE(state.BeginFrameNeededToDrawByImplThread()); state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting()); EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.NextAction()); EXPECT_TRUE(state.RedrawPending()); // Fail the draw. state.UpdateState(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE); EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction()); state.DidDrawIfPossibleCompleted(false); EXPECT_TRUE(state.RedrawPending()); // But the commit is ongoing. EXPECT_TRUE(state.CommitPending()); // Finish the commit. Note, we should not yet be forcing a draw, but should // continue the commit as usual. state.FinishCommit(); EXPECT_EQ(SchedulerStateMachine::ACTION_COMMIT, state.NextAction()); state.UpdateState(SchedulerStateMachine::ACTION_COMMIT); EXPECT_TRUE(state.RedrawPending()); // The redraw should be forced in this case. EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_FORCED, state.NextAction()); } TEST(SchedulerStateMachineTest, TestFailedDrawIsRetriedInNextBeginFrameForImplThread) { SchedulerSettings default_scheduler_settings; SchedulerStateMachine state(default_scheduler_settings); state.SetCanStart(); state.UpdateState(state.NextAction()); state.DidCreateAndInitializeOutputSurface(); state.SetVisible(true); state.SetCanDraw(true); // Start a draw. state.SetNeedsRedraw(); EXPECT_TRUE(state.BeginFrameNeededToDrawByImplThread()); state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting()); EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.NextAction()); EXPECT_TRUE(state.RedrawPending()); // Fail the draw. state.UpdateState(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE); EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction()); state.DidDrawIfPossibleCompleted(false); EXPECT_TRUE(state.RedrawPending()); // We should not be trying to draw again now, but we have a commit pending. EXPECT_EQ(SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD, state.NextAction()); state.DidLeaveBeginFrame(); EXPECT_TRUE(state.BeginFrameNeededToDrawByImplThread()); state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting()); // We should try to draw again in the next begin frame on the impl thread. EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.NextAction()); } TEST(SchedulerStateMachineTest, TestDoestDrawTwiceInSameFrame) { SchedulerSettings default_scheduler_settings; SchedulerStateMachine state(default_scheduler_settings); state.SetCanStart(); state.UpdateState(state.NextAction()); state.DidCreateAndInitializeOutputSurface(); state.SetVisible(true); state.SetCanDraw(true); state.SetNeedsRedraw(); EXPECT_TRUE(state.BeginFrameNeededToDrawByImplThread()); state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting()); EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.NextAction()); state.UpdateState(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE); // While still in the same begin frame for the impl thread, set needs redraw // again. This should not redraw. state.SetNeedsRedraw(); EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction()); // Move to another frame. This should now draw. state.DidDrawIfPossibleCompleted(true); state.DidLeaveBeginFrame(); EXPECT_TRUE(state.BeginFrameNeededToDrawByImplThread()); state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting()); EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.NextAction()); state.UpdateState(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE); state.DidDrawIfPossibleCompleted(true); EXPECT_FALSE(state.BeginFrameNeededToDrawByImplThread()); } TEST(SchedulerStateMachineTest, TestNextActionDrawsOnBeginFrame) { SchedulerSettings default_scheduler_settings; // When not in BeginFrame, or in BeginFrame but not visible, // don't draw. size_t num_commit_states = sizeof(all_commit_states) / sizeof(SchedulerStateMachine::CommitState); for (size_t i = 0; i < num_commit_states; ++i) { for (size_t j = 0; j < 2; ++j) { StateMachine state(default_scheduler_settings); state.SetCanStart(); state.UpdateState(state.NextAction()); state.DidCreateAndInitializeOutputSurface(); state.SetCommitState(all_commit_states[i]); bool visible = j; if (!visible) { state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting()); state.SetVisible(false); } else { state.SetVisible(true); } // Case 1: needs_commit=false EXPECT_NE(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.NextAction()); // Case 2: needs_commit=true state.SetNeedsCommit(); EXPECT_NE(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.NextAction()); } } // When in BeginFrame, or not in BeginFrame but needs_forced_dedraw // set, should always draw except if you're ready to commit, in which case // commit. for (size_t i = 0; i < num_commit_states; ++i) { for (size_t j = 0; j < 2; ++j) { StateMachine state(default_scheduler_settings); state.SetCanStart(); state.UpdateState(state.NextAction()); state.DidCreateAndInitializeOutputSurface(); state.SetCanDraw(true); state.SetCommitState(all_commit_states[i]); bool forced_draw = j; if (!forced_draw) { state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting()); state.SetNeedsRedraw(true); state.SetVisible(true); } else { state.SetNeedsForcedRedraw(true); } SchedulerStateMachine::Action expected_action; if (all_commit_states[i] != SchedulerStateMachine::COMMIT_STATE_READY_TO_COMMIT) { expected_action = forced_draw ? SchedulerStateMachine::ACTION_DRAW_FORCED : SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE; } else { expected_action = SchedulerStateMachine::ACTION_COMMIT; } // Case 1: needs_commit=false. EXPECT_TRUE(state.BeginFrameNeededToDrawByImplThread()); EXPECT_EQ(expected_action, state.NextAction()); // Case 2: needs_commit=true. state.SetNeedsCommit(); EXPECT_TRUE(state.BeginFrameNeededToDrawByImplThread()); EXPECT_EQ(expected_action, state.NextAction()); } } } TEST(SchedulerStateMachineTest, TestNoCommitStatesRedrawWhenInvisible) { SchedulerSettings default_scheduler_settings; size_t num_commit_states = sizeof(all_commit_states) / sizeof(SchedulerStateMachine::CommitState); for (size_t i = 0; i < num_commit_states; ++i) { // There shouldn't be any drawing regardless of BeginFrame. for (size_t j = 0; j < 2; ++j) { StateMachine state(default_scheduler_settings); state.SetCanStart(); state.UpdateState(state.NextAction()); state.DidCreateAndInitializeOutputSurface(); state.SetCommitState(all_commit_states[i]); state.SetVisible(false); state.SetNeedsRedraw(true); state.SetNeedsForcedRedraw(false); if (j == 1) state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting()); // Case 1: needs_commit=false. EXPECT_NE(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.NextAction()); // Case 2: needs_commit=true. state.SetNeedsCommit(); EXPECT_NE(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.NextAction()); } } } TEST(SchedulerStateMachineTest, TestCanRedraw_StopsDraw) { SchedulerSettings default_scheduler_settings; size_t num_commit_states = sizeof(all_commit_states) / sizeof(SchedulerStateMachine::CommitState); for (size_t i = 0; i < num_commit_states; ++i) { // There shouldn't be any drawing regardless of BeginFrame. for (size_t j = 0; j < 2; ++j) { StateMachine state(default_scheduler_settings); state.SetCanStart(); state.UpdateState(state.NextAction()); state.DidCreateAndInitializeOutputSurface(); state.SetCommitState(all_commit_states[i]); state.SetVisible(false); state.SetNeedsRedraw(true); state.SetNeedsForcedRedraw(false); if (j == 1) state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting()); state.SetCanDraw(false); EXPECT_NE(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.NextAction()); } } } TEST(SchedulerStateMachineTest, TestCanRedrawWithWaitingForFirstDrawMakesProgress) { SchedulerSettings default_scheduler_settings; StateMachine state(default_scheduler_settings); state.SetCanStart(); state.UpdateState(state.NextAction()); state.DidCreateAndInitializeOutputSurface(); state.SetCommitState( SchedulerStateMachine::COMMIT_STATE_WAITING_FOR_FIRST_DRAW); state.SetNeedsCommit(); state.SetNeedsRedraw(true); state.SetVisible(true); state.SetCanDraw(false); EXPECT_EQ(SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD, state.NextAction()); } TEST(SchedulerStateMachineTest, TestsetNeedsCommitIsNotLost) { SchedulerSettings default_scheduler_settings; StateMachine state(default_scheduler_settings); state.SetCanStart(); state.UpdateState(state.NextAction()); state.DidCreateAndInitializeOutputSurface(); state.SetNeedsCommit(); state.SetVisible(true); state.SetCanDraw(true); // Begin the frame. EXPECT_EQ(SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD, state.NextAction()); state.UpdateState(state.NextAction()); EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_FRAME_IN_PROGRESS, state.CommitState()); // Now, while the frame is in progress, set another commit. state.SetNeedsCommit(); EXPECT_TRUE(state.NeedsCommit()); // Let the frame finish. state.FinishCommit(); EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_READY_TO_COMMIT, state.CommitState()); // Expect to commit regardless of BeginFrame state. state.DidLeaveBeginFrame(); EXPECT_EQ(SchedulerStateMachine::ACTION_COMMIT, state.NextAction()); state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting()); EXPECT_EQ(SchedulerStateMachine::ACTION_COMMIT, state.NextAction()); // Commit and make sure we draw on next BeginFrame state.UpdateState(SchedulerStateMachine::ACTION_COMMIT); EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.NextAction()); EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_WAITING_FOR_FIRST_DRAW, state.CommitState()); state.UpdateState(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE); state.DidDrawIfPossibleCompleted(true); // Verify that another commit will begin. state.DidLeaveBeginFrame(); EXPECT_EQ(SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD, state.NextAction()); } TEST(SchedulerStateMachineTest, TestFullCycle) { SchedulerSettings default_scheduler_settings; StateMachine state(default_scheduler_settings); state.SetCanStart(); state.UpdateState(state.NextAction()); state.DidCreateAndInitializeOutputSurface(); state.SetVisible(true); state.SetCanDraw(true); // Start clean and set commit. state.SetNeedsCommit(); EXPECT_EQ(SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD, state.NextAction()); // Begin the frame. state.UpdateState( SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD); EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_FRAME_IN_PROGRESS, state.CommitState()); EXPECT_FALSE(state.NeedsCommit()); EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction()); // Tell the scheduler the frame finished. state.FinishCommit(); EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_READY_TO_COMMIT, state.CommitState()); EXPECT_EQ(SchedulerStateMachine::ACTION_COMMIT, state.NextAction()); // Commit. state.UpdateState(SchedulerStateMachine::ACTION_COMMIT); EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_WAITING_FOR_FIRST_DRAW, state.CommitState()); EXPECT_TRUE(state.NeedsRedraw()); // Expect to do nothing until BeginFrame. EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction()); // At BeginFrame, draw. state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting()); EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.NextAction()); state.UpdateState(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE); state.DidDrawIfPossibleCompleted(true); state.DidLeaveBeginFrame(); // Should be synchronized, no draw needed, no action needed. EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_IDLE, state.CommitState()); EXPECT_FALSE(state.NeedsRedraw()); EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction()); } TEST(SchedulerStateMachineTest, TestFullCycleWithCommitRequestInbetween) { SchedulerSettings default_scheduler_settings; StateMachine state(default_scheduler_settings); state.SetCanStart(); state.UpdateState(state.NextAction()); state.DidCreateAndInitializeOutputSurface(); state.SetVisible(true); state.SetCanDraw(true); // Start clean and set commit. state.SetNeedsCommit(); EXPECT_EQ(SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD, state.NextAction()); // Begin the frame. state.UpdateState( SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD); EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_FRAME_IN_PROGRESS, state.CommitState()); EXPECT_FALSE(state.NeedsCommit()); EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction()); // Request another commit while the commit is in flight. state.SetNeedsCommit(); EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction()); // Tell the scheduler the frame finished. state.FinishCommit(); EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_READY_TO_COMMIT, state.CommitState()); EXPECT_EQ(SchedulerStateMachine::ACTION_COMMIT, state.NextAction()); // Commit. state.UpdateState(SchedulerStateMachine::ACTION_COMMIT); EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_WAITING_FOR_FIRST_DRAW, state.CommitState()); EXPECT_TRUE(state.NeedsRedraw()); // Expect to do nothing until BeginFrame. EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction()); // At BeginFrame, draw. state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting()); EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.NextAction()); state.UpdateState(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE); state.DidDrawIfPossibleCompleted(true); state.DidLeaveBeginFrame(); // Should be synchronized, no draw needed, no action needed. EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_IDLE, state.CommitState()); EXPECT_FALSE(state.NeedsRedraw()); EXPECT_EQ(SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD, state.NextAction()); } TEST(SchedulerStateMachineTest, TestRequestCommitInvisible) { SchedulerSettings default_scheduler_settings; StateMachine state(default_scheduler_settings); state.SetCanStart(); state.UpdateState(state.NextAction()); state.DidCreateAndInitializeOutputSurface(); state.SetNeedsCommit(); EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction()); } TEST(SchedulerStateMachineTest, TestGoesInvisibleBeforeFinishCommit) { SchedulerSettings default_scheduler_settings; StateMachine state(default_scheduler_settings); state.SetCanStart(); state.UpdateState(state.NextAction()); state.DidCreateAndInitializeOutputSurface(); state.SetVisible(true); state.SetCanDraw(true); // Start clean and set commit. state.SetNeedsCommit(); EXPECT_EQ(SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD, state.NextAction()); // Begin the frame while visible. state.UpdateState( SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD); EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_FRAME_IN_PROGRESS, state.CommitState()); EXPECT_FALSE(state.NeedsCommit()); EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction()); // Become invisible and abort the main thread's begin frame. state.SetVisible(false); state.BeginFrameAbortedByMainThread(); // We should now be back in the idle state as if we didn't start a frame at // all. EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_IDLE, state.CommitState()); EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction()); // Become visible again. state.SetVisible(true); // We should be beginning a frame now. EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_IDLE, state.CommitState()); EXPECT_EQ(SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD, state.NextAction()); // Begin the frame. state.UpdateState(state.NextAction()); // We should be starting the commit now. EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_FRAME_IN_PROGRESS, state.CommitState()); } TEST(SchedulerStateMachineTest, TestFirstContextCreation) { SchedulerSettings default_scheduler_settings; StateMachine state(default_scheduler_settings); state.SetCanStart(); state.SetVisible(true); state.SetCanDraw(true); EXPECT_EQ(SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION, state.NextAction()); state.UpdateState(state.NextAction()); state.DidCreateAndInitializeOutputSurface(); EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction()); // Check that the first init does not SetNeedsCommit. state.SetNeedsCommit(); EXPECT_NE(SchedulerStateMachine::ACTION_NONE, state.NextAction()); } TEST(SchedulerStateMachineTest, TestContextLostWhenCompletelyIdle) { SchedulerSettings default_scheduler_settings; StateMachine state(default_scheduler_settings); state.SetCanStart(); state.UpdateState(state.NextAction()); state.DidCreateAndInitializeOutputSurface(); state.SetVisible(true); state.SetCanDraw(true); EXPECT_NE(SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION, state.NextAction()); state.DidLoseOutputSurface(); EXPECT_EQ(SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION, state.NextAction()); state.UpdateState(state.NextAction()); // Once context recreation begins, nothing should happen. EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction()); // Recreate the context. state.DidCreateAndInitializeOutputSurface(); // When the context is recreated, we should begin a commit. EXPECT_EQ(SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD, state.NextAction()); state.UpdateState(state.NextAction()); } TEST(SchedulerStateMachineTest, TestContextLostWhenIdleAndCommitRequestedWhileRecreating) { SchedulerSettings default_scheduler_settings; StateMachine state(default_scheduler_settings); state.SetCanStart(); state.UpdateState(state.NextAction()); state.DidCreateAndInitializeOutputSurface(); state.SetVisible(true); state.SetCanDraw(true); EXPECT_NE(SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION, state.NextAction()); state.DidLoseOutputSurface(); EXPECT_EQ(SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION, state.NextAction()); state.UpdateState(state.NextAction()); // Once context recreation begins, nothing should happen. EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction()); // While context is recreating, commits shouldn't begin. state.SetNeedsCommit(); EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction()); // Recreate the context state.DidCreateAndInitializeOutputSurface(); // When the context is recreated, we should begin a commit EXPECT_EQ(SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD, state.NextAction()); state.UpdateState(state.NextAction()); // Once the context is recreated, whether we draw should be based on // SetCanDraw. state.SetNeedsRedraw(true); state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting()); EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.NextAction()); state.SetCanDraw(false); EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction()); state.SetCanDraw(true); state.DidLeaveBeginFrame(); } TEST(SchedulerStateMachineTest, TestContextLostWhileCommitInProgress) { SchedulerSettings default_scheduler_settings; StateMachine state(default_scheduler_settings); state.SetCanStart(); state.UpdateState(state.NextAction()); state.DidCreateAndInitializeOutputSurface(); state.SetVisible(true); state.SetCanDraw(true); // Get a commit in flight. state.SetNeedsCommit(); EXPECT_EQ(SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD, state.NextAction()); state.UpdateState(state.NextAction()); // Set damage and expect a draw. state.SetNeedsRedraw(true); state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting()); EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.NextAction()); state.UpdateState(state.NextAction()); state.DidLeaveBeginFrame(); // Cause a lost context while the begin frame is in flight // for the main thread. state.DidLoseOutputSurface(); // Ask for another draw. Expect nothing happens. state.SetNeedsRedraw(true); EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction()); // Finish the frame, and commit. state.FinishCommit(); EXPECT_EQ(SchedulerStateMachine::ACTION_COMMIT, state.NextAction()); state.UpdateState(state.NextAction()); EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_WAITING_FOR_FIRST_DRAW, state.CommitState()); EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.NextAction()); state.UpdateState(state.NextAction()); // Expect to be told to begin context recreation, independent of // BeginFrame state. state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting()); EXPECT_EQ(SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION, state.NextAction()); state.DidLeaveBeginFrame(); EXPECT_EQ(SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION, state.NextAction()); } TEST(SchedulerStateMachineTest, TestContextLostWhileCommitInProgressAndAnotherCommitRequested) { SchedulerSettings default_scheduler_settings; StateMachine state(default_scheduler_settings); state.SetCanStart(); state.UpdateState(state.NextAction()); state.DidCreateAndInitializeOutputSurface(); state.SetVisible(true); state.SetCanDraw(true); // Get a commit in flight. state.SetNeedsCommit(); EXPECT_EQ(SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD, state.NextAction()); state.UpdateState(state.NextAction()); // Set damage and expect a draw. state.SetNeedsRedraw(true); state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting()); EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.NextAction()); state.UpdateState(state.NextAction()); state.DidLeaveBeginFrame(); // Cause a lost context while the begin frame is in flight // for the main thread. state.DidLoseOutputSurface(); // Ask for another draw and also set needs commit. Expect nothing happens. state.SetNeedsRedraw(true); state.SetNeedsCommit(); EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction()); // Finish the frame, and commit. state.FinishCommit(); EXPECT_EQ(SchedulerStateMachine::ACTION_COMMIT, state.NextAction()); state.UpdateState(state.NextAction()); EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_WAITING_FOR_FIRST_DRAW, state.CommitState()); EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.NextAction()); state.UpdateState(state.NextAction()); // Expect to be told to begin context recreation, independent of // BeginFrame state state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting()); EXPECT_EQ(SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION, state.NextAction()); state.DidLeaveBeginFrame(); EXPECT_EQ(SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION, state.NextAction()); } TEST(SchedulerStateMachineTest, TestFinishAllRenderingWhileContextLost) { SchedulerSettings default_scheduler_settings; StateMachine state(default_scheduler_settings); state.SetCanStart(); state.UpdateState(state.NextAction()); state.DidCreateAndInitializeOutputSurface(); state.SetVisible(true); state.SetCanDraw(true); // Cause a lost context lost. state.DidLoseOutputSurface(); // Ask a forced redraw and verify it ocurrs. state.SetNeedsForcedRedraw(true); state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting()); EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_FORCED, state.NextAction()); state.DidLeaveBeginFrame(); // Clear the forced redraw bit. state.SetNeedsForcedRedraw(false); // Expect to be told to begin context recreation, independent of // BeginFrame state EXPECT_EQ(SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION, state.NextAction()); state.UpdateState(state.NextAction()); // Ask a forced redraw and verify it ocurrs. state.SetNeedsForcedRedraw(true); state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting()); EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_FORCED, state.NextAction()); state.DidLeaveBeginFrame(); } TEST(SchedulerStateMachineTest, DontDrawBeforeCommitAfterLostOutputSurface) { SchedulerSettings default_scheduler_settings; StateMachine state(default_scheduler_settings); state.SetCanStart(); state.UpdateState(state.NextAction()); state.DidCreateAndInitializeOutputSurface(); state.SetVisible(true); state.SetCanDraw(true); state.SetNeedsRedraw(true); // Cause a lost output surface, and restore it. state.DidLoseOutputSurface(); EXPECT_EQ(SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION, state.NextAction()); state.UpdateState(state.NextAction()); state.DidCreateAndInitializeOutputSurface(); EXPECT_FALSE(state.RedrawPending()); EXPECT_EQ(SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD, state.NextAction()); } TEST(SchedulerStateMachineTest, TestSendBeginFrameToMainThreadWhenInvisibleAndForceCommit) { SchedulerSettings default_scheduler_settings; StateMachine state(default_scheduler_settings); state.SetCanStart(); state.UpdateState(state.NextAction()); state.DidCreateAndInitializeOutputSurface(); state.SetVisible(false); state.SetNeedsCommit(); state.SetNeedsForcedCommit(); EXPECT_EQ(SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD, state.NextAction()); } TEST(SchedulerStateMachineTest, TestSendBeginFrameToMainThreadWhenCanStartFalseAndForceCommit) { SchedulerSettings default_scheduler_settings; StateMachine state(default_scheduler_settings); state.SetVisible(true); state.SetCanDraw(true); state.SetNeedsCommit(); state.SetNeedsForcedCommit(); EXPECT_EQ(SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD, state.NextAction()); } TEST(SchedulerStateMachineTest, TestFinishCommitWhenCommitInProgress) { SchedulerSettings default_scheduler_settings; StateMachine state(default_scheduler_settings); state.SetCanStart(); state.UpdateState(state.NextAction()); state.DidCreateAndInitializeOutputSurface(); state.SetVisible(false); state.SetCommitState(SchedulerStateMachine::COMMIT_STATE_FRAME_IN_PROGRESS); state.SetNeedsCommit(); state.FinishCommit(); EXPECT_EQ(SchedulerStateMachine::ACTION_COMMIT, state.NextAction()); state.UpdateState(state.NextAction()); EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_WAITING_FOR_FIRST_DRAW, state.CommitState()); EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction()); } TEST(SchedulerStateMachineTest, TestFinishCommitWhenForcedCommitInProgress) { SchedulerSettings default_scheduler_settings; StateMachine state(default_scheduler_settings); state.SetCanStart(); state.UpdateState(state.NextAction()); state.DidCreateAndInitializeOutputSurface(); state.SetVisible(false); state.SetCommitState(SchedulerStateMachine::COMMIT_STATE_FRAME_IN_PROGRESS); state.SetNeedsCommit(); state.SetNeedsForcedCommit(); state.FinishCommit(); EXPECT_EQ(SchedulerStateMachine::ACTION_COMMIT, state.NextAction()); state.UpdateState(state.NextAction()); EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_WAITING_FOR_FIRST_FORCED_DRAW, state.CommitState()); // If we are waiting for forced draw then we know a begin frame is already // in flight for the main thread. EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction()); } TEST(SchedulerStateMachineTest, TestSendBeginFrameToMainThreadWhenContextLost) { SchedulerSettings default_scheduler_settings; StateMachine state(default_scheduler_settings); state.SetCanStart(); state.UpdateState(state.NextAction()); state.DidCreateAndInitializeOutputSurface(); state.SetVisible(true); state.SetCanDraw(true); state.SetNeedsCommit(); state.SetNeedsForcedCommit(); state.DidLoseOutputSurface(); EXPECT_EQ(SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD, state.NextAction()); } TEST(SchedulerStateMachineTest, TestImmediateFinishCommit) { SchedulerSettings default_scheduler_settings; StateMachine state(default_scheduler_settings); state.SetCanStart(); state.UpdateState(state.NextAction()); state.DidCreateAndInitializeOutputSurface(); state.SetVisible(true); state.SetCanDraw(true); // Schedule a forced frame, commit it, draw it. state.SetNeedsCommit(); state.SetNeedsForcedCommit(); state.UpdateState(state.NextAction()); state.FinishCommit(); EXPECT_EQ(SchedulerStateMachine::ACTION_COMMIT, state.NextAction()); EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_READY_TO_COMMIT, state.CommitState()); state.UpdateState(state.NextAction()); EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_WAITING_FOR_FIRST_FORCED_DRAW, state.CommitState()); state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting()); EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction()); state.SetNeedsForcedRedraw(true); EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_FORCED, state.NextAction()); state.UpdateState(state.NextAction()); state.DidDrawIfPossibleCompleted(true); state.DidLeaveBeginFrame(); // Should be waiting for the normal begin frame from the main thread. EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_FRAME_IN_PROGRESS, state.CommitState()); } TEST(SchedulerStateMachineTest, TestImmediateFinishCommitDuringCommit) { SchedulerSettings default_scheduler_settings; StateMachine state(default_scheduler_settings); state.SetCanStart(); state.UpdateState(state.NextAction()); state.DidCreateAndInitializeOutputSurface(); state.SetVisible(true); state.SetCanDraw(true); // Start a normal commit. state.SetNeedsCommit(); state.UpdateState(state.NextAction()); // Schedule a forced frame, commit it, draw it. state.SetNeedsCommit(); state.SetNeedsForcedCommit(); state.UpdateState(state.NextAction()); state.FinishCommit(); EXPECT_EQ(SchedulerStateMachine::ACTION_COMMIT, state.NextAction()); EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_READY_TO_COMMIT, state.CommitState()); state.UpdateState(state.NextAction()); EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_WAITING_FOR_FIRST_FORCED_DRAW, state.CommitState()); state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting()); EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction()); state.SetNeedsForcedRedraw(true); EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_FORCED, state.NextAction()); state.UpdateState(state.NextAction()); state.DidDrawIfPossibleCompleted(true); state.DidLeaveBeginFrame(); // Should be waiting for the normal begin frame from the main thread. EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_FRAME_IN_PROGRESS, state.CommitState()) << state.ToString(); } TEST(SchedulerStateMachineTest, ImmediateBeginFrameAbortedByMainThreadWhileInvisible) { SchedulerSettings default_scheduler_settings; StateMachine state(default_scheduler_settings); state.SetCanStart(); state.UpdateState(state.NextAction()); state.DidCreateAndInitializeOutputSurface(); state.SetVisible(true); state.SetCanDraw(true); state.SetNeedsCommit(); state.UpdateState(state.NextAction()); state.SetNeedsCommit(); state.SetNeedsForcedCommit(); state.UpdateState(state.NextAction()); state.FinishCommit(); EXPECT_EQ(SchedulerStateMachine::ACTION_COMMIT, state.NextAction()); EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_READY_TO_COMMIT, state.CommitState()); state.UpdateState(state.NextAction()); EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_WAITING_FOR_FIRST_FORCED_DRAW, state.CommitState()); state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting()); EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction()); state.SetNeedsForcedRedraw(true); EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_FORCED, state.NextAction()); state.UpdateState(state.NextAction()); state.DidDrawIfPossibleCompleted(true); state.DidLeaveBeginFrame(); // Should be waiting for the main thread's begin frame. EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_FRAME_IN_PROGRESS, state.CommitState()) << state.ToString(); // Become invisible and abort the main thread's begin frame. state.SetVisible(false); state.BeginFrameAbortedByMainThread(); // Should be back in the idle state, but needing a commit. EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_IDLE, state.CommitState()); EXPECT_TRUE(state.NeedsCommit()); } TEST(SchedulerStateMachineTest, ImmediateFinishCommitWhileCantDraw) { SchedulerSettings default_scheduler_settings; StateMachine state(default_scheduler_settings); state.SetCanStart(); state.UpdateState(state.NextAction()); state.DidCreateAndInitializeOutputSurface(); state.SetVisible(true); state.SetCanDraw(false); state.SetNeedsCommit(); state.UpdateState(state.NextAction()); state.SetNeedsCommit(); state.SetNeedsForcedCommit(); state.UpdateState(state.NextAction()); state.FinishCommit(); EXPECT_EQ(SchedulerStateMachine::ACTION_COMMIT, state.NextAction()); EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_READY_TO_COMMIT, state.CommitState()); state.UpdateState(state.NextAction()); EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_WAITING_FOR_FIRST_FORCED_DRAW, state.CommitState()); state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting()); EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction()); state.SetNeedsForcedRedraw(true); EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_FORCED, state.NextAction()); state.UpdateState(state.NextAction()); state.DidDrawIfPossibleCompleted(true); state.DidLeaveBeginFrame(); } 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.SetCanStart(); state.UpdateState(state.NextAction()); state.DidCreateAndInitializeOutputSurface(); 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_SEND_BEGIN_FRAME_TO_MAIN_THREAD, state.NextAction()); state.UpdateState(state.NextAction()); EXPECT_TRUE(state.DrawSuspendedUntilCommit()); EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction()); state.FinishCommit(); EXPECT_TRUE(state.DrawSuspendedUntilCommit()); EXPECT_EQ(SchedulerStateMachine::ACTION_COMMIT, state.NextAction()); state.UpdateState(state.NextAction()); EXPECT_FALSE(state.DrawSuspendedUntilCommit()); } } // namespace } // namespace cc