summaryrefslogtreecommitdiffstats
path: root/cc
diff options
context:
space:
mode:
authorbrianderson@chromium.org <brianderson@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-09-21 04:10:03 +0000
committerbrianderson@chromium.org <brianderson@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-09-21 04:10:03 +0000
commit425e533958102722cf6137501978a8be886e420f (patch)
treee978ecbeeaf1100f101ca4221ace01eb7506b29e /cc
parent73eb9571e2adb83ae26e1fc53104d88e66c35ff6 (diff)
downloadchromium_src-425e533958102722cf6137501978a8be886e420f.zip
chromium_src-425e533958102722cf6137501978a8be886e420f.tar.gz
chromium_src-425e533958102722cf6137501978a8be886e420f.tar.bz2
cc: Implement deadline scheduling disabled by default
This patch adds logic to the Scheduler to actually use the BeginFrame and deadline, but is not enabled by default on any platform yet. This will ensure emulation of old scheduler in the fallback path is sane. Emulation of the old path is implemented using an immediate deadline. SchedulerStateMachine::begin_frame_state has been added and can be in one of 4 states: Idle, InsideBeginFrame, DeadlinePending, or InsideDeadline. Notable restrictions of the states are: - We start a commit as soon after InsideBeginFrame as we can, since the BeginFrame will be coordinated with user input. (True on Android. Soon to be true on other platforms.) - We do not start a commit while Idle, in order to wait for the next batch of user input. - Draw and swap only occurs during the InsideDeadline state. The deadlines of the Browser and Renderer compositors can be nested in order to have a total draw latency of < 1 frame when starting off. If we can't hit 1 frame of latency consistently, we will fall back to a higher latency mode to increase throughput. TBR=enne@chromium.org BUG=243461 Review URL: https://chromiumcodereview.appspot.com/24070006 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@224560 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'cc')
-rw-r--r--cc/output/begin_frame_args.cc11
-rw-r--r--cc/output/begin_frame_args.h4
-rw-r--r--cc/output/output_surface.cc23
-rw-r--r--cc/output/output_surface.h10
-rw-r--r--cc/output/output_surface_unittest.cc55
-rw-r--r--cc/scheduler/frame_rate_controller.cc13
-rw-r--r--cc/scheduler/scheduler.cc119
-rw-r--r--cc/scheduler/scheduler.h8
-rw-r--r--cc/scheduler/scheduler_settings.cc3
-rw-r--r--cc/scheduler/scheduler_settings.h1
-rw-r--r--cc/scheduler/scheduler_state_machine.cc176
-rw-r--r--cc/scheduler/scheduler_state_machine.h56
-rw-r--r--cc/scheduler/scheduler_state_machine_unittest.cc900
-rw-r--r--cc/scheduler/scheduler_unittest.cc394
-rw-r--r--cc/trees/layer_tree_host_impl.cc18
-rw-r--r--cc/trees/layer_tree_host_impl.h5
-rw-r--r--cc/trees/layer_tree_settings.cc1
-rw-r--r--cc/trees/layer_tree_settings.h1
-rw-r--r--cc/trees/thread_proxy.cc48
-rw-r--r--cc/trees/thread_proxy.h3
20 files changed, 1261 insertions, 588 deletions
diff --git a/cc/output/begin_frame_args.cc b/cc/output/begin_frame_args.cc
index cefb22b..d7da76c 100644
--- a/cc/output/begin_frame_args.cc
+++ b/cc/output/begin_frame_args.cc
@@ -48,10 +48,14 @@ BeginFrameArgs BeginFrameArgs::CreateExpiredForTesting() {
DefaultInterval());
}
+// This is a hard-coded deadline adjustment that assumes 60Hz, to be used in
+// cases where a good estimated draw time is not known. Using 1/3 of the vsync
+// as the default adjustment gives the Browser the last 1/3 of a frame to
+// produce output, the Renderer Impl thread the middle 1/3 of a frame to produce
+// ouput, and the Renderer Main thread the first 1/3 of a frame to produce
+// output.
base::TimeDelta BeginFrameArgs::DefaultDeadlineAdjustment() {
- // Using a large deadline adjustment will effectively revert BeginFrame
- // scheduling to the hard vsync scheduling we used to have.
- return base::TimeDelta::FromSeconds(-1);
+ return base::TimeDelta::FromMicroseconds(-16666 / 3);
}
base::TimeDelta BeginFrameArgs::DefaultInterval() {
@@ -62,5 +66,4 @@ base::TimeDelta BeginFrameArgs::DefaultRetroactiveBeginFramePeriod() {
return base::TimeDelta::FromMicroseconds(4444);
}
-
} // namespace cc
diff --git a/cc/output/begin_frame_args.h b/cc/output/begin_frame_args.h
index 22693db..025a407 100644
--- a/cc/output/begin_frame_args.h
+++ b/cc/output/begin_frame_args.h
@@ -37,9 +37,7 @@ struct CC_EXPORT BeginFrameArgs {
// retroactively.
static base::TimeDelta DefaultRetroactiveBeginFramePeriod();
- bool IsValid() const {
- return interval >= base::TimeDelta();
- }
+ bool IsValid() const { return interval >= base::TimeDelta(); }
base::TimeTicks frame_time;
base::TimeTicks deadline;
diff --git a/cc/output/output_surface.cc b/cc/output/output_surface.cc
index 0dc04ad..555fea2 100644
--- a/cc/output/output_surface.cc
+++ b/cc/output/output_surface.cc
@@ -4,6 +4,7 @@
#include "cc/output/output_surface.h"
+#include <algorithm>
#include <set>
#include <string>
#include <vector>
@@ -161,8 +162,13 @@ void OutputSurface::BeginFrame(const BeginFrameArgs& args) {
}
}
-base::TimeDelta OutputSurface::AlternateRetroactiveBeginFramePeriod() {
- return BeginFrameArgs::DefaultRetroactiveBeginFramePeriod();
+base::TimeTicks OutputSurface::RetroactiveBeginFrameDeadline() {
+ // TODO(brianderson): Remove the alternative deadline once we have better
+ // deadline estimations.
+ base::TimeTicks alternative_deadline =
+ skipped_begin_frame_args_.frame_time +
+ BeginFrameArgs::DefaultRetroactiveBeginFramePeriod();
+ return std::max(skipped_begin_frame_args_.deadline, alternative_deadline);
}
void OutputSurface::PostCheckForRetroactiveBeginFrame() {
@@ -180,16 +186,8 @@ void OutputSurface::PostCheckForRetroactiveBeginFrame() {
void OutputSurface::CheckForRetroactiveBeginFrame() {
TRACE_EVENT0("cc", "OutputSurface::CheckForRetroactiveBeginFrame");
check_for_retroactive_begin_frame_pending_ = false;
- base::TimeTicks now = base::TimeTicks::Now();
- // TODO(brianderson): Remove the alternative deadline once we have better
- // deadline estimations.
- base::TimeTicks alternative_deadline =
- skipped_begin_frame_args_.frame_time +
- AlternateRetroactiveBeginFramePeriod();
- if (now < skipped_begin_frame_args_.deadline ||
- now < alternative_deadline) {
+ if (base::TimeTicks::Now() < RetroactiveBeginFrameDeadline())
BeginFrame(skipped_begin_frame_args_);
- }
}
void OutputSurface::DidSwapBuffers() {
@@ -219,6 +217,9 @@ void OutputSurface::DidLoseOutputSurface() {
TRACE_EVENT0("cc", "OutputSurface::DidLoseOutputSurface");
client_ready_for_begin_frame_ = true;
pending_swap_buffers_ = 0;
+ skipped_begin_frame_args_ = BeginFrameArgs();
+ if (frame_rate_controller_)
+ frame_rate_controller_->SetActive(false);
client_->DidLoseOutputSurface();
}
diff --git a/cc/output/output_surface.h b/cc/output/output_surface.h
index 1c49b0d..13db5fa 100644
--- a/cc/output/output_surface.h
+++ b/cc/output/output_surface.h
@@ -165,6 +165,10 @@ class CC_EXPORT OutputSurface : public FrameRateControllerClient {
bool needs_begin_frame_;
bool client_ready_for_begin_frame_;
+ // This stores a BeginFrame that we couldn't process immediately, but might
+ // process retroactively in the near future.
+ BeginFrameArgs skipped_begin_frame_args_;
+
// Forwarded to OutputSurfaceClient but threaded through OutputSurface
// first so OutputSurface has a chance to update the FrameRateController
void SetNeedsRedrawRect(gfx::Rect damage_rect);
@@ -180,7 +184,7 @@ class CC_EXPORT OutputSurface : public FrameRateControllerClient {
bool valid_for_tile_management);
// virtual for testing.
- virtual base::TimeDelta AlternateRetroactiveBeginFramePeriod();
+ virtual base::TimeTicks RetroactiveBeginFrameDeadline();
virtual void PostCheckForRetroactiveBeginFrame();
void CheckForRetroactiveBeginFrame();
@@ -193,10 +197,6 @@ class CC_EXPORT OutputSurface : public FrameRateControllerClient {
void SetMemoryPolicy(const ManagedMemoryPolicy& policy,
bool discard_backbuffer_when_not_visible);
- // This stores a BeginFrame that we couldn't process immediately, but might
- // process retroactively in the near future.
- BeginFrameArgs skipped_begin_frame_args_;
-
// check_for_retroactive_begin_frame_pending_ is used to avoid posting
// redundant checks for a retroactive BeginFrame.
bool check_for_retroactive_begin_frame_pending_;
diff --git a/cc/output/output_surface_unittest.cc b/cc/output/output_surface_unittest.cc
index c8324a6..91e7c39 100644
--- a/cc/output/output_surface_unittest.cc
+++ b/cc/output/output_surface_unittest.cc
@@ -22,15 +22,21 @@ namespace {
class TestOutputSurface : public OutputSurface {
public:
explicit TestOutputSurface(scoped_refptr<ContextProvider> context_provider)
- : OutputSurface(context_provider) {}
+ : OutputSurface(context_provider),
+ retroactive_begin_frame_deadline_enabled_(false),
+ override_retroactive_period_(false) {}
explicit TestOutputSurface(
scoped_ptr<cc::SoftwareOutputDevice> software_device)
- : OutputSurface(software_device.Pass()) {}
+ : OutputSurface(software_device.Pass()),
+ retroactive_begin_frame_deadline_enabled_(false),
+ override_retroactive_period_(false) {}
TestOutputSurface(scoped_refptr<ContextProvider> context_provider,
scoped_ptr<cc::SoftwareOutputDevice> software_device)
- : OutputSurface(context_provider, software_device.Pass()) {}
+ : OutputSurface(context_provider, software_device.Pass()),
+ retroactive_begin_frame_deadline_enabled_(false),
+ override_retroactive_period_(false) {}
bool InitializeNewContext3d(
scoped_refptr<ContextProvider> new_context_provider) {
@@ -61,8 +67,12 @@ class TestOutputSurface : public OutputSurface {
OnSwapBuffersComplete();
}
- void SetAlternateRetroactiveBeginFramePeriod(base::TimeDelta period) {
- alternate_retroactive_begin_frame_period_ = period;
+ void EnableRetroactiveBeginFrameDeadline(bool enable,
+ bool override_retroactive_period,
+ base::TimeDelta period_override) {
+ retroactive_begin_frame_deadline_enabled_ = enable;
+ override_retroactive_period_ = override_retroactive_period;
+ retroactive_period_override_ = period_override;
}
protected:
@@ -71,11 +81,21 @@ class TestOutputSurface : public OutputSurface {
CheckForRetroactiveBeginFrame();
}
- virtual base::TimeDelta AlternateRetroactiveBeginFramePeriod() OVERRIDE {
- return alternate_retroactive_begin_frame_period_;
+ virtual base::TimeTicks RetroactiveBeginFrameDeadline() OVERRIDE {
+ if (retroactive_begin_frame_deadline_enabled_) {
+ if (override_retroactive_period_) {
+ return skipped_begin_frame_args_.frame_time +
+ retroactive_period_override_;
+ } else {
+ return OutputSurface::RetroactiveBeginFrameDeadline();
+ }
+ }
+ return base::TimeTicks();
}
- base::TimeDelta alternate_retroactive_begin_frame_period_;
+ bool retroactive_begin_frame_deadline_enabled_;
+ bool override_retroactive_period_;
+ base::TimeDelta retroactive_period_override_;
};
class TestSoftwareOutputDevice : public SoftwareOutputDevice {
@@ -220,8 +240,8 @@ TEST(OutputSurfaceTest, BeginFrameEmulation) {
display_refresh_interval);
output_surface.SetMaxFramesPending(2);
- output_surface.SetAlternateRetroactiveBeginFramePeriod(
- base::TimeDelta::FromSeconds(-1));
+ output_surface.EnableRetroactiveBeginFrameDeadline(
+ false, false, base::TimeDelta());
// We should start off with 0 BeginFrames
EXPECT_EQ(client.begin_frame_count(), 0);
@@ -294,10 +314,8 @@ TEST(OutputSurfaceTest, OptimisticAndRetroactiveBeginFrames) {
EXPECT_FALSE(client.deferred_initialize_called());
output_surface.SetMaxFramesPending(2);
-
- // Enable retroactive BeginFrames.
- output_surface.SetAlternateRetroactiveBeginFramePeriod(
- base::TimeDelta::FromSeconds(100000));
+ output_surface.EnableRetroactiveBeginFrameDeadline(
+ true, false, base::TimeDelta());
// Optimistically injected BeginFrames should be throttled if
// SetNeedsBeginFrame is false...
@@ -348,7 +366,7 @@ TEST(OutputSurfaceTest, RetroactiveBeginFrameDoesNotDoubleTickWhenEmulating) {
EXPECT_TRUE(output_surface.HasClient());
EXPECT_FALSE(client.deferred_initialize_called());
- base::TimeDelta big_interval = base::TimeDelta::FromSeconds(1000);
+ base::TimeDelta big_interval = base::TimeDelta::FromSeconds(10);
// Initialize BeginFrame emulation
scoped_refptr<base::TestSimpleTaskRunner> task_runner =
@@ -364,18 +382,17 @@ TEST(OutputSurfaceTest, RetroactiveBeginFrameDoesNotDoubleTickWhenEmulating) {
// We need to subtract an epsilon from Now() because some platforms have
// a slow clock.
output_surface.OnVSyncParametersChangedForTesting(
- base::TimeTicks::Now() - base::TimeDelta::FromMilliseconds(1),
- display_refresh_interval);
+ base::TimeTicks::Now() - base::TimeDelta::FromSeconds(1), big_interval);
output_surface.SetMaxFramesPending(2);
- output_surface.SetAlternateRetroactiveBeginFramePeriod(
- base::TimeDelta::FromSeconds(-1));
+ output_surface.EnableRetroactiveBeginFrameDeadline(true, true, big_interval);
// We should start off with 0 BeginFrames
EXPECT_EQ(client.begin_frame_count(), 0);
EXPECT_EQ(output_surface.pending_swap_buffers(), 0);
// The first SetNeedsBeginFrame(true) should start a retroactive BeginFrame.
+ EXPECT_FALSE(task_runner->HasPendingTask());
output_surface.SetNeedsBeginFrame(true);
EXPECT_TRUE(task_runner->HasPendingTask());
EXPECT_GT(task_runner->NextPendingTaskDelay(), big_interval / 2);
diff --git a/cc/scheduler/frame_rate_controller.cc b/cc/scheduler/frame_rate_controller.cc
index 54c1bda..cefe764 100644
--- a/cc/scheduler/frame_rate_controller.cc
+++ b/cc/scheduler/frame_rate_controller.cc
@@ -76,9 +76,8 @@ BeginFrameArgs FrameRateController::SetActive(bool active) {
base::TimeTicks missed_tick_time = time_source_->SetActive(active);
if (!missed_tick_time.is_null()) {
base::TimeTicks deadline = NextTickTime();
- return BeginFrameArgs::Create(missed_tick_time,
- deadline,
- interval_);
+ return BeginFrameArgs::Create(
+ missed_tick_time, deadline + deadline_adjustment_, interval_);
}
} else {
if (active)
@@ -118,10 +117,10 @@ void FrameRateController::OnTimerTick() {
if (client_) {
// TODO(brianderson): Use an adaptive parent compositor deadline.
base::TimeTicks frame_time = LastTickTime();
- base::TimeTicks deadline = NextTickTime() + deadline_adjustment_;
- client_->FrameRateControllerTick(
- throttled,
- BeginFrameArgs::Create(frame_time, deadline, interval_));
+ base::TimeTicks deadline = NextTickTime();
+ BeginFrameArgs args = BeginFrameArgs::Create(
+ frame_time, deadline + deadline_adjustment_, interval_);
+ client_->FrameRateControllerTick(throttled, args);
}
if (!is_time_source_throttling_ && !throttled)
diff --git a/cc/scheduler/scheduler.cc b/cc/scheduler/scheduler.cc
index 72bade3..8ffd575 100644
--- a/cc/scheduler/scheduler.cc
+++ b/cc/scheduler/scheduler.cc
@@ -4,6 +4,7 @@
#include "cc/scheduler/scheduler.h"
+#include <algorithm>
#include "base/auto_reset.h"
#include "base/debug/trace_event.h"
#include "base/logging.h"
@@ -21,12 +22,10 @@ Scheduler::Scheduler(SchedulerClient* client,
inside_process_scheduled_actions_(false),
inside_action_(SchedulerStateMachine::ACTION_NONE) {
DCHECK(client_);
- DCHECK(!state_machine_.BeginFrameNeededToDrawByImplThread());
+ DCHECK(!state_machine_.BeginFrameNeededByImplThread());
}
-Scheduler::~Scheduler() {
- client_->SetNeedsBeginFrameOnImplThread(false);
-}
+Scheduler::~Scheduler() {}
void Scheduler::SetCanStart() {
state_machine_.SetCanStart();
@@ -48,6 +47,12 @@ void Scheduler::NotifyReadyToActivate() {
ProcessScheduledActions();
}
+void Scheduler::ActivatePendingTree() {
+ client_->ScheduledActionActivatePendingTree();
+ if (state_machine_.ShouldTriggerBeginFrameDeadlineEarly())
+ PostBeginFrameDeadline(base::TimeTicks());
+}
+
void Scheduler::SetNeedsCommit() {
state_machine_.SetNeedsCommit();
ProcessScheduledActions();
@@ -84,6 +89,9 @@ void Scheduler::FinishCommit() {
TRACE_EVENT0("cc", "Scheduler::FinishCommit");
state_machine_.FinishCommit();
ProcessScheduledActions();
+
+ if (state_machine_.ShouldTriggerBeginFrameDeadlineEarly())
+ PostBeginFrameDeadline(base::TimeTicks());
}
void Scheduler::BeginFrameAbortedByMainThread(bool did_handle) {
@@ -94,14 +102,17 @@ void Scheduler::BeginFrameAbortedByMainThread(bool did_handle) {
void Scheduler::DidLoseOutputSurface() {
TRACE_EVENT0("cc", "Scheduler::DidLoseOutputSurface");
+ last_set_needs_begin_frame_ = false;
+ begin_frame_deadline_closure_.Cancel();
state_machine_.DidLoseOutputSurface();
ProcessScheduledActions();
}
void Scheduler::DidCreateAndInitializeOutputSurface() {
TRACE_EVENT0("cc", "Scheduler::DidCreateAndInitializeOutputSurface");
+ DCHECK(!last_set_needs_begin_frame_);
+ DCHECK(begin_frame_deadline_closure_.IsCancelled());
state_machine_.DidCreateAndInitializeOutputSurface();
- last_set_needs_begin_frame_ = false;
ProcessScheduledActions();
}
@@ -112,12 +123,11 @@ base::TimeTicks Scheduler::AnticipatedDrawTime() {
last_begin_frame_args_.interval <= base::TimeDelta())
return base::TimeTicks();
- // TODO(brianderson): Express this in terms of the deadline.
base::TimeTicks now = base::TimeTicks::Now();
- int64 intervals = 1 + ((now - last_begin_frame_args_.frame_time) /
- last_begin_frame_args_.interval);
- return last_begin_frame_args_.frame_time +
- (last_begin_frame_args_.interval * intervals);
+ base::TimeTicks timebase = std::max(last_begin_frame_args_.frame_time,
+ last_begin_frame_args_.deadline);
+ int64 intervals = 1 + ((now - timebase) / last_begin_frame_args_.interval);
+ return timebase + (last_begin_frame_args_.interval * intervals);
}
base::TimeTicks Scheduler::LastBeginFrameOnImplThreadTime() {
@@ -125,32 +135,30 @@ base::TimeTicks Scheduler::LastBeginFrameOnImplThreadTime() {
}
void Scheduler::SetupNextBeginFrameIfNeeded() {
- bool needs_begin_frame_to_draw =
- state_machine_.BeginFrameNeededToDrawByImplThread();
- // We want to avoid proactive begin frames with the synchronous compositor
- // because every SetNeedsBeginFrame will force a redraw.
- bool proactive_begin_frame_wanted =
- state_machine_.ProactiveBeginFrameWantedByImplThread() &&
- !settings_.using_synchronous_renderer_compositor &&
- settings_.throttle_frame_production;
- bool needs_begin_frame = needs_begin_frame_to_draw ||
- proactive_begin_frame_wanted;
+ bool needs_begin_frame =
+ state_machine_.BeginFrameNeededByImplThread();
+
+ bool at_end_of_deadline =
+ state_machine_.begin_frame_state() ==
+ SchedulerStateMachine::BEGIN_FRAME_STATE_INSIDE_DEADLINE;
bool should_call_set_needs_begin_frame =
// Always request the BeginFrame immediately if it wasn't needed before.
(needs_begin_frame && !last_set_needs_begin_frame_) ||
// We always need to explicitly request our next BeginFrame.
- state_machine_.inside_begin_frame();
+ at_end_of_deadline;
if (should_call_set_needs_begin_frame) {
client_->SetNeedsBeginFrameOnImplThread(needs_begin_frame);
last_set_needs_begin_frame_ = needs_begin_frame;
}
- // Setup PollForAnticipatedDrawTriggers for cases where we want a proactive
- // BeginFrame but aren't requesting one.
- if (!needs_begin_frame &&
- state_machine_.ProactiveBeginFrameWantedByImplThread()) {
+ // Setup PollForAnticipatedDrawTriggers if we need to monitor state but
+ // aren't expecting any more BeginFrames. This should only be needed by the
+ // synchronous compositor when BeginFrameNeededByImplThread is false.
+ if (state_machine_.ShouldPollForAnticipatedDrawTriggers()) {
+ DCHECK(settings_.using_synchronous_renderer_compositor);
+ DCHECK(!needs_begin_frame);
if (poll_for_draw_triggers_closure_.IsCancelled()) {
poll_for_draw_triggers_closure_.Reset(
base::Bind(&Scheduler::PollForAnticipatedDrawTriggers,
@@ -167,11 +175,64 @@ void Scheduler::SetupNextBeginFrameIfNeeded() {
void Scheduler::BeginFrame(const BeginFrameArgs& args) {
TRACE_EVENT0("cc", "Scheduler::BeginFrame");
- DCHECK(!state_machine_.inside_begin_frame());
+ DCHECK(state_machine_.begin_frame_state() ==
+ SchedulerStateMachine::BEGIN_FRAME_STATE_IDLE);
last_begin_frame_args_ = args;
- state_machine_.DidEnterBeginFrame(args);
+ last_begin_frame_args_.deadline -= client_->DrawDurationEstimate();
+ state_machine_.OnBeginFrame(last_begin_frame_args_);
+ ProcessScheduledActions();
+ state_machine_.OnBeginFrameDeadlinePending();
+
+ if (settings_.using_synchronous_renderer_compositor) {
+ // The synchronous renderer compositor has to make its GL calls
+ // within this call to BeginFrame.
+ // TODO(brianderson): Have the OutputSurface initiate the deadline tasks
+ // so the sychronous renderer compoistor can take advantage of splitting
+ // up the BeginFrame and deadline as well.
+ OnBeginFrameDeadline();
+ } else if (!settings_.deadline_scheduling_enabled) {
+ // We emulate the old non-deadline scheduler here by posting the
+ // deadline task without any delay.
+ PostBeginFrameDeadline(base::TimeTicks());
+ } else if (state_machine_.ShouldTriggerBeginFrameDeadlineEarly()) {
+ // We are ready to draw a new active tree immediately.
+ PostBeginFrameDeadline(base::TimeTicks());
+ } else if (state_machine_.needs_redraw()) {
+ // We have an animation or fast input path on the impl thread that wants
+ // to draw, so don't wait too long for a new active tree.
+ PostBeginFrameDeadline(last_begin_frame_args_.deadline);
+ } else {
+ // The impl thread doesn't have anything it wants to draw and we are just
+ // waiting for a new active tree, so post the deadline for the next
+ // expected BeginFrame start. This allows us to draw immediately when
+ // there is a new active tree, instead of waiting for the next BeginFrame.
+ // TODO(brianderson): Handle long deadlines (that are past the next frame's
+ // frame time) properly instead of using this hack.
+ PostBeginFrameDeadline(last_begin_frame_args_.frame_time +
+ last_begin_frame_args_.interval);
+ }
+}
+
+void Scheduler::PostBeginFrameDeadline(base::TimeTicks deadline) {
+ begin_frame_deadline_closure_.Cancel();
+ begin_frame_deadline_closure_.Reset(
+ base::Bind(&Scheduler::OnBeginFrameDeadline, weak_factory_.GetWeakPtr()));
+ client_->PostBeginFrameDeadline(begin_frame_deadline_closure_.callback(),
+ deadline);
+}
+
+void Scheduler::OnBeginFrameDeadline() {
+ TRACE_EVENT0("cc", "Scheduler::OnBeginFrameDeadline");
+ begin_frame_deadline_closure_.Cancel();
+ state_machine_.OnBeginFrameDeadline();
ProcessScheduledActions();
- state_machine_.DidLeaveBeginFrame();
+ // We only transition out of BEGIN_FRAME_STATE_INSIDE_DEADLINE when all
+ // actions that occur back-to-back in response to entering
+ // BEGIN_FRAME_STATE_INSIDE_DEADLINE have completed. This is important
+ // because sending the BeginFrame to the main thread will not occur if
+ // we transition to BEGIN_FRAME_STATE_IDLE too early.
+ state_machine_.OnBeginFrameIdle();
+ client_->DidBeginFrameDeadlineOnImplThread();
}
void Scheduler::PollForAnticipatedDrawTriggers() {
@@ -230,7 +291,7 @@ void Scheduler::ProcessScheduledActions() {
client_->ScheduledActionUpdateVisibleTiles();
break;
case SchedulerStateMachine::ACTION_ACTIVATE_PENDING_TREE:
- client_->ScheduledActionActivatePendingTree();
+ ActivatePendingTree();
break;
case SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE:
DrawAndSwapIfPossible();
diff --git a/cc/scheduler/scheduler.h b/cc/scheduler/scheduler.h
index 4d1b0b8..0b7da61 100644
--- a/cc/scheduler/scheduler.h
+++ b/cc/scheduler/scheduler.h
@@ -8,6 +8,7 @@
#include <string>
#include "base/basictypes.h"
+#include "base/cancelable_callback.h"
#include "base/memory/scoped_ptr.h"
#include "base/time/time.h"
#include "cc/base/cc_export.h"
@@ -47,6 +48,9 @@ class SchedulerClient {
virtual base::TimeDelta DrawDurationEstimate() = 0;
virtual base::TimeDelta BeginFrameToCommitDurationEstimate() = 0;
virtual base::TimeDelta CommitToActivateDurationEstimate() = 0;
+ virtual void PostBeginFrameDeadline(const base::Closure& closure,
+ base::TimeTicks deadline) = 0;
+ virtual void DidBeginFrameDeadlineOnImplThread() = 0;
protected:
virtual ~SchedulerClient() {}
@@ -104,6 +108,7 @@ class CC_EXPORT Scheduler {
base::TimeTicks LastBeginFrameOnImplThreadTime();
void BeginFrame(const BeginFrameArgs& args);
+ void OnBeginFrameDeadline();
void PollForAnticipatedDrawTriggers();
scoped_ptr<base::Value> StateAsValue() {
@@ -118,7 +123,9 @@ class CC_EXPORT Scheduler {
Scheduler(SchedulerClient* client,
const SchedulerSettings& scheduler_settings);
+ void PostBeginFrameDeadline(base::TimeTicks deadline);
void SetupNextBeginFrameIfNeeded();
+ void ActivatePendingTree();
void DrawAndSwapIfPossible();
void DrawAndSwapForced();
void DrawAndReadback();
@@ -130,6 +137,7 @@ class CC_EXPORT Scheduler {
base::WeakPtrFactory<Scheduler> weak_factory_;
bool last_set_needs_begin_frame_;
BeginFrameArgs last_begin_frame_args_;
+ base::CancelableClosure begin_frame_deadline_closure_;
base::CancelableClosure poll_for_draw_triggers_closure_;
SchedulerStateMachine state_machine_;
diff --git a/cc/scheduler/scheduler_settings.cc b/cc/scheduler/scheduler_settings.cc
index 086028e..6c1db6b 100644
--- a/cc/scheduler/scheduler_settings.cc
+++ b/cc/scheduler/scheduler_settings.cc
@@ -7,7 +7,8 @@
namespace cc {
SchedulerSettings::SchedulerSettings()
- : impl_side_painting(false),
+ : deadline_scheduling_enabled(false),
+ impl_side_painting(false),
timeout_and_draw_when_animation_checkerboards(true),
maximum_number_of_failed_draws_before_draw_is_forced_(3),
using_synchronous_renderer_compositor(false),
diff --git a/cc/scheduler/scheduler_settings.h b/cc/scheduler/scheduler_settings.h
index 66d276c..7857a70 100644
--- a/cc/scheduler/scheduler_settings.h
+++ b/cc/scheduler/scheduler_settings.h
@@ -14,6 +14,7 @@ class CC_EXPORT SchedulerSettings {
SchedulerSettings();
~SchedulerSettings();
+ bool deadline_scheduling_enabled;
bool impl_side_painting;
bool timeout_and_draw_when_animation_checkerboards;
int maximum_number_of_failed_draws_before_draw_is_forced_;
diff --git a/cc/scheduler/scheduler_state_machine.cc b/cc/scheduler/scheduler_state_machine.cc
index c80d9cc..0d64d2d 100644
--- a/cc/scheduler/scheduler_state_machine.cc
+++ b/cc/scheduler/scheduler_state_machine.cc
@@ -14,22 +14,22 @@ namespace cc {
SchedulerStateMachine::SchedulerStateMachine(const SchedulerSettings& settings)
: settings_(settings),
output_surface_state_(OUTPUT_SURFACE_LOST),
+ begin_frame_state_(BEGIN_FRAME_STATE_IDLE),
commit_state_(COMMIT_STATE_IDLE),
texture_state_(LAYER_TEXTURE_STATE_UNLOCKED),
forced_redraw_state_(FORCED_REDRAW_STATE_IDLE),
readback_state_(READBACK_STATE_IDLE),
commit_count_(0),
current_frame_number_(0),
- last_frame_number_where_begin_frame_sent_to_main_thread_(-1),
last_frame_number_swap_performed_(-1),
- last_frame_number_where_update_visible_tiles_was_called_(-1),
+ last_frame_number_begin_frame_sent_to_main_thread_(-1),
+ last_frame_number_update_visible_tiles_was_called_(-1),
consecutive_failed_draws_(0),
needs_redraw_(false),
needs_manage_tiles_(false),
swap_used_incomplete_tile_(false),
needs_commit_(false),
main_thread_needs_layer_textures_(false),
- inside_begin_frame_(false),
inside_poll_for_anticipated_draw_triggers_(false),
visible_(false),
can_start_(false),
@@ -58,6 +58,22 @@ const char* SchedulerStateMachine::OutputSurfaceStateToString(
return "???";
}
+const char* SchedulerStateMachine::BeginFrameStateToString(
+ BeginFrameState state) {
+ switch (state) {
+ case BEGIN_FRAME_STATE_IDLE:
+ return "BEGIN_FRAME_STATE_IDLE";
+ case BEGIN_FRAME_STATE_BEGIN_FRAME_STARTING:
+ return "BEGIN_FRAME_STATE_BEGIN_FRAME_STARTING";
+ case BEGIN_FRAME_STATE_INSIDE_BEGIN_FRAME:
+ return "BEGIN_FRAME_STATE_INSIDE_BEGIN_FRAME";
+ case BEGIN_FRAME_STATE_INSIDE_DEADLINE:
+ return "BEGIN_FRAME_STATE_INSIDE_DEADLINE";
+ }
+ NOTREACHED();
+ return "???";
+}
+
const char* SchedulerStateMachine::CommitStateToString(CommitState state) {
switch (state) {
case COMMIT_STATE_IDLE:
@@ -160,6 +176,8 @@ scoped_ptr<base::Value> SchedulerStateMachine::AsValue() const {
scoped_ptr<base::DictionaryValue> major_state(new base::DictionaryValue);
major_state->SetString("next_action", ActionToString(NextAction()));
+ major_state->SetString("begin_frame_state",
+ BeginFrameStateToString(begin_frame_state_));
major_state->SetString("commit_state", CommitStateToString(commit_state_));
major_state->SetString("texture_state_",
TextureStateToString(texture_state_));
@@ -202,14 +220,16 @@ scoped_ptr<base::Value> SchedulerStateMachine::AsValue() const {
scoped_ptr<base::DictionaryValue> minor_state(new base::DictionaryValue);
minor_state->SetInteger("commit_count", commit_count_);
minor_state->SetInteger("current_frame_number", current_frame_number_);
- minor_state->SetInteger(
- "last_frame_number_where_begin_frame_sent_to_main_thread",
- last_frame_number_where_begin_frame_sent_to_main_thread_);
- minor_state->SetInteger("last_frame_number_swap_performed_",
+
+ minor_state->SetInteger("last_frame_number_swap_performed",
last_frame_number_swap_performed_);
minor_state->SetInteger(
- "last_frame_number_where_update_visible_tiles_was_called",
- last_frame_number_where_update_visible_tiles_was_called_);
+ "last_frame_number_begin_frame_sent_to_main_thread",
+ last_frame_number_begin_frame_sent_to_main_thread_);
+ minor_state->SetInteger(
+ "last_frame_number_update_visible_tiles_was_called",
+ last_frame_number_update_visible_tiles_was_called_);
+
minor_state->SetInteger("consecutive_failed_draws",
consecutive_failed_draws_);
minor_state->SetBoolean("needs_redraw", needs_redraw_);
@@ -219,14 +239,13 @@ scoped_ptr<base::Value> SchedulerStateMachine::AsValue() const {
minor_state->SetBoolean("needs_commit", needs_commit_);
minor_state->SetBoolean("main_thread_needs_layer_textures",
main_thread_needs_layer_textures_);
- minor_state->SetBoolean("inside_begin_frame", inside_begin_frame_);
minor_state->SetBoolean("visible", visible_);
minor_state->SetBoolean("can_start", can_start_);
minor_state->SetBoolean("can_draw", can_draw_);
minor_state->SetBoolean("has_pending_tree", has_pending_tree_);
- minor_state->SetBoolean("pending_tree_is_ready_for_activation_",
+ minor_state->SetBoolean("pending_tree_is_ready_for_activation",
pending_tree_is_ready_for_activation_);
- minor_state->SetBoolean("active_tree_needs_first_draw_",
+ minor_state->SetBoolean("active_tree_needs_first_draw",
active_tree_needs_first_draw_);
minor_state->SetBoolean("draw_if_possible_failed", draw_if_possible_failed_);
minor_state->SetBoolean("did_create_and_initialize_first_output_surface",
@@ -236,18 +255,18 @@ scoped_ptr<base::Value> SchedulerStateMachine::AsValue() const {
return state.PassAs<base::Value>();
}
-bool SchedulerStateMachine::HasDrawnAndSwappedThisFrame() const {
- return current_frame_number_ == last_frame_number_swap_performed_;
+bool SchedulerStateMachine::HasSentBeginFrameToMainThreadThisFrame() const {
+ return current_frame_number_ ==
+ last_frame_number_begin_frame_sent_to_main_thread_;
}
bool SchedulerStateMachine::HasUpdatedVisibleTilesThisFrame() const {
return current_frame_number_ ==
- last_frame_number_where_update_visible_tiles_was_called_;
+ last_frame_number_update_visible_tiles_was_called_;
}
-bool SchedulerStateMachine::HasSentBeginFrameToMainThreadThisFrame() const {
- return current_frame_number_ ==
- last_frame_number_where_begin_frame_sent_to_main_thread_;
+bool SchedulerStateMachine::HasSwappedThisFrame() const {
+ return current_frame_number_ == last_frame_number_swap_performed_;
}
bool SchedulerStateMachine::PendingDrawsShouldBeAborted() const {
@@ -330,12 +349,12 @@ bool SchedulerStateMachine::ShouldDraw() const {
if (PendingDrawsShouldBeAborted())
return active_tree_needs_first_draw_;
- // After this line, we only want to draw once per frame.
- if (HasDrawnAndSwappedThisFrame())
+ // After this line, we only want to swap once per frame.
+ if (HasSwappedThisFrame())
return false;
- // We currently only draw within the BeginFrame.
- if (!inside_begin_frame_)
+ // Except for the cases above, do not draw outside of the BeginFrame deadline.
+ if (begin_frame_state_ != BEGIN_FRAME_STATE_INSIDE_DEADLINE)
return false;
// Only handle forced redraws due to timeouts on the regular deadline.
@@ -385,10 +404,10 @@ bool SchedulerStateMachine::ShouldUpdateVisibleTiles() const {
if (!HasInitializedOutputSurface())
return false;
- // We always want to update the most recent visible tiles before drawing
- // so we draw with fewer missing tiles.
- if (ShouldDraw())
- return true;
+ // We should not check for visible tiles until we've entered the deadline so
+ // we check as late as possible and give the tiles more time to initialize.
+ if (begin_frame_state_ != BEGIN_FRAME_STATE_INSIDE_DEADLINE)
+ return false;
// If the last swap drew with checkerboard or missing tiles, we should
// poll for any new visible tiles so we can be notified to draw again
@@ -403,7 +422,8 @@ bool SchedulerStateMachine::ShouldSendBeginFrameToMainThread() const {
if (!needs_commit_)
return false;
- // Only send BeginFrame to the main thread when idle.
+ // Only send BeginFrame to the main thread when there isn't another commit
+ // pending already.
if (commit_state_ != COMMIT_STATE_IDLE)
return false;
@@ -419,7 +439,7 @@ bool SchedulerStateMachine::ShouldSendBeginFrameToMainThread() const {
return !CommitPending();
// We do not need commits if we are not visible, unless there's a
- // request for a forced commit.
+ // request for a readback.
if (!visible_)
return false;
@@ -427,6 +447,23 @@ bool SchedulerStateMachine::ShouldSendBeginFrameToMainThread() const {
if (output_surface_state_ == OUTPUT_SURFACE_WAITING_FOR_FIRST_COMMIT)
return true;
+ // With deadline scheduling enabled, we should not send BeginFrame to the
+ // main thread while we are in BEGIN_FRAME_STATE_IDLE, since we might have
+ // new user input coming in soon.
+ // However, if we are not expecting a BeginFrame on the Impl thread to take
+ // us out of idle, we should not early out here to avoid blocking commits
+ // forever.
+ // This only works well when deadline scheduling is enabled because there is
+ // an interval over which to accept the commit and draw. Without deadline
+ // scheduling, delaying the commit could prevent us from having something
+ // to draw on the next BeginFrame.
+ // TODO(brianderson): Allow sending BeginFrame to main thread while idle
+ // when the main thread isn't consuming user input.
+ if (settings_.deadline_scheduling_enabled &&
+ begin_frame_state_ == BEGIN_FRAME_STATE_IDLE &&
+ BeginFrameNeededByImplThread())
+ return false;
+
// We need a new commit for the forced redraw. This honors the
// single commit per interval because the result will be swapped to screen.
if (forced_redraw_state_ == FORCED_REDRAW_STATE_WAITING_FOR_COMMIT)
@@ -451,7 +488,8 @@ bool SchedulerStateMachine::ShouldManageTiles() const {
// Limiting to once per-frame is not enough, since we only want to
// manage tiles _after_ draws. Polling for draw triggers and
// begin-frame are mutually exclusive, so we limit to these two cases.
- if (!inside_begin_frame_ && !inside_poll_for_anticipated_draw_triggers_)
+ if (begin_frame_state_ != BEGIN_FRAME_STATE_INSIDE_DEADLINE &&
+ !inside_poll_for_anticipated_draw_triggers_)
return false;
return needs_manage_tiles_;
}
@@ -497,7 +535,7 @@ void SchedulerStateMachine::UpdateState(Action action) {
return;
case ACTION_UPDATE_VISIBLE_TILES:
- last_frame_number_where_update_visible_tiles_was_called_ =
+ last_frame_number_update_visible_tiles_was_called_ =
current_frame_number_;
return;
@@ -512,7 +550,7 @@ void SchedulerStateMachine::UpdateState(Action action) {
needs_commit_ = false;
if (readback_state_ == READBACK_STATE_NEEDS_BEGIN_FRAME)
readback_state_ = READBACK_STATE_WAITING_FOR_COMMIT;
- last_frame_number_where_begin_frame_sent_to_main_thread_ =
+ last_frame_number_begin_frame_sent_to_main_thread_ =
current_frame_number_;
return;
@@ -707,6 +745,33 @@ void SchedulerStateMachine::SetMainThreadNeedsLayerTextures() {
main_thread_needs_layer_textures_ = true;
}
+bool SchedulerStateMachine::BeginFrameNeededByImplThread() const {
+ // Proactive BeginFrames are bad for the synchronous compositor because we
+ // have to draw when we get the BeginFrame and could end up drawing many
+ // duplicate frames if our new frame isn't ready in time.
+ // To poll for state with the synchronous compositor without having to draw,
+ // we rely on ShouldPollForAnticipatedDrawTriggers instead.
+ if (settings_.using_synchronous_renderer_compositor)
+ return BeginFrameNeededToDrawByImplThread();
+
+ return BeginFrameNeededToDrawByImplThread() ||
+ ProactiveBeginFrameWantedByImplThread();
+}
+
+bool SchedulerStateMachine::ShouldPollForAnticipatedDrawTriggers() const {
+ // ShouldPollForAnticipatedDrawTriggers is what we use in place of
+ // ProactiveBeginFrameWantedByImplThread when we are using the synchronous
+ // compositor.
+ if (settings_.using_synchronous_renderer_compositor) {
+ return !BeginFrameNeededToDrawByImplThread() &&
+ ProactiveBeginFrameWantedByImplThread();
+ }
+
+ // Non synchronous compositors should rely on
+ // ProactiveBeginFrameWantedByImplThread to poll for state instead.
+ return false;
+}
+
// These are the cases where we definitely (or almost definitely) have a
// new frame to draw and can draw.
bool SchedulerStateMachine::BeginFrameNeededToDrawByImplThread() const {
@@ -741,15 +806,16 @@ bool SchedulerStateMachine::BeginFrameNeededToDrawByImplThread() const {
// actually have a new frame to draw when we receive the next BeginFrame.
// Proactively requesting the BeginFrame helps hide the round trip latency of
// the SetNeedsBeginFrame request that has to go to the Browser.
-// However, this is bad for the synchronous compositor because we have to
-// draw when we get the BeginFrame and could end up drawing many duplicate
-// frames.
bool SchedulerStateMachine::ProactiveBeginFrameWantedByImplThread() const {
// The output surface is the provider of BeginFrames for the impl thread,
// so we are not going to get them even if we ask for them.
if (!HasInitializedOutputSurface())
return false;
+ // Do not be proactive if vsync is off.
+ if (!settings_.throttle_frame_production)
+ return false;
+
// Do not be proactive when invisible.
if (!visible_)
return false;
@@ -779,14 +845,43 @@ bool SchedulerStateMachine::ProactiveBeginFrameWantedByImplThread() const {
return false;
}
-void SchedulerStateMachine::DidEnterBeginFrame(const BeginFrameArgs& args) {
+void SchedulerStateMachine::OnBeginFrame(const BeginFrameArgs& args) {
current_frame_number_++;
- inside_begin_frame_ = true;
last_begin_frame_args_ = args;
+ DCHECK_EQ(begin_frame_state_, BEGIN_FRAME_STATE_IDLE)
+ << *AsValue();
+ begin_frame_state_ = BEGIN_FRAME_STATE_BEGIN_FRAME_STARTING;
}
-void SchedulerStateMachine::DidLeaveBeginFrame() {
- inside_begin_frame_ = false;
+void SchedulerStateMachine::OnBeginFrameDeadlinePending() {
+ DCHECK_EQ(begin_frame_state_, BEGIN_FRAME_STATE_BEGIN_FRAME_STARTING)
+ << *AsValue();
+ begin_frame_state_ = BEGIN_FRAME_STATE_INSIDE_BEGIN_FRAME;
+}
+
+void SchedulerStateMachine::OnBeginFrameDeadline() {
+ DCHECK_EQ(begin_frame_state_, BEGIN_FRAME_STATE_INSIDE_BEGIN_FRAME)
+ << *AsValue();
+ begin_frame_state_ = BEGIN_FRAME_STATE_INSIDE_DEADLINE;
+}
+
+void SchedulerStateMachine::OnBeginFrameIdle() {
+ if (HasInitializedOutputSurface()) {
+ DCHECK_EQ(begin_frame_state_, BEGIN_FRAME_STATE_INSIDE_DEADLINE)
+ << *AsValue();
+ }
+ begin_frame_state_ = BEGIN_FRAME_STATE_IDLE;
+}
+
+bool SchedulerStateMachine::ShouldTriggerBeginFrameDeadlineEarly() const {
+ // If we are in the middle of the readback, we won't swap, so there is
+ // no reason to trigger the deadline early.
+ if (readback_state_ != READBACK_STATE_IDLE)
+ return false;
+
+ // TODO(brianderson): This should take into account multiple commit sources.
+ return begin_frame_state_ == BEGIN_FRAME_STATE_INSIDE_BEGIN_FRAME &&
+ active_tree_needs_first_draw_;
}
void SchedulerStateMachine::DidEnterPollForAnticipatedDrawTriggers() {
@@ -800,6 +895,8 @@ void SchedulerStateMachine::DidLeavePollForAnticipatedDrawTriggers() {
void SchedulerStateMachine::SetVisible(bool visible) { visible_ = visible; }
+void SchedulerStateMachine::SetCanDraw(bool can_draw) { can_draw_ = can_draw; }
+
void SchedulerStateMachine::SetNeedsRedraw() { needs_redraw_ = true; }
void SchedulerStateMachine::SetNeedsManageTiles() {
@@ -876,6 +973,7 @@ void SchedulerStateMachine::DidLoseOutputSurface() {
return;
output_surface_state_ = OUTPUT_SURFACE_LOST;
needs_redraw_ = false;
+ begin_frame_state_ = BEGIN_FRAME_STATE_IDLE;
}
void SchedulerStateMachine::NotifyReadyToActivate() {
@@ -883,8 +981,6 @@ void SchedulerStateMachine::NotifyReadyToActivate() {
pending_tree_is_ready_for_activation_ = true;
}
-void SchedulerStateMachine::SetCanDraw(bool can) { can_draw_ = can; }
-
void SchedulerStateMachine::DidCreateAndInitializeOutputSurface() {
DCHECK_EQ(output_surface_state_, OUTPUT_SURFACE_CREATING);
output_surface_state_ = OUTPUT_SURFACE_WAITING_FOR_FIRST_COMMIT;
diff --git a/cc/scheduler/scheduler_state_machine.h b/cc/scheduler/scheduler_state_machine.h
index 48dd828..55e54b5 100644
--- a/cc/scheduler/scheduler_state_machine.h
+++ b/cc/scheduler/scheduler_state_machine.h
@@ -45,6 +45,18 @@ class CC_EXPORT SchedulerStateMachine {
};
static const char* OutputSurfaceStateToString(OutputSurfaceState state);
+ // Note: BeginFrameState will always cycle through all the states in order.
+ // Whether or not it actually waits or draws, it will at least try to wait in
+ // BEGIN_FRAME_STATE_INSIDE_BEGIN_FRAME and try to draw in
+ // BEGIN_FRAME_STATE_INSIDE_DEADLINE
+ enum BeginFrameState {
+ BEGIN_FRAME_STATE_IDLE,
+ BEGIN_FRAME_STATE_BEGIN_FRAME_STARTING,
+ BEGIN_FRAME_STATE_INSIDE_BEGIN_FRAME,
+ BEGIN_FRAME_STATE_INSIDE_DEADLINE,
+ };
+ static const char* BeginFrameStateToString(BeginFrameState state);
+
enum CommitState {
COMMIT_STATE_IDLE,
COMMIT_STATE_FRAME_IN_PROGRESS,
@@ -109,19 +121,30 @@ class CC_EXPORT SchedulerStateMachine {
Action NextAction() const;
void UpdateState(Action action);
+
void CheckInvariants();
// Indicates whether the main thread needs a begin frame callback in order to
// make progress.
- bool BeginFrameNeededToDrawByImplThread() const;
- bool ProactiveBeginFrameWantedByImplThread() const;
+ bool BeginFrameNeededByImplThread() const;
+
+ // Idicates that we need to independently poll for new state and actions
+ // because we can't expect a BeginFrame. This is mostly used to avoid
+ // drawing repeat frames with the synchronous compositor without dropping
+ // necessary actions on the floor.
+ bool ShouldPollForAnticipatedDrawTriggers() const;
// Indicates that the system has entered and left a BeginFrame callback.
// The scheduler will not draw more than once in a given BeginFrame
// callback nor send more than one BeginFrame message.
- void DidEnterBeginFrame(const BeginFrameArgs& args);
- void DidLeaveBeginFrame();
- bool inside_begin_frame() const { return inside_begin_frame_; }
+ void OnBeginFrame(const BeginFrameArgs& args);
+ void OnBeginFrameDeadlinePending();
+ void OnBeginFrameDeadline();
+ void OnBeginFrameIdle();
+ bool ShouldTriggerBeginFrameDeadlineEarly() const;
+ BeginFrameState begin_frame_state() const {
+ return begin_frame_state_;
+ }
// PollForAnticipatedDrawTriggers is used by the synchronous compositor to
// avoid requesting BeginImplFrames when we won't actually draw but still
@@ -138,6 +161,7 @@ class CC_EXPORT SchedulerStateMachine {
// Indicates that a redraw is required, either due to the impl tree changing
// or the screen being damaged and simply needing redisplay.
void SetNeedsRedraw();
+ bool needs_redraw() const { return needs_redraw_; }
// Indicates that manage-tiles is required. This guarantees another
// ManageTiles will occur shortly (even if no redraw is required).
@@ -147,8 +171,7 @@ class CC_EXPORT SchedulerStateMachine {
// with a low resolution or checkerboarded tile.
void SetSwapUsedIncompleteTile(bool used_incomplete_tile);
- // Indicates whether ACTION_DRAW_AND_SWAP_IF_POSSIBLE drew to the screen or
- // not.
+ // Indicates whether ACTION_DRAW_AND_SWAP_IF_POSSIBLE drew to the screen.
void DidDrawIfPossibleCompleted(bool success);
// Indicates that a new commit flow needs to be performed, either to pull
@@ -201,6 +224,9 @@ class CC_EXPORT SchedulerStateMachine {
bool PendingDrawsShouldBeAborted() const;
protected:
+ bool BeginFrameNeededToDrawByImplThread() const;
+ bool ProactiveBeginFrameWantedByImplThread() const;
+
// True if we need to force activations to make forward progress.
bool PendingActivationsShouldBeForced() const;
@@ -214,11 +240,10 @@ class CC_EXPORT SchedulerStateMachine {
bool ShouldCommit() const;
bool ShouldManageTiles() const;
- bool HasDrawnAndSwappedThisFrame() const;
- bool HasActivatedPendingTreeThisFrame() const;
- bool HasUpdatedVisibleTilesThisFrame() const;
bool HasSentBeginFrameToMainThreadThisFrame() const;
bool HasScheduledManageTilesThisFrame() const;
+ bool HasUpdatedVisibleTilesThisFrame() const;
+ bool HasSwappedThisFrame() const;
void UpdateStateOnCommit(bool commit_was_aborted);
void UpdateStateOnActivation();
@@ -228,26 +253,27 @@ class CC_EXPORT SchedulerStateMachine {
const SchedulerSettings settings_;
OutputSurfaceState output_surface_state_;
+ BeginFrameState begin_frame_state_;
CommitState commit_state_;
TextureState texture_state_;
ForcedRedrawOnTimeoutState forced_redraw_state_;
SynchronousReadbackState readback_state_;
+ BeginFrameArgs last_begin_frame_args_;
+
int commit_count_;
int current_frame_number_;
- int last_frame_number_where_begin_frame_sent_to_main_thread_;
int last_frame_number_swap_performed_;
- int last_frame_number_where_update_visible_tiles_was_called_;
+ int last_frame_number_begin_frame_sent_to_main_thread_;
+ int last_frame_number_update_visible_tiles_was_called_;
+
int consecutive_failed_draws_;
bool needs_redraw_;
bool needs_manage_tiles_;
bool swap_used_incomplete_tile_;
bool needs_commit_;
bool main_thread_needs_layer_textures_;
- bool inside_begin_frame_;
bool inside_poll_for_anticipated_draw_triggers_;
-
- BeginFrameArgs last_begin_frame_args_;
bool visible_;
bool can_start_;
bool can_draw_;
diff --git a/cc/scheduler/scheduler_state_machine_unittest.cc b/cc/scheduler/scheduler_state_machine_unittest.cc
index e3cedfd..2c4a8f1 100644
--- a/cc/scheduler/scheduler_state_machine_unittest.cc
+++ b/cc/scheduler/scheduler_state_machine_unittest.cc
@@ -7,20 +7,44 @@
#include "cc/scheduler/scheduler.h"
#include "testing/gtest/include/gtest/gtest.h"
-#define EXPECT_ACTION_UPDATE_STATE(action) \
- EXPECT_EQ(action, state.NextAction()) << *state.AsValue(); \
- state.UpdateState(action);
+#define EXPECT_ACTION_UPDATE_STATE(action) \
+ EXPECT_EQ(action, state.NextAction()) << *state.AsValue(); \
+ if (action == SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE || \
+ action == SchedulerStateMachine::ACTION_DRAW_AND_SWAP_FORCED) { \
+ if (SchedulerStateMachine::COMMIT_STATE_WAITING_FOR_FIRST_DRAW == \
+ state.CommitState() && \
+ SchedulerStateMachine::OUTPUT_SURFACE_ACTIVE != \
+ state.output_surface_state()) \
+ return; \
+ EXPECT_EQ(SchedulerStateMachine::BEGIN_FRAME_STATE_INSIDE_DEADLINE, \
+ state.begin_frame_state()) \
+ << *state.AsValue(); \
+ } \
+ state.UpdateState(action); \
+ if (action == SchedulerStateMachine::ACTION_NONE) { \
+ if (state.begin_frame_state() == \
+ SchedulerStateMachine::BEGIN_FRAME_STATE_BEGIN_FRAME_STARTING) \
+ state.OnBeginFrameDeadlinePending(); \
+ if (state.begin_frame_state() == \
+ SchedulerStateMachine::BEGIN_FRAME_STATE_INSIDE_DEADLINE) \
+ state.OnBeginFrameIdle(); \
+ }
namespace cc {
namespace {
+const SchedulerStateMachine::BeginFrameState all_begin_frame_states[] = {
+ SchedulerStateMachine::BEGIN_FRAME_STATE_IDLE,
+ SchedulerStateMachine::BEGIN_FRAME_STATE_BEGIN_FRAME_STARTING,
+ SchedulerStateMachine::BEGIN_FRAME_STATE_INSIDE_BEGIN_FRAME,
+ SchedulerStateMachine::BEGIN_FRAME_STATE_INSIDE_DEADLINE, };
+
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
-};
+ 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 {
@@ -36,6 +60,10 @@ class StateMachine : public SchedulerStateMachine {
void SetCommitState(CommitState cs) { commit_state_ = cs; }
CommitState CommitState() const { return commit_state_; }
+ void SetBeginFrameState(BeginFrameState bfs) { begin_frame_state_ = bfs; }
+
+ BeginFrameState begin_frame_state() const { return begin_frame_state_; }
+
OutputSurfaceState output_surface_state() const {
return output_surface_state_;
}
@@ -62,8 +90,16 @@ class StateMachine : public SchedulerStateMachine {
return readback_state_ != READBACK_STATE_IDLE;
}
+ void SetActiveTreeNeedsFirstDraw(bool needs_first_draw) {
+ active_tree_needs_first_draw_ = needs_first_draw;
+ }
+
bool CanDraw() const { return can_draw_; }
bool Visible() const { return visible_; }
+
+ bool PendingActivationsShouldBeForced() const {
+ return SchedulerStateMachine::PendingActivationsShouldBeForced();
+ }
};
TEST(SchedulerStateMachineTest, TestNextActionBeginsMainFrameIfNeeded) {
@@ -80,13 +116,14 @@ TEST(SchedulerStateMachineTest, TestNextActionBeginsMainFrameIfNeeded) {
state.SetNeedsRedraw(false);
state.SetVisible(true);
- EXPECT_FALSE(state.BeginFrameNeededToDrawByImplThread());
+ EXPECT_FALSE(state.BeginFrameNeededByImplThread());
+
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
+ EXPECT_FALSE(state.BeginFrameNeededByImplThread());
+ state.OnBeginFrame(BeginFrameArgs::CreateForTesting());
- state.DidLeaveBeginFrame();
- EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction());
- EXPECT_FALSE(state.BeginFrameNeededToDrawByImplThread());
- state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting());
- EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction());
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
+ state.OnBeginFrameDeadline();
}
// If commit requested but can_start is still false, do nothing.
@@ -96,13 +133,13 @@ TEST(SchedulerStateMachineTest, TestNextActionBeginsMainFrameIfNeeded) {
state.SetNeedsRedraw(false);
state.SetVisible(true);
- EXPECT_FALSE(state.BeginFrameNeededToDrawByImplThread());
+ EXPECT_FALSE(state.BeginFrameNeededByImplThread());
- state.DidLeaveBeginFrame();
- EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction());
- EXPECT_FALSE(state.BeginFrameNeededToDrawByImplThread());
- state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting());
- EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction());
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
+ EXPECT_FALSE(state.BeginFrameNeededByImplThread());
+ state.OnBeginFrame(BeginFrameArgs::CreateForTesting());
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
+ state.OnBeginFrameDeadline();
}
// If commit requested, begin a main frame.
@@ -112,7 +149,7 @@ TEST(SchedulerStateMachineTest, TestNextActionBeginsMainFrameIfNeeded) {
state.SetCanStart();
state.SetNeedsRedraw(false);
state.SetVisible(true);
- EXPECT_FALSE(state.BeginFrameNeededToDrawByImplThread());
+ EXPECT_FALSE(state.BeginFrameNeededByImplThread());
}
// Begin the frame, make sure needs_commit and commit_state update correctly.
@@ -127,7 +164,6 @@ TEST(SchedulerStateMachineTest, TestNextActionBeginsMainFrameIfNeeded) {
EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_FRAME_IN_PROGRESS,
state.CommitState());
EXPECT_FALSE(state.NeedsCommit());
- EXPECT_FALSE(state.BeginFrameNeededToDrawByImplThread());
}
}
@@ -142,22 +178,23 @@ TEST(SchedulerStateMachineTest,
state.SetCanDraw(true);
state.SetNeedsRedraw(true);
EXPECT_TRUE(state.RedrawPending());
- EXPECT_TRUE(state.BeginFrameNeededToDrawByImplThread());
- state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting());
+ EXPECT_TRUE(state.BeginFrameNeededByImplThread());
+ state.OnBeginFrame(BeginFrameArgs::CreateForTesting());
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
+ state.OnBeginFrameDeadline();
// We're drawing now.
- EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE,
- state.NextAction());
- state.UpdateState(SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE);
- EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction());
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE);
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
+
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(
+ state.OnBeginFrame(BeginFrameArgs::CreateForTesting());
+ EXPECT_ACTION_UPDATE_STATE(
SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD);
EXPECT_TRUE(state.RedrawPending());
EXPECT_TRUE(state.CommitPending());
@@ -175,78 +212,36 @@ TEST(SchedulerStateMachineTest,
state.SetCanDraw(true);
state.SetNeedsRedraw(true);
EXPECT_TRUE(state.RedrawPending());
- EXPECT_TRUE(state.BeginFrameNeededToDrawByImplThread());
- state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting());
+ EXPECT_TRUE(state.BeginFrameNeededByImplThread());
+ state.OnBeginFrame(BeginFrameArgs::CreateForTesting());
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
+ state.OnBeginFrameDeadline();
// We're drawing now.
- EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE,
- state.NextAction());
- state.UpdateState(SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE);
- EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction());
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE);
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
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(true);
- EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction());
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
// 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;
- StateMachine state(default_scheduler_settings);
- state.SetCanStart();
- state.UpdateState(state.NextAction());
- state.CreateAndInitializeOutputSurfaceWithActivatedCommit();
- 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(
+ state.OnBeginFrame(BeginFrameArgs::CreateForTesting());
+ EXPECT_ACTION_UPDATE_STATE(
SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD);
- EXPECT_TRUE(state.CommitPending());
-
- // Then initiate a draw.
- state.SetNeedsRedraw(true);
- EXPECT_TRUE(state.BeginFrameNeededToDrawByImplThread());
- state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting());
- EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE,
- state.NextAction());
EXPECT_TRUE(state.RedrawPending());
-
- // Fail the draw.
- state.UpdateState(SchedulerStateMachine::ACTION_DRAW_AND_SWAP_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_AND_SWAP_IF_POSSIBLE,
- state.NextAction());
}
-TEST(SchedulerStateMachineTest,
- TestFailedDrawsWillEventuallyForceADrawAfterTheNextCommit) {
+void TestFailedDrawsWillEventuallyForceADrawAfterTheNextCommit(
+ bool deadline_scheduling_enabled) {
SchedulerSettings scheduler_settings;
scheduler_settings.maximum_number_of_failed_draws_before_draw_is_forced_ = 1;
+ scheduler_settings.deadline_scheduling_enabled = deadline_scheduling_enabled;
StateMachine state(scheduler_settings);
state.SetCanStart();
state.UpdateState(state.NextAction());
@@ -256,24 +251,29 @@ TEST(SchedulerStateMachineTest,
// 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);
+ if (!deadline_scheduling_enabled) {
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD);
+ }
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
+ state.OnBeginFrame(BeginFrameArgs::CreateForTesting());
+ if (deadline_scheduling_enabled) {
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD);
+ }
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
EXPECT_TRUE(state.CommitPending());
// Then initiate a draw.
state.SetNeedsRedraw(true);
- EXPECT_TRUE(state.BeginFrameNeededToDrawByImplThread());
- state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting());
- EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE,
- state.NextAction());
- EXPECT_TRUE(state.RedrawPending());
+ state.OnBeginFrameDeadline();
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE);
// Fail the draw.
- state.UpdateState(SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE);
- EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction());
state.DidDrawIfPossibleCompleted(false);
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
+ EXPECT_TRUE(state.BeginFrameNeededByImplThread());
EXPECT_TRUE(state.RedrawPending());
// But the commit is ongoing.
EXPECT_TRUE(state.CommitPending());
@@ -281,13 +281,30 @@ TEST(SchedulerStateMachineTest,
// 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_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_COMMIT);
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
EXPECT_TRUE(state.RedrawPending());
- // The redraw should be forced in this case.
- EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_AND_SWAP_FORCED,
- state.NextAction());
+ // The redraw should be forced at the end of the next BeginFrame.
+ state.OnBeginFrame(BeginFrameArgs::CreateForTesting());
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
+ state.OnBeginFrameDeadline();
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_DRAW_AND_SWAP_FORCED);
+}
+
+TEST(SchedulerStateMachineTest,
+ TestFailedDrawsWillEventuallyForceADrawAfterTheNextCommit) {
+ bool deadline_scheduling_enabled = false;
+ TestFailedDrawsWillEventuallyForceADrawAfterTheNextCommit(
+ deadline_scheduling_enabled);
+}
+
+TEST(SchedulerStateMachineTest,
+ TestFailedDrawsWillEventuallyForceADrawAfterTheNextCommit_Deadline) {
+ bool deadline_scheduling_enabled = true;
+ TestFailedDrawsWillEventuallyForceADrawAfterTheNextCommit(
+ deadline_scheduling_enabled);
}
TEST(SchedulerStateMachineTest,
@@ -302,29 +319,32 @@ TEST(SchedulerStateMachineTest,
// Start a draw.
state.SetNeedsRedraw(true);
- EXPECT_TRUE(state.BeginFrameNeededToDrawByImplThread());
- state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting());
- EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE,
- state.NextAction());
+ EXPECT_TRUE(state.BeginFrameNeededByImplThread());
+ state.OnBeginFrame(BeginFrameArgs::CreateForTesting());
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
+ state.OnBeginFrameDeadline();
EXPECT_TRUE(state.RedrawPending());
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE);
- // Fail the draw.
- state.UpdateState(SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE);
- EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction());
+ // Fail the draw
state.DidDrawIfPossibleCompleted(false);
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD);
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
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());
+ EXPECT_TRUE(state.BeginFrameNeededByImplThread());
+ state.OnBeginFrame(BeginFrameArgs::CreateForTesting());
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
- // We should try to draw again in the next begin frame on the impl thread.
- EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE,
- state.NextAction());
+ // We should try to draw again at the end of the next BeginFrame on
+ // the impl thread.
+ state.OnBeginFrameDeadline();
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE);
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
}
TEST(SchedulerStateMachineTest, TestDoestDrawTwiceInSameFrame) {
@@ -336,51 +356,60 @@ TEST(SchedulerStateMachineTest, TestDoestDrawTwiceInSameFrame) {
state.SetVisible(true);
state.SetCanDraw(true);
state.SetNeedsRedraw(true);
- EXPECT_TRUE(state.BeginFrameNeededToDrawByImplThread());
- state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting());
- EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE,
- state.NextAction());
- state.UpdateState(SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE);
- // While still in the same begin frame for the impl thread, set needs redraw
- // again. This should not redraw.
+ // Draw the first frame.
+ EXPECT_TRUE(state.BeginFrameNeededByImplThread());
+ state.OnBeginFrame(BeginFrameArgs::CreateForTesting());
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
+
+ state.OnBeginFrameDeadline();
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE);
+ state.DidDrawIfPossibleCompleted(true);
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
+
+ // Before the next begin frame for the impl thread, set needs redraw
+ // again. This should not redraw until the next begin frame.
state.SetNeedsRedraw(true);
- EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction());
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
// Move to another frame. This should now draw.
- state.DidDrawIfPossibleCompleted(true);
- state.DidLeaveBeginFrame();
- EXPECT_TRUE(state.BeginFrameNeededToDrawByImplThread());
- state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting());
+ EXPECT_TRUE(state.BeginFrameNeededByImplThread());
+ state.OnBeginFrame(BeginFrameArgs::CreateForTesting());
- EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE,
- state.NextAction());
- state.UpdateState(SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE);
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
+
+ state.OnBeginFrameDeadline();
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE);
state.DidDrawIfPossibleCompleted(true);
- EXPECT_FALSE(state.BeginFrameNeededToDrawByImplThread());
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
+
+ // We just swapped, so we should proactively request another BeginFrame.
+ EXPECT_TRUE(state.BeginFrameNeededByImplThread());
}
TEST(SchedulerStateMachineTest, TestNextActionDrawsOnBeginFrame) {
SchedulerSettings default_scheduler_settings;
- // When not in BeginFrame, or in BeginFrame but not visible,
+ // When not in BeginFrame deadline, or in BeginFrame deadline but not visible,
// don't draw.
size_t num_commit_states =
sizeof(all_commit_states) / sizeof(SchedulerStateMachine::CommitState);
+ size_t num_begin_frame_states =
+ sizeof(all_begin_frame_states) /
+ sizeof(SchedulerStateMachine::BeginFrameState);
for (size_t i = 0; i < num_commit_states; ++i) {
- for (size_t j = 0; j < 2; ++j) {
+ for (size_t j = 0; j < num_begin_frame_states; ++j) {
StateMachine state(default_scheduler_settings);
state.SetCanStart();
state.UpdateState(state.NextAction());
state.CreateAndInitializeOutputSurfaceWithActivatedCommit();
state.SetCommitState(all_commit_states[i]);
- bool visible = j;
- if (!visible) {
- state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting());
- state.SetVisible(false);
- } else {
- state.SetVisible(true);
- }
+ state.SetBeginFrameState(all_begin_frame_states[j]);
+ bool visible = (all_begin_frame_states[j] !=
+ SchedulerStateMachine::BEGIN_FRAME_STATE_INSIDE_DEADLINE);
+ state.SetVisible(visible);
// Case 1: needs_commit=false
EXPECT_NE(SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE,
@@ -389,13 +418,14 @@ TEST(SchedulerStateMachineTest, TestNextActionDrawsOnBeginFrame) {
// Case 2: needs_commit=true
state.SetNeedsCommit();
EXPECT_NE(SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE,
- state.NextAction());
+ state.NextAction())
+ << *state.AsValue();
}
}
- // 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.
+ // When in BeginFrame deadline we should always draw for SetNeedsRedraw or
+ // SetNeedsForcedRedrawForReadback have been called... except if we're
+ // ready to commit, in which case we expect a commit first.
for (size_t i = 0; i < num_commit_states; ++i) {
for (size_t j = 0; j < 2; ++j) {
bool request_readback = j;
@@ -412,33 +442,38 @@ TEST(SchedulerStateMachineTest, TestNextActionDrawsOnBeginFrame) {
state.CreateAndInitializeOutputSurfaceWithActivatedCommit();
state.SetCanDraw(true);
state.SetCommitState(all_commit_states[i]);
- if (!request_readback) {
- state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting());
+ state.SetBeginFrameState(
+ SchedulerStateMachine::BEGIN_FRAME_STATE_INSIDE_DEADLINE);
+ if (request_readback) {
+ state.SetNeedsForcedRedrawForReadback();
+ } else {
state.SetNeedsRedraw(true);
state.SetVisible(true);
- } else {
- state.SetNeedsForcedRedrawForReadback();
}
SchedulerStateMachine::Action expected_action;
- if (all_commit_states[i] !=
+ if (all_commit_states[i] ==
SchedulerStateMachine::COMMIT_STATE_READY_TO_COMMIT) {
- expected_action =
- request_readback
- ? SchedulerStateMachine::ACTION_DRAW_AND_READBACK
- : SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE;
- } else {
expected_action = SchedulerStateMachine::ACTION_COMMIT;
+ } else if (request_readback) {
+ if (all_commit_states[i] ==
+ SchedulerStateMachine::COMMIT_STATE_WAITING_FOR_FIRST_DRAW)
+ expected_action = SchedulerStateMachine::ACTION_DRAW_AND_READBACK;
+ else
+ expected_action = SchedulerStateMachine::ACTION_NONE;
+ } else {
+ expected_action =
+ SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE;
}
// Case 1: needs_commit=false.
- EXPECT_NE(state.BeginFrameNeededToDrawByImplThread(), request_readback)
+ EXPECT_NE(state.BeginFrameNeededByImplThread(), request_readback)
<< *state.AsValue();
EXPECT_EQ(expected_action, state.NextAction()) << *state.AsValue();
// Case 2: needs_commit=true.
state.SetNeedsCommit();
- EXPECT_NE(state.BeginFrameNeededToDrawByImplThread(), request_readback)
+ EXPECT_NE(state.BeginFrameNeededByImplThread(), request_readback)
<< *state.AsValue();
EXPECT_EQ(expected_action, state.NextAction()) << *state.AsValue();
}
@@ -460,8 +495,10 @@ TEST(SchedulerStateMachineTest, TestNoCommitStatesRedrawWhenInvisible) {
state.SetCommitState(all_commit_states[i]);
state.SetVisible(false);
state.SetNeedsRedraw(true);
- if (j == 1)
- state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting());
+ if (j == 1) {
+ state.SetBeginFrameState(
+ SchedulerStateMachine::BEGIN_FRAME_STATE_INSIDE_DEADLINE);
+ }
// Case 1: needs_commit=false.
EXPECT_NE(SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE,
@@ -470,7 +507,8 @@ TEST(SchedulerStateMachineTest, TestNoCommitStatesRedrawWhenInvisible) {
// Case 2: needs_commit=true.
state.SetNeedsCommit();
EXPECT_NE(SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE,
- state.NextAction());
+ state.NextAction())
+ << *state.AsValue();
}
}
}
@@ -491,7 +529,7 @@ TEST(SchedulerStateMachineTest, TestCanRedraw_StopsDraw) {
state.SetVisible(false);
state.SetNeedsRedraw(true);
if (j == 1)
- state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting());
+ state.OnBeginFrame(BeginFrameArgs::CreateForTesting());
state.SetCanDraw(false);
EXPECT_NE(SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE,
@@ -508,25 +546,22 @@ TEST(SchedulerStateMachineTest,
state.UpdateState(state.NextAction());
state.CreateAndInitializeOutputSurfaceWithActivatedCommit();
- state.SetVisible(true);
- state.SetNeedsCommit();
- EXPECT_ACTION_UPDATE_STATE(
- SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD);
- state.FinishCommit();
- EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_COMMIT);
- EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_WAITING_FOR_FIRST_DRAW,
- state.CommitState());
-
+ state.SetCommitState(
+ SchedulerStateMachine::COMMIT_STATE_WAITING_FOR_FIRST_DRAW);
+ state.SetActiveTreeNeedsFirstDraw(true);
state.SetNeedsCommit();
state.SetNeedsRedraw(true);
state.SetVisible(true);
state.SetCanDraw(false);
- EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_DRAW_AND_SWAP_ABORT)
-
- state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting());
+ state.OnBeginFrame(BeginFrameArgs::CreateForTesting());
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_DRAW_AND_SWAP_ABORT);
EXPECT_ACTION_UPDATE_STATE(
SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD);
- state.DidLeaveBeginFrame();
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
+ state.FinishCommit();
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_COMMIT);
+ state.OnBeginFrameDeadline();
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_DRAW_AND_SWAP_ABORT);
EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
}
@@ -540,10 +575,12 @@ TEST(SchedulerStateMachineTest, TestsetNeedsCommitIsNotLost) {
state.SetVisible(true);
state.SetCanDraw(true);
+ EXPECT_TRUE(state.BeginFrameNeededByImplThread());
+
// Begin the frame.
- EXPECT_EQ(SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD,
- state.NextAction());
- state.UpdateState(state.NextAction());
+ state.OnBeginFrame(BeginFrameArgs::CreateForTesting());
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD);
EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_FRAME_IN_PROGRESS,
state.CommitState());
@@ -557,24 +594,44 @@ TEST(SchedulerStateMachineTest, TestsetNeedsCommitIsNotLost) {
state.CommitState());
// Expect to commit regardless of BeginFrame state.
- state.DidLeaveBeginFrame();
+ EXPECT_EQ(SchedulerStateMachine::BEGIN_FRAME_STATE_BEGIN_FRAME_STARTING,
+ state.begin_frame_state());
+ EXPECT_EQ(SchedulerStateMachine::ACTION_COMMIT, state.NextAction());
+
+ state.OnBeginFrameDeadlinePending();
+ EXPECT_EQ(SchedulerStateMachine::BEGIN_FRAME_STATE_INSIDE_BEGIN_FRAME,
+ state.begin_frame_state());
+ EXPECT_EQ(SchedulerStateMachine::ACTION_COMMIT, state.NextAction());
+
+ state.OnBeginFrameDeadline();
+ EXPECT_EQ(SchedulerStateMachine::BEGIN_FRAME_STATE_INSIDE_DEADLINE,
+ state.begin_frame_state());
EXPECT_EQ(SchedulerStateMachine::ACTION_COMMIT, state.NextAction());
- state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting());
+
+ state.OnBeginFrameIdle();
+ EXPECT_EQ(SchedulerStateMachine::BEGIN_FRAME_STATE_IDLE,
+ state.begin_frame_state());
+ EXPECT_EQ(SchedulerStateMachine::ACTION_COMMIT, state.NextAction());
+
+ state.OnBeginFrame(BeginFrameArgs::CreateForTesting());
+ EXPECT_EQ(SchedulerStateMachine::BEGIN_FRAME_STATE_BEGIN_FRAME_STARTING,
+ state.begin_frame_state());
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_AND_SWAP_IF_POSSIBLE,
- state.NextAction());
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_COMMIT);
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
+ state.OnBeginFrameDeadline();
EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_WAITING_FOR_FIRST_DRAW,
state.CommitState());
- state.UpdateState(SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE);
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_DRAW_AND_SWAP_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());
+ // Verify that another commit will start immediately after draw.
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD);
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
}
TEST(SchedulerStateMachineTest, TestFullCycle) {
@@ -588,44 +645,40 @@ TEST(SchedulerStateMachineTest, TestFullCycle) {
// Start clean and set commit.
state.SetNeedsCommit();
- EXPECT_EQ(SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD,
- state.NextAction());
// Begin the frame.
- state.UpdateState(
+ state.OnBeginFrame(BeginFrameArgs::CreateForTesting());
+ EXPECT_ACTION_UPDATE_STATE(
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());
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
// 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_ACTION_UPDATE_STATE(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());
+ // Expect to do nothing until BeginFrame deadline
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
- // At BeginFrame, draw.
- state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting());
- EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE,
- state.NextAction());
- state.UpdateState(SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE);
+ // At BeginFrame deadline, draw.
+ state.OnBeginFrameDeadline();
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE);
state.DidDrawIfPossibleCompleted(true);
- state.DidLeaveBeginFrame();
// Should be synchronized, no draw needed, no action needed.
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_IDLE, state.CommitState());
EXPECT_FALSE(state.NeedsRedraw());
- EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction());
}
TEST(SchedulerStateMachineTest, TestFullCycleWithCommitRequestInbetween) {
@@ -639,49 +692,49 @@ TEST(SchedulerStateMachineTest, TestFullCycleWithCommitRequestInbetween) {
// Start clean and set commit.
state.SetNeedsCommit();
- EXPECT_EQ(SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD,
- state.NextAction());
// Begin the frame.
- state.UpdateState(
+ state.OnBeginFrame(BeginFrameArgs::CreateForTesting());
+ EXPECT_ACTION_UPDATE_STATE(
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());
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
// Request another commit while the commit is in flight.
state.SetNeedsCommit();
- EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction());
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
// 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);
+ // First commit.
+ EXPECT_ACTION_UPDATE_STATE(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());
+ // Expect to do nothing until BeginFrame deadline.
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
- // At BeginFrame, draw.
- state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting());
- EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE,
- state.NextAction());
- state.UpdateState(SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE);
+ // At BeginFrame deadline, draw.
+ state.OnBeginFrameDeadline();
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE);
state.DidDrawIfPossibleCompleted(true);
- state.DidLeaveBeginFrame();
// Should be synchronized, no draw needed, no action needed.
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_IDLE, state.CommitState());
EXPECT_FALSE(state.NeedsRedraw());
- EXPECT_EQ(SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD,
- state.NextAction());
+
+ // Next BeginFrame should initiate second commit.
+ state.OnBeginFrame(BeginFrameArgs::CreateForTesting());
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD);
}
TEST(SchedulerStateMachineTest, TestRequestCommitInvisible) {
@@ -691,7 +744,7 @@ TEST(SchedulerStateMachineTest, TestRequestCommitInvisible) {
state.UpdateState(state.NextAction());
state.CreateAndInitializeOutputSurfaceWithActivatedCommit();
state.SetNeedsCommit();
- EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction());
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
}
TEST(SchedulerStateMachineTest, TestGoesInvisibleBeforeFinishCommit) {
@@ -705,25 +758,27 @@ TEST(SchedulerStateMachineTest, TestGoesInvisibleBeforeFinishCommit) {
// 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(
+ state.OnBeginFrame(BeginFrameArgs::CreateForTesting());
+ EXPECT_ACTION_UPDATE_STATE(
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());
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
// Become invisible and abort the main thread's begin frame.
state.SetVisible(false);
state.BeginFrameAbortedByMainThread(false);
- // We should now be back in the idle state as if we didn't start a frame at
- // all.
+ // We should now be back in the idle state as if we never started the frame.
EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_IDLE, state.CommitState());
- EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction());
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
+
+ // We shouldn't do anything on the BeginFrame deadline.
+ state.OnBeginFrameDeadline();
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
// Become visible again.
state.SetVisible(true);
@@ -735,16 +790,14 @@ TEST(SchedulerStateMachineTest, TestGoesInvisibleBeforeFinishCommit) {
EXPECT_TRUE(state.NeedsCommit());
// Start a new frame.
- state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting());
- EXPECT_EQ(SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD,
- state.NextAction());
-
- // Begin the frame.
- state.UpdateState(state.NextAction());
+ state.OnBeginFrame(BeginFrameArgs::CreateForTesting());
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD);
// We should be starting the commit now.
EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_FRAME_IN_PROGRESS,
state.CommitState());
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
}
TEST(SchedulerStateMachineTest, AbortBeginFrameAndCancelCommit) {
@@ -758,9 +811,8 @@ TEST(SchedulerStateMachineTest, AbortBeginFrameAndCancelCommit) {
// Get into a begin frame / commit state.
state.SetNeedsCommit();
- EXPECT_EQ(SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD,
- state.NextAction());
- state.UpdateState(
+
+ EXPECT_ACTION_UPDATE_STATE(
SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD);
EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_FRAME_IN_PROGRESS,
state.CommitState());
@@ -777,11 +829,11 @@ TEST(SchedulerStateMachineTest, AbortBeginFrameAndCancelCommit) {
// Start a new frame; draw because this is the first frame since output
// surface init'd.
- state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting());
- EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE,
- state.NextAction())
- << *state.AsValue();
- state.DidLeaveBeginFrame();
+ state.OnBeginFrame(BeginFrameArgs::CreateForTesting());
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
+ state.OnBeginFrameDeadline();
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE);
// Verify another commit doesn't start on another frame either.
EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_IDLE, state.CommitState());
@@ -802,15 +854,22 @@ TEST(SchedulerStateMachineTest, TestFirstContextCreation) {
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());
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION);
+ state.CreateAndInitializeOutputSurfaceWithActivatedCommit();
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
// Check that the first init does not SetNeedsCommit.
+ state.OnBeginFrame(BeginFrameArgs::CreateForTesting());
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
+ state.OnBeginFrameDeadline();
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
+
+ // Check that a needs commit initiates a BeginFrame to the main thread.
state.SetNeedsCommit();
- EXPECT_NE(SchedulerStateMachine::ACTION_NONE, state.NextAction());
+ state.OnBeginFrame(BeginFrameArgs::CreateForTesting());
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD);
}
TEST(SchedulerStateMachineTest, TestContextLostWhenCompletelyIdle) {
@@ -818,7 +877,7 @@ TEST(SchedulerStateMachineTest, TestContextLostWhenCompletelyIdle) {
StateMachine state(default_scheduler_settings);
state.SetCanStart();
state.UpdateState(state.NextAction());
- state.DidCreateAndInitializeOutputSurface();
+ state.CreateAndInitializeOutputSurfaceWithActivatedCommit();
state.SetVisible(true);
state.SetCanDraw(true);
@@ -832,15 +891,15 @@ TEST(SchedulerStateMachineTest, TestContextLostWhenCompletelyIdle) {
state.UpdateState(state.NextAction());
// Once context recreation begins, nothing should happen.
- EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction());
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
// Recreate the context.
- state.DidCreateAndInitializeOutputSurface();
+ state.CreateAndInitializeOutputSurfaceWithActivatedCommit();
// 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());
+ state.OnBeginFrame(BeginFrameArgs::CreateForTesting());
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD);
}
TEST(SchedulerStateMachineTest,
@@ -849,7 +908,7 @@ TEST(SchedulerStateMachineTest,
StateMachine state(default_scheduler_settings);
state.SetCanStart();
state.UpdateState(state.NextAction());
- state.DidCreateAndInitializeOutputSurface();
+ state.CreateAndInitializeOutputSurfaceWithActivatedCommit();
state.SetVisible(true);
state.SetCanDraw(true);
@@ -857,49 +916,59 @@ TEST(SchedulerStateMachineTest,
state.NextAction());
state.DidLoseOutputSurface();
- EXPECT_EQ(SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION,
- state.NextAction());
- state.UpdateState(state.NextAction());
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION);
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
// Once context recreation begins, nothing should happen.
- EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction());
+ state.OnBeginFrame(BeginFrameArgs::CreateForTesting());
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
+ state.OnBeginFrameDeadline();
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
// While context is recreating, commits shouldn't begin.
state.SetNeedsCommit();
- EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction());
+ state.OnBeginFrame(BeginFrameArgs::CreateForTesting());
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
+ state.OnBeginFrameDeadline();
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
// Recreate the context
state.DidCreateAndInitializeOutputSurface();
EXPECT_FALSE(state.RedrawPending());
// 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());
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD);
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_FRAME_IN_PROGRESS,
state.CommitState());
state.FinishCommit();
- EXPECT_EQ(SchedulerStateMachine::ACTION_COMMIT, state.NextAction());
- state.UpdateState(state.NextAction());
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_COMMIT);
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
// Finishing the first commit after initializing an output surface should
// automatically cause a redraw.
EXPECT_TRUE(state.RedrawPending());
// Once the context is recreated, whether we draw should be based on
// SetCanDraw.
- state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting());
+ state.OnBeginFrame(BeginFrameArgs::CreateForTesting());
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
+ state.OnBeginFrameDeadline();
EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE,
state.NextAction());
state.SetCanDraw(false);
EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_AND_SWAP_ABORT,
state.NextAction());
state.SetCanDraw(true);
- state.DidLeaveBeginFrame();
+ EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE,
+ state.NextAction());
}
-TEST(SchedulerStateMachineTest, TestContextLostWhileCommitInProgress) {
- SchedulerSettings default_scheduler_settings;
- StateMachine state(default_scheduler_settings);
+void TestContextLostWhileCommitInProgress(bool deadline_scheduling_enabled) {
+ SchedulerSettings scheduler_settings;
+ scheduler_settings.deadline_scheduling_enabled = deadline_scheduling_enabled;
+ StateMachine state(scheduler_settings);
state.SetCanStart();
state.UpdateState(state.NextAction());
state.CreateAndInitializeOutputSurfaceWithActivatedCommit();
@@ -908,17 +977,23 @@ TEST(SchedulerStateMachineTest, TestContextLostWhileCommitInProgress) {
// Get a commit in flight.
state.SetNeedsCommit();
- EXPECT_EQ(SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD,
- state.NextAction());
- state.UpdateState(state.NextAction());
+ if (!deadline_scheduling_enabled) {
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD);
+ }
// Set damage and expect a draw.
state.SetNeedsRedraw(true);
- state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting());
- EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE,
- state.NextAction());
- state.UpdateState(state.NextAction());
- state.DidLeaveBeginFrame();
+ state.OnBeginFrame(BeginFrameArgs::CreateForTesting());
+ if (deadline_scheduling_enabled) {
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD);
+ }
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
+ state.OnBeginFrameDeadline();
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE);
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
// Cause a lost context while the begin frame is in flight
// for the main thread.
@@ -930,30 +1005,55 @@ TEST(SchedulerStateMachineTest, TestContextLostWhileCommitInProgress) {
// Finish the frame, and commit.
state.FinishCommit();
- EXPECT_EQ(SchedulerStateMachine::ACTION_COMMIT, state.NextAction());
- state.UpdateState(state.NextAction());
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_COMMIT);
+ // We will abort the draw when the output surface is lost if we are
+ // waiting for the first draw to unblock the main thread.
EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_WAITING_FOR_FIRST_DRAW,
state.CommitState());
-
- EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_AND_SWAP_ABORT,
- state.NextAction());
- state.UpdateState(state.NextAction());
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_DRAW_AND_SWAP_ABORT);
// Expect to be told to begin context recreation, independent of
// BeginFrame state.
- state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting());
+ EXPECT_EQ(SchedulerStateMachine::BEGIN_FRAME_STATE_IDLE,
+ state.begin_frame_state());
EXPECT_EQ(SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION,
state.NextAction());
- state.DidLeaveBeginFrame();
+
+ state.OnBeginFrame(BeginFrameArgs::CreateForTesting());
+ EXPECT_EQ(SchedulerStateMachine::BEGIN_FRAME_STATE_BEGIN_FRAME_STARTING,
+ state.begin_frame_state());
+ EXPECT_EQ(SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION,
+ state.NextAction());
+
+ state.OnBeginFrameDeadlinePending();
+ EXPECT_EQ(SchedulerStateMachine::BEGIN_FRAME_STATE_INSIDE_BEGIN_FRAME,
+ state.begin_frame_state());
+ EXPECT_EQ(SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION,
+ state.NextAction());
+
+ state.OnBeginFrameDeadline();
+ EXPECT_EQ(SchedulerStateMachine::BEGIN_FRAME_STATE_INSIDE_DEADLINE,
+ state.begin_frame_state());
EXPECT_EQ(SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION,
state.NextAction());
}
-TEST(SchedulerStateMachineTest,
- TestContextLostWhileCommitInProgressAndAnotherCommitRequested) {
- SchedulerSettings default_scheduler_settings;
- StateMachine state(default_scheduler_settings);
+TEST(SchedulerStateMachineTest, TestContextLostWhileCommitInProgress) {
+ bool deadline_scheduling_enabled = false;
+ TestContextLostWhileCommitInProgress(deadline_scheduling_enabled);
+}
+
+TEST(SchedulerStateMachineTest, TestContextLostWhileCommitInProgress_Deadline) {
+ bool deadline_scheduling_enabled = true;
+ TestContextLostWhileCommitInProgress(deadline_scheduling_enabled);
+}
+
+void TestContextLostWhileCommitInProgressAndAnotherCommitRequested(
+ bool deadline_scheduling_enabled) {
+ SchedulerSettings scheduler_settings;
+ scheduler_settings.deadline_scheduling_enabled = deadline_scheduling_enabled;
+ StateMachine state(scheduler_settings);
state.SetCanStart();
state.UpdateState(state.NextAction());
state.CreateAndInitializeOutputSurfaceWithActivatedCommit();
@@ -962,17 +1062,24 @@ TEST(SchedulerStateMachineTest,
// Get a commit in flight.
state.SetNeedsCommit();
- EXPECT_EQ(SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD,
- state.NextAction());
- state.UpdateState(state.NextAction());
+ if (!deadline_scheduling_enabled) {
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD);
+ }
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
// Set damage and expect a draw.
state.SetNeedsRedraw(true);
- state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting());
- EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE,
- state.NextAction());
- state.UpdateState(state.NextAction());
- state.DidLeaveBeginFrame();
+ state.OnBeginFrame(BeginFrameArgs::CreateForTesting());
+ if (deadline_scheduling_enabled) {
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD);
+ }
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
+ state.OnBeginFrameDeadline();
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE);
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
// Cause a lost context while the begin frame is in flight
// for the main thread.
@@ -981,28 +1088,72 @@ TEST(SchedulerStateMachineTest,
// Ask for another draw and also set needs commit. Expect nothing happens.
state.SetNeedsRedraw(true);
state.SetNeedsCommit();
- EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction());
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
// Finish the frame, and commit.
state.FinishCommit();
- EXPECT_EQ(SchedulerStateMachine::ACTION_COMMIT, state.NextAction());
- state.UpdateState(state.NextAction());
-
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_COMMIT);
EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_WAITING_FOR_FIRST_DRAW,
state.CommitState());
- EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_AND_SWAP_ABORT,
- state.NextAction());
- state.UpdateState(state.NextAction());
+ // Because the output surface is missing, we expect the draw to abort.
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_DRAW_AND_SWAP_ABORT);
// Expect to be told to begin context recreation, independent of
// BeginFrame state
- state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting());
+ EXPECT_EQ(SchedulerStateMachine::BEGIN_FRAME_STATE_IDLE,
+ state.begin_frame_state());
EXPECT_EQ(SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION,
state.NextAction());
- state.DidLeaveBeginFrame();
+
+ state.OnBeginFrame(BeginFrameArgs::CreateForTesting());
+ EXPECT_EQ(SchedulerStateMachine::BEGIN_FRAME_STATE_BEGIN_FRAME_STARTING,
+ state.begin_frame_state());
+ EXPECT_EQ(SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION,
+ state.NextAction());
+
+ state.OnBeginFrameDeadlinePending();
+ EXPECT_EQ(SchedulerStateMachine::BEGIN_FRAME_STATE_INSIDE_BEGIN_FRAME,
+ state.begin_frame_state());
EXPECT_EQ(SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION,
state.NextAction());
+
+ state.OnBeginFrameDeadline();
+ EXPECT_EQ(SchedulerStateMachine::BEGIN_FRAME_STATE_INSIDE_DEADLINE,
+ state.begin_frame_state());
+ EXPECT_EQ(SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION,
+ state.NextAction());
+
+ // After we get a new output surface, the commit flow should start.
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION);
+ state.CreateAndInitializeOutputSurfaceWithActivatedCommit();
+ state.OnBeginFrameIdle();
+ state.OnBeginFrame(BeginFrameArgs::CreateForTesting());
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD);
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
+ state.FinishCommit();
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_COMMIT);
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
+ state.OnBeginFrameDeadline();
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE);
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
+}
+
+TEST(SchedulerStateMachineTest,
+ TestContextLostWhileCommitInProgressAndAnotherCommitRequested) {
+ bool deadline_scheduling_enabled = false;
+ TestContextLostWhileCommitInProgressAndAnotherCommitRequested(
+ deadline_scheduling_enabled);
+}
+
+TEST(SchedulerStateMachineTest,
+ TestContextLostWhileCommitInProgressAndAnotherCommitRequested_Deadline) {
+ bool deadline_scheduling_enabled = true;
+ TestContextLostWhileCommitInProgressAndAnotherCommitRequested(
+ deadline_scheduling_enabled);
}
TEST(SchedulerStateMachineTest, TestFinishAllRenderingWhileContextLost) {
@@ -1010,17 +1161,20 @@ TEST(SchedulerStateMachineTest, TestFinishAllRenderingWhileContextLost) {
StateMachine state(default_scheduler_settings);
state.SetCanStart();
state.UpdateState(state.NextAction());
- state.DidCreateAndInitializeOutputSurface();
+ state.CreateAndInitializeOutputSurfaceWithActivatedCommit();
state.SetVisible(true);
state.SetCanDraw(true);
// Cause a lost context lost.
state.DidLoseOutputSurface();
- // Ask a forced redraw and verify it ocurrs, even with a lost context,
- // independent of the BeginFrame stae.
+ // Ask a forced redraw for readback and verify it ocurrs.
+ state.SetCommitState(
+ SchedulerStateMachine::COMMIT_STATE_WAITING_FOR_FIRST_DRAW);
state.SetNeedsForcedRedrawForReadback();
+ state.OnBeginFrame(BeginFrameArgs::CreateForTesting());
EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_DRAW_AND_READBACK);
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
// Forced redraws for readbacks need to be followed by a new commit
// to replace the readback commit.
@@ -1033,14 +1187,21 @@ TEST(SchedulerStateMachineTest, TestFinishAllRenderingWhileContextLost) {
EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_DRAW_AND_SWAP_ABORT);
// Expect to be told to begin context recreation, independent of
- // BeginFrame state.
- EXPECT_ACTION_UPDATE_STATE(
- SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION);
+ // BeginFrame state
+ EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_IDLE, state.CommitState());
+ EXPECT_EQ(SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION,
+ state.NextAction());
- // Ask a readback and verify it ocurrs.
- state.SetNeedsForcedRedrawForReadback();
- EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_AND_READBACK,
+ state.OnBeginFrameDeadline();
+ EXPECT_EQ(SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION,
state.NextAction());
+
+ // Ask a readback and verify it occurs.
+ state.SetCommitState(
+ SchedulerStateMachine::COMMIT_STATE_WAITING_FOR_FIRST_DRAW);
+ state.SetNeedsForcedRedrawForReadback();
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_DRAW_AND_READBACK);
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
}
TEST(SchedulerStateMachineTest, DontDrawBeforeCommitAfterLostOutputSurface) {
@@ -1048,7 +1209,7 @@ TEST(SchedulerStateMachineTest, DontDrawBeforeCommitAfterLostOutputSurface) {
StateMachine state(default_scheduler_settings);
state.SetCanStart();
state.UpdateState(state.NextAction());
- state.DidCreateAndInitializeOutputSurface();
+ state.CreateAndInitializeOutputSurfaceWithActivatedCommit();
state.SetVisible(true);
state.SetCanDraw(true);
@@ -1062,6 +1223,7 @@ TEST(SchedulerStateMachineTest, DontDrawBeforeCommitAfterLostOutputSurface) {
state.DidCreateAndInitializeOutputSurface();
EXPECT_FALSE(state.RedrawPending());
+ state.OnBeginFrame(BeginFrameArgs::CreateForTesting());
EXPECT_EQ(SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD,
state.NextAction());
}
@@ -1072,7 +1234,7 @@ TEST(SchedulerStateMachineTest,
StateMachine state(default_scheduler_settings);
state.SetCanStart();
state.UpdateState(state.NextAction());
- state.DidCreateAndInitializeOutputSurface();
+ state.CreateAndInitializeOutputSurfaceWithActivatedCommit();
state.SetVisible(false);
state.SetNeedsCommit();
state.SetNeedsForcedCommitForReadback();
@@ -1097,7 +1259,7 @@ TEST(SchedulerStateMachineTest, TestFinishCommitWhenCommitInProgress) {
StateMachine state(default_scheduler_settings);
state.SetCanStart();
state.UpdateState(state.NextAction());
- state.DidCreateAndInitializeOutputSurface();
+ state.CreateAndInitializeOutputSurfaceWithActivatedCommit();
state.SetVisible(false);
state.SetCommitState(SchedulerStateMachine::COMMIT_STATE_FRAME_IN_PROGRESS);
state.SetNeedsCommit();
@@ -1108,11 +1270,7 @@ TEST(SchedulerStateMachineTest, TestFinishCommitWhenCommitInProgress) {
EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_WAITING_FOR_FIRST_DRAW,
state.CommitState());
- EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_AND_SWAP_ABORT,
- state.NextAction());
- state.UpdateState(state.NextAction());
-
- EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction());
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_DRAW_AND_SWAP_ABORT);
}
TEST(SchedulerStateMachineTest, TestFinishCommitWhenForcedCommitInProgress) {
@@ -1149,7 +1307,7 @@ TEST(SchedulerStateMachineTest, TestInitialActionsWhenContextLost) {
StateMachine state(default_scheduler_settings);
state.SetCanStart();
state.UpdateState(state.NextAction());
- state.DidCreateAndInitializeOutputSurface();
+ state.CreateAndInitializeOutputSurfaceWithActivatedCommit();
state.SetVisible(true);
state.SetCanDraw(true);
state.SetNeedsCommit();
@@ -1185,7 +1343,7 @@ TEST(SchedulerStateMachineTest, TestImmediateFinishCommit) {
StateMachine state(default_scheduler_settings);
state.SetCanStart();
state.UpdateState(state.NextAction());
- state.DidCreateAndInitializeOutputSurface();
+ state.CreateAndInitializeOutputSurfaceWithActivatedCommit();
state.SetVisible(true);
state.SetCanDraw(true);
@@ -1195,7 +1353,7 @@ TEST(SchedulerStateMachineTest, TestImmediateFinishCommit) {
EXPECT_ACTION_UPDATE_STATE(
SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD);
state.FinishCommit();
- EXPECT_EQ(SchedulerStateMachine::ACTION_COMMIT, state.NextAction());
+
EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_READY_TO_COMMIT,
state.CommitState());
EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_COMMIT);
@@ -1205,6 +1363,7 @@ TEST(SchedulerStateMachineTest, TestImmediateFinishCommit) {
EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_DRAW_AND_READBACK);
state.DidDrawIfPossibleCompleted(true);
+
EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
// Should be waiting for the normal begin frame from the main thread.
@@ -1212,9 +1371,10 @@ TEST(SchedulerStateMachineTest, TestImmediateFinishCommit) {
state.CommitState());
}
-TEST(SchedulerStateMachineTest, TestImmediateFinishCommitDuringCommit) {
- SchedulerSettings default_scheduler_settings;
- StateMachine state(default_scheduler_settings);
+void TestImmediateFinishCommitDuringCommit(bool deadline_scheduling_enabled) {
+ SchedulerSettings scheduler_settings;
+ scheduler_settings.deadline_scheduling_enabled = deadline_scheduling_enabled;
+ StateMachine state(scheduler_settings);
state.SetCanStart();
state.UpdateState(state.NextAction());
state.CreateAndInitializeOutputSurfaceWithActivatedCommit();
@@ -1223,12 +1383,18 @@ TEST(SchedulerStateMachineTest, TestImmediateFinishCommitDuringCommit) {
// Start a normal commit.
state.SetNeedsCommit();
- EXPECT_ACTION_UPDATE_STATE(
- SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD);
+ if (!deadline_scheduling_enabled) {
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD);
+ }
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
// Schedule a readback, commit it, draw it.
- state.SetNeedsCommit();
state.SetNeedsForcedCommitForReadback();
+ if (deadline_scheduling_enabled) {
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD);
+ }
EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
state.FinishCommit();
EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_READY_TO_COMMIT,
@@ -1249,9 +1415,22 @@ TEST(SchedulerStateMachineTest, TestImmediateFinishCommitDuringCommit) {
}
TEST(SchedulerStateMachineTest,
- ImmediateBeginFrameAbortedByMainThreadWhileInvisible) {
- SchedulerSettings default_scheduler_settings;
- StateMachine state(default_scheduler_settings);
+ TestImmediateFinishCommitDuringCommit) {
+ bool deadline_scheduling_enabled = false;
+ TestImmediateFinishCommitDuringCommit(deadline_scheduling_enabled);
+}
+
+TEST(SchedulerStateMachineTest,
+ TestImmediateFinishCommitDuringCommit_Deadline) {
+ bool deadline_scheduling_enabled = true;
+ TestImmediateFinishCommitDuringCommit(deadline_scheduling_enabled);
+}
+
+void ImmediateBeginFrameAbortedByMainThreadWhileInvisible(
+ bool deadline_scheduling_enabled) {
+ SchedulerSettings scheduler_settings;
+ scheduler_settings.deadline_scheduling_enabled = deadline_scheduling_enabled;
+ StateMachine state(scheduler_settings);
state.SetCanStart();
state.UpdateState(state.NextAction());
state.CreateAndInitializeOutputSurfaceWithActivatedCommit();
@@ -1259,12 +1438,18 @@ TEST(SchedulerStateMachineTest,
state.SetCanDraw(true);
state.SetNeedsCommit();
- EXPECT_ACTION_UPDATE_STATE(
- SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD);
+ if (!deadline_scheduling_enabled) {
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD);
+ }
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
state.SetNeedsCommit();
state.SetNeedsForcedCommitForReadback();
- EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
+ if (deadline_scheduling_enabled) {
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD);
+ }
state.FinishCommit();
EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_READY_TO_COMMIT,
@@ -1292,6 +1477,20 @@ TEST(SchedulerStateMachineTest,
EXPECT_TRUE(state.NeedsCommit());
}
+TEST(SchedulerStateMachineTest,
+ ImmediateBeginFrameAbortedByMainThreadWhileInvisible) {
+ bool deadline_scheduling_enabled = false;
+ ImmediateBeginFrameAbortedByMainThreadWhileInvisible(
+ deadline_scheduling_enabled);
+}
+
+TEST(SchedulerStateMachineTest,
+ ImmediateBeginFrameAbortedByMainThreadWhileInvisible_Deadline) {
+ bool deadline_scheduling_enabled = true;
+ ImmediateBeginFrameAbortedByMainThreadWhileInvisible(
+ deadline_scheduling_enabled);
+}
+
TEST(SchedulerStateMachineTest, ImmediateFinishCommitWhileCantDraw) {
SchedulerSettings default_scheduler_settings;
StateMachine state(default_scheduler_settings);
@@ -1360,20 +1559,17 @@ TEST(SchedulerStateMachineTest, ReportIfNotDrawingFromAcquiredTextures) {
EXPECT_FALSE(state.PendingDrawsShouldBeAborted());
state.SetMainThreadNeedsLayerTextures();
- EXPECT_EQ(
- SchedulerStateMachine::ACTION_ACQUIRE_LAYER_TEXTURES_FOR_MAIN_THREAD,
- state.NextAction());
- state.UpdateState(state.NextAction());
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_ACQUIRE_LAYER_TEXTURES_FOR_MAIN_THREAD);
EXPECT_TRUE(state.PendingDrawsShouldBeAborted());
-
- EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction());
+ EXPECT_TRUE(state.PendingActivationsShouldBeForced());
state.SetNeedsCommit();
- EXPECT_EQ(SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD,
- state.NextAction());
-
- state.UpdateState(state.NextAction());
+ state.OnBeginFrame(BeginFrameArgs::CreateForTesting());
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD);
EXPECT_TRUE(state.PendingDrawsShouldBeAborted());
+ EXPECT_TRUE(state.PendingActivationsShouldBeForced());
EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction());
@@ -1388,7 +1584,7 @@ TEST(SchedulerStateMachineTest, ReportIfNotDrawingFromAcquiredTextures) {
TEST(SchedulerStateMachineTest, AcquireTexturesWithAbort) {
SchedulerSettings default_scheduler_settings;
- SchedulerStateMachine state(default_scheduler_settings);
+ StateMachine state(default_scheduler_settings);
state.SetCanStart();
state.UpdateState(state.NextAction());
state.DidCreateAndInitializeOutputSurface();
diff --git a/cc/scheduler/scheduler_unittest.cc b/cc/scheduler/scheduler_unittest.cc
index ff8b11d..a1da822 100644
--- a/cc/scheduler/scheduler_unittest.cc
+++ b/cc/scheduler/scheduler_unittest.cc
@@ -1,7 +1,6 @@
// 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>
@@ -35,9 +34,10 @@ void InitializeOutputSurfaceAndFirstCommit(Scheduler* scheduler) {
scheduler->FinishCommit();
// Go through the motions to draw the commit.
scheduler->BeginFrame(BeginFrameArgs::CreateForTesting());
- // We need another BeginFrame so scheduler calls SetNeedsBeginFrame(false).
- scheduler->BeginFrame(BeginFrameArgs::CreateForTesting());
+ scheduler->OnBeginFrameDeadline();
+ // We need another BeginFrame so Scheduler calls SetNeedsBeginFrame(false).
scheduler->BeginFrame(BeginFrameArgs::CreateForTesting());
+ scheduler->OnBeginFrameDeadline();
}
class FakeSchedulerClient : public SchedulerClient {
@@ -156,6 +156,14 @@ class FakeSchedulerClient : public SchedulerClient {
return base::TimeDelta();
}
+ virtual void PostBeginFrameDeadline(const base::Closure& closure,
+ base::TimeTicks deadline) OVERRIDE {
+ actions_.push_back("PostBeginFrameDeadlineTask");
+ states_.push_back(scheduler_->StateAsValue().release());
+ }
+
+ virtual void DidBeginFrameDeadlineOnImplThread() OVERRIDE {}
+
protected:
bool needs_begin_frame_;
bool draw_will_happen_;
@@ -180,10 +188,11 @@ TEST(SchedulerTest, InitializeOutputSurfaceDoesNotBeginFrame) {
EXPECT_EQ(0, client.num_actions_());
}
-TEST(SchedulerTest, RequestCommit) {
+void RequestCommit(bool deadline_scheduling_enabled) {
FakeSchedulerClient client;
- SchedulerSettings default_scheduler_settings;
- Scheduler* scheduler = client.CreateScheduler(default_scheduler_settings);
+ 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);
@@ -191,11 +200,32 @@ TEST(SchedulerTest, RequestCommit) {
EXPECT_SINGLE_ACTION("ScheduledActionBeginOutputSurfaceCreation", client);
InitializeOutputSurfaceAndFirstCommit(scheduler);
- // SetNeedsCommit should begin the frame.
+ // SetNeedsCommit should begin the frame on the next BeginFrame.
client.Reset();
scheduler->SetNeedsCommit();
- EXPECT_ACTION("ScheduledActionSendBeginFrameToMainThread", client, 0, 2);
- EXPECT_ACTION("SetNeedsBeginFrameOnImplThread", client, 1, 2);
+ EXPECT_TRUE(client.needs_begin_frame());
+ if (deadline_scheduling_enabled) {
+ EXPECT_SINGLE_ACTION("SetNeedsBeginFrameOnImplThread", client);
+ } else {
+ EXPECT_EQ(client.num_actions_(), 2);
+ EXPECT_TRUE(client.HasAction("ScheduledActionSendBeginFrameToMainThread"));
+ EXPECT_TRUE(client.HasAction("SetNeedsBeginFrameOnImplThread"));
+ }
+ client.Reset();
+
+ scheduler->BeginFrame(BeginFrameArgs::CreateForTesting());
+ if (deadline_scheduling_enabled) {
+ EXPECT_ACTION("ScheduledActionSendBeginFrameToMainThread", client, 0, 2);
+ EXPECT_ACTION("PostBeginFrameDeadlineTask", client, 1, 2);
+ } else {
+ EXPECT_SINGLE_ACTION("PostBeginFrameDeadlineTask", client);
+ }
+ EXPECT_TRUE(client.needs_begin_frame());
+ client.Reset();
+
+ // If we don't swap on the deadline, we need to request another BeginFrame.
+ scheduler->OnBeginFrameDeadline();
+ EXPECT_SINGLE_ACTION("SetNeedsBeginFrameOnImplThread", client);
EXPECT_TRUE(client.needs_begin_frame());
client.Reset();
@@ -205,18 +235,47 @@ TEST(SchedulerTest, RequestCommit) {
EXPECT_TRUE(client.needs_begin_frame());
client.Reset();
- // BeginFrame should draw.
+ // BeginFrame should prepare the draw.
scheduler->BeginFrame(BeginFrameArgs::CreateForTesting());
+ EXPECT_SINGLE_ACTION("PostBeginFrameDeadlineTask", client);
+ EXPECT_TRUE(client.needs_begin_frame());
+ client.Reset();
+
+ // BeginFrame deadline should draw.
+ scheduler->OnBeginFrameDeadline();
EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client, 0, 2);
EXPECT_ACTION("SetNeedsBeginFrameOnImplThread", client, 1, 2);
EXPECT_TRUE(client.needs_begin_frame());
client.Reset();
+
+ // The following BeginFrame deadline should SetNeedsBeginFrame(false) to avoid
+ // excessive toggles.
+ scheduler->BeginFrame(BeginFrameArgs::CreateForTesting());
+ EXPECT_SINGLE_ACTION("PostBeginFrameDeadlineTask", client);
+ client.Reset();
+
+ scheduler->OnBeginFrameDeadline();
+ EXPECT_SINGLE_ACTION("SetNeedsBeginFrameOnImplThread", client);
+ EXPECT_FALSE(client.needs_begin_frame());
+ client.Reset();
}
-TEST(SchedulerTest, RequestCommitAfterBeginFrameSentToMainThread) {
+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 RequestCommitAfterBeginFrameSentToMainThread(
+ bool deadline_scheduling_enabled) {
FakeSchedulerClient client;
- SchedulerSettings default_scheduler_settings;
- Scheduler* scheduler = client.CreateScheduler(default_scheduler_settings);
+ 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);
@@ -225,49 +284,102 @@ TEST(SchedulerTest, RequestCommitAfterBeginFrameSentToMainThread) {
InitializeOutputSurfaceAndFirstCommit(scheduler);
client.Reset();
- // SetNedsCommit should begin the frame.
+ // SetNeedsCommit should begin the frame.
scheduler->SetNeedsCommit();
- EXPECT_ACTION("ScheduledActionSendBeginFrameToMainThread", client, 0, 2);
- EXPECT_ACTION("SetNeedsBeginFrameOnImplThread", client, 1, 2);
+ if (deadline_scheduling_enabled) {
+ EXPECT_SINGLE_ACTION("SetNeedsBeginFrameOnImplThread", client);
+ } else {
+ EXPECT_EQ(client.num_actions_(), 2);
+ EXPECT_TRUE(client.HasAction("SetNeedsBeginFrameOnImplThread"));
+ EXPECT_TRUE(client.HasAction("ScheduledActionSendBeginFrameToMainThread"));
+ }
+
client.Reset();
+ scheduler->BeginFrame(BeginFrameArgs::CreateForTesting());
+ if (deadline_scheduling_enabled) {
+ EXPECT_EQ(client.num_actions_(), 2);
+ EXPECT_TRUE(client.HasAction("ScheduledActionSendBeginFrameToMainThread"));
+ EXPECT_TRUE(client.HasAction("PostBeginFrameDeadlineTask"));
+ } else {
+ EXPECT_SINGLE_ACTION("PostBeginFrameDeadlineTask", client);
+ }
- // Now SetNeedsCommit again. Calling here means we need a second frame.
+ EXPECT_TRUE(client.needs_begin_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 commit for the first frame.
+ // Finish the first commit.
scheduler->FinishCommit();
- EXPECT_SINGLE_ACTION("ScheduledActionCommit", client);
+ EXPECT_ACTION("ScheduledActionCommit", client, 0, 2);
+ EXPECT_ACTION("PostBeginFrameDeadlineTask", client, 1, 2);
client.Reset();
+ scheduler->OnBeginFrameDeadline();
+ if (deadline_scheduling_enabled) {
+ EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client, 0, 2);
+ EXPECT_ACTION("SetNeedsBeginFrameOnImplThread", client, 1, 2);
+ } else {
+ EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client, 0, 3);
+ EXPECT_ACTION("ScheduledActionSendBeginFrameToMainThread", client, 1, 3);
+ EXPECT_ACTION("SetNeedsBeginFrameOnImplThread", client, 2, 3);
+ }
- // Tick should draw but then begin another frame for the second commit.
// Because we just swapped, the Scheduler should also request the next
// BeginFrame from the OutputSurface.
- scheduler->BeginFrame(BeginFrameArgs::CreateForTesting());
EXPECT_TRUE(client.needs_begin_frame());
- EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client, 0, 3);
- EXPECT_ACTION("ScheduledActionSendBeginFrameToMainThread", client, 1, 3);
- EXPECT_ACTION("SetNeedsBeginFrameOnImplThread", client, 2, 3);
client.Reset();
- // Finish the second commit.
- scheduler->FinishCommit();
+ // Since another commit is needed, the next BeginFrame should initiate
+ // the second commit.
scheduler->BeginFrame(BeginFrameArgs::CreateForTesting());
- EXPECT_ACTION("ScheduledActionCommit", client, 0, 3);
- EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client, 1, 3);
- EXPECT_ACTION("SetNeedsBeginFrameOnImplThread", client, 2, 3);
+ if (deadline_scheduling_enabled) {
+ EXPECT_EQ(client.num_actions_(), 2);
+ EXPECT_TRUE(client.HasAction("ScheduledActionSendBeginFrameToMainThread"));
+ EXPECT_TRUE(client.HasAction("PostBeginFrameDeadlineTask"));
+ } else {
+ EXPECT_SINGLE_ACTION("PostBeginFrameDeadlineTask", 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("PostBeginFrameDeadlineTask", client, 1, 2);
+ client.Reset();
+ scheduler->OnBeginFrameDeadline();
+ EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client, 0, 2);
+ EXPECT_ACTION("SetNeedsBeginFrameOnImplThread", client, 1, 2);
EXPECT_TRUE(client.needs_begin_frame());
client.Reset();
// On the next BeginFrame, verify we go back to a quiescent state and
// no longer request BeginFrames.
scheduler->BeginFrame(BeginFrameArgs::CreateForTesting());
+ scheduler->OnBeginFrameDeadline();
EXPECT_FALSE(client.needs_begin_frame());
+ client.Reset();
}
-TEST(SchedulerTest, TextureAcquisitionCausesCommitInsteadOfDraw) {
+TEST(SchedulerTest, RequestCommitAfterBeginFrameSentToMainThread) {
+ bool deadline_scheduling_enabled = false;
+ RequestCommitAfterBeginFrameSentToMainThread(deadline_scheduling_enabled);
+}
+
+TEST(SchedulerTest, RequestCommitAfterBeginFrameSentToMainThread_Deadline) {
+ bool deadline_scheduling_enabled = true;
+ RequestCommitAfterBeginFrameSentToMainThread(deadline_scheduling_enabled);
+}
+
+void TextureAcquisitionCausesCommitInsteadOfDraw(
+ bool deadline_scheduling_enabled) {
FakeSchedulerClient client;
- SchedulerSettings default_scheduler_settings;
- Scheduler* scheduler = client.CreateScheduler(default_scheduler_settings);
+ 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);
@@ -282,6 +394,9 @@ TEST(SchedulerTest, TextureAcquisitionCausesCommitInsteadOfDraw) {
client.Reset();
scheduler->BeginFrame(BeginFrameArgs::CreateForTesting());
+ EXPECT_SINGLE_ACTION("PostBeginFrameDeadlineTask", client);
+ client.Reset();
+ scheduler->OnBeginFrameDeadline();
EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client, 0, 2);
EXPECT_ACTION("SetNeedsBeginFrameOnImplThread", client, 1, 2);
EXPECT_FALSE(scheduler->RedrawPending());
@@ -289,6 +404,9 @@ TEST(SchedulerTest, TextureAcquisitionCausesCommitInsteadOfDraw) {
client.Reset();
scheduler->BeginFrame(BeginFrameArgs::CreateForTesting());
+ EXPECT_SINGLE_ACTION("PostBeginFrameDeadlineTask", client);
+ client.Reset();
+ scheduler->OnBeginFrameDeadline();
EXPECT_SINGLE_ACTION("SetNeedsBeginFrameOnImplThread", client);
EXPECT_FALSE(scheduler->RedrawPending());
EXPECT_FALSE(client.needs_begin_frame());
@@ -308,25 +426,40 @@ TEST(SchedulerTest, TextureAcquisitionCausesCommitInsteadOfDraw) {
// No draw happens since the textures are acquired by the main thread.
client.Reset();
scheduler->BeginFrame(BeginFrameArgs::CreateForTesting());
+ EXPECT_SINGLE_ACTION("PostBeginFrameDeadlineTask", client);
+ client.Reset();
+ scheduler->OnBeginFrameDeadline();
EXPECT_SINGLE_ACTION("SetNeedsBeginFrameOnImplThread", client);
EXPECT_TRUE(scheduler->RedrawPending());
EXPECT_TRUE(client.needs_begin_frame());
+ client.Reset();
scheduler->SetNeedsCommit();
- EXPECT_ACTION("SetNeedsBeginFrameOnImplThread", client, 0, 2);
- EXPECT_ACTION("ScheduledActionSendBeginFrameToMainThread", client, 1, 2);
- EXPECT_TRUE(client.needs_begin_frame());
+ if (deadline_scheduling_enabled) {
+ EXPECT_EQ(0, client.num_actions_());
+ } else {
+ EXPECT_SINGLE_ACTION("ScheduledActionSendBeginFrameToMainThread", client);
+ }
+
+ client.Reset();
+ scheduler->BeginFrame(BeginFrameArgs::CreateForTesting());
+ if (deadline_scheduling_enabled) {
+ EXPECT_ACTION("ScheduledActionSendBeginFrameToMainThread", client, 0, 2);
+ EXPECT_ACTION("PostBeginFrameDeadlineTask", client, 1, 2);
+ } else {
+ EXPECT_SINGLE_ACTION("PostBeginFrameDeadlineTask", client);
+ }
// Commit will release the texture.
client.Reset();
scheduler->FinishCommit();
- EXPECT_SINGLE_ACTION("ScheduledActionCommit", client);
+ EXPECT_ACTION("ScheduledActionCommit", client, 0, 2);
+ EXPECT_ACTION("PostBeginFrameDeadlineTask", client, 1, 2);
EXPECT_TRUE(scheduler->RedrawPending());
- EXPECT_TRUE(client.needs_begin_frame());
// Now we can draw again after the commit happens.
client.Reset();
- scheduler->BeginFrame(BeginFrameArgs::CreateForTesting());
+ scheduler->OnBeginFrameDeadline();
EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client, 0, 2);
EXPECT_ACTION("SetNeedsBeginFrameOnImplThread", client, 1, 2);
EXPECT_FALSE(scheduler->RedrawPending());
@@ -335,14 +468,28 @@ TEST(SchedulerTest, TextureAcquisitionCausesCommitInsteadOfDraw) {
// Make sure we stop requesting BeginFrames if we don't swap.
client.Reset();
scheduler->BeginFrame(BeginFrameArgs::CreateForTesting());
+ EXPECT_SINGLE_ACTION("PostBeginFrameDeadlineTask", client);
+ client.Reset();
+ scheduler->OnBeginFrameDeadline();
EXPECT_SINGLE_ACTION("SetNeedsBeginFrameOnImplThread", client);
EXPECT_FALSE(client.needs_begin_frame());
}
-TEST(SchedulerTest, TextureAcquisitionCollision) {
+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 default_scheduler_settings;
- Scheduler* scheduler = client.CreateScheduler(default_scheduler_settings);
+ 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);
@@ -352,12 +499,30 @@ TEST(SchedulerTest, TextureAcquisitionCollision) {
client.Reset();
scheduler->SetNeedsCommit();
+if (deadline_scheduling_enabled) {
+ EXPECT_SINGLE_ACTION("SetNeedsBeginFrameOnImplThread", client);
+ } else {
+ EXPECT_ACTION("ScheduledActionSendBeginFrameToMainThread", client, 0, 2);
+ EXPECT_ACTION("SetNeedsBeginFrameOnImplThread", client, 1, 2);
+ }
+
+ client.Reset();
scheduler->SetMainThreadNeedsLayerTextures();
- EXPECT_ACTION("ScheduledActionSendBeginFrameToMainThread", client, 0, 3);
- EXPECT_ACTION("SetNeedsBeginFrameOnImplThread", client, 1, 3);
- EXPECT_ACTION(
- "ScheduledActionAcquireLayerTexturesForMainThread", client, 2, 3);
+ EXPECT_SINGLE_ACTION(
+ "ScheduledActionAcquireLayerTexturesForMainThread", client);
+
+ client.Reset();
+ scheduler->BeginFrame(BeginFrameArgs::CreateForTesting());
+ if (deadline_scheduling_enabled) {
+ EXPECT_ACTION("ScheduledActionSendBeginFrameToMainThread", client, 0, 2);
+ EXPECT_ACTION("PostBeginFrameDeadlineTask", client, 1, 2);
+ } else {
+ EXPECT_SINGLE_ACTION("PostBeginFrameDeadlineTask", client);
+ }
+
client.Reset();
+ scheduler->OnBeginFrameDeadline();
+ EXPECT_SINGLE_ACTION("SetNeedsBeginFrameOnImplThread", client);
// Although the compositor cannot draw because textures are locked by main
// thread, we continue requesting SetNeedsBeginFrame in anticipation of the
@@ -367,56 +532,88 @@ TEST(SchedulerTest, TextureAcquisitionCollision) {
// Trigger the commit
scheduler->FinishCommit();
EXPECT_TRUE(client.needs_begin_frame());
- client.Reset();
// Between commit and draw, texture acquisition for main thread delayed,
// and main thread blocks.
+ client.Reset();
scheduler->SetMainThreadNeedsLayerTextures();
EXPECT_EQ(0, client.num_actions_());
- client.Reset();
// No implicit commit is expected.
+ client.Reset();
scheduler->BeginFrame(BeginFrameArgs::CreateForTesting());
+ EXPECT_SINGLE_ACTION("PostBeginFrameDeadlineTask", client);
+
+ client.Reset();
+ scheduler->OnBeginFrameDeadline();
EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client, 0, 3);
- EXPECT_ACTION("ScheduledActionAcquireLayerTexturesForMainThread",
- client,
- 1,
- 3);
+ EXPECT_ACTION(
+ "ScheduledActionAcquireLayerTexturesForMainThread", client, 1, 3);
EXPECT_ACTION("SetNeedsBeginFrameOnImplThread", client, 2, 3);
EXPECT_TRUE(client.needs_begin_frame());
- client.Reset();
- // Compositor not scheduled to draw because textures are locked by main
+ // The compositor should not draw because textures are locked by main
// thread.
+ client.Reset();
scheduler->BeginFrame(BeginFrameArgs::CreateForTesting());
+ EXPECT_SINGLE_ACTION("PostBeginFrameDeadlineTask", client);
+ client.Reset();
+ scheduler->OnBeginFrameDeadline();
EXPECT_SINGLE_ACTION("SetNeedsBeginFrameOnImplThread", client);
EXPECT_FALSE(client.needs_begin_frame());
- client.Reset();
- // Needs an explicit commit from the main thread.
+ // The impl thread need an explicit commit from the main thread to lock
+ // the textures.
+ client.Reset();
scheduler->SetNeedsCommit();
- EXPECT_ACTION("ScheduledActionSendBeginFrameToMainThread", client, 0, 2);
- EXPECT_ACTION("SetNeedsBeginFrameOnImplThread", client, 1, 2);
+ if (deadline_scheduling_enabled) {
+ EXPECT_SINGLE_ACTION("SetNeedsBeginFrameOnImplThread", client);
+ } else {
+ EXPECT_ACTION("ScheduledActionSendBeginFrameToMainThread", client, 0, 2);
+ EXPECT_ACTION("SetNeedsBeginFrameOnImplThread", client, 1, 2);
+ }
+ EXPECT_TRUE(client.needs_begin_frame());
+
+ client.Reset();
+ scheduler->BeginFrame(BeginFrameArgs::CreateForTesting());
+ if (deadline_scheduling_enabled) {
+ EXPECT_ACTION("ScheduledActionSendBeginFrameToMainThread", client, 0, 2);
+ EXPECT_ACTION("PostBeginFrameDeadlineTask", client, 1, 2);
+ } else {
+ EXPECT_SINGLE_ACTION("PostBeginFrameDeadlineTask", client);
+ }
client.Reset();
- // Trigger the commit
+ // Trigger the commit, which will trigger the deadline task early.
scheduler->FinishCommit();
- EXPECT_SINGLE_ACTION("ScheduledActionCommit", client);
+ EXPECT_ACTION("ScheduledActionCommit", client, 0, 2);
+ EXPECT_ACTION("PostBeginFrameDeadlineTask", client, 1, 2);
EXPECT_TRUE(client.needs_begin_frame());
client.Reset();
- // Verify we draw on the next BeginFrame.
- scheduler->BeginFrame(BeginFrameArgs::CreateForTesting());
+ // Verify we draw on the next BeginFrame deadline
+ scheduler->OnBeginFrameDeadline();
EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client, 0, 2);
EXPECT_ACTION("SetNeedsBeginFrameOnImplThread", client, 1, 2);
EXPECT_TRUE(client.needs_begin_frame());
client.Reset();
}
-TEST(SchedulerTest, VisibilitySwitchWithTextureAcquisition) {
+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 default_scheduler_settings;
- Scheduler* scheduler = client.CreateScheduler(default_scheduler_settings);
+ 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);
@@ -426,6 +623,10 @@ TEST(SchedulerTest, VisibilitySwitchWithTextureAcquisition) {
scheduler->DidCreateAndInitializeOutputSurface();
scheduler->SetNeedsCommit();
+ if (deadline_scheduling_enabled) {
+ scheduler->BeginFrame(BeginFrameArgs::CreateForTesting());
+ scheduler->OnBeginFrameDeadline();
+ }
scheduler->FinishCommit();
scheduler->SetMainThreadNeedsLayerTextures();
scheduler->SetNeedsCommit();
@@ -435,19 +636,29 @@ TEST(SchedulerTest, VisibilitySwitchWithTextureAcquisition) {
scheduler->SetVisible(false);
EXPECT_SINGLE_ACTION("ScheduledActionAcquireLayerTexturesForMainThread",
client);
- client.Reset();
- // Already sent a begin frame on this current frame, so wait.
+ client.Reset();
scheduler->SetVisible(true);
EXPECT_EQ(0, client.num_actions_());
- client.Reset();
+ EXPECT_TRUE(client.needs_begin_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->BeginFrame(BeginFrameArgs::CreateForTesting());
EXPECT_ACTION("ScheduledActionSendBeginFrameToMainThread", client, 0, 2);
- EXPECT_ACTION("SetNeedsBeginFrameOnImplThread", client, 1, 2);
+ EXPECT_ACTION("PostBeginFrameDeadlineTask", 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 {
@@ -494,17 +705,20 @@ TEST(SchedulerTest, RequestRedrawInsideDraw) {
EXPECT_EQ(0, client.num_draws());
scheduler->BeginFrame(BeginFrameArgs::CreateForTesting());
+ scheduler->OnBeginFrameDeadline();
EXPECT_EQ(1, client.num_draws());
EXPECT_TRUE(scheduler->RedrawPending());
EXPECT_TRUE(client.needs_begin_frame());
scheduler->BeginFrame(BeginFrameArgs::CreateForTesting());
+ scheduler->OnBeginFrameDeadline();
EXPECT_EQ(2, client.num_draws());
EXPECT_FALSE(scheduler->RedrawPending());
EXPECT_TRUE(client.needs_begin_frame());
// We stop requesting BeginFrames after a BeginFrame where we don't swap.
scheduler->BeginFrame(BeginFrameArgs::CreateForTesting());
+ scheduler->OnBeginFrameDeadline();
EXPECT_EQ(2, client.num_draws());
EXPECT_FALSE(scheduler->RedrawPending());
EXPECT_FALSE(client.needs_begin_frame());
@@ -530,6 +744,7 @@ TEST(SchedulerTest, RequestRedrawInsideFailedDraw) {
// Fail the draw.
scheduler->BeginFrame(BeginFrameArgs::CreateForTesting());
+ scheduler->OnBeginFrameDeadline();
EXPECT_EQ(1, client.num_draws());
// We have a commit pending and the draw failed, and we didn't lose the redraw
@@ -540,6 +755,7 @@ TEST(SchedulerTest, RequestRedrawInsideFailedDraw) {
// Fail the draw again.
scheduler->BeginFrame(BeginFrameArgs::CreateForTesting());
+ scheduler->OnBeginFrameDeadline();
EXPECT_EQ(2, client.num_draws());
EXPECT_TRUE(scheduler->CommitPending());
EXPECT_TRUE(scheduler->RedrawPending());
@@ -548,6 +764,7 @@ TEST(SchedulerTest, RequestRedrawInsideFailedDraw) {
// Draw successfully.
client.SetDrawWillHappen(true);
scheduler->BeginFrame(BeginFrameArgs::CreateForTesting());
+ scheduler->OnBeginFrameDeadline();
EXPECT_EQ(3, client.num_draws());
EXPECT_TRUE(scheduler->CommitPending());
EXPECT_FALSE(scheduler->RedrawPending());
@@ -600,6 +817,7 @@ TEST(SchedulerTest, RequestCommitInsideDraw) {
InitializeOutputSurfaceAndFirstCommit(scheduler);
client.Reset();
+ EXPECT_FALSE(client.needs_begin_frame());
scheduler->SetNeedsRedraw();
EXPECT_TRUE(scheduler->RedrawPending());
EXPECT_EQ(0, client.num_draws());
@@ -607,19 +825,24 @@ TEST(SchedulerTest, RequestCommitInsideDraw) {
client.SetNeedsCommitOnNextDraw();
scheduler->BeginFrame(BeginFrameArgs::CreateForTesting());
+ client.SetNeedsCommitOnNextDraw();
+ scheduler->OnBeginFrameDeadline();
EXPECT_EQ(1, client.num_draws());
EXPECT_TRUE(scheduler->CommitPending());
EXPECT_TRUE(client.needs_begin_frame());
scheduler->FinishCommit();
scheduler->BeginFrame(BeginFrameArgs::CreateForTesting());
- EXPECT_EQ(2, client.num_draws());;
+ scheduler->OnBeginFrameDeadline();
+ EXPECT_EQ(2, client.num_draws());
+
EXPECT_FALSE(scheduler->RedrawPending());
EXPECT_FALSE(scheduler->CommitPending());
EXPECT_TRUE(client.needs_begin_frame());
// We stop requesting BeginFrames after a BeginFrame where we don't swap.
scheduler->BeginFrame(BeginFrameArgs::CreateForTesting());
+ scheduler->OnBeginFrameDeadline();
EXPECT_EQ(2, client.num_draws());
EXPECT_FALSE(scheduler->RedrawPending());
EXPECT_FALSE(scheduler->CommitPending());
@@ -646,6 +869,7 @@ TEST(SchedulerTest, RequestCommitInsideFailedDraw) {
// Fail the draw.
scheduler->BeginFrame(BeginFrameArgs::CreateForTesting());
+ scheduler->OnBeginFrameDeadline();
EXPECT_EQ(1, client.num_draws());
// We have a commit pending and the draw failed, and we didn't lose the commit
@@ -656,6 +880,7 @@ TEST(SchedulerTest, RequestCommitInsideFailedDraw) {
// Fail the draw again.
scheduler->BeginFrame(BeginFrameArgs::CreateForTesting());
+ scheduler->OnBeginFrameDeadline();
EXPECT_EQ(2, client.num_draws());
EXPECT_TRUE(scheduler->CommitPending());
EXPECT_TRUE(scheduler->RedrawPending());
@@ -664,6 +889,7 @@ TEST(SchedulerTest, RequestCommitInsideFailedDraw) {
// Draw successfully.
client.SetDrawWillHappen(true);
scheduler->BeginFrame(BeginFrameArgs::CreateForTesting());
+ scheduler->OnBeginFrameDeadline();
EXPECT_EQ(3, client.num_draws());
EXPECT_TRUE(scheduler->CommitPending());
EXPECT_FALSE(scheduler->RedrawPending());
@@ -688,6 +914,7 @@ TEST(SchedulerTest, NoSwapWhenDrawFails) {
// Draw successfully, this starts a new frame.
client.SetNeedsCommitOnNextDraw();
scheduler->BeginFrame(BeginFrameArgs::CreateForTesting());
+ scheduler->OnBeginFrameDeadline();
EXPECT_EQ(1, client.num_draws());
scheduler->SetNeedsRedraw();
@@ -698,6 +925,7 @@ TEST(SchedulerTest, NoSwapWhenDrawFails) {
client.SetDrawWillHappen(false);
client.SetNeedsCommitOnNextDraw();
scheduler->BeginFrame(BeginFrameArgs::CreateForTesting());
+ scheduler->OnBeginFrameDeadline();
EXPECT_EQ(2, client.num_draws());
}
@@ -774,8 +1002,16 @@ TEST(SchedulerTest, ManageTiles) {
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 BeginFrame should post
+ // the deadline task.
+ client.Reset();
scheduler->BeginFrame(BeginFrameArgs::CreateForTesting());
- // The actions should have occured, in the right order.
+ EXPECT_SINGLE_ACTION("PostBeginFrameDeadlineTask", client);
+
+ // On the deadline, he actions should have occured in the right order.
+ client.Reset();
+ scheduler->OnBeginFrameDeadline();
EXPECT_EQ(1, client.num_draws());
EXPECT_TRUE(client.HasAction("ScheduledActionDrawAndSwapIfPossible"));
EXPECT_TRUE(client.HasAction("ScheduledActionManageTiles"));
@@ -792,10 +1028,17 @@ TEST(SchedulerTest, ManageTiles) {
EXPECT_TRUE(client.needs_begin_frame());
EXPECT_EQ(0, client.num_draws());
+ // We have no immediate actions to perform, so the BeginFrame should post
+ // the deadline task.
+ client.Reset();
+ scheduler->BeginFrame(BeginFrameArgs::CreateForTesting());
+ EXPECT_SINGLE_ACTION("PostBeginFrameDeadlineTask", 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.
- scheduler->BeginFrame(BeginFrameArgs::CreateForTesting());
+ client.Reset();
+ scheduler->OnBeginFrameDeadline();
EXPECT_EQ(1, client.num_draws());
EXPECT_TRUE(client.HasAction("ScheduledActionDrawAndSwapIfPossible"));
EXPECT_TRUE(client.HasAction("ScheduledActionManageTiles"));
@@ -807,7 +1050,10 @@ TEST(SchedulerTest, ManageTiles) {
// We need a BeginFrame where we don't swap to go idle.
client.Reset();
scheduler->BeginFrame(BeginFrameArgs::CreateForTesting());
- EXPECT_SINGLE_ACTION("SetNeedsBeginFrameOnImplThread", client);
+ EXPECT_SINGLE_ACTION("PostBeginFrameDeadlineTask", client);
+ client.Reset();
+ scheduler->OnBeginFrameDeadline();
+ EXPECT_SINGLE_ACTION("SetNeedsBeginFrameOnImplThread", client);;
EXPECT_EQ(0, client.num_draws());
// Now trigger a ManageTiles outside of a draw. We will then need
@@ -820,7 +1066,11 @@ TEST(SchedulerTest, ManageTiles) {
EXPECT_FALSE(scheduler->RedrawPending());
// BeginFrame. There will be no draw, only ManageTiles.
+ client.Reset();
scheduler->BeginFrame(BeginFrameArgs::CreateForTesting());
+ EXPECT_SINGLE_ACTION("PostBeginFrameDeadlineTask", client);
+ client.Reset();
+ scheduler->OnBeginFrameDeadline();
EXPECT_EQ(0, client.num_draws());
EXPECT_FALSE(client.HasAction("ScheduledActionDrawAndSwapIfPossible"));
EXPECT_TRUE(client.HasAction("ScheduledActionManageTiles"));
diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc
index 97ceea5..5f5cbee 100644
--- a/cc/trees/layer_tree_host_impl.cc
+++ b/cc/trees/layer_tree_host_impl.cc
@@ -1054,11 +1054,11 @@ bool LayerTreeHostImpl::PrepareToDraw(FrameData* frame,
"SourceFrameNumber",
active_tree_->source_frame_number());
- if (need_to_update_visible_tiles_before_draw_) {
- DCHECK(tile_manager_);
- if (tile_manager_->UpdateVisibleTiles())
- DidInitializeVisibleTile();
+ if (need_to_update_visible_tiles_before_draw_ &&
+ tile_manager_ && tile_manager_->UpdateVisibleTiles()) {
+ DidInitializeVisibleTile();
}
+ need_to_update_visible_tiles_before_draw_ = true;
active_tree_->UpdateDrawProperties();
@@ -1096,6 +1096,10 @@ void LayerTreeHostImpl::BlockNotifyReadyToActivateForTesting(bool block) {
NOTREACHED();
}
+void LayerTreeHostImpl::DidInitializeVisibleTileForTesting() {
+ DidInitializeVisibleTile();
+}
+
void LayerTreeHostImpl::EnforceManagedMemoryPolicy(
const ManagedMemoryPolicy& policy) {
@@ -1154,7 +1158,7 @@ void LayerTreeHostImpl::DidInitializeVisibleTile() {
// TODO(reveman): Determine tiles that changed and only damage
// what's necessary.
SetFullRootLayerDamage();
- if (client_)
+ if (client_ && !client_->IsInsideDraw())
client_->DidInitializeVisibleTileOnImplThread();
}
@@ -1502,12 +1506,8 @@ void LayerTreeHostImpl::CreatePendingTree() {
}
void LayerTreeHostImpl::UpdateVisibleTiles() {
- DCHECK(!client_->IsInsideDraw()) <<
- "Updating visible tiles within a draw may trigger "
- "spurious redraws.";
if (tile_manager_ && tile_manager_->UpdateVisibleTiles())
DidInitializeVisibleTile();
-
need_to_update_visible_tiles_before_draw_ = false;
}
diff --git a/cc/trees/layer_tree_host_impl.h b/cc/trees/layer_tree_host_impl.h
index 7ea6cc9..f9d4f88 100644
--- a/cc/trees/layer_tree_host_impl.h
+++ b/cc/trees/layer_tree_host_impl.h
@@ -185,6 +185,9 @@ class CC_EXPORT LayerTreeHostImpl
// immediately if any notifications had been blocked while blocking.
virtual void BlockNotifyReadyToActivateForTesting(bool block);
+ // This allows us to inject DidInitializeVisibleTile events for testing.
+ void DidInitializeVisibleTileForTesting();
+
bool device_viewport_valid_for_tile_management() const {
return device_viewport_valid_for_tile_management_;
}
@@ -410,8 +413,6 @@ class CC_EXPORT LayerTreeHostImpl
virtual ResourceProvider::ResourceId ResourceIdForUIResource(
UIResourceId uid) const;
- void DidInitializeVisibleTileForTesting() { DidInitializeVisibleTile(); }
-
protected:
LayerTreeHostImpl(
const LayerTreeSettings& settings,
diff --git a/cc/trees/layer_tree_settings.cc b/cc/trees/layer_tree_settings.cc
index 3e9cb21..8e8e35d 100644
--- a/cc/trees/layer_tree_settings.cc
+++ b/cc/trees/layer_tree_settings.cc
@@ -17,6 +17,7 @@ LayerTreeSettings::LayerTreeSettings()
allow_antialiasing(true),
throttle_frame_production(true),
begin_frame_scheduling_enabled(false),
+ deadline_scheduling_enabled(false),
using_synchronous_renderer_compositor(false),
per_tile_painting_enabled(false),
partial_swap_enabled(false),
diff --git a/cc/trees/layer_tree_settings.h b/cc/trees/layer_tree_settings.h
index 53b97dc..aa8cdd9 100644
--- a/cc/trees/layer_tree_settings.h
+++ b/cc/trees/layer_tree_settings.h
@@ -22,6 +22,7 @@ class CC_EXPORT LayerTreeSettings {
bool allow_antialiasing;
bool throttle_frame_production;
bool begin_frame_scheduling_enabled;
+ bool deadline_scheduling_enabled;
bool using_synchronous_renderer_compositor;
bool per_tile_painting_enabled;
bool partial_swap_enabled;
diff --git a/cc/trees/thread_proxy.cc b/cc/trees/thread_proxy.cc
index 7f590f0..7f1c833 100644
--- a/cc/trees/thread_proxy.cc
+++ b/cc/trees/thread_proxy.cc
@@ -397,14 +397,24 @@ void ThreadProxy::SetNeedsBeginFrameOnImplThread(bool enable) {
TRACE_EVENT1("cc", "ThreadProxy::SetNeedsBeginFrameOnImplThread",
"enable", enable);
layer_tree_host_impl_->SetNeedsBeginFrame(enable);
+ UpdateBackgroundAnimateTicking();
}
void ThreadProxy::BeginFrameOnImplThread(const BeginFrameArgs& args) {
DCHECK(IsImplThread());
TRACE_EVENT0("cc", "ThreadProxy::BeginFrameOnImplThread");
+
+ // Sample the frame time now. This time will be used for updating animations
+ // when we draw.
+ layer_tree_host_impl_->CurrentFrameTimeTicks();
+
scheduler_on_impl_thread_->BeginFrame(args);
}
+void ThreadProxy::DidBeginFrameDeadlineOnImplThread() {
+ layer_tree_host_impl_->ResetCurrentFrameTimeForNextFrame();
+}
+
void ThreadProxy::OnCanDrawStateChanged(bool can_draw) {
DCHECK(IsImplThread());
TRACE_EVENT1(
@@ -1035,19 +1045,15 @@ DrawSwapReadbackResult ThreadProxy::DrawSwapReadbackInternal(
if (!layer_tree_host_impl_->renderer())
return result;
+ base::TimeTicks start_time = base::TimeTicks::HighResNow();
+ base::TimeDelta draw_duration_estimate = DrawDurationEstimate();
+ base::AutoReset<bool> mark_inside(&inside_draw_, true);
+
+ // Advance our animations.
base::TimeTicks monotonic_time =
layer_tree_host_impl_->CurrentFrameTimeTicks();
base::Time wall_clock_time = layer_tree_host_impl_->CurrentFrameTime();
-
- // TODO(enne): This should probably happen post-animate.
- if (layer_tree_host_impl_->pending_tree())
- layer_tree_host_impl_->pending_tree()->UpdateDrawProperties();
layer_tree_host_impl_->Animate(monotonic_time, wall_clock_time);
- UpdateBackgroundAnimateTicking();
-
- base::TimeTicks start_time = base::TimeTicks::HighResNow();
- base::TimeDelta draw_duration_estimate = DrawDurationEstimate();
- base::AutoReset<bool> mark_inside(&inside_draw_, true);
// This method is called on a forced draw, regardless of whether we are able
// to produce a frame, as the calling site on main thread is blocked until its
@@ -1060,15 +1066,12 @@ DrawSwapReadbackResult ThreadProxy::DrawSwapReadbackInternal(
// DrawLayers() depends on the result of PrepareToDraw(), it is guarded on
// CanDraw() as well.
- // readback_request_on_impl_thread_ may be for the pending tree, do
- // not perform the readback unless explicitly requested.
bool drawing_for_readback =
readback_requested && !!readback_request_on_impl_thread_;
bool can_do_readback = layer_tree_host_impl_->renderer()->CanReadPixels();
LayerTreeHostImpl::FrameData frame;
bool draw_frame = false;
- bool start_ready_animations = true;
if (layer_tree_host_impl_->CanDraw() &&
(!drawing_for_readback || can_do_readback)) {
@@ -1077,13 +1080,9 @@ DrawSwapReadbackResult ThreadProxy::DrawSwapReadbackInternal(
if (drawing_for_readback)
readback_rect = readback_request_on_impl_thread_->rect;
- // Do not start animations if we skip drawing the frame to avoid
- // checkerboarding.
if (layer_tree_host_impl_->PrepareToDraw(&frame, readback_rect) ||
forced_draw)
draw_frame = true;
- else
- start_ready_animations = false;
}
if (draw_frame) {
@@ -1093,6 +1092,8 @@ DrawSwapReadbackResult ThreadProxy::DrawSwapReadbackInternal(
result.did_draw = true;
}
layer_tree_host_impl_->DidDrawAllLayers(frame);
+
+ bool start_ready_animations = draw_frame;
layer_tree_host_impl_->UpdateAnimationState(start_ready_animations);
// Check for a pending CompositeAndReadback.
@@ -1233,9 +1234,8 @@ DrawSwapReadbackResult ThreadProxy::ScheduledActionDrawAndReadback() {
void ThreadProxy::DidAnticipatedDrawTimeChange(base::TimeTicks time) {
if (current_resource_update_controller_on_impl_thread_)
- current_resource_update_controller_on_impl_thread_
- ->PerformMoreUpdates(time);
- layer_tree_host_impl_->ResetCurrentFrameTimeForNextFrame();
+ current_resource_update_controller_on_impl_thread_->PerformMoreUpdates(
+ time);
}
base::TimeDelta ThreadProxy::DrawDurationEstimate() {
@@ -1256,6 +1256,14 @@ base::TimeDelta ThreadProxy::CommitToActivateDurationEstimate() {
kCommitAndActivationDurationEstimationPercentile);
}
+void ThreadProxy::PostBeginFrameDeadline(const base::Closure& closure,
+ base::TimeTicks deadline) {
+ base::TimeDelta delta = deadline - base::TimeTicks::Now();
+ if (delta <= base::TimeDelta())
+ delta = base::TimeDelta();
+ Proxy::ImplThreadTaskRunner()->PostDelayedTask(FROM_HERE, closure, delta);
+}
+
void ThreadProxy::ReadyToFinalizeTextureUpdates() {
DCHECK(IsImplThread());
scheduler_on_impl_thread_->FinishCommit();
@@ -1326,6 +1334,8 @@ void ThreadProxy::InitializeImplOnImplThread(CompletionEvent* completion) {
layer_tree_host_impl_ = layer_tree_host_->CreateLayerTreeHostImpl(this);
const LayerTreeSettings& settings = layer_tree_host_->settings();
SchedulerSettings scheduler_settings;
+ scheduler_settings.deadline_scheduling_enabled =
+ settings.deadline_scheduling_enabled;
scheduler_settings.impl_side_painting = settings.impl_side_painting;
scheduler_settings.timeout_and_draw_when_animation_checkerboards =
settings.timeout_and_draw_when_animation_checkerboards;
diff --git a/cc/trees/thread_proxy.h b/cc/trees/thread_proxy.h
index 5518ede..d4e2f7c 100644
--- a/cc/trees/thread_proxy.h
+++ b/cc/trees/thread_proxy.h
@@ -70,6 +70,7 @@ class ThreadProxy : public Proxy,
virtual void DidLoseOutputSurfaceOnImplThread() OVERRIDE;
virtual void OnSwapBuffersCompleteOnImplThread() OVERRIDE;
virtual void BeginFrameOnImplThread(const BeginFrameArgs& args) OVERRIDE;
+ virtual void DidBeginFrameDeadlineOnImplThread() OVERRIDE;
virtual void OnCanDrawStateChanged(bool can_draw) OVERRIDE;
virtual void NotifyReadyToActivate() OVERRIDE;
virtual void SetNeedsRedrawOnImplThread() OVERRIDE;
@@ -108,6 +109,8 @@ class ThreadProxy : public Proxy,
virtual base::TimeDelta DrawDurationEstimate() OVERRIDE;
virtual base::TimeDelta BeginFrameToCommitDurationEstimate() OVERRIDE;
virtual base::TimeDelta CommitToActivateDurationEstimate() OVERRIDE;
+ virtual void PostBeginFrameDeadline(const base::Closure& closure,
+ base::TimeTicks deadline) OVERRIDE;
// ResourceUpdateControllerClient implementation
virtual void ReadyToFinalizeTextureUpdates() OVERRIDE;