blob: 6f5eb2c2dcd2a83648b8727947578424d58de89f [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/scheduler.h"
#include <string>
#include <vector>
#include "base/logging.h"
#include "base/memory/scoped_vector.h"
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "base/time/time.h"
#include "cc/test/scheduler_test_common.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#define EXPECT_ACTION(action, client, action_index, expected_num_actions) \
EXPECT_EQ(expected_num_actions, client.num_actions_()); \
ASSERT_LT(action_index, client.num_actions_()); \
do { \
EXPECT_STREQ(action, client.Action(action_index)); \
for (int i = expected_num_actions; i < client.num_actions_(); ++i) \
ADD_FAILURE() << "Unexpected action: " << client.Action(i) << \
" with state:\n" << client.StateForAction(action_index); \
} while (false)
#define EXPECT_SINGLE_ACTION(action, client) \
EXPECT_ACTION(action, client, 0, 1)
namespace cc {
namespace {
void InitializeOutputSurfaceAndFirstCommit(Scheduler* scheduler) {
scheduler->DidCreateAndInitializeOutputSurface();
scheduler->SetNeedsCommit();
scheduler->FinishCommit();
// Go through the motions to draw the commit.
scheduler->BeginImplFrame(BeginFrameArgs::CreateForTesting());
scheduler->OnBeginImplFrameDeadline();
// We need another BeginImplFrame so Scheduler calls
// SetNeedsBeginImplFrame(false).
scheduler->BeginImplFrame(BeginFrameArgs::CreateForTesting());
scheduler->OnBeginImplFrameDeadline();
}
class FakeSchedulerClient : public SchedulerClient {
public:
FakeSchedulerClient()
: needs_begin_impl_frame_(false) {
Reset();
}
void Reset() {
actions_.clear();
states_.clear();
draw_will_happen_ = true;
swap_will_happen_if_draw_happens_ = true;
num_draws_ = 0;
log_anticipated_draw_time_change_ = false;
}
Scheduler* CreateScheduler(const SchedulerSettings& settings) {
scheduler_ = Scheduler::Create(this, settings, 0);
return scheduler_.get();
}
// Most tests don't care about DidAnticipatedDrawTimeChange, so only record it
// for tests that do.
void set_log_anticipated_draw_time_change(bool log) {
log_anticipated_draw_time_change_ = log;
}
bool needs_begin_impl_frame() { return needs_begin_impl_frame_; }
int num_draws() const { return num_draws_; }
int num_actions_() const { return static_cast<int>(actions_.size()); }
const char* Action(int i) const { return actions_[i]; }
base::Value& StateForAction(int i) const { return *states_[i]; }
int ActionIndex(const char* action) const {
for (size_t i = 0; i < actions_.size(); i++)
if (!strcmp(actions_[i], action))
return i;
return -1;
}
bool HasAction(const char* action) const {
return ActionIndex(action) >= 0;
}
void SetDrawWillHappen(bool draw_will_happen) {
draw_will_happen_ = draw_will_happen;
}
void SetSwapWillHappenIfDrawHappens(bool swap_will_happen_if_draw_happens) {
swap_will_happen_if_draw_happens_ = swap_will_happen_if_draw_happens;
}
// SchedulerClient implementation.
virtual void SetNeedsBeginImplFrame(bool enable) OVERRIDE {
actions_.push_back("SetNeedsBeginImplFrame");
states_.push_back(scheduler_->StateAsValue().release());
needs_begin_impl_frame_ = enable;
}
virtual void ScheduledActionSendBeginMainFrame() OVERRIDE {
actions_.push_back("ScheduledActionSendBeginMainFrame");
states_.push_back(scheduler_->StateAsValue().release());
}
virtual DrawSwapReadbackResult ScheduledActionDrawAndSwapIfPossible()
OVERRIDE {
actions_.push_back("ScheduledActionDrawAndSwapIfPossible");
states_.push_back(scheduler_->StateAsValue().release());
num_draws_++;
bool did_readback = false;
return DrawSwapReadbackResult(
draw_will_happen_,
draw_will_happen_ && swap_will_happen_if_draw_happens_,
did_readback);
}
virtual DrawSwapReadbackResult ScheduledActionDrawAndSwapForced() OVERRIDE {
actions_.push_back("ScheduledActionDrawAndSwapForced");
states_.push_back(scheduler_->StateAsValue().release());
bool did_draw = true;
bool did_swap = swap_will_happen_if_draw_happens_;
bool did_readback = false;
return DrawSwapReadbackResult(did_draw, did_swap, did_readback);
}
virtual DrawSwapReadbackResult ScheduledActionDrawAndReadback() OVERRIDE {
actions_.push_back("ScheduledActionDrawAndReadback");
states_.push_back(scheduler_->StateAsValue().release());
bool did_draw = true;
bool did_swap = false;
bool did_readback = true;
return DrawSwapReadbackResult(did_draw, did_swap, did_readback);
}
virtual void ScheduledActionCommit() OVERRIDE {
actions_.push_back("ScheduledActionCommit");
states_.push_back(scheduler_->StateAsValue().release());
}
virtual void ScheduledActionUpdateVisibleTiles() OVERRIDE {
actions_.push_back("ScheduledActionUpdateVisibleTiles");
states_.push_back(scheduler_->StateAsValue().release());
}
virtual void ScheduledActionActivatePendingTree() OVERRIDE {
actions_.push_back("ScheduledActionActivatePendingTree");
states_.push_back(scheduler_->StateAsValue().release());
}
virtual void ScheduledActionBeginOutputSurfaceCreation() OVERRIDE {
actions_.push_back("ScheduledActionBeginOutputSurfaceCreation");
states_.push_back(scheduler_->StateAsValue().release());
}
virtual void ScheduledActionAcquireLayerTexturesForMainThread() OVERRIDE {
actions_.push_back("ScheduledActionAcquireLayerTexturesForMainThread");
states_.push_back(scheduler_->StateAsValue().release());
}
virtual void ScheduledActionManageTiles() OVERRIDE {
actions_.push_back("ScheduledActionManageTiles");
states_.push_back(scheduler_->StateAsValue().release());
}
virtual void DidAnticipatedDrawTimeChange(base::TimeTicks) OVERRIDE {
if (log_anticipated_draw_time_change_)
actions_.push_back("DidAnticipatedDrawTimeChange");
}
virtual base::TimeDelta DrawDurationEstimate() OVERRIDE {
return base::TimeDelta();
}
virtual base::TimeDelta BeginMainFrameToCommitDurationEstimate() OVERRIDE {
return base::TimeDelta();
}
virtual base::TimeDelta CommitToActivateDurationEstimate() OVERRIDE {
return base::TimeDelta();
}
virtual void PostBeginImplFrameDeadline(const base::Closure& closure,
base::TimeTicks deadline) OVERRIDE {
actions_.push_back("PostBeginImplFrameDeadlineTask");
states_.push_back(scheduler_->StateAsValue().release());
}
virtual void DidBeginImplFrameDeadline() OVERRIDE {}
protected:
bool needs_begin_impl_frame_;
bool draw_will_happen_;
bool swap_will_happen_if_draw_happens_;
int num_draws_;
bool log_anticipated_draw_time_change_;
std::vector<const char*> actions_;
ScopedVector<base::Value> states_;
scoped_ptr<Scheduler> scheduler_;
};
TEST(SchedulerTest, InitializeOutputSurfaceDoesNotBeginImplFrame) {
FakeSchedulerClient client;
SchedulerSettings default_scheduler_settings;
Scheduler* scheduler = client.CreateScheduler(default_scheduler_settings);
scheduler->SetCanStart();
scheduler->SetVisible(true);
scheduler->SetCanDraw(true);
EXPECT_SINGLE_ACTION("ScheduledActionBeginOutputSurfaceCreation", client);
client.Reset();
scheduler->DidCreateAndInitializeOutputSurface();
EXPECT_EQ(0, client.num_actions_());
}
void RequestCommit(bool deadline_scheduling_enabled) {
FakeSchedulerClient client;
SchedulerSettings scheduler_settings;
scheduler_settings.deadline_scheduling_enabled = deadline_scheduling_enabled;
Scheduler* scheduler = client.CreateScheduler(scheduler_settings);
scheduler->SetCanStart();
scheduler->SetVisible(true);
scheduler->SetCanDraw(true);
EXPECT_SINGLE_ACTION("ScheduledActionBeginOutputSurfaceCreation", client);
InitializeOutputSurfaceAndFirstCommit(scheduler);
// SetNeedsCommit should begin the frame on the next BeginImplFrame.
client.Reset();
scheduler->SetNeedsCommit();
EXPECT_TRUE(client.needs_begin_impl_frame());
if (deadline_scheduling_enabled) {
EXPECT_SINGLE_ACTION("SetNeedsBeginImplFrame", client);
} else {
EXPECT_EQ(client.num_actions_(), 2);
EXPECT_TRUE(client.HasAction("ScheduledActionSendBeginMainFrame"));
EXPECT_TRUE(client.HasAction("SetNeedsBeginImplFrame"));
}
client.Reset();
scheduler->BeginImplFrame(BeginFrameArgs::CreateForTesting());
if (deadline_scheduling_enabled) {
EXPECT_ACTION("ScheduledActionSendBeginMainFrame", client, 0, 2);
EXPECT_ACTION("PostBeginImplFrameDeadlineTask", client, 1, 2);
} else {
EXPECT_SINGLE_ACTION("PostBeginImplFrameDeadlineTask", client);
}
EXPECT_TRUE(client.needs_begin_impl_frame());
client.Reset();
// If we don't swap on the deadline, we need to request another
// BeginImplFrame.
scheduler->OnBeginImplFrameDeadline();
EXPECT_SINGLE_ACTION("SetNeedsBeginImplFrame", client);
EXPECT_TRUE(client.needs_begin_impl_frame());
client.Reset();
// FinishCommit should commit
scheduler->FinishCommit();
EXPECT_SINGLE_ACTION("ScheduledActionCommit", client);
EXPECT_TRUE(client.needs_begin_impl_frame());
client.Reset();
// BeginImplFrame should prepare the draw.
scheduler->BeginImplFrame(BeginFrameArgs::CreateForTesting());
EXPECT_SINGLE_ACTION("PostBeginImplFrameDeadlineTask", client);
EXPECT_TRUE(client.needs_begin_impl_frame());
client.Reset();
// BeginImplFrame deadline should draw.
scheduler->OnBeginImplFrameDeadline();
EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client, 0, 2);
EXPECT_ACTION("SetNeedsBeginImplFrame", client, 1, 2);
EXPECT_TRUE(client.needs_begin_impl_frame());
client.Reset();
// The following BeginImplFrame deadline should SetNeedsBeginImplFrame(false)
// to avoid excessive toggles.
scheduler->BeginImplFrame(BeginFrameArgs::CreateForTesting());
EXPECT_SINGLE_ACTION("PostBeginImplFrameDeadlineTask", client);
client.Reset();
scheduler->OnBeginImplFrameDeadline();
EXPECT_SINGLE_ACTION("SetNeedsBeginImplFrame", client);
EXPECT_FALSE(client.needs_begin_impl_frame());
client.Reset();
}
TEST(SchedulerTest, RequestCommit) {
bool deadline_scheduling_enabled = false;
RequestCommit(deadline_scheduling_enabled);
}
TEST(SchedulerTest, RequestCommit_Deadline) {
bool deadline_scheduling_enabled = true;
RequestCommit(deadline_scheduling_enabled);
}
void RequestCommitAfterBeginMainFrameSent(
bool deadline_scheduling_enabled) {
FakeSchedulerClient client;
SchedulerSettings scheduler_settings;
scheduler_settings.deadline_scheduling_enabled = deadline_scheduling_enabled;
Scheduler* scheduler = client.CreateScheduler(scheduler_settings);
scheduler->SetCanStart();
scheduler->SetVisible(true);
scheduler->SetCanDraw(true);
EXPECT_SINGLE_ACTION("ScheduledActionBeginOutputSurfaceCreation", client);
InitializeOutputSurfaceAndFirstCommit(scheduler);
client.Reset();
// SetNeedsCommit should begin the frame.
scheduler->SetNeedsCommit();
if (deadline_scheduling_enabled) {
EXPECT_SINGLE_ACTION("SetNeedsBeginImplFrame", client);
} else {
EXPECT_EQ(client.num_actions_(), 2);
EXPECT_TRUE(client.HasAction("SetNeedsBeginImplFrame"));
EXPECT_TRUE(client.HasAction("ScheduledActionSendBeginMainFrame"));
}
client.Reset();
scheduler->BeginImplFrame(BeginFrameArgs::CreateForTesting());
if (deadline_scheduling_enabled) {
EXPECT_EQ(client.num_actions_(), 2);
EXPECT_TRUE(client.HasAction("ScheduledActionSendBeginMainFrame"));
EXPECT_TRUE(client.HasAction("PostBeginImplFrameDeadlineTask"));
} else {
EXPECT_SINGLE_ACTION("PostBeginImplFrameDeadlineTask", client);
}
EXPECT_TRUE(client.needs_begin_impl_frame());
client.Reset();
// Now SetNeedsCommit again. Calling here means we need a second commit.
scheduler->SetNeedsCommit();
EXPECT_EQ(client.num_actions_(), 0);
client.Reset();
// Finish the first commit.
scheduler->FinishCommit();
EXPECT_ACTION("ScheduledActionCommit", client, 0, 2);
EXPECT_ACTION("PostBeginImplFrameDeadlineTask", client, 1, 2);
client.Reset();
scheduler->OnBeginImplFrameDeadline();
if (deadline_scheduling_enabled) {
EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client, 0, 2);
EXPECT_ACTION("SetNeedsBeginImplFrame", client, 1, 2);
} else {
EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client, 0, 3);
EXPECT_ACTION("ScheduledActionSendBeginMainFrame", client, 1, 3);
EXPECT_ACTION("SetNeedsBeginImplFrame", client, 2, 3);
}
// Because we just swapped, the Scheduler should also request the next
// BeginImplFrame from the OutputSurface.
EXPECT_TRUE(client.needs_begin_impl_frame());
client.Reset();
// Since another commit is needed, the next BeginImplFrame should initiate
// the second commit.
scheduler->BeginImplFrame(BeginFrameArgs::CreateForTesting());
if (deadline_scheduling_enabled) {
EXPECT_EQ(client.num_actions_(), 2);
EXPECT_TRUE(client.HasAction("ScheduledActionSendBeginMainFrame"));
EXPECT_TRUE(client.HasAction("PostBeginImplFrameDeadlineTask"));
} else {
EXPECT_SINGLE_ACTION("PostBeginImplFrameDeadlineTask", client);
}
client.Reset();
// Finishing the commit before the deadline should post a new deadline task
// to trigger the deadline early.
scheduler->FinishCommit();
EXPECT_ACTION("ScheduledActionCommit", client, 0, 2);
EXPECT_ACTION("PostBeginImplFrameDeadlineTask", client, 1, 2);
client.Reset();
scheduler->OnBeginImplFrameDeadline();
EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client, 0, 2);
EXPECT_ACTION("SetNeedsBeginImplFrame", client, 1, 2);
EXPECT_TRUE(client.needs_begin_impl_frame());
client.Reset();
// On the next BeginImplFrame, verify we go back to a quiescent state and
// no longer request BeginImplFrames.
scheduler->BeginImplFrame(BeginFrameArgs::CreateForTesting());
scheduler->OnBeginImplFrameDeadline();
EXPECT_FALSE(client.needs_begin_impl_frame());
client.Reset();
}
TEST(SchedulerTest, RequestCommitAfterBeginMainFrameSent) {
bool deadline_scheduling_enabled = false;
RequestCommitAfterBeginMainFrameSent(deadline_scheduling_enabled);
}
TEST(SchedulerTest, RequestCommitAfterBeginMainFrameSent_Deadline) {
bool deadline_scheduling_enabled = true;
RequestCommitAfterBeginMainFrameSent(deadline_scheduling_enabled);
}
void TextureAcquisitionCausesCommitInsteadOfDraw(
bool deadline_scheduling_enabled) {
FakeSchedulerClient client;
SchedulerSettings scheduler_settings;
scheduler_settings.deadline_scheduling_enabled = deadline_scheduling_enabled;
Scheduler* scheduler = client.CreateScheduler(scheduler_settings);
scheduler->SetCanStart();
scheduler->SetVisible(true);
scheduler->SetCanDraw(true);
EXPECT_SINGLE_ACTION("ScheduledActionBeginOutputSurfaceCreation", client);
InitializeOutputSurfaceAndFirstCommit(scheduler);
client.Reset();
scheduler->SetNeedsRedraw();
EXPECT_TRUE(scheduler->RedrawPending());
EXPECT_SINGLE_ACTION("SetNeedsBeginImplFrame", client);
EXPECT_TRUE(client.needs_begin_impl_frame());
client.Reset();
scheduler->BeginImplFrame(BeginFrameArgs::CreateForTesting());
EXPECT_SINGLE_ACTION("PostBeginImplFrameDeadlineTask", client);
client.Reset();
scheduler->OnBeginImplFrameDeadline();
EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client, 0, 2);
EXPECT_ACTION("SetNeedsBeginImplFrame", client, 1, 2);
EXPECT_FALSE(scheduler->RedrawPending());
EXPECT_TRUE(client.needs_begin_impl_frame());
client.Reset();
scheduler->BeginImplFrame(BeginFrameArgs::CreateForTesting());
EXPECT_SINGLE_ACTION("PostBeginImplFrameDeadlineTask", client);
client.Reset();
scheduler->OnBeginImplFrameDeadline();
EXPECT_SINGLE_ACTION("SetNeedsBeginImplFrame", client);
EXPECT_FALSE(scheduler->RedrawPending());
EXPECT_FALSE(client.needs_begin_impl_frame());
client.Reset();
scheduler->SetMainThreadNeedsLayerTextures();
EXPECT_SINGLE_ACTION("ScheduledActionAcquireLayerTexturesForMainThread",
client);
// We should request a BeginImplFrame in anticipation of a draw.
client.Reset();
scheduler->SetNeedsRedraw();
EXPECT_TRUE(scheduler->RedrawPending());
EXPECT_SINGLE_ACTION("SetNeedsBeginImplFrame", client);
EXPECT_TRUE(client.needs_begin_impl_frame());
// No draw happens since the textures are acquired by the main thread.
client.Reset();
scheduler->BeginImplFrame(BeginFrameArgs::CreateForTesting());
EXPECT_SINGLE_ACTION("PostBeginImplFrameDeadlineTask", client);
client.Reset();
scheduler->OnBeginImplFrameDeadline();
EXPECT_SINGLE_ACTION("SetNeedsBeginImplFrame", client);
EXPECT_TRUE(scheduler->RedrawPending());
EXPECT_TRUE(client.needs_begin_impl_frame());
client.Reset();
scheduler->SetNeedsCommit();
if (deadline_scheduling_enabled) {
EXPECT_EQ(0, client.num_actions_());
} else {
EXPECT_SINGLE_ACTION("ScheduledActionSendBeginMainFrame", client);
}
client.Reset();
scheduler->BeginImplFrame(BeginFrameArgs::CreateForTesting());
if (deadline_scheduling_enabled) {
EXPECT_ACTION("ScheduledActionSendBeginMainFrame", client, 0, 2);
EXPECT_ACTION("PostBeginImplFrameDeadlineTask", client, 1, 2);
} else {
EXPECT_SINGLE_ACTION("PostBeginImplFrameDeadlineTask", client);
}
// Commit will release the texture.
client.Reset();
scheduler->FinishCommit();
EXPECT_ACTION("ScheduledActionCommit", client, 0, 2);
EXPECT_ACTION("PostBeginImplFrameDeadlineTask", client, 1, 2);
EXPECT_TRUE(scheduler->RedrawPending());
// Now we can draw again after the commit happens.
client.Reset();
scheduler->OnBeginImplFrameDeadline();
EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client, 0, 2);
EXPECT_ACTION("SetNeedsBeginImplFrame", client, 1, 2);
EXPECT_FALSE(scheduler->RedrawPending());
EXPECT_TRUE(client.needs_begin_impl_frame());
// Make sure we stop requesting BeginImplFrames if we don't swap.
client.Reset();
scheduler->BeginImplFrame(BeginFrameArgs::CreateForTesting());
EXPECT_SINGLE_ACTION("PostBeginImplFrameDeadlineTask", client);
client.Reset();
scheduler->OnBeginImplFrameDeadline();
EXPECT_SINGLE_ACTION("SetNeedsBeginImplFrame", client);
EXPECT_FALSE(client.needs_begin_impl_frame());
}
TEST(SchedulerTest, TextureAcquisitionCausesCommitInsteadOfDraw) {
bool deadline_scheduling_enabled = false;
TextureAcquisitionCausesCommitInsteadOfDraw(deadline_scheduling_enabled);
}
TEST(SchedulerTest, TextureAcquisitionCausesCommitInsteadOfDraw_Deadline) {
bool deadline_scheduling_enabled = true;
TextureAcquisitionCausesCommitInsteadOfDraw(deadline_scheduling_enabled);
}
void TextureAcquisitionCollision(bool deadline_scheduling_enabled) {
FakeSchedulerClient client;
SchedulerSettings scheduler_settings;
scheduler_settings.deadline_scheduling_enabled = deadline_scheduling_enabled;
Scheduler* scheduler = client.CreateScheduler(scheduler_settings);
scheduler->SetCanStart();
scheduler->SetVisible(true);
scheduler->SetCanDraw(true);
EXPECT_SINGLE_ACTION("ScheduledActionBeginOutputSurfaceCreation", client);
InitializeOutputSurfaceAndFirstCommit(scheduler);
client.Reset();
scheduler->SetNeedsCommit();
if (deadline_scheduling_enabled) {
EXPECT_SINGLE_ACTION("SetNeedsBeginImplFrame", client);
} else {
EXPECT_ACTION("ScheduledActionSendBeginMainFrame", client, 0, 2);
EXPECT_ACTION("SetNeedsBeginImplFrame", client, 1, 2);
}
client.Reset();
scheduler->SetMainThreadNeedsLayerTextures();
EXPECT_SINGLE_ACTION(
"ScheduledActionAcquireLayerTexturesForMainThread", client);
client.Reset();
scheduler->BeginImplFrame(BeginFrameArgs::CreateForTesting());
if (deadline_scheduling_enabled) {
EXPECT_ACTION("ScheduledActionSendBeginMainFrame", client, 0, 2);
EXPECT_ACTION("PostBeginImplFrameDeadlineTask", client, 1, 2);
} else {
EXPECT_SINGLE_ACTION("PostBeginImplFrameDeadlineTask", client);
}
client.Reset();
scheduler->OnBeginImplFrameDeadline();
EXPECT_SINGLE_ACTION("SetNeedsBeginImplFrame", client);
// Although the compositor cannot draw because textures are locked by main
// thread, we continue requesting SetNeedsBeginImplFrame in anticipation of
// the unlock.
EXPECT_TRUE(client.needs_begin_impl_frame());
// Trigger the commit
scheduler->FinishCommit();
EXPECT_TRUE(client.needs_begin_impl_frame());
// Between commit and draw, texture acquisition for main thread delayed,
// and main thread blocks.
client.Reset();
scheduler->SetMainThreadNeedsLayerTextures();
EXPECT_EQ(0, client.num_actions_());
// No implicit commit is expected.
client.Reset();
scheduler->BeginImplFrame(BeginFrameArgs::CreateForTesting());
EXPECT_SINGLE_ACTION("PostBeginImplFrameDeadlineTask", client);
client.Reset();
scheduler->OnBeginImplFrameDeadline();
EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client, 0, 3);
EXPECT_ACTION(
"ScheduledActionAcquireLayerTexturesForMainThread", client, 1, 3);
EXPECT_ACTION("SetNeedsBeginImplFrame", client, 2, 3);
EXPECT_TRUE(client.needs_begin_impl_frame());
// The compositor should not draw because textures are locked by main
// thread.
client.Reset();
scheduler->BeginImplFrame(BeginFrameArgs::CreateForTesting());
EXPECT_SINGLE_ACTION("PostBeginImplFrameDeadlineTask", client);
client.Reset();
scheduler->OnBeginImplFrameDeadline();
EXPECT_SINGLE_ACTION("SetNeedsBeginImplFrame", client);
EXPECT_FALSE(client.needs_begin_impl_frame());
// The impl thread need an explicit commit from the main thread to lock
// the textures.
client.Reset();
scheduler->SetNeedsCommit();
if (deadline_scheduling_enabled) {
EXPECT_SINGLE_ACTION("SetNeedsBeginImplFrame", client);
} else {
EXPECT_ACTION("ScheduledActionSendBeginMainFrame", client, 0, 2);
EXPECT_ACTION("SetNeedsBeginImplFrame", client, 1, 2);
}
EXPECT_TRUE(client.needs_begin_impl_frame());
client.Reset();
scheduler->BeginImplFrame(BeginFrameArgs::CreateForTesting());
if (deadline_scheduling_enabled) {
EXPECT_ACTION("ScheduledActionSendBeginMainFrame", client, 0, 2);
EXPECT_ACTION("PostBeginImplFrameDeadlineTask", client, 1, 2);
} else {
EXPECT_SINGLE_ACTION("PostBeginImplFrameDeadlineTask", client);
}
client.Reset();
// Trigger the commit, which will trigger the deadline task early.
scheduler->FinishCommit();
EXPECT_ACTION("ScheduledActionCommit", client, 0, 2);
EXPECT_ACTION("PostBeginImplFrameDeadlineTask", client, 1, 2);
EXPECT_TRUE(client.needs_begin_impl_frame());
client.Reset();
// Verify we draw on the next BeginImplFrame deadline
scheduler->OnBeginImplFrameDeadline();
EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client, 0, 2);
EXPECT_ACTION("SetNeedsBeginImplFrame", client, 1, 2);
EXPECT_TRUE(client.needs_begin_impl_frame());
client.Reset();
}
TEST(SchedulerTest, TextureAcquisitionCollision) {
bool deadline_scheduling_enabled = false;
TextureAcquisitionCollision(deadline_scheduling_enabled);
}
TEST(SchedulerTest, TextureAcquisitionCollision_Deadline) {
bool deadline_scheduling_enabled = true;
TextureAcquisitionCollision(deadline_scheduling_enabled);
}
void VisibilitySwitchWithTextureAcquisition(bool deadline_scheduling_enabled) {
FakeSchedulerClient client;
SchedulerSettings scheduler_settings;
scheduler_settings.deadline_scheduling_enabled = deadline_scheduling_enabled;
Scheduler* scheduler = client.CreateScheduler(scheduler_settings);
scheduler->SetCanStart();
scheduler->SetVisible(true);
scheduler->SetCanDraw(true);
EXPECT_SINGLE_ACTION("ScheduledActionBeginOutputSurfaceCreation", client);
client.Reset();
scheduler->DidCreateAndInitializeOutputSurface();
scheduler->SetNeedsCommit();
if (deadline_scheduling_enabled) {
scheduler->BeginImplFrame(BeginFrameArgs::CreateForTesting());
scheduler->OnBeginImplFrameDeadline();
}
scheduler->FinishCommit();
scheduler->SetMainThreadNeedsLayerTextures();
scheduler->SetNeedsCommit();
client.Reset();
// Verify that pending texture acquisition fires when visibility
// is lost in order to avoid a deadlock.
scheduler->SetVisible(false);
EXPECT_SINGLE_ACTION("ScheduledActionAcquireLayerTexturesForMainThread",
client);
client.Reset();
scheduler->SetVisible(true);
EXPECT_EQ(0, client.num_actions_());
EXPECT_TRUE(client.needs_begin_impl_frame());
// Regaining visibility with textures acquired by main thread while
// compositor is waiting for first draw should result in a request
// for a new frame in order to escape a deadlock.
client.Reset();
scheduler->BeginImplFrame(BeginFrameArgs::CreateForTesting());
EXPECT_ACTION("ScheduledActionSendBeginMainFrame", client, 0, 2);
EXPECT_ACTION("PostBeginImplFrameDeadlineTask", client, 1, 2);
}
TEST(SchedulerTest, VisibilitySwitchWithTextureAcquisition) {
bool deadline_scheduling_enabled = false;
VisibilitySwitchWithTextureAcquisition(deadline_scheduling_enabled);
}
TEST(SchedulerTest, VisibilitySwitchWithTextureAcquisition_Deadline) {
bool deadline_scheduling_enabled = true;
VisibilitySwitchWithTextureAcquisition(deadline_scheduling_enabled);
}
class SchedulerClientThatsetNeedsDrawInsideDraw : public FakeSchedulerClient {
public:
virtual void ScheduledActionSendBeginMainFrame() OVERRIDE {}
virtual DrawSwapReadbackResult ScheduledActionDrawAndSwapIfPossible()
OVERRIDE {
// Only SetNeedsRedraw the first time this is called
if (!num_draws_)
scheduler_->SetNeedsRedraw();
return FakeSchedulerClient::ScheduledActionDrawAndSwapIfPossible();
}
virtual DrawSwapReadbackResult ScheduledActionDrawAndSwapForced() OVERRIDE {
NOTREACHED();
bool did_draw = true;
bool did_swap = true;
bool did_readback = false;
return DrawSwapReadbackResult(did_draw, did_swap, did_readback);
}
virtual void ScheduledActionCommit() OVERRIDE {}
virtual void ScheduledActionBeginOutputSurfaceCreation() OVERRIDE {}
virtual void DidAnticipatedDrawTimeChange(base::TimeTicks) OVERRIDE {}
};
// Tests for two different situations:
// 1. the scheduler dropping SetNeedsRedraw requests that happen inside
// a ScheduledActionDrawAndSwap
// 2. the scheduler drawing twice inside a single tick
TEST(SchedulerTest, RequestRedrawInsideDraw) {
SchedulerClientThatsetNeedsDrawInsideDraw client;
SchedulerSettings default_scheduler_settings;
Scheduler* scheduler = client.CreateScheduler(default_scheduler_settings);
scheduler->SetCanStart();
scheduler->SetVisible(true);
scheduler->SetCanDraw(true);
InitializeOutputSurfaceAndFirstCommit(scheduler);
client.Reset();
scheduler->SetNeedsRedraw();
EXPECT_TRUE(scheduler->RedrawPending());
EXPECT_TRUE(client.needs_begin_impl_frame());
EXPECT_EQ(0, client.num_draws());
scheduler->BeginImplFrame(BeginFrameArgs::CreateForTesting());
scheduler->OnBeginImplFrameDeadline();
EXPECT_EQ(1, client.num_draws());
EXPECT_TRUE(scheduler->RedrawPending());
EXPECT_TRUE(client.needs_begin_impl_frame());
scheduler->BeginImplFrame(BeginFrameArgs::CreateForTesting());
scheduler->OnBeginImplFrameDeadline();
EXPECT_EQ(2, client.num_draws());
EXPECT_FALSE(scheduler->RedrawPending());
EXPECT_TRUE(client.needs_begin_impl_frame());
// We stop requesting BeginImplFrames after a BeginImplFrame where we don't
// swap.
scheduler->BeginImplFrame(BeginFrameArgs::CreateForTesting());
scheduler->OnBeginImplFrameDeadline();
EXPECT_EQ(2, client.num_draws());
EXPECT_FALSE(scheduler->RedrawPending());
EXPECT_FALSE(client.needs_begin_impl_frame());
}
// Test that requesting redraw inside a failed draw doesn't lose the request.
TEST(SchedulerTest, RequestRedrawInsideFailedDraw) {
SchedulerClientThatsetNeedsDrawInsideDraw client;
SchedulerSettings default_scheduler_settings;
Scheduler* scheduler = client.CreateScheduler(default_scheduler_settings);
scheduler->SetCanStart();
scheduler->SetVisible(true);
scheduler->SetCanDraw(true);
InitializeOutputSurfaceAndFirstCommit(scheduler);
client.Reset();
client.SetDrawWillHappen(false);
scheduler->SetNeedsRedraw();
EXPECT_TRUE(scheduler->RedrawPending());
EXPECT_TRUE(client.needs_begin_impl_frame());
EXPECT_EQ(0, client.num_draws());
// Fail the draw.
scheduler->BeginImplFrame(BeginFrameArgs::CreateForTesting());
scheduler->OnBeginImplFrameDeadline();
EXPECT_EQ(1, client.num_draws());
// We have a commit pending and the draw failed, and we didn't lose the redraw
// request.
EXPECT_TRUE(scheduler->CommitPending());
EXPECT_TRUE(scheduler->RedrawPending());
EXPECT_TRUE(client.needs_begin_impl_frame());
// Fail the draw again.
scheduler->BeginImplFrame(BeginFrameArgs::CreateForTesting());
scheduler->OnBeginImplFrameDeadline();
EXPECT_EQ(2, client.num_draws());
EXPECT_TRUE(scheduler->CommitPending());
EXPECT_TRUE(scheduler->RedrawPending());
EXPECT_TRUE(client.needs_begin_impl_frame());
// Draw successfully.
client.SetDrawWillHappen(true);
scheduler->BeginImplFrame(BeginFrameArgs::CreateForTesting());
scheduler->OnBeginImplFrameDeadline();
EXPECT_EQ(3, client.num_draws());
EXPECT_TRUE(scheduler->CommitPending());
EXPECT_FALSE(scheduler->RedrawPending());
EXPECT_TRUE(client.needs_begin_impl_frame());
}
class SchedulerClientThatSetNeedsCommitInsideDraw : public FakeSchedulerClient {
public:
SchedulerClientThatSetNeedsCommitInsideDraw()
: set_needs_commit_on_next_draw_(false) {}
virtual void ScheduledActionSendBeginMainFrame() OVERRIDE {}
virtual DrawSwapReadbackResult ScheduledActionDrawAndSwapIfPossible()
OVERRIDE {
// Only SetNeedsCommit the first time this is called
if (set_needs_commit_on_next_draw_) {
scheduler_->SetNeedsCommit();
set_needs_commit_on_next_draw_ = false;
}
return FakeSchedulerClient::ScheduledActionDrawAndSwapIfPossible();
}
virtual DrawSwapReadbackResult ScheduledActionDrawAndSwapForced() OVERRIDE {
NOTREACHED();
bool did_draw = true;
bool did_swap = false;
bool did_readback = false;
return DrawSwapReadbackResult(did_draw, did_swap, did_readback);
}
virtual void ScheduledActionCommit() OVERRIDE {}
virtual void ScheduledActionBeginOutputSurfaceCreation() OVERRIDE {}
virtual void DidAnticipatedDrawTimeChange(base::TimeTicks) OVERRIDE {}
void SetNeedsCommitOnNextDraw() { set_needs_commit_on_next_draw_ = true; }
private:
bool set_needs_commit_on_next_draw_;
};
// Tests for the scheduler infinite-looping on SetNeedsCommit requests that
// happen inside a ScheduledActionDrawAndSwap
TEST(SchedulerTest, RequestCommitInsideDraw) {
SchedulerClientThatSetNeedsCommitInsideDraw client;
SchedulerSettings default_scheduler_settings;
Scheduler* scheduler = client.CreateScheduler(default_scheduler_settings);
scheduler->SetCanStart();
scheduler->SetVisible(true);
scheduler->SetCanDraw(true);
InitializeOutputSurfaceAndFirstCommit(scheduler);
client.Reset();
EXPECT_FALSE(client.needs_begin_impl_frame());
scheduler->SetNeedsRedraw();
EXPECT_TRUE(scheduler->RedrawPending());
EXPECT_EQ(0, client.num_draws());
EXPECT_TRUE(client.needs_begin_impl_frame());
client.SetNeedsCommitOnNextDraw();
scheduler->BeginImplFrame(BeginFrameArgs::CreateForTesting());
client.SetNeedsCommitOnNextDraw();
scheduler->OnBeginImplFrameDeadline();
EXPECT_EQ(1, client.num_draws());
EXPECT_TRUE(scheduler->CommitPending());
EXPECT_TRUE(client.needs_begin_impl_frame());
scheduler->FinishCommit();
scheduler->BeginImplFrame(BeginFrameArgs::CreateForTesting());
scheduler->OnBeginImplFrameDeadline();
EXPECT_EQ(2, client.num_draws());
EXPECT_FALSE(scheduler->RedrawPending());
EXPECT_FALSE(scheduler->CommitPending());
EXPECT_TRUE(client.needs_begin_impl_frame());
// We stop requesting BeginImplFrames after a BeginImplFrame where we don't
// swap.
scheduler->BeginImplFrame(BeginFrameArgs::CreateForTesting());
scheduler->OnBeginImplFrameDeadline();
EXPECT_EQ(2, client.num_draws());
EXPECT_FALSE(scheduler->RedrawPending());
EXPECT_FALSE(scheduler->CommitPending());
EXPECT_FALSE(client.needs_begin_impl_frame());
}
// Tests that when a draw fails then the pending commit should not be dropped.
TEST(SchedulerTest, RequestCommitInsideFailedDraw) {
SchedulerClientThatsetNeedsDrawInsideDraw client;
SchedulerSettings default_scheduler_settings;
Scheduler* scheduler = client.CreateScheduler(default_scheduler_settings);
scheduler->SetCanStart();
scheduler->SetVisible(true);
scheduler->SetCanDraw(true);
InitializeOutputSurfaceAndFirstCommit(scheduler);
client.Reset();
client.SetDrawWillHappen(false);
scheduler->SetNeedsRedraw();
EXPECT_TRUE(scheduler->RedrawPending());
EXPECT_TRUE(client.needs_begin_impl_frame());
EXPECT_EQ(0, client.num_draws());
// Fail the draw.
scheduler->BeginImplFrame(BeginFrameArgs::CreateForTesting());
scheduler->OnBeginImplFrameDeadline();
EXPECT_EQ(1, client.num_draws());
// We have a commit pending and the draw failed, and we didn't lose the commit
// request.
EXPECT_TRUE(scheduler->CommitPending());
EXPECT_TRUE(scheduler->RedrawPending());
EXPECT_TRUE(client.needs_begin_impl_frame());
// Fail the draw again.
scheduler->BeginImplFrame(BeginFrameArgs::CreateForTesting());
scheduler->OnBeginImplFrameDeadline();
EXPECT_EQ(2, client.num_draws());
EXPECT_TRUE(scheduler->CommitPending());
EXPECT_TRUE(scheduler->RedrawPending());
EXPECT_TRUE(client.needs_begin_impl_frame());
// Draw successfully.
client.SetDrawWillHappen(true);
scheduler->BeginImplFrame(BeginFrameArgs::CreateForTesting());
scheduler->OnBeginImplFrameDeadline();
EXPECT_EQ(3, client.num_draws());
EXPECT_TRUE(scheduler->CommitPending());
EXPECT_FALSE(scheduler->RedrawPending());
EXPECT_TRUE(client.needs_begin_impl_frame());
}
TEST(SchedulerTest, NoSwapWhenDrawFails) {
SchedulerClientThatSetNeedsCommitInsideDraw client;
SchedulerSettings default_scheduler_settings;
Scheduler* scheduler = client.CreateScheduler(default_scheduler_settings);
scheduler->SetCanStart();
scheduler->SetVisible(true);
scheduler->SetCanDraw(true);
InitializeOutputSurfaceAndFirstCommit(scheduler);
client.Reset();
scheduler->SetNeedsRedraw();
EXPECT_TRUE(scheduler->RedrawPending());
EXPECT_TRUE(client.needs_begin_impl_frame());
EXPECT_EQ(0, client.num_draws());
// Draw successfully, this starts a new frame.
client.SetNeedsCommitOnNextDraw();
scheduler->BeginImplFrame(BeginFrameArgs::CreateForTesting());
scheduler->OnBeginImplFrameDeadline();
EXPECT_EQ(1, client.num_draws());
scheduler->SetNeedsRedraw();
EXPECT_TRUE(scheduler->RedrawPending());
EXPECT_TRUE(client.needs_begin_impl_frame());
// Fail to draw, this should not start a frame.
client.SetDrawWillHappen(false);
client.SetNeedsCommitOnNextDraw();
scheduler->BeginImplFrame(BeginFrameArgs::CreateForTesting());
scheduler->OnBeginImplFrameDeadline();
EXPECT_EQ(2, client.num_draws());
}
TEST(SchedulerTest, NoSwapWhenSwapFailsDuringForcedCommit) {
FakeSchedulerClient client;
SchedulerSettings default_scheduler_settings;
Scheduler* scheduler = client.CreateScheduler(default_scheduler_settings);
// Tell the client that it will fail to swap.
client.SetDrawWillHappen(true);
client.SetSwapWillHappenIfDrawHappens(false);
// Get the compositor to do a ScheduledActionDrawAndReadback.
scheduler->SetCanDraw(true);
scheduler->SetNeedsRedraw();
scheduler->SetNeedsForcedCommitForReadback();
scheduler->FinishCommit();
EXPECT_TRUE(client.HasAction("ScheduledActionDrawAndReadback"));
}
TEST(SchedulerTest, BackToBackReadbackAllowed) {
// Some clients call readbacks twice in a row before the replacement
// commit comes in. Make sure it is allowed.
FakeSchedulerClient client;
SchedulerSettings default_scheduler_settings;
Scheduler* scheduler = client.CreateScheduler(default_scheduler_settings);
// Get the compositor to do 2 ScheduledActionDrawAndReadbacks before
// the replacement commit comes in.
scheduler->SetCanDraw(true);
scheduler->SetNeedsRedraw();
scheduler->SetNeedsForcedCommitForReadback();
scheduler->FinishCommit();
EXPECT_TRUE(client.HasAction("ScheduledActionDrawAndReadback"));
client.Reset();
scheduler->SetNeedsForcedCommitForReadback();
scheduler->FinishCommit();
EXPECT_TRUE(client.HasAction("ScheduledActionDrawAndReadback"));
// The replacement commit comes in after 2 readbacks.
client.Reset();
scheduler->FinishCommit();
}
class SchedulerClientNeedsManageTilesInDraw : public FakeSchedulerClient {
public:
virtual DrawSwapReadbackResult ScheduledActionDrawAndSwapIfPossible()
OVERRIDE {
scheduler_->SetNeedsManageTiles();
return FakeSchedulerClient::ScheduledActionDrawAndSwapIfPossible();
}
};
// Test manage tiles is independant of draws.
TEST(SchedulerTest, ManageTiles) {
SchedulerClientNeedsManageTilesInDraw client;
SchedulerSettings default_scheduler_settings;
Scheduler* scheduler = client.CreateScheduler(default_scheduler_settings);
scheduler->SetCanStart();
scheduler->SetVisible(true);
scheduler->SetCanDraw(true);
InitializeOutputSurfaceAndFirstCommit(scheduler);
// Request both draw and manage tiles. ManageTiles shouldn't
// be trigged until BeginImplFrame.
client.Reset();
scheduler->SetNeedsManageTiles();
scheduler->SetNeedsRedraw();
EXPECT_TRUE(scheduler->RedrawPending());
EXPECT_TRUE(scheduler->ManageTilesPending());
EXPECT_TRUE(client.needs_begin_impl_frame());
EXPECT_EQ(0, client.num_draws());
EXPECT_FALSE(client.HasAction("ScheduledActionManageTiles"));
EXPECT_FALSE(client.HasAction("ScheduledActionDrawAndSwapIfPossible"));
// We have no immediate actions to perform, so the BeginImplFrame should post
// the deadline task.
client.Reset();
scheduler->BeginImplFrame(BeginFrameArgs::CreateForTesting());
EXPECT_SINGLE_ACTION("PostBeginImplFrameDeadlineTask", client);
// On the deadline, he actions should have occured in the right order.
client.Reset();
scheduler->OnBeginImplFrameDeadline();
EXPECT_EQ(1, client.num_draws());
EXPECT_TRUE(client.HasAction("ScheduledActionDrawAndSwapIfPossible"));
EXPECT_TRUE(client.HasAction("ScheduledActionManageTiles"));
EXPECT_LT(client.ActionIndex("ScheduledActionDrawAndSwapIfPossible"),
client.ActionIndex("ScheduledActionManageTiles"));
EXPECT_FALSE(scheduler->RedrawPending());
EXPECT_FALSE(scheduler->ManageTilesPending());
// Request a draw. We don't need a ManageTiles yet.
client.Reset();
scheduler->SetNeedsRedraw();
EXPECT_TRUE(scheduler->RedrawPending());
EXPECT_FALSE(scheduler->ManageTilesPending());
EXPECT_TRUE(client.needs_begin_impl_frame());
EXPECT_EQ(0, client.num_draws());
// We have no immediate actions to perform, so the BeginImplFrame should post
// the deadline task.
client.Reset();
scheduler->BeginImplFrame(BeginFrameArgs::CreateForTesting());
EXPECT_SINGLE_ACTION("PostBeginImplFrameDeadlineTask", client);
// Draw. The draw will trigger SetNeedsManageTiles, and
// then the ManageTiles action will be triggered after the Draw.
// Afterwards, neither a draw nor ManageTiles are pending.
client.Reset();
scheduler->OnBeginImplFrameDeadline();
EXPECT_EQ(1, client.num_draws());
EXPECT_TRUE(client.HasAction("ScheduledActionDrawAndSwapIfPossible"));
EXPECT_TRUE(client.HasAction("ScheduledActionManageTiles"));
EXPECT_LT(client.ActionIndex("ScheduledActionDrawAndSwapIfPossible"),
client.ActionIndex("ScheduledActionManageTiles"));
EXPECT_FALSE(scheduler->RedrawPending());
EXPECT_FALSE(scheduler->ManageTilesPending());
// We need a BeginImplFrame where we don't swap to go idle.
client.Reset();
scheduler->BeginImplFrame(BeginFrameArgs::CreateForTesting());
EXPECT_SINGLE_ACTION("PostBeginImplFrameDeadlineTask", client);
client.Reset();
scheduler->OnBeginImplFrameDeadline();
EXPECT_SINGLE_ACTION("SetNeedsBeginImplFrame", client);;
EXPECT_EQ(0, client.num_draws());
// Now trigger a ManageTiles outside of a draw. We will then need
// a begin-frame for the ManageTiles, but we don't need a draw.
client.Reset();
EXPECT_FALSE(client.needs_begin_impl_frame());
scheduler->SetNeedsManageTiles();
EXPECT_TRUE(client.needs_begin_impl_frame());
EXPECT_TRUE(scheduler->ManageTilesPending());
EXPECT_FALSE(scheduler->RedrawPending());
// BeginImplFrame. There will be no draw, only ManageTiles.
client.Reset();
scheduler->BeginImplFrame(BeginFrameArgs::CreateForTesting());
EXPECT_SINGLE_ACTION("PostBeginImplFrameDeadlineTask", client);
client.Reset();
scheduler->OnBeginImplFrameDeadline();
EXPECT_EQ(0, client.num_draws());
EXPECT_FALSE(client.HasAction("ScheduledActionDrawAndSwapIfPossible"));
EXPECT_TRUE(client.HasAction("ScheduledActionManageTiles"));
}
// Test that ManageTiles only happens once per frame. If an external caller
// initiates it, then the state machine should not on that frame.
TEST(SchedulerTest, ManageTilesOncePerFrame) {
FakeSchedulerClient client;
SchedulerSettings default_scheduler_settings;
Scheduler* scheduler = client.CreateScheduler(default_scheduler_settings);
scheduler->SetCanStart();
scheduler->SetVisible(true);
scheduler->SetCanDraw(true);
InitializeOutputSurfaceAndFirstCommit(scheduler);
// If DidManageTiles during a frame, then ManageTiles should not occur again.
scheduler->SetNeedsManageTiles();
scheduler->SetNeedsRedraw();
client.Reset();
scheduler->BeginImplFrame(BeginFrameArgs::CreateForTesting());
EXPECT_SINGLE_ACTION("PostBeginImplFrameDeadlineTask", client);
EXPECT_TRUE(scheduler->ManageTilesPending());
scheduler->DidManageTiles();
EXPECT_FALSE(scheduler->ManageTilesPending());
client.Reset();
scheduler->OnBeginImplFrameDeadline();
EXPECT_EQ(1, client.num_draws());
EXPECT_TRUE(client.HasAction("ScheduledActionDrawAndSwapIfPossible"));
EXPECT_FALSE(client.HasAction("ScheduledActionManageTiles"));
EXPECT_FALSE(scheduler->RedrawPending());
EXPECT_FALSE(scheduler->ManageTilesPending());
// Next frame without DidManageTiles should ManageTiles with draw.
scheduler->SetNeedsManageTiles();
scheduler->SetNeedsRedraw();
client.Reset();
scheduler->BeginImplFrame(BeginFrameArgs::CreateForTesting());
EXPECT_SINGLE_ACTION("PostBeginImplFrameDeadlineTask", client);
client.Reset();
scheduler->OnBeginImplFrameDeadline();
EXPECT_EQ(1, client.num_draws());
EXPECT_TRUE(client.HasAction("ScheduledActionDrawAndSwapIfPossible"));
EXPECT_TRUE(client.HasAction("ScheduledActionManageTiles"));
EXPECT_LT(client.ActionIndex("ScheduledActionDrawAndSwapIfPossible"),
client.ActionIndex("ScheduledActionManageTiles"));
EXPECT_FALSE(scheduler->RedrawPending());
EXPECT_FALSE(scheduler->ManageTilesPending());
}
class SchedulerClientWithFixedEstimates : public FakeSchedulerClient {
public:
SchedulerClientWithFixedEstimates(
base::TimeDelta draw_duration,
base::TimeDelta begin_main_frame_to_commit_duration,
base::TimeDelta commit_to_activate_duration)
: draw_duration_(draw_duration),
begin_main_frame_to_commit_duration_(
begin_main_frame_to_commit_duration),
commit_to_activate_duration_(commit_to_activate_duration) {}
virtual base::TimeDelta DrawDurationEstimate() OVERRIDE {
return draw_duration_;
}
virtual base::TimeDelta BeginMainFrameToCommitDurationEstimate() OVERRIDE {
return begin_main_frame_to_commit_duration_;
}
virtual base::TimeDelta CommitToActivateDurationEstimate() OVERRIDE {
return commit_to_activate_duration_;
}
private:
base::TimeDelta draw_duration_;
base::TimeDelta begin_main_frame_to_commit_duration_;
base::TimeDelta commit_to_activate_duration_;
};
void MainFrameInHighLatencyMode(int64 begin_main_frame_to_commit_estimate_in_ms,
int64 commit_to_activate_estimate_in_ms,
bool should_send_begin_main_frame) {
// Set up client with specified estimates (draw duration is set to 1).
SchedulerClientWithFixedEstimates client(
base::TimeDelta::FromMilliseconds(1),
base::TimeDelta::FromMilliseconds(
begin_main_frame_to_commit_estimate_in_ms),
base::TimeDelta::FromMilliseconds(commit_to_activate_estimate_in_ms));
SchedulerSettings scheduler_settings;
scheduler_settings.deadline_scheduling_enabled = true;
scheduler_settings.switch_to_low_latency_if_possible = true;
Scheduler* scheduler = client.CreateScheduler(scheduler_settings);
scheduler->SetCanStart();
scheduler->SetVisible(true);
scheduler->SetCanDraw(true);
InitializeOutputSurfaceAndFirstCommit(scheduler);
// Impl thread hits deadline before commit finishes.
client.Reset();
scheduler->SetNeedsCommit();
EXPECT_FALSE(scheduler->MainThreadIsInHighLatencyMode());
scheduler->BeginImplFrame(BeginFrameArgs::CreateForTesting());
EXPECT_FALSE(scheduler->MainThreadIsInHighLatencyMode());
scheduler->OnBeginImplFrameDeadline();
EXPECT_TRUE(scheduler->MainThreadIsInHighLatencyMode());
scheduler->FinishCommit();
EXPECT_TRUE(scheduler->MainThreadIsInHighLatencyMode());
EXPECT_TRUE(client.HasAction("ScheduledActionSendBeginMainFrame"));
client.Reset();
scheduler->SetNeedsCommit();
EXPECT_TRUE(scheduler->MainThreadIsInHighLatencyMode());
scheduler->BeginImplFrame(BeginFrameArgs::CreateForTesting());
EXPECT_TRUE(scheduler->MainThreadIsInHighLatencyMode());
scheduler->OnBeginImplFrameDeadline();
EXPECT_EQ(scheduler->MainThreadIsInHighLatencyMode(),
should_send_begin_main_frame);
EXPECT_EQ(client.HasAction("ScheduledActionSendBeginMainFrame"),
should_send_begin_main_frame);
}
TEST(SchedulerTest,
SkipMainFrameIfHighLatencyAndCanCommitAndActivateBeforeDeadline) {
// Set up client so that estimates indicate that we can commit and activate
// before the deadline (~8ms by default).
MainFrameInHighLatencyMode(1, 1, false);
}
TEST(SchedulerTest, NotSkipMainFrameIfHighLatencyAndCanCommitTooLong) {
// Set up client so that estimates indicate that the commit cannot finish
// before the deadline (~8ms by default).
MainFrameInHighLatencyMode(10, 1, true);
}
TEST(SchedulerTest, NotSkipMainFrameIfHighLatencyAndCanActivateTooLong) {
// Set up client so that estimates indicate that the activate cannot finish
// before the deadline (~8ms by default).
MainFrameInHighLatencyMode(1, 10, true);
}
void SpinForMillis(int millis) {
base::RunLoop run_loop;
base::MessageLoop::current()->PostDelayedTask(
FROM_HERE,
run_loop.QuitClosure(),
base::TimeDelta::FromMilliseconds(millis));
run_loop.Run();
}
TEST(SchedulerTest, PollForCommitCompletion) {
FakeSchedulerClient client;
client.set_log_anticipated_draw_time_change(true);
SchedulerSettings settings = SchedulerSettings();
settings.throttle_frame_production = false;
Scheduler* scheduler = client.CreateScheduler(settings);
scheduler->SetCanDraw(true);
scheduler->SetCanStart();
scheduler->SetVisible(true);
scheduler->DidCreateAndInitializeOutputSurface();
scheduler->SetNeedsCommit();
EXPECT_TRUE(scheduler->CommitPending());
scheduler->FinishCommit();
scheduler->SetNeedsRedraw();
BeginFrameArgs impl_frame_args = BeginFrameArgs::CreateForTesting();
const int interval = 1;
impl_frame_args.interval = base::TimeDelta::FromMilliseconds(interval);
scheduler->BeginImplFrame(impl_frame_args);
scheduler->OnBeginImplFrameDeadline();
// At this point, we've drawn a frame. Start another commit, but hold off on
// the FinishCommit for now.
EXPECT_FALSE(scheduler->CommitPending());
scheduler->SetNeedsCommit();
EXPECT_TRUE(scheduler->CommitPending());
// Spin the event loop a few times and make sure we get more
// DidAnticipateDrawTimeChange calls every time.
int actions_so_far = client.num_actions_();
// Does three iterations to make sure that the timer is properly repeating.
for (int i = 0; i < 3; ++i) {
// Wait for 2x the frame interval to match
// Scheduler::advance_commit_state_timer_'s rate.
SpinForMillis(interval * 2);
EXPECT_GT(client.num_actions_(), actions_so_far);
EXPECT_STREQ(client.Action(client.num_actions_() - 1),
"DidAnticipatedDrawTimeChange");
actions_so_far = client.num_actions_();
}
}
} // namespace
} // namespace cc