blob: df6a3df4efff62f07bf8030b6ac7486559382369 [file] [log] [blame]
// 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_state_machine.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace cc {
namespace {
const SchedulerStateMachine::CommitState allCommitStates[] = {
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:
void setCommitState(CommitState cs) { m_commitState = cs; }
CommitState commitState() const { return m_commitState; }
bool needsCommit() const { return m_needsCommit; }
void setNeedsRedraw(bool b) { m_needsRedraw = b; }
bool needsRedraw() const { return m_needsRedraw; }
void setNeedsForcedRedraw(bool b) { m_needsForcedRedraw = b; }
bool needsForcedRedraw() const { return m_needsForcedRedraw; }
bool canDraw() const { return m_canDraw; }
bool insideVSync() const { return m_insideVSync; }
bool visible() const { return m_visible; }
};
TEST(SchedulerStateMachineTest, TestNextActionBeginsFrameIfNeeded)
{
// If no commit needed, do nothing
{
StateMachine state;
state.setCommitState(SchedulerStateMachine::COMMIT_STATE_IDLE);
state.setCanBeginFrame(true);
state.setNeedsRedraw(false);
state.setVisible(true);
EXPECT_FALSE(state.vsyncCallbackNeeded());
state.didLeaveVSync();
EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.nextAction());
EXPECT_FALSE(state.vsyncCallbackNeeded());
state.didEnterVSync();
EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.nextAction());
}
// If commit requested but canBeginFrame is still false, do nothing.
{
StateMachine state;
state.setCommitState(SchedulerStateMachine::COMMIT_STATE_IDLE);
state.setNeedsRedraw(false);
state.setVisible(true);
EXPECT_FALSE(state.vsyncCallbackNeeded());
state.didLeaveVSync();
EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.nextAction());
EXPECT_FALSE(state.vsyncCallbackNeeded());
state.didEnterVSync();
EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.nextAction());
}
// If commit requested, begin a frame
{
StateMachine state;
state.setCommitState(SchedulerStateMachine::COMMIT_STATE_IDLE);
state.setCanBeginFrame(true);
state.setNeedsRedraw(false);
state.setVisible(true);
EXPECT_FALSE(state.vsyncCallbackNeeded());
}
// Begin the frame, make sure needsCommit and commitState update correctly.
{
StateMachine state;
state.setCanBeginFrame(true);
state.setVisible(true);
state.updateState(SchedulerStateMachine::ACTION_BEGIN_FRAME);
EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_FRAME_IN_PROGRESS, state.commitState());
EXPECT_FALSE(state.needsCommit());
EXPECT_FALSE(state.vsyncCallbackNeeded());
}
}
TEST(SchedulerStateMachineTest, TestSetForcedRedrawDoesNotSetsNormalRedraw)
{
SchedulerStateMachine state;
state.setCanDraw(true);
state.setNeedsForcedRedraw();
EXPECT_FALSE(state.redrawPending());
EXPECT_TRUE(state.vsyncCallbackNeeded());
}
TEST(SchedulerStateMachineTest, TestFailedDrawSetsNeedsCommitAndDoesNotDrawAgain)
{
SchedulerStateMachine state;
state.setCanBeginFrame(true);
state.setVisible(true);
state.setCanDraw(true);
state.setNeedsRedraw();
EXPECT_TRUE(state.redrawPending());
EXPECT_TRUE(state.vsyncCallbackNeeded());
state.didEnterVSync();
// 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_BEGIN_FRAME, state.nextAction());
state.updateState(SchedulerStateMachine::ACTION_BEGIN_FRAME);
EXPECT_TRUE(state.redrawPending());
EXPECT_TRUE(state.commitPending());
}
TEST(SchedulerStateMachineTest, TestSetNeedsRedrawDuringFailedDrawDoesNotRemoveNeedsRedraw)
{
SchedulerStateMachine state;
state.setCanBeginFrame(true);
state.setVisible(true);
state.setCanDraw(true);
state.setNeedsRedraw();
EXPECT_TRUE(state.redrawPending());
EXPECT_TRUE(state.vsyncCallbackNeeded());
state.didEnterVSync();
// 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 vsync callback, 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_BEGIN_FRAME, state.nextAction());
EXPECT_TRUE(state.redrawPending());
}
TEST(SchedulerStateMachineTest, TestCommitAfterFailedDrawAllowsDrawInSameFrame)
{
SchedulerStateMachine state;
state.setCanBeginFrame(true);
state.setVisible(true);
state.setCanDraw(true);
// Start a commit.
state.setNeedsCommit();
EXPECT_EQ(SchedulerStateMachine::ACTION_BEGIN_FRAME, state.nextAction());
state.updateState(SchedulerStateMachine::ACTION_BEGIN_FRAME);
EXPECT_TRUE(state.commitPending());
// Then initiate a draw.
state.setNeedsRedraw();
EXPECT_TRUE(state.vsyncCallbackNeeded());
state.didEnterVSync();
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.beginFrameComplete();
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)
{
SchedulerStateMachine state;
state.setCanBeginFrame(true);
state.setVisible(true);
state.setCanDraw(true);
// Start a commit.
state.setNeedsCommit();
EXPECT_EQ(SchedulerStateMachine::ACTION_BEGIN_FRAME, state.nextAction());
state.updateState(SchedulerStateMachine::ACTION_BEGIN_FRAME);
EXPECT_TRUE(state.commitPending());
// Then initiate a draw.
state.setNeedsRedraw();
EXPECT_TRUE(state.vsyncCallbackNeeded());
state.didEnterVSync();
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.beginFrameComplete();
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)
{
SchedulerStateMachine state;
state.setCanBeginFrame(true);
state.setVisible(true);
state.setCanDraw(true);
state.setMaximumNumberOfFailedDrawsBeforeDrawIsForced(1);
// Start a commit.
state.setNeedsCommit();
EXPECT_EQ(SchedulerStateMachine::ACTION_BEGIN_FRAME, state.nextAction());
state.updateState(SchedulerStateMachine::ACTION_BEGIN_FRAME);
EXPECT_TRUE(state.commitPending());
// Then initiate a draw.
state.setNeedsRedraw();
EXPECT_TRUE(state.vsyncCallbackNeeded());
state.didEnterVSync();
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.beginFrameComplete();
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, TestFailedDrawIsRetriedNextVSync)
{
SchedulerStateMachine state;
state.setCanBeginFrame(true);
state.setVisible(true);
state.setCanDraw(true);
// Start a draw.
state.setNeedsRedraw();
EXPECT_TRUE(state.vsyncCallbackNeeded());
state.didEnterVSync();
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_BEGIN_FRAME, state.nextAction());
state.didLeaveVSync();
EXPECT_TRUE(state.vsyncCallbackNeeded());
state.didEnterVSync();
// We should try draw again in the next vsync.
EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.nextAction());
}
TEST(SchedulerStateMachineTest, TestDoestDrawTwiceInSameFrame)
{
SchedulerStateMachine state;
state.setVisible(true);
state.setCanDraw(true);
state.setNeedsRedraw();
EXPECT_TRUE(state.vsyncCallbackNeeded());
state.didEnterVSync();
EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.nextAction());
state.updateState(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE);
// While still in the same vsync callback, 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.didLeaveVSync();
EXPECT_TRUE(state.vsyncCallbackNeeded());
state.didEnterVSync();
EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.nextAction());
state.updateState(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE);
state.didDrawIfPossibleCompleted(true);
EXPECT_FALSE(state.vsyncCallbackNeeded());
}
TEST(SchedulerStateMachineTest, TestNextActionDrawsOnVSync)
{
// When not on vsync, or on vsync but not visible, don't draw.
size_t numCommitStates = sizeof(allCommitStates) / sizeof(SchedulerStateMachine::CommitState);
for (size_t i = 0; i < numCommitStates; ++i) {
for (unsigned j = 0; j < 2; ++j) {
StateMachine state;
state.setCommitState(allCommitStates[i]);
bool visible = j;
if (!visible) {
state.didEnterVSync();
state.setVisible(false);
} else
state.setVisible(true);
// Case 1: needsCommit=false
EXPECT_NE(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.nextAction());
// Case 2: needsCommit=true
state.setNeedsCommit();
EXPECT_NE(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.nextAction());
}
}
// When on vsync, or not on vsync but needsForcedRedraw set, should always draw except if you're ready to commit, in which case commit.
for (size_t i = 0; i < numCommitStates; ++i) {
for (unsigned j = 0; j < 2; ++j) {
StateMachine state;
state.setCanDraw(true);
state.setCommitState(allCommitStates[i]);
bool forcedDraw = j;
if (!forcedDraw) {
state.didEnterVSync();
state.setNeedsRedraw(true);
state.setVisible(true);
} else
state.setNeedsForcedRedraw(true);
SchedulerStateMachine::Action expectedAction;
if (allCommitStates[i] != SchedulerStateMachine::COMMIT_STATE_READY_TO_COMMIT)
expectedAction = forcedDraw ? SchedulerStateMachine::ACTION_DRAW_FORCED : SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE;
else
expectedAction = SchedulerStateMachine::ACTION_COMMIT;
// Case 1: needsCommit=false.
EXPECT_TRUE(state.vsyncCallbackNeeded());
EXPECT_EQ(expectedAction, state.nextAction());
// Case 2: needsCommit=true.
state.setNeedsCommit();
EXPECT_TRUE(state.vsyncCallbackNeeded());
EXPECT_EQ(expectedAction, state.nextAction());
}
}
}
TEST(SchedulerStateMachineTest, TestNoCommitStatesRedrawWhenInvisible)
{
size_t numCommitStates = sizeof(allCommitStates) / sizeof(SchedulerStateMachine::CommitState);
for (size_t i = 0; i < numCommitStates; ++i) {
// There shouldn't be any drawing regardless of vsync.
for (unsigned j = 0; j < 2; ++j) {
StateMachine state;
state.setCommitState(allCommitStates[i]);
state.setVisible(false);
state.setNeedsRedraw(true);
state.setNeedsForcedRedraw(false);
if (j == 1)
state.didEnterVSync();
// Case 1: needsCommit=false.
EXPECT_NE(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.nextAction());
// Case 2: needsCommit=true.
state.setNeedsCommit();
EXPECT_NE(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.nextAction());
}
}
}
TEST(SchedulerStateMachineTest, TestCanRedraw_StopsDraw)
{
size_t numCommitStates = sizeof(allCommitStates) / sizeof(SchedulerStateMachine::CommitState);
for (size_t i = 0; i < numCommitStates; ++i) {
// There shouldn't be any drawing regardless of vsync.
for (unsigned j = 0; j < 2; ++j) {
StateMachine state;
state.setCommitState(allCommitStates[i]);
state.setVisible(false);
state.setNeedsRedraw(true);
state.setNeedsForcedRedraw(false);
if (j == 1)
state.didEnterVSync();
state.setCanDraw(false);
EXPECT_NE(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.nextAction());
}
}
}
TEST(SchedulerStateMachineTest, TestCanRedrawWithWaitingForFirstDrawMakesProgress)
{
StateMachine state;
state.setCommitState(SchedulerStateMachine::COMMIT_STATE_WAITING_FOR_FIRST_DRAW);
state.setCanBeginFrame(true);
state.setNeedsCommit();
state.setNeedsRedraw(true);
state.setVisible(true);
state.setCanDraw(false);
EXPECT_EQ(SchedulerStateMachine::ACTION_BEGIN_FRAME, state.nextAction());
}
TEST(SchedulerStateMachineTest, TestSetNeedsCommitIsNotLost)
{
StateMachine state;
state.setCanBeginFrame(true);
state.setNeedsCommit();
state.setVisible(true);
state.setCanDraw(true);
// Begin the frame.
EXPECT_EQ(SchedulerStateMachine::ACTION_BEGIN_FRAME, 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.beginFrameComplete();
EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_READY_TO_COMMIT, state.commitState());
// Expect to commit regardless of vsync state.
state.didLeaveVSync();
EXPECT_EQ(SchedulerStateMachine::ACTION_COMMIT, state.nextAction());
state.didEnterVSync();
EXPECT_EQ(SchedulerStateMachine::ACTION_COMMIT, state.nextAction());
// Commit and make sure we draw on next vsync
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.didLeaveVSync();
EXPECT_EQ(SchedulerStateMachine::ACTION_BEGIN_FRAME, state.nextAction());
}
TEST(SchedulerStateMachineTest, TestFullCycle)
{
StateMachine state;
state.setCanBeginFrame(true);
state.setVisible(true);
state.setCanDraw(true);
// Start clean and set commit.
state.setNeedsCommit();
EXPECT_EQ(SchedulerStateMachine::ACTION_BEGIN_FRAME, state.nextAction());
// Begin the frame.
state.updateState(SchedulerStateMachine::ACTION_BEGIN_FRAME);
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.beginFrameComplete();
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 vsync.
EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.nextAction());
// At vsync, draw.
state.didEnterVSync();
EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.nextAction());
state.updateState(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE);
state.didDrawIfPossibleCompleted(true);
state.didLeaveVSync();
// 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)
{
StateMachine state;
state.setCanBeginFrame(true);
state.setVisible(true);
state.setCanDraw(true);
// Start clean and set commit.
state.setNeedsCommit();
EXPECT_EQ(SchedulerStateMachine::ACTION_BEGIN_FRAME, state.nextAction());
// Begin the frame.
state.updateState(SchedulerStateMachine::ACTION_BEGIN_FRAME);
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.beginFrameComplete();
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 vsync.
EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.nextAction());
// At vsync, draw.
state.didEnterVSync();
EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.nextAction());
state.updateState(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE);
state.didDrawIfPossibleCompleted(true);
state.didLeaveVSync();
// 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_BEGIN_FRAME, state.nextAction());
}
TEST(SchedulerStateMachineTest, TestRequestCommitInvisible)
{
StateMachine state;
state.setNeedsCommit();
EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.nextAction());
}
TEST(SchedulerStateMachineTest, TestGoesInvisibleBeforeBeginFrameCompletes)
{
StateMachine state;
state.setCanBeginFrame(true);
state.setVisible(true);
state.setCanDraw(true);
// Start clean and set commit.
state.setNeedsCommit();
EXPECT_EQ(SchedulerStateMachine::ACTION_BEGIN_FRAME, state.nextAction());
// Begin the frame while visible.
state.updateState(SchedulerStateMachine::ACTION_BEGIN_FRAME);
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 beginFrame.
state.setVisible(false);
state.beginFrameAborted();
// 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_BEGIN_FRAME, 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, TestContextLostWhenCompletelyIdle)
{
StateMachine state;
state.setCanBeginFrame(true);
state.setVisible(true);
state.setCanDraw(true);
state.didLoseContext();
EXPECT_EQ(SchedulerStateMachine::ACTION_BEGIN_CONTEXT_RECREATION, state.nextAction());
state.updateState(state.nextAction());
// Once context recreation begins, nothing should happen.
EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.nextAction());
// Recreate the context
state.didRecreateContext();
// When the context is recreated, we should begin a commit
EXPECT_EQ(SchedulerStateMachine::ACTION_BEGIN_FRAME, state.nextAction());
state.updateState(state.nextAction());
}
TEST(SchedulerStateMachineTest, TestContextLostWhenIdleAndCommitRequestedWhileRecreating)
{
StateMachine state;
state.setCanBeginFrame(true);
state.setVisible(true);
state.setCanDraw(true);
state.didLoseContext();
EXPECT_EQ(SchedulerStateMachine::ACTION_BEGIN_CONTEXT_RECREATION, 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.didRecreateContext();
// When the context is recreated, we should begin a commit
EXPECT_EQ(SchedulerStateMachine::ACTION_BEGIN_FRAME, state.nextAction());
state.updateState(state.nextAction());
// Once the context is recreated, whether we draw should be based on
// setCanDraw.
state.setNeedsRedraw(true);
state.didEnterVSync();
EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.nextAction());
state.setCanDraw(false);
EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.nextAction());
state.setCanDraw(true);
state.didLeaveVSync();
}
TEST(SchedulerStateMachineTest, TestContextLostWhileCommitInProgress)
{
StateMachine state;
state.setCanBeginFrame(true);
state.setVisible(true);
state.setCanDraw(true);
// Get a commit in flight.
state.setNeedsCommit();
EXPECT_EQ(SchedulerStateMachine::ACTION_BEGIN_FRAME, state.nextAction());
state.updateState(state.nextAction());
// Set damage and expect a draw.
state.setNeedsRedraw(true);
state.didEnterVSync();
EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.nextAction());
state.updateState(state.nextAction());
state.didLeaveVSync();
// Cause a lost context while the begin frame is in flight.
state.didLoseContext();
// Ask for another draw. Expect nothing happens.
state.setNeedsRedraw(true);
EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.nextAction());
// Finish the frame, and commit.
state.beginFrameComplete();
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 vsync state
state.didEnterVSync();
EXPECT_EQ(SchedulerStateMachine::ACTION_BEGIN_CONTEXT_RECREATION, state.nextAction());
state.didLeaveVSync();
EXPECT_EQ(SchedulerStateMachine::ACTION_BEGIN_CONTEXT_RECREATION, state.nextAction());
}
TEST(SchedulerStateMachineTest, TestContextLostWhileCommitInProgressAndAnotherCommitRequested)
{
StateMachine state;
state.setCanBeginFrame(true);
state.setVisible(true);
state.setCanDraw(true);
// Get a commit in flight.
state.setNeedsCommit();
EXPECT_EQ(SchedulerStateMachine::ACTION_BEGIN_FRAME, state.nextAction());
state.updateState(state.nextAction());
// Set damage and expect a draw.
state.setNeedsRedraw(true);
state.didEnterVSync();
EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.nextAction());
state.updateState(state.nextAction());
state.didLeaveVSync();
// Cause a lost context while the begin frame is in flight.
state.didLoseContext();
// 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.beginFrameComplete();
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 vsync state
state.didEnterVSync();
EXPECT_EQ(SchedulerStateMachine::ACTION_BEGIN_CONTEXT_RECREATION, state.nextAction());
state.didLeaveVSync();
EXPECT_EQ(SchedulerStateMachine::ACTION_BEGIN_CONTEXT_RECREATION, state.nextAction());
}
TEST(SchedulerStateMachineTest, TestFinishAllRenderingWhileContextLost)
{
StateMachine state;
state.setVisible(true);
state.setCanDraw(true);
// Cause a lost context lost.
state.didLoseContext();
// Ask a forced redraw and verify it ocurrs.
state.setNeedsForcedRedraw(true);
state.didEnterVSync();
EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_FORCED, state.nextAction());
state.didLeaveVSync();
// Clear the forced redraw bit.
state.setNeedsForcedRedraw(false);
// Expect to be told to begin context recreation, independent of vsync state
EXPECT_EQ(SchedulerStateMachine::ACTION_BEGIN_CONTEXT_RECREATION, state.nextAction());
state.updateState(state.nextAction());
// Ask a forced redraw and verify it ocurrs.
state.setNeedsForcedRedraw(true);
state.didEnterVSync();
EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_FORCED, state.nextAction());
state.didLeaveVSync();
}
TEST(SchedulerStateMachineTest, TestBeginFrameWhenInvisibleAndForceCommit)
{
StateMachine state;
state.setCanBeginFrame(true);
state.setVisible(false);
state.setNeedsCommit();
state.setNeedsForcedCommit();
EXPECT_EQ(SchedulerStateMachine::ACTION_BEGIN_FRAME, state.nextAction());
}
TEST(SchedulerStateMachineTest, TestBeginFrameWhenCanBeginFrameFalseAndForceCommit)
{
StateMachine state;
state.setVisible(true);
state.setCanDraw(true);
state.setNeedsCommit();
state.setNeedsForcedCommit();
EXPECT_EQ(SchedulerStateMachine::ACTION_BEGIN_FRAME, state.nextAction());
}
TEST(SchedulerStateMachineTest, TestBeginFrameWhenCommitInProgress)
{
StateMachine state;
state.setCanBeginFrame(true);
state.setVisible(false);
state.setCommitState(SchedulerStateMachine::COMMIT_STATE_FRAME_IN_PROGRESS);
state.setNeedsCommit();
state.setNeedsForcedCommit();
state.beginFrameComplete();
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_BEGIN_FRAME, state.nextAction());
}
TEST(SchedulerStateMachineTest, TestBeginFrameWhenContextLost)
{
StateMachine state;
state.setCanBeginFrame(true);
state.setVisible(true);
state.setCanDraw(true);
state.setNeedsCommit();
state.setNeedsForcedCommit();
state.didLoseContext();
EXPECT_EQ(SchedulerStateMachine::ACTION_BEGIN_FRAME, state.nextAction());
}
TEST(SchedulerStateMachineTest, TestImmediateBeginFrame)
{
StateMachine state;
state.setCanBeginFrame(true);
state.setVisible(true);
state.setCanDraw(true);
// Schedule a forced frame, commit it, draw it.
state.setNeedsCommit();
state.setNeedsForcedCommit();
state.updateState(state.nextAction());
state.beginFrameComplete();
EXPECT_EQ(SchedulerStateMachine::ACTION_COMMIT, state.nextAction());
EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_READY_TO_COMMIT, state.commitState());
state.updateState(state.nextAction());
state.didEnterVSync();
EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.nextAction());
state.updateState(state.nextAction());
state.didDrawIfPossibleCompleted(true);
state.didLeaveVSync();
// Should be waiting for the normal begin frame
EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_FRAME_IN_PROGRESS, state.commitState());
}
TEST(SchedulerStateMachineTest, TestImmediateBeginFrameDuringCommit)
{
StateMachine state;
state.setCanBeginFrame(true);
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.beginFrameComplete();
EXPECT_EQ(SchedulerStateMachine::ACTION_COMMIT, state.nextAction());
EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_READY_TO_COMMIT, state.commitState());
state.updateState(state.nextAction());
state.didEnterVSync();
EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.nextAction());
state.updateState(state.nextAction());
state.didDrawIfPossibleCompleted(true);
state.didLeaveVSync();
// Should be waiting for the normal begin frame
EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_FRAME_IN_PROGRESS, state.commitState()) << state.toString();
}
TEST(SchedulerStateMachineTest, ImmediateBeginFrameWhileInvisible)
{
StateMachine state;
state.setCanBeginFrame(true);
state.setVisible(true);
state.setCanDraw(true);
state.setNeedsCommit();
state.updateState(state.nextAction());
state.setNeedsCommit();
state.setNeedsForcedCommit();
state.updateState(state.nextAction());
state.beginFrameComplete();
EXPECT_EQ(SchedulerStateMachine::ACTION_COMMIT, state.nextAction());
EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_READY_TO_COMMIT, state.commitState());
state.updateState(state.nextAction());
state.didEnterVSync();
EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.nextAction());
state.updateState(state.nextAction());
state.didDrawIfPossibleCompleted(true);
state.didLeaveVSync();
// Should be waiting for the normal begin frame
EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_FRAME_IN_PROGRESS, state.commitState()) << state.toString();
// Become invisible and abort the "normal" begin frame.
state.setVisible(false);
state.beginFrameAborted();
// Should be back in the idle state, but needing a commit.
EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_IDLE, state.commitState());
EXPECT_TRUE(state.needsCommit());
}
} // namespace
} // namespace cc