summaryrefslogtreecommitdiffstats
path: root/chromium/cc/scheduler
diff options
context:
space:
mode:
authorJocelyn Turcotte <jocelyn.turcotte@digia.com>2014-08-08 14:30:41 +0200
committerJocelyn Turcotte <jocelyn.turcotte@digia.com>2014-08-12 13:49:54 +0200
commitab0a50979b9eb4dfa3320eff7e187e41efedf7a9 (patch)
tree498dfb8a97ff3361a9f7486863a52bb4e26bb898 /chromium/cc/scheduler
parent4ce69f7403811819800e7c5ae1318b2647e778d1 (diff)
Update Chromium to beta version 37.0.2062.68
Change-Id: I188e3b5aff1bec75566014291b654eb19f5bc8ca Reviewed-by: Andras Becsi <andras.becsi@digia.com>
Diffstat (limited to 'chromium/cc/scheduler')
-rw-r--r--chromium/cc/scheduler/delay_based_time_source.cc56
-rw-r--r--chromium/cc/scheduler/delay_based_time_source.h36
-rw-r--r--chromium/cc/scheduler/delay_based_time_source_unittest.cc50
-rw-r--r--chromium/cc/scheduler/draw_result.h21
-rw-r--r--chromium/cc/scheduler/frame_rate_controller.cc178
-rw-r--r--chromium/cc/scheduler/frame_rate_controller.h101
-rw-r--r--chromium/cc/scheduler/frame_rate_controller_unittest.cc211
-rw-r--r--chromium/cc/scheduler/rolling_time_delta_history.cc65
-rw-r--r--chromium/cc/scheduler/rolling_time_delta_history.h44
-rw-r--r--chromium/cc/scheduler/rolling_time_delta_history_unittest.cc109
-rw-r--r--chromium/cc/scheduler/scheduler.cc654
-rw-r--r--chromium/cc/scheduler/scheduler.h176
-rw-r--r--chromium/cc/scheduler/scheduler_settings.cc44
-rw-r--r--chromium/cc/scheduler/scheduler_settings.h11
-rw-r--r--chromium/cc/scheduler/scheduler_state_machine.cc653
-rw-r--r--chromium/cc/scheduler/scheduler_state_machine.h113
-rw-r--r--chromium/cc/scheduler/scheduler_state_machine_unittest.cc1387
-rw-r--r--chromium/cc/scheduler/scheduler_unittest.cc1931
-rw-r--r--chromium/cc/scheduler/texture_uploader.cc333
-rw-r--r--chromium/cc/scheduler/texture_uploader.h122
-rw-r--r--chromium/cc/scheduler/texture_uploader_unittest.cc235
-rw-r--r--chromium/cc/scheduler/time_source.h51
22 files changed, 3218 insertions, 3363 deletions
diff --git a/chromium/cc/scheduler/delay_based_time_source.cc b/chromium/cc/scheduler/delay_based_time_source.cc
index 00515b72f76..2be3d3d472a 100644
--- a/chromium/cc/scheduler/delay_based_time_source.cc
+++ b/chromium/cc/scheduler/delay_based_time_source.cc
@@ -6,6 +6,7 @@
#include <algorithm>
#include <cmath>
+#include <string>
#include "base/bind.h"
#include "base/debug/trace_event.h"
@@ -43,8 +44,10 @@ scoped_refptr<DelayBasedTimeSourceHighRes> DelayBasedTimeSourceHighRes::Create(
}
DelayBasedTimeSourceHighRes::DelayBasedTimeSourceHighRes(
- base::TimeDelta interval, base::SingleThreadTaskRunner* task_runner)
- : DelayBasedTimeSource(interval, task_runner) {}
+ base::TimeDelta interval,
+ base::SingleThreadTaskRunner* task_runner)
+ : DelayBasedTimeSource(interval, task_runner) {
+}
DelayBasedTimeSourceHighRes::~DelayBasedTimeSourceHighRes() {}
@@ -61,14 +64,17 @@ scoped_refptr<DelayBasedTimeSource> DelayBasedTimeSource::Create(
}
DelayBasedTimeSource::DelayBasedTimeSource(
- base::TimeDelta interval, base::SingleThreadTaskRunner* task_runner)
+ base::TimeDelta interval,
+ base::SingleThreadTaskRunner* task_runner)
: client_(NULL),
last_tick_time_(base::TimeTicks() - interval),
current_parameters_(interval, base::TimeTicks()),
next_parameters_(interval, base::TimeTicks()),
active_(false),
task_runner_(task_runner),
- weak_factory_(this) {}
+ weak_factory_(this) {
+ DCHECK_GT(interval.ToInternalValue(), 0);
+}
DelayBasedTimeSource::~DelayBasedTimeSource() {}
@@ -100,9 +106,11 @@ base::TimeTicks DelayBasedTimeSource::SetActive(bool active) {
bool DelayBasedTimeSource::Active() const { return active_; }
-base::TimeTicks DelayBasedTimeSource::LastTickTime() { return last_tick_time_; }
+base::TimeTicks DelayBasedTimeSource::LastTickTime() const {
+ return last_tick_time_;
+}
-base::TimeTicks DelayBasedTimeSource::NextTickTime() {
+base::TimeTicks DelayBasedTimeSource::NextTickTime() const {
return Active() ? current_parameters_.tick_target : base::TimeTicks();
}
@@ -124,6 +132,7 @@ void DelayBasedTimeSource::SetClient(TimeSourceClient* client) {
void DelayBasedTimeSource::SetTimebaseAndInterval(base::TimeTicks timebase,
base::TimeDelta interval) {
+ DCHECK_GT(interval.ToInternalValue(), 0);
next_parameters_.interval = interval;
next_parameters_.tick_target = timebase;
@@ -272,4 +281,39 @@ void DelayBasedTimeSource::PostNextTickTask(base::TimeTicks now) {
current_parameters_ = next_parameters_;
}
+std::string DelayBasedTimeSource::TypeString() const {
+ return "DelayBasedTimeSource";
+}
+
+std::string DelayBasedTimeSourceHighRes::TypeString() const {
+ return "DelayBasedTimeSourceHighRes";
+}
+
+scoped_ptr<base::Value> DelayBasedTimeSource::AsValue() const {
+ scoped_ptr<base::DictionaryValue> state(new base::DictionaryValue);
+ state->SetString("type", TypeString());
+ state->SetDouble("last_tick_time_us", LastTickTime().ToInternalValue());
+ state->SetDouble("next_tick_time_us", NextTickTime().ToInternalValue());
+
+ scoped_ptr<base::DictionaryValue> state_current_parameters(
+ new base::DictionaryValue);
+ state_current_parameters->SetDouble(
+ "interval_us", current_parameters_.interval.InMicroseconds());
+ state_current_parameters->SetDouble(
+ "tick_target_us", current_parameters_.tick_target.ToInternalValue());
+ state->Set("current_parameters", state_current_parameters.release());
+
+ scoped_ptr<base::DictionaryValue> state_next_parameters(
+ new base::DictionaryValue);
+ state_next_parameters->SetDouble("interval_us",
+ next_parameters_.interval.InMicroseconds());
+ state_next_parameters->SetDouble(
+ "tick_target_us", next_parameters_.tick_target.ToInternalValue());
+ state->Set("next_parameters", state_next_parameters.release());
+
+ state->SetBoolean("active", active_);
+
+ return state.PassAs<base::Value>();
+}
+
} // namespace cc
diff --git a/chromium/cc/scheduler/delay_based_time_source.h b/chromium/cc/scheduler/delay_based_time_source.h
index ddb89da3566..18c8002f292 100644
--- a/chromium/cc/scheduler/delay_based_time_source.h
+++ b/chromium/cc/scheduler/delay_based_time_source.h
@@ -5,44 +5,59 @@
#ifndef CC_SCHEDULER_DELAY_BASED_TIME_SOURCE_H_
#define CC_SCHEDULER_DELAY_BASED_TIME_SOURCE_H_
+#include <string>
+
#include "base/memory/weak_ptr.h"
+#include "base/values.h"
#include "cc/base/cc_export.h"
-#include "cc/scheduler/time_source.h"
namespace base { class SingleThreadTaskRunner; }
namespace cc {
+class CC_EXPORT TimeSourceClient {
+ public:
+ virtual void OnTimerTick() = 0;
+
+ protected:
+ virtual ~TimeSourceClient() {}
+};
+
// This timer implements a time source that achieves the specified interval
// in face of millisecond-precision delayed callbacks and random queueing
// delays. DelayBasedTimeSource uses base::TimeTicks::Now as its timebase.
-class CC_EXPORT DelayBasedTimeSource : public TimeSource {
+class CC_EXPORT DelayBasedTimeSource
+ : public base::RefCounted<DelayBasedTimeSource> {
public:
static scoped_refptr<DelayBasedTimeSource> Create(
base::TimeDelta interval, base::SingleThreadTaskRunner* task_runner);
- virtual void SetClient(TimeSourceClient* client) OVERRIDE;
+ virtual void SetClient(TimeSourceClient* client);
// TimeSource implementation
virtual void SetTimebaseAndInterval(base::TimeTicks timebase,
- base::TimeDelta interval) OVERRIDE;
+ base::TimeDelta interval);
- virtual base::TimeTicks SetActive(bool active) OVERRIDE;
- virtual bool Active() const OVERRIDE;
+ virtual base::TimeTicks SetActive(bool active);
+ virtual bool Active() const;
- // Get the last and next tick times. nextTimeTime() returns null when
+ // Get the last and next tick times. NextTickTime() returns null when
// inactive.
- virtual base::TimeTicks LastTickTime() OVERRIDE;
- virtual base::TimeTicks NextTickTime() OVERRIDE;
+ virtual base::TimeTicks LastTickTime() const;
+ virtual base::TimeTicks NextTickTime() const;
// Virtual for testing.
virtual base::TimeTicks Now() const;
+ virtual scoped_ptr<base::Value> AsValue() const;
+
protected:
DelayBasedTimeSource(base::TimeDelta interval,
base::SingleThreadTaskRunner* task_runner);
virtual ~DelayBasedTimeSource();
+ virtual std::string TypeString() const;
+
base::TimeTicks NextTickTarget(base::TimeTicks now);
void PostNextTickTask(base::TimeTicks now);
void OnTimerFired();
@@ -70,6 +85,7 @@ class CC_EXPORT DelayBasedTimeSource : public TimeSource {
base::WeakPtrFactory<DelayBasedTimeSource> weak_factory_;
private:
+ friend class base::RefCounted<DelayBasedTimeSource>;
DISALLOW_COPY_AND_ASSIGN(DelayBasedTimeSource);
};
@@ -86,6 +102,8 @@ class DelayBasedTimeSourceHighRes : public DelayBasedTimeSource {
base::SingleThreadTaskRunner* task_runner);
virtual ~DelayBasedTimeSourceHighRes();
+ virtual std::string TypeString() const OVERRIDE;
+
private:
DISALLOW_COPY_AND_ASSIGN(DelayBasedTimeSourceHighRes);
};
diff --git a/chromium/cc/scheduler/delay_based_time_source_unittest.cc b/chromium/cc/scheduler/delay_based_time_source_unittest.cc
index 42652e21868..0af8b02f4b9 100644
--- a/chromium/cc/scheduler/delay_based_time_source_unittest.cc
+++ b/chromium/cc/scheduler/delay_based_time_source_unittest.cc
@@ -35,7 +35,7 @@ TEST(DelayBasedTimeSourceTest, TaskPostedAndTickCalled) {
EXPECT_TRUE(client.TickCalled());
}
-TEST(DelayBasedTimeSource, TickNotCalledWithTaskPosted) {
+TEST(DelayBasedTimeSourceTest, TickNotCalledWithTaskPosted) {
scoped_refptr<base::TestSimpleTaskRunner> task_runner =
new base::TestSimpleTaskRunner;
FakeTimeSourceClient client;
@@ -49,7 +49,7 @@ TEST(DelayBasedTimeSource, TickNotCalledWithTaskPosted) {
EXPECT_FALSE(client.TickCalled());
}
-TEST(DelayBasedTimeSource, StartTwiceEnqueuesOneTask) {
+TEST(DelayBasedTimeSourceTest, StartTwiceEnqueuesOneTask) {
scoped_refptr<base::TestSimpleTaskRunner> task_runner =
new base::TestSimpleTaskRunner;
FakeTimeSourceClient client;
@@ -63,7 +63,7 @@ TEST(DelayBasedTimeSource, StartTwiceEnqueuesOneTask) {
EXPECT_FALSE(task_runner->HasPendingTask());
}
-TEST(DelayBasedTimeSource, StartWhenRunningDoesntTick) {
+TEST(DelayBasedTimeSourceTest, StartWhenRunningDoesntTick) {
scoped_refptr<base::TestSimpleTaskRunner> task_runner =
new base::TestSimpleTaskRunner;
FakeTimeSourceClient client;
@@ -80,7 +80,7 @@ TEST(DelayBasedTimeSource, StartWhenRunningDoesntTick) {
// At 60Hz, when the tick returns at exactly the requested next time, make sure
// a 16ms next delay is posted.
-TEST(DelayBasedTimeSource, NextDelaySaneWhenExactlyOnRequestedTime) {
+TEST(DelayBasedTimeSourceTest, NextDelaySaneWhenExactlyOnRequestedTime) {
scoped_refptr<base::TestSimpleTaskRunner> task_runner =
new base::TestSimpleTaskRunner;
FakeTimeSourceClient client;
@@ -101,7 +101,7 @@ TEST(DelayBasedTimeSource, NextDelaySaneWhenExactlyOnRequestedTime) {
// At 60Hz, when the tick returns at slightly after the requested next time,
// make sure a 16ms next delay is posted.
-TEST(DelayBasedTimeSource, NextDelaySaneWhenSlightlyAfterRequestedTime) {
+TEST(DelayBasedTimeSourceTest, NextDelaySaneWhenSlightlyAfterRequestedTime) {
scoped_refptr<base::TestSimpleTaskRunner> task_runner =
new base::TestSimpleTaskRunner;
FakeTimeSourceClient client;
@@ -123,7 +123,8 @@ TEST(DelayBasedTimeSource, NextDelaySaneWhenSlightlyAfterRequestedTime) {
// At 60Hz, when the tick returns at exactly 2*interval after the requested next
// time, make sure a 0ms next delay is posted.
-TEST(DelayBasedTimeSource, NextDelaySaneWhenExactlyTwiceAfterRequestedTime) {
+TEST(DelayBasedTimeSourceTest,
+ NextDelaySaneWhenExactlyTwiceAfterRequestedTime) {
scoped_refptr<base::TestSimpleTaskRunner> task_runner =
new base::TestSimpleTaskRunner;
FakeTimeSourceClient client;
@@ -144,7 +145,8 @@ TEST(DelayBasedTimeSource, NextDelaySaneWhenExactlyTwiceAfterRequestedTime) {
// At 60Hz, when the tick returns at 2*interval and a bit after the requested
// next time, make sure a 16ms next delay is posted.
-TEST(DelayBasedTimeSource, NextDelaySaneWhenSlightlyAfterTwiceRequestedTime) {
+TEST(DelayBasedTimeSourceTest,
+ NextDelaySaneWhenSlightlyAfterTwiceRequestedTime) {
scoped_refptr<base::TestSimpleTaskRunner> task_runner =
new base::TestSimpleTaskRunner;
FakeTimeSourceClient client;
@@ -166,7 +168,7 @@ TEST(DelayBasedTimeSource, NextDelaySaneWhenSlightlyAfterTwiceRequestedTime) {
// At 60Hz, when the tick returns halfway to the next frame time, make sure
// a correct next delay value is posted.
-TEST(DelayBasedTimeSource, NextDelaySaneWhenHalfAfterRequestedTime) {
+TEST(DelayBasedTimeSourceTest, NextDelaySaneWhenHalfAfterRequestedTime) {
scoped_refptr<base::TestSimpleTaskRunner> task_runner =
new base::TestSimpleTaskRunner;
FakeTimeSourceClient client;
@@ -188,7 +190,7 @@ TEST(DelayBasedTimeSource, NextDelaySaneWhenHalfAfterRequestedTime) {
// If the timebase and interval are updated with a jittery source, we want to
// make sure we do not double tick.
-TEST(DelayBasedTimeSource, SaneHandlingOfJitteryTimebase) {
+TEST(DelayBasedTimeSourceTest, SaneHandlingOfJitteryTimebase) {
scoped_refptr<base::TestSimpleTaskRunner> task_runner =
new base::TestSimpleTaskRunner;
FakeTimeSourceClient client;
@@ -219,7 +221,7 @@ TEST(DelayBasedTimeSource, SaneHandlingOfJitteryTimebase) {
EXPECT_EQ(15, task_runner->NextPendingTaskDelay().InMilliseconds());
}
-TEST(DelayBasedTimeSource, HandlesSignificantTimebaseChangesImmediately) {
+TEST(DelayBasedTimeSourceTest, HandlesSignificantTimebaseChangesImmediately) {
scoped_refptr<base::TestSimpleTaskRunner> task_runner =
new base::TestSimpleTaskRunner;
FakeTimeSourceClient client;
@@ -263,7 +265,7 @@ TEST(DelayBasedTimeSource, HandlesSignificantTimebaseChangesImmediately) {
EXPECT_EQ(16 - 7, task_runner->NextPendingTaskDelay().InMilliseconds());
}
-TEST(DelayBasedTimeSource, HanldlesSignificantIntervalChangesImmediately) {
+TEST(DelayBasedTimeSourceTest, HanldlesSignificantIntervalChangesImmediately) {
scoped_refptr<base::TestSimpleTaskRunner> task_runner =
new base::TestSimpleTaskRunner;
FakeTimeSourceClient client;
@@ -305,7 +307,7 @@ TEST(DelayBasedTimeSource, HanldlesSignificantIntervalChangesImmediately) {
EXPECT_EQ(16, task_runner->NextPendingTaskDelay().InMilliseconds());
}
-TEST(DelayBasedTimeSource, JitteryRuntimeWithFutureTimebases) {
+TEST(DelayBasedTimeSourceTest, JitteryRuntimeWithFutureTimebases) {
scoped_refptr<base::TestSimpleTaskRunner> task_runner =
new base::TestSimpleTaskRunner;
FakeTimeSourceClient client;
@@ -441,7 +443,7 @@ TEST(DelayBasedTimeSourceTest, AchievesTargetRateWithNoNoise) {
EXPECT_NEAR(1.0 / 60.0, average_interval, 0.1);
}
-TEST(DelayBasedTimeSource, TestDeactivateWhilePending) {
+TEST(DelayBasedTimeSourceTest, TestDeactivateWhilePending) {
scoped_refptr<base::TestSimpleTaskRunner> task_runner =
new base::TestSimpleTaskRunner;
FakeTimeSourceClient client;
@@ -456,7 +458,7 @@ TEST(DelayBasedTimeSource, TestDeactivateWhilePending) {
task_runner->RunPendingTasks();
}
-TEST(DelayBasedTimeSource, TestDeactivateAndReactivateBeforeNextTickTime) {
+TEST(DelayBasedTimeSourceTest, TestDeactivateAndReactivateBeforeNextTickTime) {
scoped_refptr<base::TestSimpleTaskRunner> task_runner =
new base::TestSimpleTaskRunner;
FakeTimeSourceClient client;
@@ -481,7 +483,7 @@ TEST(DelayBasedTimeSource, TestDeactivateAndReactivateBeforeNextTickTime) {
EXPECT_EQ(12, task_runner->NextPendingTaskDelay().InMilliseconds());
}
-TEST(DelayBasedTimeSource, TestDeactivateAndReactivateAfterNextTickTime) {
+TEST(DelayBasedTimeSourceTest, TestDeactivateAndReactivateAfterNextTickTime) {
scoped_refptr<base::TestSimpleTaskRunner> task_runner =
new base::TestSimpleTaskRunner;
FakeTimeSourceClient client;
@@ -506,7 +508,7 @@ TEST(DelayBasedTimeSource, TestDeactivateAndReactivateAfterNextTickTime) {
EXPECT_EQ(13, task_runner->NextPendingTaskDelay().InMilliseconds());
}
-TEST(DelayBasedTimeSource, TestOverflow) {
+TEST(DelayBasedTimeSourceTest, TestOverflow) {
// int(big_now / interval) < 0, so this causes a crash if the number of
// intervals elapsed is attempted to be stored in an int.
base::TimeDelta interval = base::TimeDelta::FromInternalValue(4000);
@@ -523,5 +525,21 @@ TEST(DelayBasedTimeSource, TestOverflow) {
EXPECT_EQ(0, task_runner->NextPendingTaskDelay().InMilliseconds());
}
+TEST(DelayBasedTimeSourceTest, TestReturnValueWhenTimerIsDeActivated) {
+ scoped_refptr<base::TestSimpleTaskRunner> task_runner =
+ new base::TestSimpleTaskRunner;
+ FakeTimeSourceClient client;
+ scoped_refptr<FakeDelayBasedTimeSource> timer =
+ FakeDelayBasedTimeSource::Create(Interval(), task_runner.get());
+ timer->SetClient(&client);
+
+ timer->SetActive(true);
+ task_runner->RunPendingTasks();
+
+ // SetActive should return empty TimeTicks when the timer is deactivated.
+ base::TimeTicks missed_tick_time = timer->SetActive(false);
+ EXPECT_TRUE(missed_tick_time.is_null());
+}
+
} // namespace
} // namespace cc
diff --git a/chromium/cc/scheduler/draw_result.h b/chromium/cc/scheduler/draw_result.h
new file mode 100644
index 00000000000..0809a413e06
--- /dev/null
+++ b/chromium/cc/scheduler/draw_result.h
@@ -0,0 +1,21 @@
+// Copyright 2014 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.
+
+#ifndef CC_SCHEDULER_DRAW_RESULT_H_
+#define CC_SCHEDULER_DRAW_RESULT_H_
+
+namespace cc {
+
+enum DrawResult {
+ INVALID_RESULT,
+ DRAW_SUCCESS,
+ DRAW_ABORTED_CHECKERBOARD_ANIMATIONS,
+ DRAW_ABORTED_MISSING_HIGH_RES_CONTENT,
+ DRAW_ABORTED_CONTEXT_LOST,
+ DRAW_ABORTED_CANT_DRAW,
+};
+
+} // namespace cc
+
+#endif // CC_SCHEDULER_DRAW_RESULT_H_
diff --git a/chromium/cc/scheduler/frame_rate_controller.cc b/chromium/cc/scheduler/frame_rate_controller.cc
deleted file mode 100644
index 243ef6b6291..00000000000
--- a/chromium/cc/scheduler/frame_rate_controller.cc
+++ /dev/null
@@ -1,178 +0,0 @@
-// 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/frame_rate_controller.h"
-
-#include "base/bind.h"
-#include "base/debug/trace_event.h"
-#include "base/location.h"
-#include "base/logging.h"
-#include "base/single_thread_task_runner.h"
-#include "cc/scheduler/delay_based_time_source.h"
-#include "cc/scheduler/time_source.h"
-#include "ui/gfx/frame_time.h"
-
-namespace cc {
-
-class FrameRateControllerTimeSourceAdapter : public TimeSourceClient {
- public:
- static scoped_ptr<FrameRateControllerTimeSourceAdapter> Create(
- FrameRateController* frame_rate_controller) {
- return make_scoped_ptr(
- new FrameRateControllerTimeSourceAdapter(frame_rate_controller));
- }
- virtual ~FrameRateControllerTimeSourceAdapter() {}
-
- virtual void OnTimerTick() OVERRIDE {
- frame_rate_controller_->OnTimerTick();
- }
-
- private:
- explicit FrameRateControllerTimeSourceAdapter(
- FrameRateController* frame_rate_controller)
- : frame_rate_controller_(frame_rate_controller) {}
-
- FrameRateController* frame_rate_controller_;
-};
-
-FrameRateController::FrameRateController(scoped_refptr<TimeSource> timer)
- : client_(NULL),
- num_frames_pending_(0),
- max_swaps_pending_(0),
- interval_(BeginFrameArgs::DefaultInterval()),
- time_source_(timer),
- active_(false),
- is_time_source_throttling_(true),
- manual_tick_pending_(false),
- task_runner_(NULL),
- weak_factory_(this) {
- time_source_client_adapter_ =
- FrameRateControllerTimeSourceAdapter::Create(this);
- time_source_->SetClient(time_source_client_adapter_.get());
-}
-
-FrameRateController::FrameRateController(
- base::SingleThreadTaskRunner* task_runner)
- : client_(NULL),
- num_frames_pending_(0),
- max_swaps_pending_(0),
- interval_(BeginFrameArgs::DefaultInterval()),
- active_(false),
- is_time_source_throttling_(false),
- manual_tick_pending_(false),
- task_runner_(task_runner),
- weak_factory_(this) {}
-
-FrameRateController::~FrameRateController() {
- if (is_time_source_throttling_)
- time_source_->SetActive(false);
-}
-
-BeginFrameArgs FrameRateController::SetActive(bool active) {
- if (active_ == active)
- return BeginFrameArgs();
- TRACE_EVENT1("cc", "FrameRateController::SetActive", "active", active);
- active_ = active;
-
- if (is_time_source_throttling_) {
- 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 + deadline_adjustment_, interval_);
- }
- } else {
- if (active) {
- PostManualTick();
- } else {
- weak_factory_.InvalidateWeakPtrs();
- manual_tick_pending_ = false;
- }
- }
-
- return BeginFrameArgs();
-}
-
-void FrameRateController::SetMaxSwapsPending(int max_swaps_pending) {
- DCHECK_GE(max_swaps_pending, 0);
- max_swaps_pending_ = max_swaps_pending;
-}
-
-void FrameRateController::SetTimebaseAndInterval(base::TimeTicks timebase,
- base::TimeDelta interval) {
- interval_ = interval;
- if (is_time_source_throttling_)
- time_source_->SetTimebaseAndInterval(timebase, interval);
-}
-
-void FrameRateController::SetDeadlineAdjustment(base::TimeDelta delta) {
- deadline_adjustment_ = delta;
-}
-
-void FrameRateController::OnTimerTick() {
- TRACE_EVENT0("cc", "FrameRateController::OnTimerTick");
- DCHECK(active_);
-
- // Check if we have too many frames in flight.
- bool throttled =
- max_swaps_pending_ && num_frames_pending_ >= max_swaps_pending_;
- TRACE_COUNTER_ID1("cc", "ThrottledCompositor", task_runner_, throttled);
-
- if (client_) {
- // TODO(brianderson): Use an adaptive parent compositor deadline.
- base::TimeTicks frame_time = LastTickTime();
- base::TimeTicks deadline = NextTickTime();
- BeginFrameArgs args = BeginFrameArgs::Create(
- frame_time, deadline + deadline_adjustment_, interval_);
- client_->FrameRateControllerTick(throttled, args);
- }
-
- if (!is_time_source_throttling_ && !throttled)
- PostManualTick();
-}
-
-void FrameRateController::PostManualTick() {
- if (active_ && !manual_tick_pending_) {
- manual_tick_pending_ = true;
- task_runner_->PostTask(FROM_HERE,
- base::Bind(&FrameRateController::ManualTick,
- weak_factory_.GetWeakPtr()));
- }
-}
-
-void FrameRateController::ManualTick() {
- manual_tick_pending_ = false;
- OnTimerTick();
-}
-
-void FrameRateController::DidSwapBuffers() {
- num_frames_pending_++;
-}
-
-void FrameRateController::DidSwapBuffersComplete() {
- DCHECK_GT(num_frames_pending_, 0);
- num_frames_pending_--;
- if (!is_time_source_throttling_)
- PostManualTick();
-}
-
-void FrameRateController::DidAbortAllPendingFrames() {
- num_frames_pending_ = 0;
-}
-
-base::TimeTicks FrameRateController::NextTickTime() {
- if (is_time_source_throttling_)
- return time_source_->NextTickTime();
-
- return base::TimeTicks();
-}
-
-base::TimeTicks FrameRateController::LastTickTime() {
- if (is_time_source_throttling_)
- return time_source_->LastTickTime();
-
- return gfx::FrameTime::Now();
-}
-
-} // namespace cc
diff --git a/chromium/cc/scheduler/frame_rate_controller.h b/chromium/cc/scheduler/frame_rate_controller.h
deleted file mode 100644
index 6d86d974966..00000000000
--- a/chromium/cc/scheduler/frame_rate_controller.h
+++ /dev/null
@@ -1,101 +0,0 @@
-// 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.
-
-#ifndef CC_SCHEDULER_FRAME_RATE_CONTROLLER_H_
-#define CC_SCHEDULER_FRAME_RATE_CONTROLLER_H_
-
-#include "base/memory/ref_counted.h"
-#include "base/memory/scoped_ptr.h"
-#include "base/memory/weak_ptr.h"
-#include "base/time/time.h"
-#include "cc/base/cc_export.h"
-#include "cc/output/begin_frame_args.h"
-
-namespace base { class SingleThreadTaskRunner; }
-
-namespace cc {
-
-class TimeSource;
-class FrameRateController;
-
-class CC_EXPORT FrameRateControllerClient {
- protected:
- virtual ~FrameRateControllerClient() {}
-
- public:
- // Throttled is true when we have a maximum number of frames pending.
- virtual void FrameRateControllerTick(bool throttled,
- const BeginFrameArgs& args) = 0;
-};
-
-class FrameRateControllerTimeSourceAdapter;
-
-// The FrameRateController is used in cases where we self-tick (i.e. BeginFrame
-// is not sent by a parent compositor.
-class CC_EXPORT FrameRateController {
- public:
- explicit FrameRateController(scoped_refptr<TimeSource> timer);
- // Alternate form of FrameRateController with unthrottled frame-rate.
- explicit FrameRateController(base::SingleThreadTaskRunner* task_runner);
- virtual ~FrameRateController();
-
- void SetClient(FrameRateControllerClient* client) { client_ = client; }
-
- // Returns a valid BeginFrame on activation to potentially be used
- // retroactively.
- BeginFrameArgs SetActive(bool active);
-
- bool IsActive() { return active_; }
-
- // Use the following methods to adjust target frame rate.
- //
- // Multiple frames can be in-progress, but for every DidSwapBuffers, a
- // DidFinishFrame should be posted.
- //
- // If the rendering pipeline crashes, call DidAbortAllPendingFrames.
- void DidSwapBuffers();
- void DidSwapBuffersComplete();
- void DidAbortAllPendingFrames();
- void SetMaxSwapsPending(int max_swaps_pending); // 0 for unlimited.
- int MaxSwapsPending() const { return max_swaps_pending_; }
- int NumSwapsPendingForTesting() const { return num_frames_pending_; }
-
- void SetTimebaseAndInterval(base::TimeTicks timebase,
- base::TimeDelta interval);
- void SetDeadlineAdjustment(base::TimeDelta delta);
-
- protected:
- friend class FrameRateControllerTimeSourceAdapter;
- void OnTimerTick();
-
- void PostManualTick();
- void ManualTick();
-
- // This returns null for unthrottled frame-rate.
- base::TimeTicks NextTickTime();
- // This returns now for unthrottled frame-rate.
- base::TimeTicks LastTickTime();
-
- FrameRateControllerClient* client_;
- int num_frames_pending_;
- int max_swaps_pending_;
- base::TimeDelta interval_;
- base::TimeDelta deadline_adjustment_;
- scoped_refptr<TimeSource> time_source_;
- scoped_ptr<FrameRateControllerTimeSourceAdapter> time_source_client_adapter_;
- bool active_;
-
- // Members for unthrottled frame-rate.
- bool is_time_source_throttling_;
- bool manual_tick_pending_;
- base::SingleThreadTaskRunner* task_runner_;
- base::WeakPtrFactory<FrameRateController> weak_factory_;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(FrameRateController);
-};
-
-} // namespace cc
-
-#endif // CC_SCHEDULER_FRAME_RATE_CONTROLLER_H_
diff --git a/chromium/cc/scheduler/frame_rate_controller_unittest.cc b/chromium/cc/scheduler/frame_rate_controller_unittest.cc
deleted file mode 100644
index e7d75802eb3..00000000000
--- a/chromium/cc/scheduler/frame_rate_controller_unittest.cc
+++ /dev/null
@@ -1,211 +0,0 @@
-// 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/frame_rate_controller.h"
-
-#include "base/test/test_simple_task_runner.h"
-#include "cc/test/scheduler_test_common.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace cc {
-namespace {
-
-class FakeFrameRateControllerClient : public FrameRateControllerClient {
- public:
- FakeFrameRateControllerClient() { Reset(); }
-
- void Reset() { frame_count_ = 0; }
- bool BeganFrame() const { return frame_count_ > 0; }
- int frame_count() const { return frame_count_; }
-
- virtual void FrameRateControllerTick(
- bool throttled, const BeginFrameArgs& args) OVERRIDE {
- frame_count_ += throttled ? 0 : 1;
- }
-
- protected:
- int frame_count_;
-};
-
-TEST(FrameRateControllerTest, TestFrameThrottling_ImmediateAck) {
- scoped_refptr<base::TestSimpleTaskRunner> task_runner =
- new base::TestSimpleTaskRunner;
- FakeFrameRateControllerClient client;
- base::TimeDelta interval = base::TimeDelta::FromMicroseconds(
- base::Time::kMicrosecondsPerSecond / 60);
- scoped_refptr<FakeDelayBasedTimeSource> time_source =
- FakeDelayBasedTimeSource::Create(interval, task_runner.get());
- FrameRateController controller(time_source);
-
- controller.SetClient(&client);
- controller.SetActive(true);
-
- base::TimeTicks elapsed; // Muck around with time a bit
-
- // Trigger one frame, make sure the BeginFrame callback is called
- elapsed += task_runner->NextPendingTaskDelay();
- time_source->SetNow(elapsed);
- task_runner->RunPendingTasks();
- EXPECT_TRUE(client.BeganFrame());
- client.Reset();
-
- // Tell the controller we drew
- controller.DidSwapBuffers();
-
- // Tell the controller the frame ended 5ms later
- time_source->SetNow(time_source->Now() +
- base::TimeDelta::FromMilliseconds(5));
- controller.DidSwapBuffersComplete();
-
- // Trigger another frame, make sure BeginFrame runs again
- elapsed += task_runner->NextPendingTaskDelay();
- // Sanity check that previous code didn't move time backward.
- EXPECT_GE(elapsed, time_source->Now());
- time_source->SetNow(elapsed);
- task_runner->RunPendingTasks();
- EXPECT_TRUE(client.BeganFrame());
-}
-
-TEST(FrameRateControllerTest, TestFrameThrottling_TwoFramesInFlight) {
- scoped_refptr<base::TestSimpleTaskRunner> task_runner =
- new base::TestSimpleTaskRunner;
- FakeFrameRateControllerClient client;
- base::TimeDelta interval = base::TimeDelta::FromMicroseconds(
- base::Time::kMicrosecondsPerSecond / 60);
- scoped_refptr<FakeDelayBasedTimeSource> time_source =
- FakeDelayBasedTimeSource::Create(interval, task_runner.get());
- FrameRateController controller(time_source);
-
- controller.SetClient(&client);
- controller.SetActive(true);
- controller.SetMaxSwapsPending(2);
-
- base::TimeTicks elapsed; // Muck around with time a bit
-
- // Trigger one frame, make sure the BeginFrame callback is called
- elapsed += task_runner->NextPendingTaskDelay();
- time_source->SetNow(elapsed);
- task_runner->RunPendingTasks();
- EXPECT_TRUE(client.BeganFrame());
- client.Reset();
-
- // Tell the controller we drew
- controller.DidSwapBuffers();
-
- // Trigger another frame, make sure BeginFrame callback runs again
- elapsed += task_runner->NextPendingTaskDelay();
- // Sanity check that previous code didn't move time backward.
- EXPECT_GE(elapsed, time_source->Now());
- time_source->SetNow(elapsed);
- task_runner->RunPendingTasks();
- EXPECT_TRUE(client.BeganFrame());
- client.Reset();
-
- // Tell the controller we drew, again.
- controller.DidSwapBuffers();
-
- // Trigger another frame. Since two frames are pending, we should not draw.
- elapsed += task_runner->NextPendingTaskDelay();
- // Sanity check that previous code didn't move time backward.
- EXPECT_GE(elapsed, time_source->Now());
- time_source->SetNow(elapsed);
- task_runner->RunPendingTasks();
- EXPECT_FALSE(client.BeganFrame());
-
- // Tell the controller the first frame ended 5ms later
- time_source->SetNow(time_source->Now() +
- base::TimeDelta::FromMilliseconds(5));
- controller.DidSwapBuffersComplete();
-
- // Tick should not have been called
- EXPECT_FALSE(client.BeganFrame());
-
- // Trigger yet another frame. Since one frames is pending, another
- // BeginFrame callback should run.
- elapsed += task_runner->NextPendingTaskDelay();
- // Sanity check that previous code didn't move time backward.
- EXPECT_GE(elapsed, time_source->Now());
- time_source->SetNow(elapsed);
- task_runner->RunPendingTasks();
- EXPECT_TRUE(client.BeganFrame());
-}
-
-TEST(FrameRateControllerTest, TestFrameThrottling_Unthrottled) {
- scoped_refptr<base::TestSimpleTaskRunner> task_runner =
- new base::TestSimpleTaskRunner;
- FakeFrameRateControllerClient client;
- FrameRateController controller(task_runner.get());
-
- controller.SetClient(&client);
- controller.SetMaxSwapsPending(2);
-
- // SetActive triggers 1st frame, make sure the BeginFrame callback
- // is called
- controller.SetActive(true);
- task_runner->RunPendingTasks();
- EXPECT_TRUE(client.BeganFrame());
- client.Reset();
-
- // Even if we don't call DidSwapBuffers, FrameRateController should
- // still attempt to tick multiple times until it does result in
- // a DidSwapBuffers.
- task_runner->RunPendingTasks();
- EXPECT_TRUE(client.BeganFrame());
- client.Reset();
-
- task_runner->RunPendingTasks();
- EXPECT_TRUE(client.BeganFrame());
- client.Reset();
-
- // DidSwapBuffers triggers 2nd frame, make sure the BeginFrame callback is
- // called
- controller.DidSwapBuffers();
- task_runner->RunPendingTasks();
- EXPECT_TRUE(client.BeganFrame());
- client.Reset();
-
- // DidSwapBuffers triggers 3rd frame (> max_frames_pending),
- // make sure the BeginFrame callback is NOT called
- controller.DidSwapBuffers();
- task_runner->RunPendingTasks();
- EXPECT_FALSE(client.BeganFrame());
- client.Reset();
-
- // Make sure there is no pending task since we can't do anything until we
- // receive a DidSwapBuffersComplete anyway.
- EXPECT_FALSE(task_runner->HasPendingTask());
-
- // DidSwapBuffersComplete triggers a frame, make sure the BeginFrame
- // callback is called
- controller.DidSwapBuffersComplete();
- task_runner->RunPendingTasks();
- EXPECT_TRUE(client.BeganFrame());
-}
-
-TEST(FrameRateControllerTest, TestFrameThrottling_NoDoubleTicking) {
- scoped_refptr<base::TestSimpleTaskRunner> task_runner =
- new base::TestSimpleTaskRunner;
- FakeFrameRateControllerClient client;
- FrameRateController controller(task_runner.get());
- controller.SetClient(&client);
-
- // SetActive triggers 1st frame and queues another tick task since the time
- // source isn't throttling.
- controller.SetActive(true);
- task_runner->RunPendingTasks();
- EXPECT_TRUE(client.BeganFrame());
- client.Reset();
- EXPECT_TRUE(task_runner->HasPendingTask());
-
- // Simulate a frame swap. This shouldn't queue a second tick task.
- controller.DidSwapBuffers();
- controller.DidSwapBuffersComplete();
-
- // The client should only be ticked once.
- task_runner->RunPendingTasks();
- EXPECT_EQ(1, client.frame_count());
-}
-
-} // namespace
-} // namespace cc
diff --git a/chromium/cc/scheduler/rolling_time_delta_history.cc b/chromium/cc/scheduler/rolling_time_delta_history.cc
deleted file mode 100644
index 8d8bba5032f..00000000000
--- a/chromium/cc/scheduler/rolling_time_delta_history.cc
+++ /dev/null
@@ -1,65 +0,0 @@
-// Copyright 2013 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 <cmath>
-
-#include "cc/scheduler/rolling_time_delta_history.h"
-
-namespace cc {
-
-RollingTimeDeltaHistory::RollingTimeDeltaHistory(size_t max_size)
- : max_size_(max_size) {
-}
-
-RollingTimeDeltaHistory::~RollingTimeDeltaHistory() {
-}
-
-void RollingTimeDeltaHistory::InsertSample(base::TimeDelta time) {
- if (max_size_ == 0)
- return;
-
- if (sample_set_.size() == max_size_) {
- sample_set_.erase(chronological_sample_deque_.front());
- chronological_sample_deque_.pop_front();
- }
-
- TimeDeltaMultiset::iterator it = sample_set_.insert(time);
- chronological_sample_deque_.push_back(it);
-}
-
-void RollingTimeDeltaHistory::Clear() {
- chronological_sample_deque_.clear();
- sample_set_.clear();
-}
-
-base::TimeDelta RollingTimeDeltaHistory::Percentile(double percent) const {
- if (sample_set_.size() == 0)
- return base::TimeDelta();
-
- double fraction = percent / 100.0;
-
- if (fraction <= 0.0)
- return *(sample_set_.begin());
-
- if (fraction >= 1.0)
- return *(sample_set_.rbegin());
-
- size_t num_smaller_samples =
- static_cast<size_t>(std::ceil(fraction * sample_set_.size())) - 1;
-
- if (num_smaller_samples > sample_set_.size() / 2) {
- size_t num_larger_samples = sample_set_.size() - num_smaller_samples - 1;
- TimeDeltaMultiset::const_reverse_iterator it = sample_set_.rbegin();
- for (size_t i = 0; i < num_larger_samples; i++)
- it++;
- return *it;
- }
-
- TimeDeltaMultiset::const_iterator it = sample_set_.begin();
- for (size_t i = 0; i < num_smaller_samples; i++)
- it++;
- return *it;
-}
-
-} // namespace cc
diff --git a/chromium/cc/scheduler/rolling_time_delta_history.h b/chromium/cc/scheduler/rolling_time_delta_history.h
deleted file mode 100644
index b79862d6dae..00000000000
--- a/chromium/cc/scheduler/rolling_time_delta_history.h
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright 2013 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.
-
-#ifndef CC_SCHEDULER_ROLLING_TIME_DELTA_HISTORY_H_
-#define CC_SCHEDULER_ROLLING_TIME_DELTA_HISTORY_H_
-
-#include <deque>
-#include <set>
-
-#include "base/time/time.h"
-#include "cc/base/cc_export.h"
-
-namespace cc {
-
-// Stores a limited number of samples. When the maximum size is reached, each
-// insertion results in the deletion of the oldest remaining sample.
-class CC_EXPORT RollingTimeDeltaHistory {
- public:
- explicit RollingTimeDeltaHistory(size_t max_size);
-
- ~RollingTimeDeltaHistory();
-
- void InsertSample(base::TimeDelta time);
-
- void Clear();
-
- // Returns the smallest sample that is greater than or equal to the specified
- // percent of samples. If there aren't any samples, returns base::TimeDelta().
- base::TimeDelta Percentile(double percent) const;
-
- private:
- typedef std::multiset<base::TimeDelta> TimeDeltaMultiset;
-
- TimeDeltaMultiset sample_set_;
- std::deque<TimeDeltaMultiset::iterator> chronological_sample_deque_;
- size_t max_size_;
-
- DISALLOW_COPY_AND_ASSIGN(RollingTimeDeltaHistory);
-};
-
-} // namespace cc
-
-#endif // CC_SCHEDULER_ROLLING_TIME_DELTA_HISTORY_H_
diff --git a/chromium/cc/scheduler/rolling_time_delta_history_unittest.cc b/chromium/cc/scheduler/rolling_time_delta_history_unittest.cc
deleted file mode 100644
index 072c98ab386..00000000000
--- a/chromium/cc/scheduler/rolling_time_delta_history_unittest.cc
+++ /dev/null
@@ -1,109 +0,0 @@
-// Copyright 2013 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/rolling_time_delta_history.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace cc {
-namespace {
-
-TEST(RollingTimeDeltaHistoryTest, EmptyHistory) {
- RollingTimeDeltaHistory empty_history(0);
-
- EXPECT_EQ(base::TimeDelta(), empty_history.Percentile(0.0));
- EXPECT_EQ(base::TimeDelta(), empty_history.Percentile(50.0));
- EXPECT_EQ(base::TimeDelta(), empty_history.Percentile(100.0));
-
- empty_history.InsertSample(base::TimeDelta::FromMilliseconds(10));
- empty_history.InsertSample(base::TimeDelta::FromMilliseconds(15));
- empty_history.InsertSample(base::TimeDelta::FromMilliseconds(20));
-
- EXPECT_EQ(base::TimeDelta(), empty_history.Percentile(0.0));
- EXPECT_EQ(base::TimeDelta(), empty_history.Percentile(50.0));
- EXPECT_EQ(base::TimeDelta(), empty_history.Percentile(100.0));
-
- empty_history.Clear();
- EXPECT_EQ(base::TimeDelta(), empty_history.Percentile(0.0));
- EXPECT_EQ(base::TimeDelta(), empty_history.Percentile(50.0));
- EXPECT_EQ(base::TimeDelta(), empty_history.Percentile(100.0));
-}
-
-TEST(RollingTimeDeltaHistoryTest, SizeOneHistory) {
- RollingTimeDeltaHistory size_one_history(1);
- base::TimeDelta sample1 = base::TimeDelta::FromMilliseconds(10);
- base::TimeDelta sample2 = base::TimeDelta::FromMilliseconds(20);
-
- EXPECT_EQ(base::TimeDelta(), size_one_history.Percentile(0.0));
- EXPECT_EQ(base::TimeDelta(), size_one_history.Percentile(50.0));
- EXPECT_EQ(base::TimeDelta(), size_one_history.Percentile(100.0));
-
- size_one_history.InsertSample(sample1);
- EXPECT_EQ(sample1, size_one_history.Percentile(0.0));
- EXPECT_EQ(sample1, size_one_history.Percentile(50.0));
- EXPECT_EQ(sample1, size_one_history.Percentile(100.0));
-
- size_one_history.InsertSample(sample2);
- EXPECT_EQ(sample2, size_one_history.Percentile(0.0));
- EXPECT_EQ(sample2, size_one_history.Percentile(50.0));
- EXPECT_EQ(sample2, size_one_history.Percentile(100.0));
-
- size_one_history.Clear();
- EXPECT_EQ(base::TimeDelta(), size_one_history.Percentile(0.0));
- EXPECT_EQ(base::TimeDelta(), size_one_history.Percentile(50.0));
- EXPECT_EQ(base::TimeDelta(), size_one_history.Percentile(100.0));
-}
-
-TEST(RollingTimeDeltaHistoryTest, LargeHistory) {
- RollingTimeDeltaHistory large_history(100);
- base::TimeDelta sample1 = base::TimeDelta::FromMilliseconds(150);
- base::TimeDelta sample2 = base::TimeDelta::FromMilliseconds(250);
- base::TimeDelta sample3 = base::TimeDelta::FromMilliseconds(200);
-
- large_history.InsertSample(sample1);
- large_history.InsertSample(sample2);
-
- EXPECT_EQ(sample1, large_history.Percentile(0.0));
- EXPECT_EQ(sample1, large_history.Percentile(25.0));
- EXPECT_EQ(sample2, large_history.Percentile(75.0));
- EXPECT_EQ(sample2, large_history.Percentile(100.0));
-
- large_history.InsertSample(sample3);
- EXPECT_EQ(sample1, large_history.Percentile(0.0));
- EXPECT_EQ(sample1, large_history.Percentile(25.0));
- EXPECT_EQ(sample3, large_history.Percentile(50.0));
- EXPECT_EQ(sample2, large_history.Percentile(100.0));
-
- // Fill the history.
- for (int i = 1; i <= 97; i++)
- large_history.InsertSample(base::TimeDelta::FromMilliseconds(i));
-
- EXPECT_EQ(base::TimeDelta::FromMilliseconds(1),
- large_history.Percentile(0.0));
- for (int i = 1; i <= 97; i++) {
- EXPECT_EQ(base::TimeDelta::FromMilliseconds(i),
- large_history.Percentile(i - 0.5));
- }
- EXPECT_EQ(sample1, large_history.Percentile(97.5));
- EXPECT_EQ(sample3, large_history.Percentile(98.5));
- EXPECT_EQ(sample2, large_history.Percentile(99.5));
-
- // Continue inserting samples, causing the oldest samples to be discarded.
- base::TimeDelta sample4 = base::TimeDelta::FromMilliseconds(100);
- base::TimeDelta sample5 = base::TimeDelta::FromMilliseconds(102);
- base::TimeDelta sample6 = base::TimeDelta::FromMilliseconds(104);
- large_history.InsertSample(sample4);
- large_history.InsertSample(sample5);
- large_history.InsertSample(sample6);
- EXPECT_EQ(sample4, large_history.Percentile(97.5));
- EXPECT_EQ(sample5, large_history.Percentile(98.5));
- EXPECT_EQ(sample6, large_history.Percentile(99.5));
-
- large_history.Clear();
- EXPECT_EQ(base::TimeDelta(), large_history.Percentile(0.0));
- EXPECT_EQ(base::TimeDelta(), large_history.Percentile(50.0));
- EXPECT_EQ(base::TimeDelta(), large_history.Percentile(100.0));
-}
-
-} // namespace
-} // namespace cc
diff --git a/chromium/cc/scheduler/scheduler.cc b/chromium/cc/scheduler/scheduler.cc
index d31f042e761..4a682cdb441 100644
--- a/chromium/cc/scheduler/scheduler.cc
+++ b/chromium/cc/scheduler/scheduler.cc
@@ -8,28 +8,141 @@
#include "base/auto_reset.h"
#include "base/debug/trace_event.h"
#include "base/logging.h"
+#include "base/single_thread_task_runner.h"
#include "cc/debug/devtools_instrumentation.h"
#include "cc/debug/traced_value.h"
+#include "cc/scheduler/delay_based_time_source.h"
#include "ui/gfx/frame_time.h"
namespace cc {
-Scheduler::Scheduler(SchedulerClient* client,
- const SchedulerSettings& scheduler_settings,
- int layer_tree_host_id)
+Scheduler::SyntheticBeginFrameSource::SyntheticBeginFrameSource(
+ Scheduler* scheduler,
+ base::SingleThreadTaskRunner* task_runner)
+ : scheduler_(scheduler) {
+ if (gfx::FrameTime::TimestampsAreHighRes()) {
+ time_source_ = DelayBasedTimeSourceHighRes::Create(
+ scheduler_->VSyncInterval(), task_runner);
+ } else {
+ time_source_ = DelayBasedTimeSource::Create(scheduler_->VSyncInterval(),
+ task_runner);
+ }
+ time_source_->SetClient(this);
+}
+
+Scheduler::SyntheticBeginFrameSource::~SyntheticBeginFrameSource() {
+}
+
+void Scheduler::SyntheticBeginFrameSource::CommitVSyncParameters(
+ base::TimeTicks timebase,
+ base::TimeDelta interval) {
+ time_source_->SetTimebaseAndInterval(timebase, interval);
+}
+
+void Scheduler::SyntheticBeginFrameSource::SetNeedsBeginFrame(
+ bool needs_begin_frame,
+ std::deque<BeginFrameArgs>* begin_retro_frame_args) {
+ DCHECK(begin_retro_frame_args);
+ base::TimeTicks missed_tick_time =
+ time_source_->SetActive(needs_begin_frame);
+ if (!missed_tick_time.is_null()) {
+ begin_retro_frame_args->push_back(
+ CreateSyntheticBeginFrameArgs(missed_tick_time));
+ }
+}
+
+bool Scheduler::SyntheticBeginFrameSource::IsActive() const {
+ return time_source_->Active();
+}
+
+void Scheduler::SyntheticBeginFrameSource::OnTimerTick() {
+ BeginFrameArgs begin_frame_args(
+ CreateSyntheticBeginFrameArgs(time_source_->LastTickTime()));
+ scheduler_->BeginFrame(begin_frame_args);
+}
+
+scoped_ptr<base::Value> Scheduler::SyntheticBeginFrameSource::AsValue() const {
+ return time_source_->AsValue();
+}
+
+BeginFrameArgs
+Scheduler::SyntheticBeginFrameSource::CreateSyntheticBeginFrameArgs(
+ base::TimeTicks frame_time) {
+ base::TimeTicks deadline = time_source_->NextTickTime();
+ return BeginFrameArgs::Create(
+ frame_time, deadline, scheduler_->VSyncInterval());
+}
+
+Scheduler::Scheduler(
+ SchedulerClient* client,
+ const SchedulerSettings& scheduler_settings,
+ int layer_tree_host_id,
+ const scoped_refptr<base::SingleThreadTaskRunner>& impl_task_runner)
: settings_(scheduler_settings),
client_(client),
layer_tree_host_id_(layer_tree_host_id),
- last_set_needs_begin_impl_frame_(false),
+ impl_task_runner_(impl_task_runner),
+ vsync_interval_(BeginFrameArgs::DefaultInterval()),
+ last_set_needs_begin_frame_(false),
+ begin_unthrottled_frame_posted_(false),
+ begin_retro_frame_posted_(false),
state_machine_(scheduler_settings),
inside_process_scheduled_actions_(false),
inside_action_(SchedulerStateMachine::ACTION_NONE),
weak_factory_(this) {
+ TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("cc.debug.scheduler"),
+ "Scheduler::Scheduler",
+ "settings",
+ ToTrace(settings_));
DCHECK(client_);
- DCHECK(!state_machine_.BeginImplFrameNeeded());
+ DCHECK(!state_machine_.BeginFrameNeeded());
+ if (settings_.main_frame_before_activation_enabled) {
+ DCHECK(settings_.main_frame_before_draw_enabled);
+ }
+
+ begin_retro_frame_closure_ =
+ base::Bind(&Scheduler::BeginRetroFrame, weak_factory_.GetWeakPtr());
+ begin_unthrottled_frame_closure_ =
+ base::Bind(&Scheduler::BeginUnthrottledFrame, weak_factory_.GetWeakPtr());
+ begin_impl_frame_deadline_closure_ = base::Bind(
+ &Scheduler::OnBeginImplFrameDeadline, weak_factory_.GetWeakPtr());
+ poll_for_draw_triggers_closure_ = base::Bind(
+ &Scheduler::PollForAnticipatedDrawTriggers, weak_factory_.GetWeakPtr());
+ advance_commit_state_closure_ = base::Bind(
+ &Scheduler::PollToAdvanceCommitState, weak_factory_.GetWeakPtr());
+
+ if (!settings_.begin_frame_scheduling_enabled) {
+ SetupSyntheticBeginFrames();
+ }
+}
+
+Scheduler::~Scheduler() {
+ if (synthetic_begin_frame_source_) {
+ synthetic_begin_frame_source_->SetNeedsBeginFrame(false,
+ &begin_retro_frame_args_);
+ }
+}
+
+void Scheduler::SetupSyntheticBeginFrames() {
+ DCHECK(!synthetic_begin_frame_source_);
+ synthetic_begin_frame_source_.reset(
+ new SyntheticBeginFrameSource(this, impl_task_runner_.get()));
}
-Scheduler::~Scheduler() {}
+void Scheduler::CommitVSyncParameters(base::TimeTicks timebase,
+ base::TimeDelta interval) {
+ // TODO(brianderson): We should not be receiving 0 intervals.
+ if (interval == base::TimeDelta())
+ interval = BeginFrameArgs::DefaultInterval();
+ vsync_interval_ = interval;
+ if (!settings_.begin_frame_scheduling_enabled)
+ synthetic_begin_frame_source_->CommitVSyncParameters(timebase, interval);
+}
+
+void Scheduler::SetEstimatedParentDrawTime(base::TimeDelta draw_time) {
+ DCHECK_GE(draw_time.ToInternalValue(), 0);
+ estimated_parent_draw_time_ = draw_time;
+}
void Scheduler::SetCanStart() {
state_machine_.SetCanStart();
@@ -51,23 +164,18 @@ void Scheduler::NotifyReadyToActivate() {
ProcessScheduledActions();
}
-void Scheduler::ActivatePendingTree() {
- client_->ScheduledActionActivatePendingTree();
-}
-
void Scheduler::SetNeedsCommit() {
state_machine_.SetNeedsCommit();
ProcessScheduledActions();
}
-void Scheduler::SetNeedsForcedCommitForReadback() {
- state_machine_.SetNeedsCommit();
- state_machine_.SetNeedsForcedCommitForReadback();
+void Scheduler::SetNeedsRedraw() {
+ state_machine_.SetNeedsRedraw();
ProcessScheduledActions();
}
-void Scheduler::SetNeedsRedraw() {
- state_machine_.SetNeedsRedraw();
+void Scheduler::SetNeedsAnimate() {
+ state_machine_.SetNeedsAnimate();
ProcessScheduledActions();
}
@@ -77,24 +185,38 @@ void Scheduler::SetNeedsManageTiles() {
ProcessScheduledActions();
}
+void Scheduler::SetMaxSwapsPending(int max) {
+ state_machine_.SetMaxSwapsPending(max);
+}
+
+void Scheduler::DidSwapBuffers() {
+ state_machine_.DidSwapBuffers();
+
+ // There is no need to call ProcessScheduledActions here because
+ // swapping should not trigger any new actions.
+ if (!inside_process_scheduled_actions_) {
+ DCHECK_EQ(state_machine_.NextAction(), SchedulerStateMachine::ACTION_NONE);
+ }
+}
+
void Scheduler::SetSwapUsedIncompleteTile(bool used_incomplete_tile) {
state_machine_.SetSwapUsedIncompleteTile(used_incomplete_tile);
ProcessScheduledActions();
}
-void Scheduler::SetSmoothnessTakesPriority(bool smoothness_takes_priority) {
- state_machine_.SetSmoothnessTakesPriority(smoothness_takes_priority);
+void Scheduler::DidSwapBuffersComplete() {
+ state_machine_.DidSwapBuffersComplete();
ProcessScheduledActions();
}
-void Scheduler::SetMainThreadNeedsLayerTextures() {
- state_machine_.SetMainThreadNeedsLayerTextures();
+void Scheduler::SetSmoothnessTakesPriority(bool smoothness_takes_priority) {
+ state_machine_.SetSmoothnessTakesPriority(smoothness_takes_priority);
ProcessScheduledActions();
}
-void Scheduler::FinishCommit() {
- TRACE_EVENT0("cc", "Scheduler::FinishCommit");
- state_machine_.FinishCommit();
+void Scheduler::NotifyReadyToCommit() {
+ TRACE_EVENT0("cc", "Scheduler::NotifyReadyToCommit");
+ state_machine_.NotifyReadyToCommit();
ProcessScheduledActions();
}
@@ -110,144 +232,311 @@ void Scheduler::DidManageTiles() {
void Scheduler::DidLoseOutputSurface() {
TRACE_EVENT0("cc", "Scheduler::DidLoseOutputSurface");
- last_set_needs_begin_impl_frame_ = false;
- begin_impl_frame_deadline_closure_.Cancel();
state_machine_.DidLoseOutputSurface();
+ last_set_needs_begin_frame_ = false;
+ if (!settings_.begin_frame_scheduling_enabled) {
+ synthetic_begin_frame_source_->SetNeedsBeginFrame(false,
+ &begin_retro_frame_args_);
+ }
+ begin_retro_frame_args_.clear();
ProcessScheduledActions();
}
void Scheduler::DidCreateAndInitializeOutputSurface() {
TRACE_EVENT0("cc", "Scheduler::DidCreateAndInitializeOutputSurface");
- DCHECK(!last_set_needs_begin_impl_frame_);
- DCHECK(begin_impl_frame_deadline_closure_.IsCancelled());
+ DCHECK(!last_set_needs_begin_frame_);
+ DCHECK(begin_impl_frame_deadline_task_.IsCancelled());
state_machine_.DidCreateAndInitializeOutputSurface();
ProcessScheduledActions();
}
-base::TimeTicks Scheduler::AnticipatedDrawTime() {
- TRACE_EVENT0("cc", "Scheduler::AnticipatedDrawTime");
+void Scheduler::NotifyBeginMainFrameStarted() {
+ TRACE_EVENT0("cc", "Scheduler::NotifyBeginMainFrameStarted");
+ state_machine_.NotifyBeginMainFrameStarted();
+}
- if (!last_set_needs_begin_impl_frame_ ||
- last_begin_impl_frame_args_.interval <= base::TimeDelta())
+base::TimeTicks Scheduler::AnticipatedDrawTime() const {
+ if (!last_set_needs_begin_frame_ ||
+ begin_impl_frame_args_.interval <= base::TimeDelta())
return base::TimeTicks();
base::TimeTicks now = gfx::FrameTime::Now();
- base::TimeTicks timebase = std::max(last_begin_impl_frame_args_.frame_time,
- last_begin_impl_frame_args_.deadline);
- int64 intervals =
- 1 + ((now - timebase) / last_begin_impl_frame_args_.interval);
- return timebase + (last_begin_impl_frame_args_.interval * intervals);
+ base::TimeTicks timebase = std::max(begin_impl_frame_args_.frame_time,
+ begin_impl_frame_args_.deadline);
+ int64 intervals = 1 + ((now - timebase) / begin_impl_frame_args_.interval);
+ return timebase + (begin_impl_frame_args_.interval * intervals);
}
base::TimeTicks Scheduler::LastBeginImplFrameTime() {
- return last_begin_impl_frame_args_.frame_time;
+ return begin_impl_frame_args_.frame_time;
}
-void Scheduler::SetupNextBeginImplFrameIfNeeded() {
- bool needs_begin_impl_frame =
- state_machine_.BeginImplFrameNeeded();
+void Scheduler::SetupNextBeginFrameIfNeeded() {
+ bool needs_begin_frame = state_machine_.BeginFrameNeeded();
+ if (settings_.throttle_frame_production) {
+ SetupNextBeginFrameWhenVSyncThrottlingEnabled(needs_begin_frame);
+ } else {
+ SetupNextBeginFrameWhenVSyncThrottlingDisabled(needs_begin_frame);
+ }
+ SetupPollingMechanisms(needs_begin_frame);
+}
+
+// When we are throttling frame production, we request BeginFrames
+// from the OutputSurface.
+void Scheduler::SetupNextBeginFrameWhenVSyncThrottlingEnabled(
+ bool needs_begin_frame) {
bool at_end_of_deadline =
state_machine_.begin_impl_frame_state() ==
SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_INSIDE_DEADLINE;
- bool should_call_set_needs_begin_impl_frame =
- // Always request the BeginImplFrame immediately if it wasn't needed
- // before.
- (needs_begin_impl_frame && !last_set_needs_begin_impl_frame_) ||
- // We always need to explicitly request our next BeginImplFrame.
- at_end_of_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_) ||
+ // Only stop requesting BeginFrames after a deadline.
+ (!needs_begin_frame && last_set_needs_begin_frame_ && at_end_of_deadline);
+
+ if (should_call_set_needs_begin_frame) {
+ if (settings_.begin_frame_scheduling_enabled) {
+ client_->SetNeedsBeginFrame(needs_begin_frame);
+ } else {
+ synthetic_begin_frame_source_->SetNeedsBeginFrame(
+ needs_begin_frame, &begin_retro_frame_args_);
+ }
+ last_set_needs_begin_frame_ = needs_begin_frame;
+ }
+
+ PostBeginRetroFrameIfNeeded();
+}
- if (should_call_set_needs_begin_impl_frame) {
- client_->SetNeedsBeginImplFrame(needs_begin_impl_frame);
- last_set_needs_begin_impl_frame_ = needs_begin_impl_frame;
+// When we aren't throttling frame production, we initiate a BeginFrame
+// as soon as one is needed.
+void Scheduler::SetupNextBeginFrameWhenVSyncThrottlingDisabled(
+ bool needs_begin_frame) {
+ last_set_needs_begin_frame_ = needs_begin_frame;
+
+ if (!needs_begin_frame || begin_unthrottled_frame_posted_)
+ return;
+
+ if (state_machine_.begin_impl_frame_state() !=
+ SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_IDLE &&
+ state_machine_.begin_impl_frame_state() !=
+ SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_INSIDE_DEADLINE) {
+ return;
}
+ begin_unthrottled_frame_posted_ = true;
+ impl_task_runner_->PostTask(FROM_HERE, begin_unthrottled_frame_closure_);
+}
+
+// BeginUnthrottledFrame is used when we aren't throttling frame production.
+// This will usually be because VSync is disabled.
+void Scheduler::BeginUnthrottledFrame() {
+ DCHECK(!settings_.throttle_frame_production);
+ DCHECK(begin_retro_frame_args_.empty());
+
+ base::TimeTicks now = gfx::FrameTime::Now();
+ base::TimeTicks deadline = now + vsync_interval_;
+
+ BeginFrameArgs begin_frame_args =
+ BeginFrameArgs::Create(now, deadline, vsync_interval_);
+ BeginImplFrame(begin_frame_args);
+
+ begin_unthrottled_frame_posted_ = false;
+}
+
+// We may need to poll when we can't rely on BeginFrame to advance certain
+// state or to avoid deadlock.
+void Scheduler::SetupPollingMechanisms(bool needs_begin_frame) {
bool needs_advance_commit_state_timer = false;
// Setup PollForAnticipatedDrawTriggers if we need to monitor state but
- // aren't expecting any more BeginImplFrames. This should only be needed by
- // the synchronous compositor when BeginImplFrameNeeded is false.
+ // aren't expecting any more BeginFrames. This should only be needed by
+ // the synchronous compositor when BeginFrameNeeded is false.
if (state_machine_.ShouldPollForAnticipatedDrawTriggers()) {
- DCHECK(!state_machine_.SupportsProactiveBeginImplFrame());
- DCHECK(!needs_begin_impl_frame);
- if (poll_for_draw_triggers_closure_.IsCancelled()) {
- poll_for_draw_triggers_closure_.Reset(
- base::Bind(&Scheduler::PollForAnticipatedDrawTriggers,
- weak_factory_.GetWeakPtr()));
- base::MessageLoop::current()->PostDelayedTask(
- FROM_HERE,
- poll_for_draw_triggers_closure_.callback(),
- last_begin_impl_frame_args_.interval);
+ DCHECK(!state_machine_.SupportsProactiveBeginFrame());
+ DCHECK(!needs_begin_frame);
+ if (poll_for_draw_triggers_task_.IsCancelled()) {
+ poll_for_draw_triggers_task_.Reset(poll_for_draw_triggers_closure_);
+ base::TimeDelta delay = begin_impl_frame_args_.IsValid()
+ ? begin_impl_frame_args_.interval
+ : BeginFrameArgs::DefaultInterval();
+ impl_task_runner_->PostDelayedTask(
+ FROM_HERE, poll_for_draw_triggers_task_.callback(), delay);
}
} else {
- poll_for_draw_triggers_closure_.Cancel();
+ poll_for_draw_triggers_task_.Cancel();
// At this point we'd prefer to advance through the commit flow by
// drawing a frame, however it's possible that the frame rate controller
- // will not give us a BeginImplFrame until the commit completes. See
+ // will not give us a BeginFrame until the commit completes. See
// crbug.com/317430 for an example of a swap ack being held on commit. Thus
// we set a repeating timer to poll on ProcessScheduledActions until we
- // successfully reach BeginImplFrame.
- if (state_machine_.IsCommitStateWaiting())
+ // successfully reach BeginFrame. Synchronous compositor does not use
+ // frame rate controller or have the circular wait in the bug.
+ if (IsBeginMainFrameSentOrStarted() &&
+ !settings_.using_synchronous_renderer_compositor) {
needs_advance_commit_state_timer = true;
+ }
}
- if (needs_advance_commit_state_timer !=
- advance_commit_state_timer_.IsRunning()) {
- if (needs_advance_commit_state_timer &&
- last_begin_impl_frame_args_.IsValid()) {
- // Since we'd rather get a BeginImplFrame by the normally mechanism, we set
- // the interval to twice the interval from the previous frame.
- advance_commit_state_timer_.Start(
- FROM_HERE,
- last_begin_impl_frame_args_.interval * 2,
- base::Bind(&Scheduler::ProcessScheduledActions,
- base::Unretained(this)));
- } else {
- advance_commit_state_timer_.Stop();
+
+ if (needs_advance_commit_state_timer) {
+ if (advance_commit_state_task_.IsCancelled() &&
+ begin_impl_frame_args_.IsValid()) {
+ // Since we'd rather get a BeginImplFrame by the normal mechanism, we
+ // set the interval to twice the interval from the previous frame.
+ advance_commit_state_task_.Reset(advance_commit_state_closure_);
+ impl_task_runner_->PostDelayedTask(FROM_HERE,
+ advance_commit_state_task_.callback(),
+ begin_impl_frame_args_.interval * 2);
}
+ } else {
+ advance_commit_state_task_.Cancel();
+ }
+}
+
+// BeginFrame is the mechanism that tells us that now is a good time to start
+// making a frame. Usually this means that user input for the frame is complete.
+// If the scheduler is busy, we queue the BeginFrame to be handled later as
+// a BeginRetroFrame.
+void Scheduler::BeginFrame(const BeginFrameArgs& args) {
+ TRACE_EVENT1("cc", "Scheduler::BeginFrame", "args", ToTrace(args));
+ DCHECK(settings_.throttle_frame_production);
+
+ BeginFrameArgs adjusted_args(args);
+ adjusted_args.deadline -= EstimatedParentDrawTime();
+
+ bool should_defer_begin_frame;
+ if (settings_.using_synchronous_renderer_compositor) {
+ should_defer_begin_frame = false;
+ } else {
+ should_defer_begin_frame =
+ !begin_retro_frame_args_.empty() || begin_retro_frame_posted_ ||
+ !last_set_needs_begin_frame_ ||
+ (state_machine_.begin_impl_frame_state() !=
+ SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_IDLE);
+ }
+
+ if (should_defer_begin_frame) {
+ begin_retro_frame_args_.push_back(adjusted_args);
+ TRACE_EVENT_INSTANT0(
+ "cc", "Scheduler::BeginFrame deferred", TRACE_EVENT_SCOPE_THREAD);
+ return;
+ }
+
+ BeginImplFrame(adjusted_args);
+}
+
+// BeginRetroFrame is called for BeginFrames that we've deferred because
+// the scheduler was in the middle of processing a previous BeginFrame.
+void Scheduler::BeginRetroFrame() {
+ TRACE_EVENT0("cc", "Scheduler::BeginRetroFrame");
+ DCHECK(!settings_.using_synchronous_renderer_compositor);
+ DCHECK(begin_retro_frame_posted_);
+ begin_retro_frame_posted_ = false;
+
+ // If there aren't any retroactive BeginFrames, then we've lost the
+ // OutputSurface and should abort.
+ if (begin_retro_frame_args_.empty())
+ return;
+
+ // Discard expired BeginRetroFrames
+ // Today, we should always end up with at most one un-expired BeginRetroFrame
+ // because deadlines will not be greater than the next frame time. We don't
+ // DCHECK though because some systems don't always have monotonic timestamps.
+ // TODO(brianderson): In the future, long deadlines could result in us not
+ // draining the queue if we don't catch up. If we consistently can't catch
+ // up, our fallback should be to lower our frame rate.
+ base::TimeTicks now = gfx::FrameTime::Now();
+ base::TimeDelta draw_duration_estimate = client_->DrawDurationEstimate();
+ while (!begin_retro_frame_args_.empty() &&
+ now > AdjustedBeginImplFrameDeadline(begin_retro_frame_args_.front(),
+ draw_duration_estimate)) {
+ TRACE_EVENT1("cc",
+ "Scheduler::BeginRetroFrame discarding",
+ "frame_time",
+ begin_retro_frame_args_.front().frame_time);
+ begin_retro_frame_args_.pop_front();
}
+
+ if (begin_retro_frame_args_.empty()) {
+ DCHECK(settings_.throttle_frame_production);
+ TRACE_EVENT_INSTANT0("cc",
+ "Scheduler::BeginRetroFrames all expired",
+ TRACE_EVENT_SCOPE_THREAD);
+ } else {
+ BeginImplFrame(begin_retro_frame_args_.front());
+ begin_retro_frame_args_.pop_front();
+ }
+}
+
+// There could be a race between the posted BeginRetroFrame and a new
+// BeginFrame arriving via the normal mechanism. Scheduler::BeginFrame
+// will check if there is a pending BeginRetroFrame to ensure we handle
+// BeginFrames in FIFO order.
+void Scheduler::PostBeginRetroFrameIfNeeded() {
+ if (!last_set_needs_begin_frame_)
+ return;
+
+ if (begin_retro_frame_args_.empty() || begin_retro_frame_posted_)
+ return;
+
+ // begin_retro_frame_args_ should always be empty for the
+ // synchronous compositor.
+ DCHECK(!settings_.using_synchronous_renderer_compositor);
+
+ if (state_machine_.begin_impl_frame_state() !=
+ SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_IDLE)
+ return;
+
+ begin_retro_frame_posted_ = true;
+ impl_task_runner_->PostTask(FROM_HERE, begin_retro_frame_closure_);
}
+// BeginImplFrame starts a compositor frame that will wait up until a deadline
+// for a BeginMainFrame+activation to complete before it times out and draws
+// any asynchronous animation and scroll/pinch updates.
void Scheduler::BeginImplFrame(const BeginFrameArgs& args) {
- TRACE_EVENT0("cc", "Scheduler::BeginImplFrame");
+ TRACE_EVENT1("cc", "Scheduler::BeginImplFrame", "args", ToTrace(args));
DCHECK(state_machine_.begin_impl_frame_state() ==
SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_IDLE);
DCHECK(state_machine_.HasInitializedOutputSurface());
- last_begin_impl_frame_args_ = args;
- last_begin_impl_frame_args_.deadline -= client_->DrawDurationEstimate();
- state_machine_.OnBeginImplFrame(last_begin_impl_frame_args_);
-
- if (settings_.switch_to_low_latency_if_possible) {
- state_machine_.SetSkipBeginMainFrameToReduceLatency(
- state_machine_.MainThreadIsInHighLatencyMode() &&
- CanCommitAndActivateBeforeDeadline());
+
+ advance_commit_state_task_.Cancel();
+
+ base::TimeDelta draw_duration_estimate = client_->DrawDurationEstimate();
+ begin_impl_frame_args_ = args;
+ begin_impl_frame_args_.deadline -= draw_duration_estimate;
+
+ if (!state_machine_.smoothness_takes_priority() &&
+ state_machine_.MainThreadIsInHighLatencyMode() &&
+ CanCommitAndActivateBeforeDeadline()) {
+ state_machine_.SetSkipNextBeginMainFrameToReduceLatency();
}
- ProcessScheduledActions();
+ client_->WillBeginImplFrame(begin_impl_frame_args_);
+ state_machine_.OnBeginImplFrame(begin_impl_frame_args_);
+ devtools_instrumentation::DidBeginFrame(layer_tree_host_id_);
- if (!state_machine_.HasInitializedOutputSurface())
- return;
+ ProcessScheduledActions();
state_machine_.OnBeginImplFrameDeadlinePending();
- devtools_instrumentation::didBeginFrame(layer_tree_host_id_);
+ ScheduleBeginImplFrameDeadline(
+ AdjustedBeginImplFrameDeadline(args, draw_duration_estimate));
+}
+
+base::TimeTicks Scheduler::AdjustedBeginImplFrameDeadline(
+ const BeginFrameArgs& args,
+ base::TimeDelta draw_duration_estimate) const {
if (settings_.using_synchronous_renderer_compositor) {
- // The synchronous renderer compositor has to make its GL calls
- // within this call to BeginImplFrame.
- // TODO(brianderson): Have the OutputSurface initiate the deadline tasks
- // so the sychronous renderer compositor can take advantage of splitting
- // up the BeginImplFrame and deadline as well.
- OnBeginImplFrameDeadline();
- } else if (!settings_.deadline_scheduling_enabled) {
- // We emulate the old non-deadline scheduler here by posting the
- // deadline task without any delay.
- PostBeginImplFrameDeadline(base::TimeTicks());
+ // The synchronous compositor needs to draw right away.
+ return base::TimeTicks();
} else if (state_machine_.ShouldTriggerBeginImplFrameDeadlineEarly()) {
// We are ready to draw a new active tree immediately.
- PostBeginImplFrameDeadline(base::TimeTicks());
+ return 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.
- PostBeginImplFrameDeadline(last_begin_impl_frame_args_.deadline);
+ return args.deadline - draw_duration_estimate;
} 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
@@ -256,60 +545,71 @@ void Scheduler::BeginImplFrame(const BeginFrameArgs& args) {
// BeginImplFrame.
// TODO(brianderson): Handle long deadlines (that are past the next frame's
// frame time) properly instead of using this hack.
- PostBeginImplFrameDeadline(last_begin_impl_frame_args_.frame_time +
- last_begin_impl_frame_args_.interval);
+ return args.frame_time + args.interval;
}
}
-void Scheduler::PostBeginImplFrameDeadline(base::TimeTicks deadline) {
- begin_impl_frame_deadline_closure_.Cancel();
- begin_impl_frame_deadline_closure_.Reset(
- base::Bind(&Scheduler::OnBeginImplFrameDeadline,
- weak_factory_.GetWeakPtr()));
- client_->PostBeginImplFrameDeadline(
- begin_impl_frame_deadline_closure_.callback(), deadline);
+void Scheduler::ScheduleBeginImplFrameDeadline(base::TimeTicks deadline) {
+ if (settings_.using_synchronous_renderer_compositor) {
+ // The synchronous renderer compositor has to make its GL calls
+ // within this call.
+ // TODO(brianderson): Have the OutputSurface initiate the deadline tasks
+ // so the sychronous renderer compositor can take advantage of splitting
+ // up the BeginImplFrame and deadline as well.
+ OnBeginImplFrameDeadline();
+ return;
+ }
+ begin_impl_frame_deadline_task_.Cancel();
+ begin_impl_frame_deadline_task_.Reset(begin_impl_frame_deadline_closure_);
+
+ base::TimeDelta delta = deadline - gfx::FrameTime::Now();
+ if (delta <= base::TimeDelta())
+ delta = base::TimeDelta();
+ impl_task_runner_->PostDelayedTask(
+ FROM_HERE, begin_impl_frame_deadline_task_.callback(), delta);
}
void Scheduler::OnBeginImplFrameDeadline() {
TRACE_EVENT0("cc", "Scheduler::OnBeginImplFrameDeadline");
- DCHECK(state_machine_.HasInitializedOutputSurface());
- begin_impl_frame_deadline_closure_.Cancel();
+ begin_impl_frame_deadline_task_.Cancel();
+
+ // We split the deadline actions up into two phases so the state machine
+ // has a chance to trigger actions that should occur durring and after
+ // the deadline separately. For example:
+ // * Sending the BeginMainFrame will not occur after the deadline in
+ // order to wait for more user-input before starting the next commit.
+ // * Creating a new OuputSurface will not occur during the deadline in
+ // order to allow the state machine to "settle" first.
state_machine_.OnBeginImplFrameDeadline();
ProcessScheduledActions();
-
- if (state_machine_.HasInitializedOutputSurface()) {
- // We only transition out of BEGIN_IMPL_FRAME_STATE_INSIDE_DEADLINE when all
- // actions that occur back-to-back in response to entering
- // BEGIN_IMPL_FRAME_STATE_INSIDE_DEADLINE have completed. This is important
- // because sending the BeginMainFrame will not occur if we transition to
- // BEGIN_IMPL_FRAME_STATE_IDLE too early.
- state_machine_.OnBeginImplFrameIdle();
- }
+ state_machine_.OnBeginImplFrameIdle();
+ ProcessScheduledActions();
client_->DidBeginImplFrameDeadline();
}
void Scheduler::PollForAnticipatedDrawTriggers() {
TRACE_EVENT0("cc", "Scheduler::PollForAnticipatedDrawTriggers");
- poll_for_draw_triggers_closure_.Cancel();
+ poll_for_draw_triggers_task_.Cancel();
state_machine_.DidEnterPollForAnticipatedDrawTriggers();
ProcessScheduledActions();
state_machine_.DidLeavePollForAnticipatedDrawTriggers();
}
-void Scheduler::DrawAndSwapIfPossible() {
- DrawSwapReadbackResult result =
- client_->ScheduledActionDrawAndSwapIfPossible();
- state_machine_.DidDrawIfPossibleCompleted(result.did_draw);
+void Scheduler::PollToAdvanceCommitState() {
+ TRACE_EVENT0("cc", "Scheduler::PollToAdvanceCommitState");
+ advance_commit_state_task_.Cancel();
+ ProcessScheduledActions();
}
-void Scheduler::DrawAndSwapForced() {
- client_->ScheduledActionDrawAndSwapForced();
+bool Scheduler::IsBeginMainFrameSent() const {
+ return state_machine_.commit_state() ==
+ SchedulerStateMachine::COMMIT_STATE_BEGIN_MAIN_FRAME_SENT;
}
-void Scheduler::DrawAndReadback() {
- DrawSwapReadbackResult result = client_->ScheduledActionDrawAndReadback();
- DCHECK(!result.did_swap);
+void Scheduler::DrawAndSwapIfPossible() {
+ DrawResult result = client_->ScheduledActionDrawAndSwapIfPossible();
+ state_machine_.DidDrawIfPossibleCompleted(result);
}
void Scheduler::ProcessScheduledActions() {
@@ -322,18 +622,20 @@ void Scheduler::ProcessScheduledActions() {
SchedulerStateMachine::Action action;
do {
- state_machine_.CheckInvariants();
action = state_machine_.NextAction();
TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("cc.debug.scheduler"),
"SchedulerStateMachine",
"state",
- TracedValue::FromValue(state_machine_.AsValue().release()));
+ ToTrace(this));
state_machine_.UpdateState(action);
base::AutoReset<SchedulerStateMachine::Action>
mark_inside_action(&inside_action_, action);
switch (action) {
case SchedulerStateMachine::ACTION_NONE:
break;
+ case SchedulerStateMachine::ACTION_ANIMATE:
+ client_->ScheduledActionAnimate();
+ break;
case SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME:
client_->ScheduledActionSendBeginMainFrame();
break;
@@ -344,53 +646,111 @@ void Scheduler::ProcessScheduledActions() {
client_->ScheduledActionUpdateVisibleTiles();
break;
case SchedulerStateMachine::ACTION_ACTIVATE_PENDING_TREE:
- ActivatePendingTree();
+ client_->ScheduledActionActivatePendingTree();
break;
case SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE:
DrawAndSwapIfPossible();
break;
case SchedulerStateMachine::ACTION_DRAW_AND_SWAP_FORCED:
- DrawAndSwapForced();
+ client_->ScheduledActionDrawAndSwapForced();
break;
case SchedulerStateMachine::ACTION_DRAW_AND_SWAP_ABORT:
// No action is actually performed, but this allows the state machine to
// advance out of its waiting to draw state without actually drawing.
break;
- case SchedulerStateMachine::ACTION_DRAW_AND_READBACK:
- DrawAndReadback();
- break;
case SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION:
client_->ScheduledActionBeginOutputSurfaceCreation();
break;
- case SchedulerStateMachine::ACTION_ACQUIRE_LAYER_TEXTURES_FOR_MAIN_THREAD:
- client_->ScheduledActionAcquireLayerTexturesForMainThread();
- break;
case SchedulerStateMachine::ACTION_MANAGE_TILES:
client_->ScheduledActionManageTiles();
break;
}
} while (action != SchedulerStateMachine::ACTION_NONE);
- SetupNextBeginImplFrameIfNeeded();
+ SetupNextBeginFrameIfNeeded();
client_->DidAnticipatedDrawTimeChange(AnticipatedDrawTime());
- if (state_machine_.ShouldTriggerBeginImplFrameDeadlineEarly())
- PostBeginImplFrameDeadline(base::TimeTicks());
+ if (state_machine_.ShouldTriggerBeginImplFrameDeadlineEarly()) {
+ DCHECK(!settings_.using_synchronous_renderer_compositor);
+ ScheduleBeginImplFrameDeadline(base::TimeTicks());
+ }
}
bool Scheduler::WillDrawIfNeeded() const {
return !state_machine_.PendingDrawsShouldBeAborted();
}
+scoped_ptr<base::Value> Scheduler::AsValue() const {
+ scoped_ptr<base::DictionaryValue> state(new base::DictionaryValue);
+ state->Set("state_machine", state_machine_.AsValue().release());
+ if (synthetic_begin_frame_source_)
+ state->Set("synthetic_begin_frame_source_",
+ synthetic_begin_frame_source_->AsValue().release());
+
+ scoped_ptr<base::DictionaryValue> scheduler_state(new base::DictionaryValue);
+ scheduler_state->SetDouble(
+ "time_until_anticipated_draw_time_ms",
+ (AnticipatedDrawTime() - base::TimeTicks::Now()).InMillisecondsF());
+ scheduler_state->SetDouble("vsync_interval_ms",
+ vsync_interval_.InMillisecondsF());
+ scheduler_state->SetDouble("estimated_parent_draw_time_ms",
+ estimated_parent_draw_time_.InMillisecondsF());
+ scheduler_state->SetBoolean("last_set_needs_begin_frame_",
+ last_set_needs_begin_frame_);
+ scheduler_state->SetBoolean("begin_unthrottled_frame_posted_",
+ begin_unthrottled_frame_posted_);
+ scheduler_state->SetBoolean("begin_retro_frame_posted_",
+ begin_retro_frame_posted_);
+ scheduler_state->SetInteger("begin_retro_frame_args_",
+ begin_retro_frame_args_.size());
+ scheduler_state->SetBoolean("begin_impl_frame_deadline_task_",
+ !begin_impl_frame_deadline_task_.IsCancelled());
+ scheduler_state->SetBoolean("poll_for_draw_triggers_task_",
+ !poll_for_draw_triggers_task_.IsCancelled());
+ scheduler_state->SetBoolean("advance_commit_state_task_",
+ !advance_commit_state_task_.IsCancelled());
+ scheduler_state->Set("begin_impl_frame_args",
+ begin_impl_frame_args_.AsValue().release());
+
+ state->Set("scheduler_state", scheduler_state.release());
+
+ scoped_ptr<base::DictionaryValue> client_state(new base::DictionaryValue);
+ client_state->SetDouble("draw_duration_estimate_ms",
+ client_->DrawDurationEstimate().InMillisecondsF());
+ client_state->SetDouble(
+ "begin_main_frame_to_commit_duration_estimate_ms",
+ client_->BeginMainFrameToCommitDurationEstimate().InMillisecondsF());
+ client_state->SetDouble(
+ "commit_to_activate_duration_estimate_ms",
+ client_->CommitToActivateDurationEstimate().InMillisecondsF());
+ state->Set("client_state", client_state.release());
+ return state.PassAs<base::Value>();
+}
+
bool Scheduler::CanCommitAndActivateBeforeDeadline() const {
// Check if the main thread computation and commit can be finished before the
// impl thread's deadline.
base::TimeTicks estimated_draw_time =
- last_begin_impl_frame_args_.frame_time +
+ begin_impl_frame_args_.frame_time +
client_->BeginMainFrameToCommitDurationEstimate() +
client_->CommitToActivateDurationEstimate();
- return estimated_draw_time < last_begin_impl_frame_args_.deadline;
+ TRACE_EVENT2(
+ TRACE_DISABLED_BY_DEFAULT("cc.debug.scheduler"),
+ "CanCommitAndActivateBeforeDeadline",
+ "time_left_after_drawing_ms",
+ (begin_impl_frame_args_.deadline - estimated_draw_time).InMillisecondsF(),
+ "state",
+ ToTrace(this));
+
+ return estimated_draw_time < begin_impl_frame_args_.deadline;
+}
+
+bool Scheduler::IsBeginMainFrameSentOrStarted() const {
+ return (state_machine_.commit_state() ==
+ SchedulerStateMachine::COMMIT_STATE_BEGIN_MAIN_FRAME_SENT ||
+ state_machine_.commit_state() ==
+ SchedulerStateMachine::COMMIT_STATE_BEGIN_MAIN_FRAME_STARTED);
}
} // namespace cc
diff --git a/chromium/cc/scheduler/scheduler.h b/chromium/cc/scheduler/scheduler.h
index 92097829390..3de06acbdc4 100644
--- a/chromium/cc/scheduler/scheduler.h
+++ b/chromium/cc/scheduler/scheduler.h
@@ -5,6 +5,7 @@
#ifndef CC_SCHEDULER_SCHEDULER_H_
#define CC_SCHEDULER_SCHEDULER_H_
+#include <deque>
#include <string>
#include "base/basictypes.h"
@@ -13,43 +14,34 @@
#include "base/time/time.h"
#include "cc/base/cc_export.h"
#include "cc/output/begin_frame_args.h"
+#include "cc/scheduler/delay_based_time_source.h"
+#include "cc/scheduler/draw_result.h"
#include "cc/scheduler/scheduler_settings.h"
#include "cc/scheduler/scheduler_state_machine.h"
-#include "cc/trees/layer_tree_host.h"
-namespace cc {
-
-class Thread;
+namespace base {
+class SingleThreadTaskRunner;
+}
-struct DrawSwapReadbackResult {
- DrawSwapReadbackResult()
- : did_draw(false), did_swap(false), did_readback(false) {}
- DrawSwapReadbackResult(bool did_draw, bool did_swap, bool did_readback)
- : did_draw(did_draw), did_swap(did_swap), did_readback(did_readback) {}
- bool did_draw;
- bool did_swap;
- bool did_readback;
-};
+namespace cc {
class SchedulerClient {
public:
- virtual void SetNeedsBeginImplFrame(bool enable) = 0;
+ virtual void SetNeedsBeginFrame(bool enable) = 0;
+ virtual void WillBeginImplFrame(const BeginFrameArgs& args) = 0;
virtual void ScheduledActionSendBeginMainFrame() = 0;
- virtual DrawSwapReadbackResult ScheduledActionDrawAndSwapIfPossible() = 0;
- virtual DrawSwapReadbackResult ScheduledActionDrawAndSwapForced() = 0;
- virtual DrawSwapReadbackResult ScheduledActionDrawAndReadback() = 0;
+ virtual DrawResult ScheduledActionDrawAndSwapIfPossible() = 0;
+ virtual DrawResult ScheduledActionDrawAndSwapForced() = 0;
+ virtual void ScheduledActionAnimate() = 0;
virtual void ScheduledActionCommit() = 0;
virtual void ScheduledActionUpdateVisibleTiles() = 0;
virtual void ScheduledActionActivatePendingTree() = 0;
virtual void ScheduledActionBeginOutputSurfaceCreation() = 0;
- virtual void ScheduledActionAcquireLayerTexturesForMainThread() = 0;
virtual void ScheduledActionManageTiles() = 0;
virtual void DidAnticipatedDrawTimeChange(base::TimeTicks time) = 0;
virtual base::TimeDelta DrawDurationEstimate() = 0;
virtual base::TimeDelta BeginMainFrameToCommitDurationEstimate() = 0;
virtual base::TimeDelta CommitToActivateDurationEstimate() = 0;
- virtual void PostBeginImplFrameDeadline(const base::Closure& closure,
- base::TimeTicks deadline) = 0;
virtual void DidBeginImplFrameDeadline() = 0;
protected:
@@ -61,13 +53,20 @@ class CC_EXPORT Scheduler {
static scoped_ptr<Scheduler> Create(
SchedulerClient* client,
const SchedulerSettings& scheduler_settings,
- int layer_tree_host_id) {
- return make_scoped_ptr(
- new Scheduler(client, scheduler_settings, layer_tree_host_id));
+ int layer_tree_host_id,
+ const scoped_refptr<base::SingleThreadTaskRunner>& impl_task_runner) {
+ return make_scoped_ptr(new Scheduler(
+ client, scheduler_settings, layer_tree_host_id, impl_task_runner));
}
virtual ~Scheduler();
+ const SchedulerSettings& settings() const { return settings_; }
+
+ void CommitVSyncParameters(base::TimeTicks timebase,
+ base::TimeDelta interval);
+ void SetEstimatedParentDrawTime(base::TimeDelta draw_time);
+
void SetCanStart();
void SetVisible(bool visible);
@@ -76,29 +75,25 @@ class CC_EXPORT Scheduler {
void SetNeedsCommit();
- // Like SetNeedsCommit(), but ensures a commit will definitely happen even if
- // we are not visible. Will eventually result in a forced draw internally.
- void SetNeedsForcedCommitForReadback();
-
void SetNeedsRedraw();
- void SetNeedsManageTiles();
+ void SetNeedsAnimate();
- void SetMainThreadNeedsLayerTextures();
+ void SetNeedsManageTiles();
+ void SetMaxSwapsPending(int max);
+ void DidSwapBuffers();
void SetSwapUsedIncompleteTile(bool used_incomplete_tile);
+ void DidSwapBuffersComplete();
void SetSmoothnessTakesPriority(bool smoothness_takes_priority);
- void FinishCommit();
+ void NotifyReadyToCommit();
void BeginMainFrameAborted(bool did_handle);
void DidManageTiles();
void DidLoseOutputSurface();
void DidCreateAndInitializeOutputSurface();
- bool HasInitializedOutputSurface() const {
- return state_machine_.HasInitializedOutputSurface();
- }
bool CommitPending() const { return state_machine_.CommitPending(); }
bool RedrawPending() const { return state_machine_.RedrawPending(); }
@@ -108,55 +103,126 @@ class CC_EXPORT Scheduler {
bool MainThreadIsInHighLatencyMode() const {
return state_machine_.MainThreadIsInHighLatencyMode();
}
+ bool BeginImplFrameDeadlinePending() const {
+ return !begin_impl_frame_deadline_task_.IsCancelled();
+ }
bool WillDrawIfNeeded() const;
- base::TimeTicks AnticipatedDrawTime();
+ base::TimeTicks AnticipatedDrawTime() const;
+
+ void NotifyBeginMainFrameStarted();
base::TimeTicks LastBeginImplFrameTime();
+ base::TimeDelta VSyncInterval() { return vsync_interval_; }
+ base::TimeDelta EstimatedParentDrawTime() {
+ return estimated_parent_draw_time_;
+ }
+
+ void BeginFrame(const BeginFrameArgs& args);
+ void PostBeginRetroFrame();
+ void BeginRetroFrame();
+ void BeginUnthrottledFrame();
void BeginImplFrame(const BeginFrameArgs& args);
void OnBeginImplFrameDeadline();
void PollForAnticipatedDrawTriggers();
+ void PollToAdvanceCommitState();
- scoped_ptr<base::Value> StateAsValue() {
- return state_machine_.AsValue().Pass();
- }
+ scoped_ptr<base::Value> AsValue() const;
bool IsInsideAction(SchedulerStateMachine::Action action) {
return inside_action_ == action;
}
- private:
- Scheduler(SchedulerClient* client,
- const SchedulerSettings& scheduler_settings,
- int layer_tree_host_id);
+ bool IsBeginMainFrameSent() const;
+ void SetContinuousPainting(bool continuous_painting) {
+ state_machine_.SetContinuousPainting(continuous_painting);
+ }
- void PostBeginImplFrameDeadline(base::TimeTicks deadline);
- void SetupNextBeginImplFrameIfNeeded();
- void ActivatePendingTree();
- void DrawAndSwapIfPossible();
- void DrawAndSwapForced();
- void DrawAndReadback();
- void ProcessScheduledActions();
+ protected:
+ class CC_EXPORT SyntheticBeginFrameSource : public TimeSourceClient {
+ public:
+ SyntheticBeginFrameSource(Scheduler* scheduler,
+ base::SingleThreadTaskRunner* task_runner);
+ virtual ~SyntheticBeginFrameSource();
- bool CanCommitAndActivateBeforeDeadline() const;
- void AdvanceCommitStateIfPossible();
+ // Updates the phase and frequency of the timer.
+ void CommitVSyncParameters(base::TimeTicks timebase,
+ base::TimeDelta interval);
+
+ // Activates future BeginFrames and, if activating, pushes the most
+ // recently missed BeginFrame to the back of a retroactive queue.
+ void SetNeedsBeginFrame(bool needs_begin_frame,
+ std::deque<BeginFrameArgs>* begin_retro_frame_args);
+
+ bool IsActive() const;
+
+ // TimeSourceClient implementation of OnTimerTick triggers a BeginFrame.
+ virtual void OnTimerTick() OVERRIDE;
+
+ scoped_ptr<base::Value> AsValue() const;
+
+ private:
+ BeginFrameArgs CreateSyntheticBeginFrameArgs(base::TimeTicks frame_time);
+
+ Scheduler* scheduler_;
+ scoped_refptr<DelayBasedTimeSource> time_source_;
+ };
+
+ Scheduler(
+ SchedulerClient* client,
+ const SchedulerSettings& scheduler_settings,
+ int layer_tree_host_id,
+ const scoped_refptr<base::SingleThreadTaskRunner>& impl_task_runner);
const SchedulerSettings settings_;
SchedulerClient* client_;
int layer_tree_host_id_;
+ scoped_refptr<base::SingleThreadTaskRunner> impl_task_runner_;
- bool last_set_needs_begin_impl_frame_;
- BeginFrameArgs last_begin_impl_frame_args_;
- base::CancelableClosure begin_impl_frame_deadline_closure_;
- base::CancelableClosure poll_for_draw_triggers_closure_;
- base::RepeatingTimer<Scheduler> advance_commit_state_timer_;
+ base::TimeDelta vsync_interval_;
+ base::TimeDelta estimated_parent_draw_time_;
+
+ bool last_set_needs_begin_frame_;
+ bool begin_unthrottled_frame_posted_;
+ bool begin_retro_frame_posted_;
+ std::deque<BeginFrameArgs> begin_retro_frame_args_;
+ BeginFrameArgs begin_impl_frame_args_;
+
+ scoped_ptr<SyntheticBeginFrameSource> synthetic_begin_frame_source_;
+
+ base::Closure begin_retro_frame_closure_;
+ base::Closure begin_unthrottled_frame_closure_;
+
+ base::Closure begin_impl_frame_deadline_closure_;
+ base::Closure poll_for_draw_triggers_closure_;
+ base::Closure advance_commit_state_closure_;
+ base::CancelableClosure begin_impl_frame_deadline_task_;
+ base::CancelableClosure poll_for_draw_triggers_task_;
+ base::CancelableClosure advance_commit_state_task_;
SchedulerStateMachine state_machine_;
bool inside_process_scheduled_actions_;
SchedulerStateMachine::Action inside_action_;
+ private:
+ base::TimeTicks AdjustedBeginImplFrameDeadline(
+ const BeginFrameArgs& args,
+ base::TimeDelta draw_duration_estimate) const;
+ void ScheduleBeginImplFrameDeadline(base::TimeTicks deadline);
+ void SetupNextBeginFrameIfNeeded();
+ void PostBeginRetroFrameIfNeeded();
+ void SetupNextBeginFrameWhenVSyncThrottlingEnabled(bool needs_begin_frame);
+ void SetupNextBeginFrameWhenVSyncThrottlingDisabled(bool needs_begin_frame);
+ void SetupPollingMechanisms(bool needs_begin_frame);
+ void DrawAndSwapIfPossible();
+ void ProcessScheduledActions();
+ bool CanCommitAndActivateBeforeDeadline() const;
+ void AdvanceCommitStateIfPossible();
+ bool IsBeginMainFrameSentOrStarted() const;
+ void SetupSyntheticBeginFrames();
+
base::WeakPtrFactory<Scheduler> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(Scheduler);
diff --git a/chromium/cc/scheduler/scheduler_settings.cc b/chromium/cc/scheduler/scheduler_settings.cc
index ad925d4d135..f1bf60e7865 100644
--- a/chromium/cc/scheduler/scheduler_settings.cc
+++ b/chromium/cc/scheduler/scheduler_settings.cc
@@ -4,17 +4,55 @@
#include "cc/scheduler/scheduler_settings.h"
+#include "cc/trees/layer_tree_settings.h"
+
namespace cc {
SchedulerSettings::SchedulerSettings()
- : deadline_scheduling_enabled(true),
+ : begin_frame_scheduling_enabled(true),
+ main_frame_before_draw_enabled(true),
+ main_frame_before_activation_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),
- throttle_frame_production(true),
- switch_to_low_latency_if_possible(false) {}
+ throttle_frame_production(true) {
+}
+
+SchedulerSettings::SchedulerSettings(const LayerTreeSettings& settings)
+ : begin_frame_scheduling_enabled(settings.begin_frame_scheduling_enabled),
+ main_frame_before_draw_enabled(settings.main_frame_before_draw_enabled),
+ main_frame_before_activation_enabled(
+ settings.main_frame_before_activation_enabled),
+ impl_side_painting(settings.impl_side_painting),
+ timeout_and_draw_when_animation_checkerboards(
+ settings.timeout_and_draw_when_animation_checkerboards),
+ maximum_number_of_failed_draws_before_draw_is_forced_(
+ settings.maximum_number_of_failed_draws_before_draw_is_forced_),
+ using_synchronous_renderer_compositor(
+ settings.using_synchronous_renderer_compositor),
+ throttle_frame_production(settings.throttle_frame_production) {
+}
SchedulerSettings::~SchedulerSettings() {}
+scoped_ptr<base::Value> SchedulerSettings::AsValue() const {
+ scoped_ptr<base::DictionaryValue> state(new base::DictionaryValue);
+ state->SetBoolean("begin_frame_scheduling_enabled",
+ begin_frame_scheduling_enabled);
+ state->SetBoolean("main_frame_before_draw_enabled",
+ main_frame_before_draw_enabled);
+ state->SetBoolean("main_frame_before_activation_enabled",
+ main_frame_before_activation_enabled);
+ state->SetBoolean("impl_side_painting", impl_side_painting);
+ state->SetBoolean("timeout_and_draw_when_animation_checkerboards",
+ timeout_and_draw_when_animation_checkerboards);
+ state->SetInteger("maximum_number_of_failed_draws_before_draw_is_forced_",
+ maximum_number_of_failed_draws_before_draw_is_forced_);
+ state->SetBoolean("using_synchronous_renderer_compositor",
+ using_synchronous_renderer_compositor);
+ state->SetBoolean("throttle_frame_production", throttle_frame_production);
+ return state.PassAs<base::Value>();
+}
+
} // namespace cc
diff --git a/chromium/cc/scheduler/scheduler_settings.h b/chromium/cc/scheduler/scheduler_settings.h
index b35cb7e6af6..c282cf6a124 100644
--- a/chromium/cc/scheduler/scheduler_settings.h
+++ b/chromium/cc/scheduler/scheduler_settings.h
@@ -5,22 +5,29 @@
#ifndef CC_SCHEDULER_SCHEDULER_SETTINGS_H_
#define CC_SCHEDULER_SCHEDULER_SETTINGS_H_
+#include "base/memory/scoped_ptr.h"
+#include "base/values.h"
#include "cc/base/cc_export.h"
namespace cc {
+class LayerTreeSettings;
class CC_EXPORT SchedulerSettings {
public:
SchedulerSettings();
+ explicit SchedulerSettings(const LayerTreeSettings& settings);
~SchedulerSettings();
- bool deadline_scheduling_enabled;
+ bool begin_frame_scheduling_enabled;
+ bool main_frame_before_draw_enabled;
+ bool main_frame_before_activation_enabled;
bool impl_side_painting;
bool timeout_and_draw_when_animation_checkerboards;
int maximum_number_of_failed_draws_before_draw_is_forced_;
bool using_synchronous_renderer_compositor;
bool throttle_frame_production;
- bool switch_to_low_latency_if_possible;
+
+ scoped_ptr<base::Value> AsValue() const;
};
} // namespace cc
diff --git a/chromium/cc/scheduler/scheduler_state_machine.cc b/chromium/cc/scheduler/scheduler_state_machine.cc
index 7d8fcd721a3..dea25b40871 100644
--- a/chromium/cc/scheduler/scheduler_state_machine.cc
+++ b/chromium/cc/scheduler/scheduler_state_machine.cc
@@ -18,21 +18,23 @@ SchedulerStateMachine::SchedulerStateMachine(const SchedulerSettings& settings)
output_surface_state_(OUTPUT_SURFACE_LOST),
begin_impl_frame_state_(BEGIN_IMPL_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_animate_performed_(-1),
last_frame_number_swap_performed_(-1),
+ last_frame_number_swap_requested_(-1),
last_frame_number_begin_main_frame_sent_(-1),
last_frame_number_update_visible_tiles_was_called_(-1),
- last_frame_number_manage_tiles_called_(-1),
- consecutive_failed_draws_(0),
+ manage_tiles_funnel_(0),
+ consecutive_checkerboard_animations_(0),
+ max_pending_swaps_(1),
+ pending_swaps_(0),
needs_redraw_(false),
+ needs_animate_(false),
needs_manage_tiles_(false),
swap_used_incomplete_tile_(false),
needs_commit_(false),
- main_thread_needs_layer_textures_(false),
inside_poll_for_anticipated_draw_triggers_(false),
visible_(false),
can_start_(false),
@@ -40,10 +42,12 @@ SchedulerStateMachine::SchedulerStateMachine(const SchedulerSettings& settings)
has_pending_tree_(false),
pending_tree_is_ready_for_activation_(false),
active_tree_needs_first_draw_(false),
- draw_if_possible_failed_(false),
did_create_and_initialize_first_output_surface_(false),
smoothness_takes_priority_(false),
- skip_begin_main_frame_to_reduce_latency_(false) {}
+ skip_next_begin_main_frame_to_reduce_latency_(false),
+ skip_begin_main_frame_to_reduce_latency_(false),
+ continuous_painting_(false) {
+}
const char* SchedulerStateMachine::OutputSurfaceStateToString(
OutputSurfaceState state) {
@@ -83,10 +87,14 @@ const char* SchedulerStateMachine::CommitStateToString(CommitState state) {
switch (state) {
case COMMIT_STATE_IDLE:
return "COMMIT_STATE_IDLE";
- case COMMIT_STATE_FRAME_IN_PROGRESS:
- return "COMMIT_STATE_FRAME_IN_PROGRESS";
+ case COMMIT_STATE_BEGIN_MAIN_FRAME_SENT:
+ return "COMMIT_STATE_BEGIN_MAIN_FRAME_SENT";
+ case COMMIT_STATE_BEGIN_MAIN_FRAME_STARTED:
+ return "COMMIT_STATE_BEGIN_MAIN_FRAME_STARTED";
case COMMIT_STATE_READY_TO_COMMIT:
return "COMMIT_STATE_READY_TO_COMMIT";
+ case COMMIT_STATE_WAITING_FOR_ACTIVATION:
+ return "COMMIT_STATE_WAITING_FOR_ACTIVATION";
case COMMIT_STATE_WAITING_FOR_FIRST_DRAW:
return "COMMIT_STATE_WAITING_FOR_FIRST_DRAW";
}
@@ -94,41 +102,6 @@ const char* SchedulerStateMachine::CommitStateToString(CommitState state) {
return "???";
}
-const char* SchedulerStateMachine::TextureStateToString(TextureState state) {
- switch (state) {
- case LAYER_TEXTURE_STATE_UNLOCKED:
- return "LAYER_TEXTURE_STATE_UNLOCKED";
- case LAYER_TEXTURE_STATE_ACQUIRED_BY_MAIN_THREAD:
- return "LAYER_TEXTURE_STATE_ACQUIRED_BY_MAIN_THREAD";
- case LAYER_TEXTURE_STATE_ACQUIRED_BY_IMPL_THREAD:
- return "LAYER_TEXTURE_STATE_ACQUIRED_BY_IMPL_THREAD";
- }
- NOTREACHED();
- return "???";
-}
-
-const char* SchedulerStateMachine::SynchronousReadbackStateToString(
- SynchronousReadbackState state) {
- switch (state) {
- case READBACK_STATE_IDLE:
- return "READBACK_STATE_IDLE";
- case READBACK_STATE_NEEDS_BEGIN_MAIN_FRAME:
- return "READBACK_STATE_NEEDS_BEGIN_MAIN_FRAME";
- case READBACK_STATE_WAITING_FOR_COMMIT:
- return "READBACK_STATE_WAITING_FOR_COMMIT";
- case READBACK_STATE_WAITING_FOR_ACTIVATION:
- return "READBACK_STATE_WAITING_FOR_ACTIVATION";
- case READBACK_STATE_WAITING_FOR_DRAW_AND_READBACK:
- return "READBACK_STATE_WAITING_FOR_DRAW_AND_READBACK";
- case READBACK_STATE_WAITING_FOR_REPLACEMENT_COMMIT:
- return "READBACK_STATE_WAITING_FOR_REPLACEMENT_COMMIT";
- case READBACK_STATE_WAITING_FOR_REPLACEMENT_ACTIVATION:
- return "READBACK_STATE_WAITING_FOR_REPLACEMENT_ACTIVATION";
- }
- NOTREACHED();
- return "???";
-}
-
const char* SchedulerStateMachine::ForcedRedrawOnTimeoutStateToString(
ForcedRedrawOnTimeoutState state) {
switch (state) {
@@ -149,6 +122,8 @@ const char* SchedulerStateMachine::ActionToString(Action action) {
switch (action) {
case ACTION_NONE:
return "ACTION_NONE";
+ case ACTION_ANIMATE:
+ return "ACTION_ANIMATE";
case ACTION_SEND_BEGIN_MAIN_FRAME:
return "ACTION_SEND_BEGIN_MAIN_FRAME";
case ACTION_COMMIT:
@@ -163,12 +138,8 @@ const char* SchedulerStateMachine::ActionToString(Action action) {
return "ACTION_DRAW_AND_SWAP_FORCED";
case ACTION_DRAW_AND_SWAP_ABORT:
return "ACTION_DRAW_AND_SWAP_ABORT";
- case ACTION_DRAW_AND_READBACK:
- return "ACTION_DRAW_AND_READBACK";
case ACTION_BEGIN_OUTPUT_SURFACE_CREATION:
return "ACTION_BEGIN_OUTPUT_SURFACE_CREATION";
- case ACTION_ACQUIRE_LAYER_TEXTURES_FOR_MAIN_THREAD:
- return "ACTION_ACQUIRE_LAYER_TEXTURES_FOR_MAIN_THREAD";
case ACTION_MANAGE_TILES:
return "ACTION_MANAGE_TILES";
}
@@ -184,45 +155,37 @@ scoped_ptr<base::Value> SchedulerStateMachine::AsValue() const {
major_state->SetString("begin_impl_frame_state",
BeginImplFrameStateToString(begin_impl_frame_state_));
major_state->SetString("commit_state", CommitStateToString(commit_state_));
- major_state->SetString("texture_state_",
- TextureStateToString(texture_state_));
major_state->SetString("output_surface_state_",
OutputSurfaceStateToString(output_surface_state_));
major_state->SetString(
"forced_redraw_state",
ForcedRedrawOnTimeoutStateToString(forced_redraw_state_));
- major_state->SetString("readback_state",
- SynchronousReadbackStateToString(readback_state_));
state->Set("major_state", major_state.release());
scoped_ptr<base::DictionaryValue> timestamps_state(new base::DictionaryValue);
base::TimeTicks now = gfx::FrameTime::Now();
timestamps_state->SetDouble(
- "0_interval",
- last_begin_impl_frame_args_.interval.InMicroseconds() / 1000.0L);
+ "0_interval", begin_impl_frame_args_.interval.InMicroseconds() / 1000.0L);
timestamps_state->SetDouble(
"1_now_to_deadline",
- (last_begin_impl_frame_args_.deadline - now).InMicroseconds() / 1000.0L);
+ (begin_impl_frame_args_.deadline - now).InMicroseconds() / 1000.0L);
timestamps_state->SetDouble(
"2_frame_time_to_now",
- (now - last_begin_impl_frame_args_.frame_time).InMicroseconds() /
- 1000.0L);
+ (now - begin_impl_frame_args_.frame_time).InMicroseconds() / 1000.0L);
timestamps_state->SetDouble(
"3_frame_time_to_deadline",
- (last_begin_impl_frame_args_.deadline -
- last_begin_impl_frame_args_.frame_time).InMicroseconds() /
+ (begin_impl_frame_args_.deadline - begin_impl_frame_args_.frame_time)
+ .InMicroseconds() /
1000.0L);
timestamps_state->SetDouble(
"4_now", (now - base::TimeTicks()).InMicroseconds() / 1000.0L);
timestamps_state->SetDouble(
"5_frame_time",
- (last_begin_impl_frame_args_.frame_time - base::TimeTicks())
- .InMicroseconds() /
+ (begin_impl_frame_args_.frame_time - base::TimeTicks()).InMicroseconds() /
1000.0L);
timestamps_state->SetDouble(
"6_deadline",
- (last_begin_impl_frame_args_.deadline - base::TimeTicks())
- .InMicroseconds() /
+ (begin_impl_frame_args_.deadline - base::TimeTicks()).InMicroseconds() /
1000.0L);
state->Set("major_timestamps_in_ms", timestamps_state.release());
@@ -230,8 +193,12 @@ scoped_ptr<base::Value> SchedulerStateMachine::AsValue() const {
minor_state->SetInteger("commit_count", commit_count_);
minor_state->SetInteger("current_frame_number", current_frame_number_);
+ minor_state->SetInteger("last_frame_number_animate_performed",
+ last_frame_number_animate_performed_);
minor_state->SetInteger("last_frame_number_swap_performed",
last_frame_number_swap_performed_);
+ minor_state->SetInteger("last_frame_number_swap_requested",
+ last_frame_number_swap_requested_);
minor_state->SetInteger(
"last_frame_number_begin_main_frame_sent",
last_frame_number_begin_main_frame_sent_);
@@ -239,15 +206,17 @@ scoped_ptr<base::Value> SchedulerStateMachine::AsValue() const {
"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->SetInteger("manage_tiles_funnel", manage_tiles_funnel_);
+ minor_state->SetInteger("consecutive_checkerboard_animations",
+ consecutive_checkerboard_animations_);
+ minor_state->SetInteger("max_pending_swaps_", max_pending_swaps_);
+ minor_state->SetInteger("pending_swaps_", pending_swaps_);
minor_state->SetBoolean("needs_redraw", needs_redraw_);
+ minor_state->SetBoolean("needs_animate_", needs_animate_);
minor_state->SetBoolean("needs_manage_tiles", needs_manage_tiles_);
minor_state->SetBoolean("swap_used_incomplete_tile",
swap_used_incomplete_tile_);
minor_state->SetBoolean("needs_commit", needs_commit_);
- minor_state->SetBoolean("main_thread_needs_layer_textures",
- main_thread_needs_layer_textures_);
minor_state->SetBoolean("visible", visible_);
minor_state->SetBoolean("can_start", can_start_);
minor_state->SetBoolean("can_draw", can_draw_);
@@ -256,7 +225,6 @@ scoped_ptr<base::Value> SchedulerStateMachine::AsValue() const {
pending_tree_is_ready_for_activation_);
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",
did_create_and_initialize_first_output_surface_);
minor_state->SetBoolean("smoothness_takes_priority",
@@ -265,11 +233,26 @@ scoped_ptr<base::Value> SchedulerStateMachine::AsValue() const {
MainThreadIsInHighLatencyMode());
minor_state->SetBoolean("skip_begin_main_frame_to_reduce_latency",
skip_begin_main_frame_to_reduce_latency_);
+ minor_state->SetBoolean("skip_next_begin_main_frame_to_reduce_latency",
+ skip_next_begin_main_frame_to_reduce_latency_);
+ minor_state->SetBoolean("continuous_painting", continuous_painting_);
state->Set("minor_state", minor_state.release());
return state.PassAs<base::Value>();
}
+void SchedulerStateMachine::AdvanceCurrentFrameNumber() {
+ current_frame_number_++;
+
+ // "Drain" the ManageTiles funnel.
+ if (manage_tiles_funnel_ > 0)
+ manage_tiles_funnel_--;
+
+ skip_begin_main_frame_to_reduce_latency_ =
+ skip_next_begin_main_frame_to_reduce_latency_;
+ skip_next_begin_main_frame_to_reduce_latency_ = false;
+}
+
bool SchedulerStateMachine::HasSentBeginMainFrameThisFrame() const {
return current_frame_number_ ==
last_frame_number_begin_main_frame_sent_;
@@ -284,6 +267,10 @@ bool SchedulerStateMachine::HasSwappedThisFrame() const {
return current_frame_number_ == last_frame_number_swap_performed_;
}
+bool SchedulerStateMachine::HasRequestedSwapThisFrame() const {
+ return current_frame_number_ == last_frame_number_swap_requested_;
+}
+
bool SchedulerStateMachine::PendingDrawsShouldBeAborted() const {
// These are all the cases where we normally cannot or do not want to draw
// but, if needs_redraw_ is true and we do not draw to make forward progress,
@@ -308,11 +295,6 @@ bool SchedulerStateMachine::PendingActivationsShouldBeForced() const {
// These are all the cases where, if we do not force activations to make
// forward progress, we might deadlock with the main thread.
- // The impl thread cannot lock layer textures unless the pending
- // tree can be activated to unblock the commit.
- if (texture_state_ == LAYER_TEXTURE_STATE_ACQUIRED_BY_MAIN_THREAD)
- return true;
-
// There is no output surface to trigger our activations.
if (output_surface_state_ == OUTPUT_SURFACE_LOST)
return true;
@@ -330,11 +312,15 @@ bool SchedulerStateMachine::ShouldBeginOutputSurfaceCreation() const {
if (commit_state_ != COMMIT_STATE_IDLE)
return false;
+ // Make sure the BeginImplFrame from any previous OutputSurfaces
+ // are complete before creating the new OutputSurface.
+ if (begin_impl_frame_state_ != BEGIN_IMPL_FRAME_STATE_IDLE)
+ return false;
+
// We want to clear the pipline of any pending draws and activations
// before starting output surface initialization. This allows us to avoid
// weird corner cases where we abort draws or force activation while we
- // are initializing the output surface and can potentially have a pending
- // readback.
+ // are initializing the output surface.
if (active_tree_needs_first_draw_ || has_pending_tree_)
return false;
@@ -344,18 +330,6 @@ bool SchedulerStateMachine::ShouldBeginOutputSurfaceCreation() const {
}
bool SchedulerStateMachine::ShouldDraw() const {
- // After a readback, make sure not to draw again until we've replaced the
- // readback commit with a real one.
- if (readback_state_ == READBACK_STATE_WAITING_FOR_REPLACEMENT_COMMIT ||
- readback_state_ == READBACK_STATE_WAITING_FOR_REPLACEMENT_ACTIVATION)
- return false;
-
- // Draw immediately for readbacks to unblock the main thread quickly.
- if (readback_state_ == READBACK_STATE_WAITING_FOR_DRAW_AND_READBACK) {
- DCHECK_EQ(commit_state_, COMMIT_STATE_WAITING_FOR_FIRST_DRAW);
- return true;
- }
-
// If we need to abort draws, we should do so ASAP since the draw could
// be blocking other important actions (like output surface initialization),
// from occuring. If we are waiting for the first draw, then perfom the
@@ -364,8 +338,12 @@ bool SchedulerStateMachine::ShouldDraw() const {
if (PendingDrawsShouldBeAborted())
return active_tree_needs_first_draw_;
- // After this line, we only want to swap once per frame.
- if (HasSwappedThisFrame())
+ // After this line, we only want to send a swap request once per frame.
+ if (HasRequestedSwapThisFrame())
+ return false;
+
+ // Do not queue too many swaps.
+ if (pending_swaps_ >= max_pending_swaps_)
return false;
// Except for the cases above, do not draw outside of the BeginImplFrame
@@ -374,23 +352,12 @@ bool SchedulerStateMachine::ShouldDraw() const {
return false;
// Only handle forced redraws due to timeouts on the regular deadline.
- if (forced_redraw_state_ == FORCED_REDRAW_STATE_WAITING_FOR_DRAW) {
- DCHECK_EQ(commit_state_, COMMIT_STATE_WAITING_FOR_FIRST_DRAW);
+ if (forced_redraw_state_ == FORCED_REDRAW_STATE_WAITING_FOR_DRAW)
return true;
- }
return needs_redraw_;
}
-bool SchedulerStateMachine::ShouldAcquireLayerTexturesForMainThread() const {
- if (!main_thread_needs_layer_textures_)
- return false;
- if (texture_state_ == LAYER_TEXTURE_STATE_UNLOCKED)
- return true;
- DCHECK_EQ(texture_state_, LAYER_TEXTURE_STATE_ACQUIRED_BY_IMPL_THREAD);
- return false;
-}
-
bool SchedulerStateMachine::ShouldActivatePendingTree() const {
// There is nothing to activate.
if (!has_pending_tree_)
@@ -416,6 +383,10 @@ bool SchedulerStateMachine::ShouldUpdateVisibleTiles() const {
if (HasUpdatedVisibleTilesThisFrame())
return false;
+ // We don't want to update visible tiles right after drawing.
+ if (HasRequestedSwapThisFrame())
+ return false;
+
// There's no reason to check for tiles if we don't have an output surface.
if (!HasInitializedOutputSurface())
return false;
@@ -434,6 +405,20 @@ bool SchedulerStateMachine::ShouldUpdateVisibleTiles() const {
return false;
}
+bool SchedulerStateMachine::ShouldAnimate() const {
+ if (!can_draw_)
+ return false;
+
+ if (last_frame_number_animate_performed_ == current_frame_number_)
+ return false;
+
+ if (begin_impl_frame_state_ != BEGIN_IMPL_FRAME_STATE_BEGIN_FRAME_STARTING &&
+ begin_impl_frame_state_ != BEGIN_IMPL_FRAME_STATE_INSIDE_DEADLINE)
+ return false;
+
+ return needs_redraw_ || needs_animate_;
+}
+
bool SchedulerStateMachine::ShouldSendBeginMainFrame() const {
if (!needs_commit_)
return false;
@@ -442,19 +427,14 @@ bool SchedulerStateMachine::ShouldSendBeginMainFrame() const {
if (commit_state_ != COMMIT_STATE_IDLE)
return false;
- // We can't accept a commit if we have a pending tree.
- if (has_pending_tree_)
+ // Don't send BeginMainFrame early if we are prioritizing the active tree
+ // because of smoothness_takes_priority.
+ if (smoothness_takes_priority_ &&
+ (has_pending_tree_ || active_tree_needs_first_draw_)) {
return false;
+ }
- // We want to handle readback commits immediately to unblock the main thread.
- // Note: This BeginMainFrame will correspond to the replacement commit that
- // comes after the readback commit itself, so we only send the BeginMainFrame
- // if a commit isn't already pending behind the readback.
- if (readback_state_ == READBACK_STATE_NEEDS_BEGIN_MAIN_FRAME)
- return !CommitPending();
-
- // We do not need commits if we are not visible, unless there's a
- // request for a readback.
+ // We do not need commits if we are not visible.
if (!visible_)
return false;
@@ -462,20 +442,13 @@ bool SchedulerStateMachine::ShouldSendBeginMainFrame() const {
if (output_surface_state_ == OUTPUT_SURFACE_WAITING_FOR_FIRST_COMMIT)
return true;
- // With deadline scheduling enabled, we should not send BeginMainFrame while
- // we are in BEGIN_IMPL_FRAME_STATE_IDLE, since we might have new user input
- // coming in soon.
- // However, if we are not expecting a BeginImplFrame 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 BeginImplFrame.
+ // We should not send BeginMainFrame while we are in
+ // BEGIN_IMPL_FRAME_STATE_IDLE since we might have new
+ // user input arriving soon.
// TODO(brianderson): Allow sending BeginMainFrame while idle when the main
// thread isn't consuming user input.
- if (settings_.deadline_scheduling_enabled &&
- begin_impl_frame_state_ == BEGIN_IMPL_FRAME_STATE_IDLE &&
- BeginImplFrameNeeded())
+ if (begin_impl_frame_state_ == BEGIN_IMPL_FRAME_STATE_IDLE &&
+ BeginFrameNeeded())
return false;
// We need a new commit for the forced redraw. This honors the
@@ -491,6 +464,14 @@ bool SchedulerStateMachine::ShouldSendBeginMainFrame() const {
if (!HasInitializedOutputSurface())
return false;
+ // SwapAck throttle the BeginMainFrames unless we just swapped.
+ // TODO(brianderson): Remove this restriction to improve throughput.
+ bool just_swapped_in_deadline =
+ begin_impl_frame_state_ == BEGIN_IMPL_FRAME_STATE_INSIDE_DEADLINE &&
+ HasSwappedThisFrame();
+ if (pending_swaps_ >= max_pending_swaps_ && !just_swapped_in_deadline)
+ return false;
+
if (skip_begin_main_frame_to_reduce_latency_)
return false;
@@ -498,18 +479,27 @@ bool SchedulerStateMachine::ShouldSendBeginMainFrame() const {
}
bool SchedulerStateMachine::ShouldCommit() const {
- return commit_state_ == COMMIT_STATE_READY_TO_COMMIT;
-}
+ if (commit_state_ != COMMIT_STATE_READY_TO_COMMIT)
+ return false;
-bool SchedulerStateMachine::IsCommitStateWaiting() const {
- return commit_state_ == COMMIT_STATE_FRAME_IN_PROGRESS;
+ // We must not finish the commit until the pending tree is free.
+ if (has_pending_tree_) {
+ DCHECK(settings_.main_frame_before_activation_enabled);
+ return false;
+ }
+
+ // Prioritize drawing the previous commit before finishing the next commit.
+ if (active_tree_needs_first_draw_)
+ return false;
+
+ return true;
}
bool SchedulerStateMachine::ShouldManageTiles() const {
// ManageTiles only really needs to be called immediately after commit
- // and then periodically after that. Limiting to once per frame prevents
- // post-commit and post-draw ManageTiles on the same frame.
- if (last_frame_number_manage_tiles_called_ == current_frame_number_)
+ // and then periodically after that. Use a funnel to make sure we average
+ // one ManageTiles per BeginImplFrame in the long run.
+ if (manage_tiles_funnel_ > 0)
return false;
// Limiting to once per-frame is not enough, since we only want to
@@ -522,18 +512,16 @@ bool SchedulerStateMachine::ShouldManageTiles() const {
}
SchedulerStateMachine::Action SchedulerStateMachine::NextAction() const {
- if (ShouldAcquireLayerTexturesForMainThread())
- return ACTION_ACQUIRE_LAYER_TEXTURES_FOR_MAIN_THREAD;
if (ShouldUpdateVisibleTiles())
return ACTION_UPDATE_VISIBLE_TILES;
if (ShouldActivatePendingTree())
return ACTION_ACTIVATE_PENDING_TREE;
if (ShouldCommit())
return ACTION_COMMIT;
+ if (ShouldAnimate())
+ return ACTION_ANIMATE;
if (ShouldDraw()) {
- if (readback_state_ == READBACK_STATE_WAITING_FOR_DRAW_AND_READBACK)
- return ACTION_DRAW_AND_READBACK;
- else if (PendingDrawsShouldBeAborted())
+ if (PendingDrawsShouldBeAborted())
return ACTION_DRAW_AND_SWAP_ABORT;
else if (forced_redraw_state_ == FORCED_REDRAW_STATE_WAITING_FOR_DRAW)
return ACTION_DRAW_AND_SWAP_FORCED;
@@ -549,13 +537,6 @@ SchedulerStateMachine::Action SchedulerStateMachine::NextAction() const {
return ACTION_NONE;
}
-void SchedulerStateMachine::CheckInvariants() {
- // We should never try to perform a draw for readback and forced draw due to
- // timeout simultaneously.
- DCHECK(!(forced_redraw_state_ == FORCED_REDRAW_STATE_WAITING_FOR_DRAW &&
- readback_state_ == READBACK_STATE_WAITING_FOR_DRAW_AND_READBACK));
-}
-
void SchedulerStateMachine::UpdateState(Action action) {
switch (action) {
case ACTION_NONE:
@@ -570,14 +551,22 @@ void SchedulerStateMachine::UpdateState(Action action) {
UpdateStateOnActivation();
return;
+ case ACTION_ANIMATE:
+ last_frame_number_animate_performed_ = current_frame_number_;
+ needs_animate_ = false;
+ // TODO(skyostil): Instead of assuming this, require the client to tell
+ // us.
+ SetNeedsRedraw();
+ return;
+
case ACTION_SEND_BEGIN_MAIN_FRAME:
- DCHECK(!has_pending_tree_);
- DCHECK(visible_ ||
- readback_state_ == READBACK_STATE_NEEDS_BEGIN_MAIN_FRAME);
- commit_state_ = COMMIT_STATE_FRAME_IN_PROGRESS;
+ DCHECK(!has_pending_tree_ ||
+ settings_.main_frame_before_activation_enabled);
+ DCHECK(!active_tree_needs_first_draw_ ||
+ settings_.main_frame_before_draw_enabled);
+ DCHECK(visible_);
+ commit_state_ = COMMIT_STATE_BEGIN_MAIN_FRAME_SENT;
needs_commit_ = false;
- if (readback_state_ == READBACK_STATE_NEEDS_BEGIN_MAIN_FRAME)
- readback_state_ = READBACK_STATE_WAITING_FOR_COMMIT;
last_frame_number_begin_main_frame_sent_ =
current_frame_number_;
return;
@@ -590,15 +579,14 @@ void SchedulerStateMachine::UpdateState(Action action) {
case ACTION_DRAW_AND_SWAP_FORCED:
case ACTION_DRAW_AND_SWAP_IF_POSSIBLE: {
- bool did_swap = true;
- UpdateStateOnDraw(did_swap);
+ bool did_request_swap = true;
+ UpdateStateOnDraw(did_request_swap);
return;
}
- case ACTION_DRAW_AND_SWAP_ABORT:
- case ACTION_DRAW_AND_READBACK: {
- bool did_swap = false;
- UpdateStateOnDraw(did_swap);
+ case ACTION_DRAW_AND_SWAP_ABORT: {
+ bool did_request_swap = false;
+ UpdateStateOnDraw(did_request_swap);
return;
}
@@ -614,11 +602,6 @@ void SchedulerStateMachine::UpdateState(Action action) {
DCHECK(!active_tree_needs_first_draw_);
return;
- case ACTION_ACQUIRE_LAYER_TEXTURES_FOR_MAIN_THREAD:
- texture_state_ = LAYER_TEXTURE_STATE_ACQUIRED_BY_MAIN_THREAD;
- main_thread_needs_layer_textures_ = false;
- return;
-
case ACTION_MANAGE_TILES:
UpdateStateOnManageTiles();
return;
@@ -628,68 +611,42 @@ void SchedulerStateMachine::UpdateState(Action action) {
void SchedulerStateMachine::UpdateStateOnCommit(bool commit_was_aborted) {
commit_count_++;
+ if (commit_was_aborted || settings_.main_frame_before_activation_enabled) {
+ commit_state_ = COMMIT_STATE_IDLE;
+ } else if (settings_.main_frame_before_draw_enabled) {
+ commit_state_ = settings_.impl_side_painting
+ ? COMMIT_STATE_WAITING_FOR_ACTIVATION
+ : COMMIT_STATE_IDLE;
+ } else {
+ commit_state_ = COMMIT_STATE_WAITING_FOR_FIRST_DRAW;
+ }
+
// If we are impl-side-painting but the commit was aborted, then we behave
// mostly as if we are not impl-side-painting since there is no pending tree.
has_pending_tree_ = settings_.impl_side_painting && !commit_was_aborted;
- // Update state related to readbacks.
- if (readback_state_ == READBACK_STATE_WAITING_FOR_COMMIT) {
- // Update the state if this is the readback commit.
- readback_state_ = has_pending_tree_
- ? READBACK_STATE_WAITING_FOR_ACTIVATION
- : READBACK_STATE_WAITING_FOR_DRAW_AND_READBACK;
- } else if (readback_state_ == READBACK_STATE_WAITING_FOR_REPLACEMENT_COMMIT) {
- // Update the state if this is the commit replacing the readback commit.
- readback_state_ = has_pending_tree_
- ? READBACK_STATE_WAITING_FOR_REPLACEMENT_ACTIVATION
- : READBACK_STATE_IDLE;
- } else {
- DCHECK(readback_state_ == READBACK_STATE_IDLE);
+ // Update state related to forced draws.
+ if (forced_redraw_state_ == FORCED_REDRAW_STATE_WAITING_FOR_COMMIT) {
+ forced_redraw_state_ = has_pending_tree_
+ ? FORCED_REDRAW_STATE_WAITING_FOR_ACTIVATION
+ : FORCED_REDRAW_STATE_WAITING_FOR_DRAW;
}
- // Readbacks can interrupt output surface initialization and forced draws,
- // so we do not want to advance those states if we are in the middle of a
- // readback. Note: It is possible for the readback's replacement commit to
- // be the output surface's first commit and/or the forced redraw's commit.
- if (readback_state_ == READBACK_STATE_IDLE ||
- readback_state_ == READBACK_STATE_WAITING_FOR_REPLACEMENT_ACTIVATION) {
- // Update state related to forced draws.
- if (forced_redraw_state_ == FORCED_REDRAW_STATE_WAITING_FOR_COMMIT) {
- forced_redraw_state_ = has_pending_tree_
- ? FORCED_REDRAW_STATE_WAITING_FOR_ACTIVATION
- : FORCED_REDRAW_STATE_WAITING_FOR_DRAW;
- }
-
- // Update the output surface state.
- DCHECK_NE(output_surface_state_,
- OUTPUT_SURFACE_WAITING_FOR_FIRST_ACTIVATION);
- if (output_surface_state_ == OUTPUT_SURFACE_WAITING_FOR_FIRST_COMMIT) {
- if (has_pending_tree_) {
- output_surface_state_ = OUTPUT_SURFACE_WAITING_FOR_FIRST_ACTIVATION;
- } else {
- output_surface_state_ = OUTPUT_SURFACE_ACTIVE;
- needs_redraw_ = true;
- }
+ // Update the output surface state.
+ DCHECK_NE(output_surface_state_, OUTPUT_SURFACE_WAITING_FOR_FIRST_ACTIVATION);
+ if (output_surface_state_ == OUTPUT_SURFACE_WAITING_FOR_FIRST_COMMIT) {
+ if (has_pending_tree_) {
+ output_surface_state_ = OUTPUT_SURFACE_WAITING_FOR_FIRST_ACTIVATION;
+ } else {
+ output_surface_state_ = OUTPUT_SURFACE_ACTIVE;
+ needs_redraw_ = true;
}
}
- // Update the commit state. We expect and wait for a draw if the commit
- // was not aborted or if we are in a readback or forced draw.
- if (!commit_was_aborted) {
- DCHECK(commit_state_ == COMMIT_STATE_READY_TO_COMMIT);
- commit_state_ = COMMIT_STATE_WAITING_FOR_FIRST_DRAW;
- } else if (readback_state_ != READBACK_STATE_IDLE ||
- forced_redraw_state_ != FORCED_REDRAW_STATE_IDLE) {
- commit_state_ = COMMIT_STATE_WAITING_FOR_FIRST_DRAW;
- } else {
- commit_state_ = COMMIT_STATE_IDLE;
- }
-
// Update state if we have a new active tree to draw, or if the active tree
- // was unchanged but we need to do a readback or forced draw.
+ // was unchanged but we need to do a forced draw.
if (!has_pending_tree_ &&
(!commit_was_aborted ||
- readback_state_ == READBACK_STATE_WAITING_FOR_DRAW_AND_READBACK ||
forced_redraw_state_ == FORCED_REDRAW_STATE_WAITING_FOR_DRAW)) {
needs_redraw_ = true;
active_tree_needs_first_draw_ = true;
@@ -698,125 +655,88 @@ void SchedulerStateMachine::UpdateStateOnCommit(bool commit_was_aborted) {
// This post-commit work is common to both completed and aborted commits.
pending_tree_is_ready_for_activation_ = false;
- if (draw_if_possible_failed_)
- last_frame_number_swap_performed_ = -1;
-
- // If we are planing to draw with the new commit, lock the layer textures for
- // use on the impl thread. Otherwise, leave them unlocked.
- if (has_pending_tree_ || needs_redraw_)
- texture_state_ = LAYER_TEXTURE_STATE_ACQUIRED_BY_IMPL_THREAD;
- else
- texture_state_ = LAYER_TEXTURE_STATE_UNLOCKED;
+ if (continuous_painting_)
+ needs_commit_ = true;
}
void SchedulerStateMachine::UpdateStateOnActivation() {
- // Update output surface state.
+ if (commit_state_ == COMMIT_STATE_WAITING_FOR_ACTIVATION)
+ commit_state_ = COMMIT_STATE_IDLE;
+
if (output_surface_state_ == OUTPUT_SURFACE_WAITING_FOR_FIRST_ACTIVATION)
output_surface_state_ = OUTPUT_SURFACE_ACTIVE;
- // Update readback state
if (forced_redraw_state_ == FORCED_REDRAW_STATE_WAITING_FOR_ACTIVATION)
forced_redraw_state_ = FORCED_REDRAW_STATE_WAITING_FOR_DRAW;
- // Update forced redraw state
- if (readback_state_ == READBACK_STATE_WAITING_FOR_ACTIVATION)
- readback_state_ = READBACK_STATE_WAITING_FOR_DRAW_AND_READBACK;
- else if (readback_state_ == READBACK_STATE_WAITING_FOR_REPLACEMENT_ACTIVATION)
- readback_state_ = READBACK_STATE_IDLE;
-
has_pending_tree_ = false;
pending_tree_is_ready_for_activation_ = false;
active_tree_needs_first_draw_ = true;
needs_redraw_ = true;
}
-void SchedulerStateMachine::UpdateStateOnDraw(bool did_swap) {
- DCHECK(readback_state_ != READBACK_STATE_WAITING_FOR_REPLACEMENT_COMMIT &&
- readback_state_ != READBACK_STATE_WAITING_FOR_REPLACEMENT_ACTIVATION)
- << *AsValue();
-
- if (readback_state_ == READBACK_STATE_WAITING_FOR_DRAW_AND_READBACK) {
- // The draw correspons to a readback commit.
- DCHECK_EQ(commit_state_, COMMIT_STATE_WAITING_FOR_FIRST_DRAW);
- // We are blocking commits from the main thread until after this draw, so
- // we should not have a pending tree.
- DCHECK(!has_pending_tree_);
- // We transition to COMMIT_STATE_FRAME_IN_PROGRESS because there is a
- // pending BeginMainFrame behind the readback request.
- commit_state_ = COMMIT_STATE_FRAME_IN_PROGRESS;
- readback_state_ = READBACK_STATE_WAITING_FOR_REPLACEMENT_COMMIT;
- } else if (forced_redraw_state_ == FORCED_REDRAW_STATE_WAITING_FOR_DRAW) {
- DCHECK_EQ(commit_state_, COMMIT_STATE_WAITING_FOR_FIRST_DRAW);
- commit_state_ = COMMIT_STATE_IDLE;
+void SchedulerStateMachine::UpdateStateOnDraw(bool did_request_swap) {
+ if (forced_redraw_state_ == FORCED_REDRAW_STATE_WAITING_FOR_DRAW)
forced_redraw_state_ = FORCED_REDRAW_STATE_IDLE;
- } else if (commit_state_ == COMMIT_STATE_WAITING_FOR_FIRST_DRAW &&
- !has_pending_tree_) {
+
+ if (!has_pending_tree_ &&
+ commit_state_ == COMMIT_STATE_WAITING_FOR_FIRST_DRAW) {
commit_state_ = COMMIT_STATE_IDLE;
}
- if (texture_state_ == LAYER_TEXTURE_STATE_ACQUIRED_BY_IMPL_THREAD)
- texture_state_ = LAYER_TEXTURE_STATE_UNLOCKED;
-
needs_redraw_ = false;
- draw_if_possible_failed_ = false;
active_tree_needs_first_draw_ = false;
- if (did_swap)
- last_frame_number_swap_performed_ = current_frame_number_;
+ if (did_request_swap)
+ last_frame_number_swap_requested_ = current_frame_number_;
}
void SchedulerStateMachine::UpdateStateOnManageTiles() {
needs_manage_tiles_ = false;
}
-void SchedulerStateMachine::SetMainThreadNeedsLayerTextures() {
- DCHECK(!main_thread_needs_layer_textures_);
- DCHECK_NE(texture_state_, LAYER_TEXTURE_STATE_ACQUIRED_BY_MAIN_THREAD);
- main_thread_needs_layer_textures_ = true;
-}
-
-void SchedulerStateMachine::SetSkipBeginMainFrameToReduceLatency(bool skip) {
- skip_begin_main_frame_to_reduce_latency_ = skip;
+void SchedulerStateMachine::SetSkipNextBeginMainFrameToReduceLatency() {
+ skip_next_begin_main_frame_to_reduce_latency_ = true;
}
-bool SchedulerStateMachine::BeginImplFrameNeeded() const {
- // Proactive BeginImplFrames are bad for the synchronous compositor because we
- // have to draw when we get the BeginImplFrame and could end up drawing many
+bool SchedulerStateMachine::BeginFrameNeeded() 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 (!SupportsProactiveBeginImplFrame())
- return BeginImplFrameNeededToDraw();
+ if (!SupportsProactiveBeginFrame())
+ return BeginFrameNeededToAnimateOrDraw();
- return BeginImplFrameNeededToDraw() ||
- ProactiveBeginImplFrameWanted();
+ return BeginFrameNeededToAnimateOrDraw() || ProactiveBeginFrameWanted();
}
bool SchedulerStateMachine::ShouldPollForAnticipatedDrawTriggers() const {
// ShouldPollForAnticipatedDrawTriggers is what we use in place of
- // ProactiveBeginImplFrameWanted when we are using the synchronous
+ // ProactiveBeginFrameWanted when we are using the synchronous
// compositor.
- if (!SupportsProactiveBeginImplFrame()) {
- return !BeginImplFrameNeededToDraw() &&
- ProactiveBeginImplFrameWanted();
+ if (!SupportsProactiveBeginFrame()) {
+ return !BeginFrameNeededToAnimateOrDraw() && ProactiveBeginFrameWanted();
}
// Non synchronous compositors should rely on
- // ProactiveBeginImplFrameWanted to poll for state instead.
+ // ProactiveBeginFrameWanted to poll for state instead.
return false;
}
-bool SchedulerStateMachine::SupportsProactiveBeginImplFrame() const {
- // Both the synchronous compositor and disabled vsync settings
- // make it undesirable to proactively request BeginImplFrames.
- // If this is true, the scheduler should poll.
- return !settings_.using_synchronous_renderer_compositor &&
- settings_.throttle_frame_production;
+// Note: If SupportsProactiveBeginFrame is false, the scheduler should poll
+// for changes in it's draw state so it can request a BeginFrame when it's
+// actually ready.
+bool SchedulerStateMachine::SupportsProactiveBeginFrame() const {
+ // It is undesirable to proactively request BeginFrames if we are
+ // using a synchronous compositor because we *must* draw for every
+ // BeginFrame, which could cause duplicate draws.
+ return !settings_.using_synchronous_renderer_compositor;
}
// These are the cases where we definitely (or almost definitely) have a
-// new frame to draw and can draw.
-bool SchedulerStateMachine::BeginImplFrameNeededToDraw() const {
+// new frame to animate and/or draw and can draw.
+bool SchedulerStateMachine::BeginFrameNeededToAnimateOrDraw() const {
// The output surface is the provider of BeginImplFrames, so we are not going
// to get them even if we ask for them.
if (!HasInitializedOutputSurface())
@@ -841,14 +761,17 @@ bool SchedulerStateMachine::BeginImplFrameNeededToDraw() const {
if (swap_used_incomplete_tile_)
return true;
+ if (needs_animate_)
+ return true;
+
return needs_redraw_;
}
// These are cases where we are very likely to draw soon, but might not
// actually have a new frame to draw when we receive the next BeginImplFrame.
// Proactively requesting the BeginImplFrame helps hide the round trip latency
-// of the SetNeedsBeginImplFrame request that has to go to the Browser.
-bool SchedulerStateMachine::ProactiveBeginImplFrameWanted() const {
+// of the SetNeedsBeginFrame request that has to go to the Browser.
+bool SchedulerStateMachine::ProactiveBeginFrameWanted() const {
// The output surface is the provider of BeginImplFrames,
// so we are not going to get them even if we ask for them.
if (!HasInitializedOutputSurface())
@@ -873,20 +796,20 @@ bool SchedulerStateMachine::ProactiveBeginImplFrameWanted() const {
if (needs_manage_tiles_)
return true;
- // If we just swapped, it's likely that we are going to produce another
- // frame soon. This helps avoid negative glitches in our
- // SetNeedsBeginImplFrame requests, which may propagate to the BeginImplFrame
+ // If we just sent a swap request, it's likely that we are going to produce
+ // another frame soon. This helps avoid negative glitches in our
+ // SetNeedsBeginFrame requests, which may propagate to the BeginImplFrame
// provider and get sampled at an inopportune time, delaying the next
// BeginImplFrame.
- if (last_frame_number_swap_performed_ == current_frame_number_)
+ if (HasRequestedSwapThisFrame())
return true;
return false;
}
void SchedulerStateMachine::OnBeginImplFrame(const BeginFrameArgs& args) {
- current_frame_number_++;
- last_begin_impl_frame_args_ = args;
+ AdvanceCurrentFrameNumber();
+ begin_impl_frame_args_ = args;
DCHECK_EQ(begin_impl_frame_state_, BEGIN_IMPL_FRAME_STATE_IDLE) << *AsValue();
begin_impl_frame_state_ = BEGIN_IMPL_FRAME_STATE_BEGIN_FRAME_STARTING;
}
@@ -913,12 +836,16 @@ void SchedulerStateMachine::OnBeginImplFrameIdle() {
bool SchedulerStateMachine::ShouldTriggerBeginImplFrameDeadlineEarly() const {
// TODO(brianderson): This should take into account multiple commit sources.
- // 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)
+ if (begin_impl_frame_state_ != BEGIN_IMPL_FRAME_STATE_INSIDE_BEGIN_FRAME)
return false;
- if (begin_impl_frame_state_ != BEGIN_IMPL_FRAME_STATE_INSIDE_BEGIN_FRAME)
+ // If we've lost the output surface, end the current BeginImplFrame ASAP
+ // so we can start creating the next output surface.
+ if (output_surface_state_ == OUTPUT_SURFACE_LOST)
+ return true;
+
+ // SwapAck throttle the deadline since we wont draw and swap anyway.
+ if (pending_swaps_ >= max_pending_swaps_)
return false;
if (active_tree_needs_first_draw_)
@@ -943,9 +870,14 @@ bool SchedulerStateMachine::ShouldTriggerBeginImplFrameDeadlineEarly() const {
}
bool SchedulerStateMachine::MainThreadIsInHighLatencyMode() const {
+ // If a commit is pending before the previous commit has been drawn, we
+ // are definitely in a high latency mode.
+ if (CommitPending() && (active_tree_needs_first_draw_ || has_pending_tree_))
+ return true;
+
// If we just sent a BeginMainFrame and haven't hit the deadline yet, the main
// thread is in a low latency mode.
- if (last_frame_number_begin_main_frame_sent_ == current_frame_number_ &&
+ if (HasSentBeginMainFrameThisFrame() &&
(begin_impl_frame_state_ == BEGIN_IMPL_FRAME_STATE_BEGIN_FRAME_STARTING ||
begin_impl_frame_state_ == BEGIN_IMPL_FRAME_STATE_INSIDE_BEGIN_FRAME))
return false;
@@ -953,8 +885,7 @@ bool SchedulerStateMachine::MainThreadIsInHighLatencyMode() const {
// If there's a commit in progress it must either be from the previous frame
// or it started after the impl thread's deadline. In either case the main
// thread is in high latency mode.
- if (commit_state_ == COMMIT_STATE_FRAME_IN_PROGRESS ||
- commit_state_ == COMMIT_STATE_READY_TO_COMMIT)
+ if (CommitPending())
return true;
// Similarly, if there's a pending tree the main thread is in high latency
@@ -968,11 +899,10 @@ bool SchedulerStateMachine::MainThreadIsInHighLatencyMode() const {
if (begin_impl_frame_state_ == BEGIN_IMPL_FRAME_STATE_INSIDE_DEADLINE) {
// Even if there's a new active tree to draw at the deadline or we've just
- // drawn it, it may have been triggered by a previous BeginImplFrame, in
+ // swapped it, it may have been triggered by a previous BeginImplFrame, in
// which case the main thread is in a high latency mode.
- return (active_tree_needs_first_draw_ ||
- last_frame_number_swap_performed_ == current_frame_number_) &&
- last_frame_number_begin_main_frame_sent_ != current_frame_number_;
+ return (active_tree_needs_first_draw_ || HasSwappedThisFrame()) &&
+ !HasSentBeginMainFrameThisFrame();
}
// If the active tree needs its first draw in any other state, we know the
@@ -981,7 +911,7 @@ bool SchedulerStateMachine::MainThreadIsInHighLatencyMode() const {
}
void SchedulerStateMachine::DidEnterPollForAnticipatedDrawTriggers() {
- current_frame_number_++;
+ AdvanceCurrentFrameNumber();
inside_poll_for_anticipated_draw_triggers_ = true;
}
@@ -995,6 +925,10 @@ void SchedulerStateMachine::SetCanDraw(bool can_draw) { can_draw_ = can_draw; }
void SchedulerStateMachine::SetNeedsRedraw() { needs_redraw_ = true; }
+void SchedulerStateMachine::SetNeedsAnimate() {
+ needs_animate_ = true;
+}
+
void SchedulerStateMachine::SetNeedsManageTiles() {
if (!needs_manage_tiles_) {
TRACE_EVENT0("cc",
@@ -1003,77 +937,88 @@ void SchedulerStateMachine::SetNeedsManageTiles() {
}
}
+void SchedulerStateMachine::SetMaxSwapsPending(int max) {
+ max_pending_swaps_ = max;
+}
+
+void SchedulerStateMachine::DidSwapBuffers() {
+ pending_swaps_++;
+ DCHECK_LE(pending_swaps_, max_pending_swaps_);
+
+ last_frame_number_swap_performed_ = current_frame_number_;
+}
+
void SchedulerStateMachine::SetSwapUsedIncompleteTile(
bool used_incomplete_tile) {
swap_used_incomplete_tile_ = used_incomplete_tile;
}
+void SchedulerStateMachine::DidSwapBuffersComplete() {
+ DCHECK_GT(pending_swaps_, 0);
+ pending_swaps_--;
+}
+
void SchedulerStateMachine::SetSmoothnessTakesPriority(
bool smoothness_takes_priority) {
smoothness_takes_priority_ = smoothness_takes_priority;
}
-void SchedulerStateMachine::DidDrawIfPossibleCompleted(bool success) {
- draw_if_possible_failed_ = !success;
- if (draw_if_possible_failed_) {
- needs_redraw_ = true;
-
- // If we're already in the middle of a redraw, we don't need to
- // restart it.
- if (forced_redraw_state_ != FORCED_REDRAW_STATE_IDLE)
- return;
-
- needs_commit_ = true;
- consecutive_failed_draws_++;
- if (settings_.timeout_and_draw_when_animation_checkerboards &&
- consecutive_failed_draws_ >=
- settings_.maximum_number_of_failed_draws_before_draw_is_forced_) {
- consecutive_failed_draws_ = 0;
- // We need to force a draw, but it doesn't make sense to do this until
- // we've committed and have new textures.
- forced_redraw_state_ = FORCED_REDRAW_STATE_WAITING_FOR_COMMIT;
- }
- } else {
- consecutive_failed_draws_ = 0;
- forced_redraw_state_ = FORCED_REDRAW_STATE_IDLE;
+void SchedulerStateMachine::DidDrawIfPossibleCompleted(DrawResult result) {
+ switch (result) {
+ case INVALID_RESULT:
+ NOTREACHED() << "Uninitialized DrawResult.";
+ break;
+ case DRAW_ABORTED_CANT_DRAW:
+ case DRAW_ABORTED_CONTEXT_LOST:
+ NOTREACHED() << "Invalid return value from DrawAndSwapIfPossible:"
+ << result;
+ break;
+ case DRAW_SUCCESS:
+ consecutive_checkerboard_animations_ = 0;
+ forced_redraw_state_ = FORCED_REDRAW_STATE_IDLE;
+ break;
+ case DRAW_ABORTED_CHECKERBOARD_ANIMATIONS:
+ needs_redraw_ = true;
+
+ // If we're already in the middle of a redraw, we don't need to
+ // restart it.
+ if (forced_redraw_state_ != FORCED_REDRAW_STATE_IDLE)
+ return;
+
+ needs_commit_ = true;
+ consecutive_checkerboard_animations_++;
+ if (settings_.timeout_and_draw_when_animation_checkerboards &&
+ consecutive_checkerboard_animations_ >=
+ settings_.maximum_number_of_failed_draws_before_draw_is_forced_) {
+ consecutive_checkerboard_animations_ = 0;
+ // We need to force a draw, but it doesn't make sense to do this until
+ // we've committed and have new textures.
+ forced_redraw_state_ = FORCED_REDRAW_STATE_WAITING_FOR_COMMIT;
+ }
+ break;
+ case DRAW_ABORTED_MISSING_HIGH_RES_CONTENT:
+ // It's not clear whether this missing content is because of missing
+ // pictures (which requires a commit) or because of memory pressure
+ // removing textures (which might not). To be safe, request a commit
+ // anyway.
+ needs_commit_ = true;
+ break;
}
}
void SchedulerStateMachine::SetNeedsCommit() { needs_commit_ = true; }
-void SchedulerStateMachine::SetNeedsForcedCommitForReadback() {
- // If this is called in READBACK_STATE_IDLE, this is a "first" readback
- // request.
- // If this is called in READBACK_STATE_WAITING_FOR_REPLACEMENT_COMMIT, this
- // is a back-to-back readback request that started before the replacement
- // commit had a chance to land.
- DCHECK(readback_state_ == READBACK_STATE_IDLE ||
- readback_state_ == READBACK_STATE_WAITING_FOR_REPLACEMENT_COMMIT);
-
- // If there is already a commit in progress when we get the readback request
- // (we are in COMMIT_STATE_FRAME_IN_PROGRESS), then we don't need to send a
- // BeginMainFrame for the replacement commit, since there's already a
- // BeginMainFrame behind the readback request. In that case, we can skip
- // READBACK_STATE_NEEDS_BEGIN_MAIN_FRAME and go directly to
- // READBACK_STATE_WAITING_FOR_COMMIT
- if (commit_state_ == COMMIT_STATE_FRAME_IN_PROGRESS)
- readback_state_ = READBACK_STATE_WAITING_FOR_COMMIT;
- else
- readback_state_ = READBACK_STATE_NEEDS_BEGIN_MAIN_FRAME;
-}
-
-void SchedulerStateMachine::FinishCommit() {
- DCHECK(commit_state_ == COMMIT_STATE_FRAME_IN_PROGRESS) << *AsValue();
+void SchedulerStateMachine::NotifyReadyToCommit() {
+ DCHECK(commit_state_ == COMMIT_STATE_BEGIN_MAIN_FRAME_STARTED) << *AsValue();
commit_state_ = COMMIT_STATE_READY_TO_COMMIT;
}
void SchedulerStateMachine::BeginMainFrameAborted(bool did_handle) {
- DCHECK_EQ(commit_state_, COMMIT_STATE_FRAME_IN_PROGRESS);
+ DCHECK_EQ(commit_state_, COMMIT_STATE_BEGIN_MAIN_FRAME_SENT);
if (did_handle) {
bool commit_was_aborted = true;
UpdateStateOnCommit(commit_was_aborted);
} else {
- DCHECK_NE(readback_state_, READBACK_STATE_WAITING_FOR_COMMIT);
commit_state_ = COMMIT_STATE_IDLE;
SetNeedsCommit();
}
@@ -1081,7 +1026,8 @@ void SchedulerStateMachine::BeginMainFrameAborted(bool did_handle) {
void SchedulerStateMachine::DidManageTiles() {
needs_manage_tiles_ = false;
- last_frame_number_manage_tiles_called_ = current_frame_number_;
+ // "Fill" the ManageTiles funnel.
+ manage_tiles_funnel_++;
}
void SchedulerStateMachine::DidLoseOutputSurface() {
@@ -1090,7 +1036,6 @@ void SchedulerStateMachine::DidLoseOutputSurface() {
return;
output_surface_state_ = OUTPUT_SURFACE_LOST;
needs_redraw_ = false;
- begin_impl_frame_state_ = BEGIN_IMPL_FRAME_STATE_IDLE;
}
void SchedulerStateMachine::NotifyReadyToActivate() {
@@ -1108,6 +1053,12 @@ void SchedulerStateMachine::DidCreateAndInitializeOutputSurface() {
needs_commit_ = true;
}
did_create_and_initialize_first_output_surface_ = true;
+ pending_swaps_ = 0;
+}
+
+void SchedulerStateMachine::NotifyBeginMainFrameStarted() {
+ DCHECK_EQ(commit_state_, COMMIT_STATE_BEGIN_MAIN_FRAME_SENT);
+ commit_state_ = COMMIT_STATE_BEGIN_MAIN_FRAME_STARTED;
}
bool SchedulerStateMachine::HasInitializedOutputSurface() const {
diff --git a/chromium/cc/scheduler/scheduler_state_machine.h b/chromium/cc/scheduler/scheduler_state_machine.h
index 2e61224fb28..a2a8b59e6c6 100644
--- a/chromium/cc/scheduler/scheduler_state_machine.h
+++ b/chromium/cc/scheduler/scheduler_state_machine.h
@@ -12,6 +12,7 @@
#include "base/time/time.h"
#include "cc/base/cc_export.h"
#include "cc/output/begin_frame_args.h"
+#include "cc/scheduler/draw_result.h"
#include "cc/scheduler/scheduler_settings.h"
namespace base {
@@ -59,31 +60,14 @@ class CC_EXPORT SchedulerStateMachine {
enum CommitState {
COMMIT_STATE_IDLE,
- COMMIT_STATE_FRAME_IN_PROGRESS,
+ COMMIT_STATE_BEGIN_MAIN_FRAME_SENT,
+ COMMIT_STATE_BEGIN_MAIN_FRAME_STARTED,
COMMIT_STATE_READY_TO_COMMIT,
+ COMMIT_STATE_WAITING_FOR_ACTIVATION,
COMMIT_STATE_WAITING_FOR_FIRST_DRAW,
};
static const char* CommitStateToString(CommitState state);
- enum TextureState {
- LAYER_TEXTURE_STATE_UNLOCKED,
- LAYER_TEXTURE_STATE_ACQUIRED_BY_MAIN_THREAD,
- LAYER_TEXTURE_STATE_ACQUIRED_BY_IMPL_THREAD,
- };
- static const char* TextureStateToString(TextureState state);
-
- enum SynchronousReadbackState {
- READBACK_STATE_IDLE,
- READBACK_STATE_NEEDS_BEGIN_MAIN_FRAME,
- READBACK_STATE_WAITING_FOR_COMMIT,
- READBACK_STATE_WAITING_FOR_ACTIVATION,
- READBACK_STATE_WAITING_FOR_DRAW_AND_READBACK,
- READBACK_STATE_WAITING_FOR_REPLACEMENT_COMMIT,
- READBACK_STATE_WAITING_FOR_REPLACEMENT_ACTIVATION,
- };
- static const char* SynchronousReadbackStateToString(
- SynchronousReadbackState state);
-
enum ForcedRedrawOnTimeoutState {
FORCED_REDRAW_STATE_IDLE,
FORCED_REDRAW_STATE_WAITING_FOR_COMMIT,
@@ -94,15 +78,18 @@ class CC_EXPORT SchedulerStateMachine {
ForcedRedrawOnTimeoutState state);
bool CommitPending() const {
- return commit_state_ == COMMIT_STATE_FRAME_IN_PROGRESS ||
+ return commit_state_ == COMMIT_STATE_BEGIN_MAIN_FRAME_SENT ||
+ commit_state_ == COMMIT_STATE_BEGIN_MAIN_FRAME_STARTED ||
commit_state_ == COMMIT_STATE_READY_TO_COMMIT;
}
+ CommitState commit_state() const { return commit_state_; }
bool RedrawPending() const { return needs_redraw_; }
bool ManageTilesPending() const { return needs_manage_tiles_; }
enum Action {
ACTION_NONE,
+ ACTION_ANIMATE,
ACTION_SEND_BEGIN_MAIN_FRAME,
ACTION_COMMIT,
ACTION_UPDATE_VISIBLE_TILES,
@@ -110,9 +97,7 @@ class CC_EXPORT SchedulerStateMachine {
ACTION_DRAW_AND_SWAP_IF_POSSIBLE,
ACTION_DRAW_AND_SWAP_FORCED,
ACTION_DRAW_AND_SWAP_ABORT,
- ACTION_DRAW_AND_READBACK,
ACTION_BEGIN_OUTPUT_SURFACE_CREATION,
- ACTION_ACQUIRE_LAYER_TEXTURES_FOR_MAIN_THREAD,
ACTION_MANAGE_TILES,
};
static const char* ActionToString(Action action);
@@ -122,11 +107,9 @@ class CC_EXPORT SchedulerStateMachine {
Action NextAction() const;
void UpdateState(Action action);
- void CheckInvariants();
-
// Indicates whether the impl thread needs a BeginImplFrame callback in order
// to make progress.
- bool BeginImplFrameNeeded() const;
+ bool BeginFrameNeeded() const;
// Indicates that we need to independently poll for new state and actions
// because we can't expect a BeginImplFrame. This is mostly used to avoid
@@ -167,63 +150,71 @@ class CC_EXPORT SchedulerStateMachine {
void SetNeedsRedraw();
bool needs_redraw() const { return needs_redraw_; }
+ void SetNeedsAnimate();
+ bool needs_animate() const { return needs_animate_; }
+
// Indicates that manage-tiles is required. This guarantees another
// ManageTiles will occur shortly (even if no redraw is required).
void SetNeedsManageTiles();
+ // Sets how many swaps can be pending to the OutputSurface.
+ void SetMaxSwapsPending(int max);
+
+ // If the scheduler attempted to draw and swap, this provides feedback
+ // regarding whether or not the swap actually occured. We might skip the
+ // swap when there is not damage, for example.
+ void DidSwapBuffers();
+
// Indicates whether a redraw is required because we are currently rendering
// with a low resolution or checkerboarded tile.
void SetSwapUsedIncompleteTile(bool used_incomplete_tile);
+ // Notification from the OutputSurface that a swap has been consumed.
+ void DidSwapBuffersComplete();
+
// Indicates whether to prioritize animation smoothness over new content
// activation.
void SetSmoothnessTakesPriority(bool smoothness_takes_priority);
+ bool smoothness_takes_priority() const { return smoothness_takes_priority_; }
// Indicates whether ACTION_DRAW_AND_SWAP_IF_POSSIBLE drew to the screen.
- void DidDrawIfPossibleCompleted(bool success);
+ void DidDrawIfPossibleCompleted(DrawResult result);
// Indicates that a new commit flow needs to be performed, either to pull
// updates from the main thread to the impl, or to push deltas from the impl
// thread to main.
void SetNeedsCommit();
- // As SetNeedsCommit(), but ensures the BeginMainFrame will be sent even
- // if we are not visible. After this call we expect to go through
- // the forced commit flow and then return to waiting for a non-forced
- // BeginMainFrame to finish.
- void SetNeedsForcedCommitForReadback();
-
// Call this only in response to receiving an ACTION_SEND_BEGIN_MAIN_FRAME
// from NextAction.
// Indicates that all painting is complete.
- void FinishCommit();
+ void NotifyReadyToCommit();
// Call this only in response to receiving an ACTION_SEND_BEGIN_MAIN_FRAME
// from NextAction if the client rejects the BeginMainFrame message.
// If did_handle is false, then another commit will be retried soon.
void BeginMainFrameAborted(bool did_handle);
- // Request exclusive access to the textures that back single buffered
- // layers on behalf of the main thread. Upon acquisition,
- // ACTION_DRAW_AND_SWAP_IF_POSSIBLE will not draw until the main thread
- // releases the
- // textures to the impl thread by committing the layers.
- void SetMainThreadNeedsLayerTextures();
-
// Set that we can create the first OutputSurface and start the scheduler.
void SetCanStart() { can_start_ = true; }
- void SetSkipBeginMainFrameToReduceLatency(bool skip);
+ void SetSkipNextBeginMainFrameToReduceLatency();
// Indicates whether drawing would, at this time, make sense.
// CanDraw can be used to suppress flashes or checkerboarding
// when such behavior would be undesirable.
void SetCanDraw(bool can);
+ // Indicates that scheduled BeginMainFrame is started.
+ void NotifyBeginMainFrameStarted();
+
// Indicates that the pending tree is ready for activation.
void NotifyReadyToActivate();
bool has_pending_tree() const { return has_pending_tree_; }
+ bool active_tree_needs_first_draw() const {
+ return active_tree_needs_first_draw_;
+ }
void DidManageTiles();
void DidLoseOutputSurface();
@@ -233,35 +224,38 @@ class CC_EXPORT SchedulerStateMachine {
// True if we need to abort draws to make forward progress.
bool PendingDrawsShouldBeAborted() const;
- bool SupportsProactiveBeginImplFrame() const;
+ bool SupportsProactiveBeginFrame() const;
- bool IsCommitStateWaiting() const;
+ void SetContinuousPainting(bool continuous_painting) {
+ continuous_painting_ = continuous_painting;
+ }
protected:
- bool BeginImplFrameNeededToDraw() const;
- bool ProactiveBeginImplFrameWanted() const;
+ bool BeginFrameNeededToAnimateOrDraw() const;
+ bool ProactiveBeginFrameWanted() const;
// True if we need to force activations to make forward progress.
bool PendingActivationsShouldBeForced() const;
+ bool ShouldAnimate() const;
bool ShouldBeginOutputSurfaceCreation() const;
bool ShouldDrawForced() const;
bool ShouldDraw() const;
bool ShouldActivatePendingTree() const;
- bool ShouldAcquireLayerTexturesForMainThread() const;
bool ShouldUpdateVisibleTiles() const;
bool ShouldSendBeginMainFrame() const;
bool ShouldCommit() const;
bool ShouldManageTiles() const;
+ void AdvanceCurrentFrameNumber();
bool HasSentBeginMainFrameThisFrame() const;
- bool HasScheduledManageTilesThisFrame() const;
bool HasUpdatedVisibleTilesThisFrame() const;
+ bool HasRequestedSwapThisFrame() const;
bool HasSwappedThisFrame() const;
void UpdateStateOnCommit(bool commit_was_aborted);
void UpdateStateOnActivation();
- void UpdateStateOnDraw(bool did_swap);
+ void UpdateStateOnDraw(bool did_request_swap);
void UpdateStateOnManageTiles();
const SchedulerSettings settings_;
@@ -269,25 +263,31 @@ class CC_EXPORT SchedulerStateMachine {
OutputSurfaceState output_surface_state_;
BeginImplFrameState begin_impl_frame_state_;
CommitState commit_state_;
- TextureState texture_state_;
ForcedRedrawOnTimeoutState forced_redraw_state_;
- SynchronousReadbackState readback_state_;
- BeginFrameArgs last_begin_impl_frame_args_;
+ BeginFrameArgs begin_impl_frame_args_;
int commit_count_;
int current_frame_number_;
+ int last_frame_number_animate_performed_;
int last_frame_number_swap_performed_;
+ int last_frame_number_swap_requested_;
int last_frame_number_begin_main_frame_sent_;
int last_frame_number_update_visible_tiles_was_called_;
- int last_frame_number_manage_tiles_called_;
- int consecutive_failed_draws_;
+ // manage_tiles_funnel_ is "filled" each time ManageTiles is called
+ // and "drained" on each BeginImplFrame. If the funnel gets too full,
+ // we start throttling ACTION_MANAGE_TILES such that we average one
+ // ManageTile per BeginImplFrame.
+ int manage_tiles_funnel_;
+ int consecutive_checkerboard_animations_;
+ int max_pending_swaps_;
+ int pending_swaps_;
bool needs_redraw_;
+ bool needs_animate_;
bool needs_manage_tiles_;
bool swap_used_incomplete_tile_;
bool needs_commit_;
- bool main_thread_needs_layer_textures_;
bool inside_poll_for_anticipated_draw_triggers_;
bool visible_;
bool can_start_;
@@ -295,10 +295,11 @@ class CC_EXPORT SchedulerStateMachine {
bool has_pending_tree_;
bool pending_tree_is_ready_for_activation_;
bool active_tree_needs_first_draw_;
- bool draw_if_possible_failed_;
bool did_create_and_initialize_first_output_surface_;
bool smoothness_takes_priority_;
+ bool skip_next_begin_main_frame_to_reduce_latency_;
bool skip_begin_main_frame_to_reduce_latency_;
+ bool continuous_painting_;
private:
DISALLOW_COPY_AND_ASSIGN(SchedulerStateMachine);
diff --git a/chromium/cc/scheduler/scheduler_state_machine_unittest.cc b/chromium/cc/scheduler/scheduler_state_machine_unittest.cc
index 67925a059b3..b5834241d8c 100644
--- a/chromium/cc/scheduler/scheduler_state_machine_unittest.cc
+++ b/chromium/cc/scheduler/scheduler_state_machine_unittest.cc
@@ -5,17 +5,13 @@
#include "cc/scheduler/scheduler_state_machine.h"
#include "cc/scheduler/scheduler.h"
+#include "cc/test/begin_frame_args_test.h"
#include "testing/gtest/include/gtest/gtest.h"
#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_IMPL_FRAME_STATE_INSIDE_DEADLINE, \
state.begin_impl_frame_state()) \
<< *state.AsValue(); \
@@ -42,9 +38,11 @@ const SchedulerStateMachine::BeginImplFrameState all_begin_impl_frame_states[] =
const SchedulerStateMachine::CommitState all_commit_states[] = {
SchedulerStateMachine::COMMIT_STATE_IDLE,
- SchedulerStateMachine::COMMIT_STATE_FRAME_IN_PROGRESS,
+ SchedulerStateMachine::COMMIT_STATE_BEGIN_MAIN_FRAME_SENT,
+ SchedulerStateMachine::COMMIT_STATE_BEGIN_MAIN_FRAME_STARTED,
SchedulerStateMachine::COMMIT_STATE_READY_TO_COMMIT,
- SchedulerStateMachine::COMMIT_STATE_WAITING_FOR_FIRST_DRAW, };
+ SchedulerStateMachine::COMMIT_STATE_WAITING_FOR_ACTIVATION,
+ SchedulerStateMachine::COMMIT_STATE_WAITING_FOR_FIRST_DRAW};
// Exposes the protected state fields of the SchedulerStateMachine for testing
class StateMachine : public SchedulerStateMachine {
@@ -82,21 +80,12 @@ class StateMachine : public SchedulerStateMachine {
void SetNeedsForcedRedrawForTimeout(bool b) {
forced_redraw_state_ = FORCED_REDRAW_STATE_WAITING_FOR_COMMIT;
- commit_state_ = COMMIT_STATE_WAITING_FOR_FIRST_DRAW;
+ active_tree_needs_first_draw_ = true;
}
bool NeedsForcedRedrawForTimeout() const {
return forced_redraw_state_ != FORCED_REDRAW_STATE_IDLE;
}
- void SetNeedsForcedRedrawForReadback() {
- readback_state_ = READBACK_STATE_WAITING_FOR_DRAW_AND_READBACK;
- commit_state_ = COMMIT_STATE_WAITING_FOR_FIRST_DRAW;
- }
-
- bool NeedsForcedRedrawForReadback() const {
- return readback_state_ != READBACK_STATE_IDLE;
- }
-
void SetActiveTreeNeedsFirstDraw(bool needs_first_draw) {
active_tree_needs_first_draw_ = needs_first_draw;
}
@@ -107,6 +96,10 @@ class StateMachine : public SchedulerStateMachine {
bool PendingActivationsShouldBeForced() const {
return SchedulerStateMachine::PendingActivationsShouldBeForced();
}
+
+ void SetHasPendingTree(bool has_pending_tree) {
+ has_pending_tree_ = has_pending_tree;
+ }
};
TEST(SchedulerStateMachineTest, TestNextActionBeginsMainFrameIfNeeded) {
@@ -123,11 +116,11 @@ TEST(SchedulerStateMachineTest, TestNextActionBeginsMainFrameIfNeeded) {
state.SetNeedsRedraw(false);
state.SetVisible(true);
- EXPECT_FALSE(state.BeginImplFrameNeeded());
+ EXPECT_FALSE(state.BeginFrameNeeded());
EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
- EXPECT_FALSE(state.BeginImplFrameNeeded());
- state.OnBeginImplFrame(BeginFrameArgs::CreateForTesting());
+ EXPECT_FALSE(state.BeginFrameNeeded());
+ state.OnBeginImplFrame(CreateBeginFrameArgsForTesting());
EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
state.OnBeginImplFrameDeadline();
@@ -139,12 +132,13 @@ TEST(SchedulerStateMachineTest, TestNextActionBeginsMainFrameIfNeeded) {
state.SetCommitState(SchedulerStateMachine::COMMIT_STATE_IDLE);
state.SetNeedsRedraw(false);
state.SetVisible(true);
+ state.SetNeedsCommit();
- EXPECT_FALSE(state.BeginImplFrameNeeded());
+ EXPECT_FALSE(state.BeginFrameNeeded());
EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
- EXPECT_FALSE(state.BeginImplFrameNeeded());
- state.OnBeginImplFrame(BeginFrameArgs::CreateForTesting());
+ EXPECT_FALSE(state.BeginFrameNeeded());
+ state.OnBeginImplFrame(CreateBeginFrameArgsForTesting());
EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
state.OnBeginImplFrameDeadline();
}
@@ -154,9 +148,17 @@ TEST(SchedulerStateMachineTest, TestNextActionBeginsMainFrameIfNeeded) {
StateMachine state(default_scheduler_settings);
state.SetCommitState(SchedulerStateMachine::COMMIT_STATE_IDLE);
state.SetCanStart();
+ state.UpdateState(state.NextAction());
+ state.CreateAndInitializeOutputSurfaceWithActivatedCommit();
state.SetNeedsRedraw(false);
state.SetVisible(true);
- EXPECT_FALSE(state.BeginImplFrameNeeded());
+ state.SetNeedsCommit();
+
+ EXPECT_TRUE(state.BeginFrameNeeded());
+
+ state.OnBeginImplFrame(CreateBeginFrameArgsForTesting());
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME);
}
// Begin the frame, make sure needs_commit and commit_state update correctly.
@@ -167,14 +169,163 @@ TEST(SchedulerStateMachineTest, TestNextActionBeginsMainFrameIfNeeded) {
state.CreateAndInitializeOutputSurfaceWithActivatedCommit();
state.SetVisible(true);
state.UpdateState(SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME);
- EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_FRAME_IN_PROGRESS,
+ EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_BEGIN_MAIN_FRAME_SENT,
state.CommitState());
EXPECT_FALSE(state.NeedsCommit());
}
}
+// Explicitly test main_frame_before_draw_enabled = false
+TEST(SchedulerStateMachineTest, MainFrameBeforeDrawDisabled) {
+ SchedulerSettings scheduler_settings;
+ scheduler_settings.impl_side_painting = true;
+ scheduler_settings.main_frame_before_draw_enabled = false;
+ StateMachine state(scheduler_settings);
+ state.SetCommitState(SchedulerStateMachine::COMMIT_STATE_IDLE);
+ state.SetCanStart();
+ state.UpdateState(state.NextAction());
+ state.CreateAndInitializeOutputSurfaceWithActivatedCommit();
+ state.SetNeedsRedraw(false);
+ state.SetVisible(true);
+ state.SetCanDraw(true);
+ state.SetNeedsCommit();
+
+ EXPECT_TRUE(state.BeginFrameNeeded());
+
+ // Commit to the pending tree.
+ state.OnBeginImplFrame(CreateBeginFrameArgsForTesting());
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME);
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
+ state.NotifyBeginMainFrameStarted();
+ state.NotifyReadyToCommit();
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_COMMIT);
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
+ EXPECT_EQ(state.CommitState(),
+ SchedulerStateMachine::COMMIT_STATE_WAITING_FOR_FIRST_DRAW);
+
+ state.OnBeginImplFrameDeadline();
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
+ EXPECT_EQ(state.CommitState(),
+ SchedulerStateMachine::COMMIT_STATE_WAITING_FOR_FIRST_DRAW);
+
+ // Verify that the next commit doesn't start until the previous
+ // commit has been drawn.
+ state.SetNeedsCommit();
+ state.OnBeginImplFrame(CreateBeginFrameArgsForTesting());
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
+
+ // Make sure that a draw of the active tree doesn't spuriously advance
+ // the commit state and unblock the next commit.
+ state.SetNeedsRedraw(true);
+ state.OnBeginImplFrameDeadline();
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_ANIMATE);
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE);
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
+ EXPECT_EQ(state.CommitState(),
+ SchedulerStateMachine::COMMIT_STATE_WAITING_FOR_FIRST_DRAW);
+ EXPECT_TRUE(state.has_pending_tree());
+
+ // Verify NotifyReadyToActivate unblocks activation, draw, and
+ // commit in that order.
+ state.OnBeginImplFrame(CreateBeginFrameArgsForTesting());
+
+ state.NotifyReadyToActivate();
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_ACTIVATE_PENDING_TREE);
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_ANIMATE);
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
+ EXPECT_EQ(state.CommitState(),
+ SchedulerStateMachine::COMMIT_STATE_WAITING_FOR_FIRST_DRAW);
+
+ EXPECT_TRUE(state.ShouldTriggerBeginImplFrameDeadlineEarly());
+ state.OnBeginImplFrameDeadline();
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE);
+ state.DidSwapBuffers();
+ state.DidSwapBuffersComplete();
+ EXPECT_EQ(state.CommitState(), SchedulerStateMachine::COMMIT_STATE_IDLE);
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME);
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
+ EXPECT_EQ(state.CommitState(),
+ SchedulerStateMachine::COMMIT_STATE_BEGIN_MAIN_FRAME_SENT);
+
+ state.NotifyBeginMainFrameStarted();
+ state.NotifyReadyToCommit();
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_COMMIT);
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
+ EXPECT_EQ(state.CommitState(),
+ SchedulerStateMachine::COMMIT_STATE_WAITING_FOR_FIRST_DRAW);
+}
+
+// Explicitly test main_frame_before_activation_enabled = true
+TEST(SchedulerStateMachineTest, MainFrameBeforeActivationEnabled) {
+ SchedulerSettings scheduler_settings;
+ scheduler_settings.impl_side_painting = true;
+ scheduler_settings.main_frame_before_activation_enabled = true;
+ StateMachine state(scheduler_settings);
+ state.SetCommitState(SchedulerStateMachine::COMMIT_STATE_IDLE);
+ state.SetCanStart();
+ state.UpdateState(state.NextAction());
+ state.CreateAndInitializeOutputSurfaceWithActivatedCommit();
+ state.SetNeedsRedraw(false);
+ state.SetVisible(true);
+ state.SetCanDraw(true);
+ state.SetNeedsCommit();
+
+ EXPECT_TRUE(state.BeginFrameNeeded());
+
+ // Commit to the pending tree.
+ state.OnBeginImplFrame(CreateBeginFrameArgsForTesting());
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME);
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
+
+ state.NotifyBeginMainFrameStarted();
+ state.NotifyReadyToCommit();
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_COMMIT);
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
+ EXPECT_EQ(state.CommitState(), SchedulerStateMachine::COMMIT_STATE_IDLE);
+
+ state.OnBeginImplFrameDeadline();
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
+
+ // Verify that the next commit starts while there is still a pending tree.
+ state.SetNeedsCommit();
+ state.OnBeginImplFrame(CreateBeginFrameArgsForTesting());
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME);
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
+
+ // Verify the pending commit doesn't overwrite the pending
+ // tree until the pending tree has been activated.
+ state.NotifyBeginMainFrameStarted();
+ state.NotifyReadyToCommit();
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
+
+ // Verify NotifyReadyToActivate unblocks activation, draw, and
+ // commit in that order.
+ state.NotifyReadyToActivate();
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_ACTIVATE_PENDING_TREE);
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
+
+ EXPECT_TRUE(state.ShouldTriggerBeginImplFrameDeadlineEarly());
+ state.OnBeginImplFrameDeadline();
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_ANIMATE);
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE);
+ state.DidSwapBuffers();
+ state.DidSwapBuffersComplete();
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_COMMIT);
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
+ EXPECT_EQ(state.CommitState(), SchedulerStateMachine::COMMIT_STATE_IDLE);
+}
+
TEST(SchedulerStateMachineTest,
- TestFailedDrawSetsNeedsCommitAndDoesNotDrawAgain) {
+ TestFailedDrawForAnimationCheckerboardSetsNeedsCommitAndDoesNotDrawAgain) {
SchedulerSettings default_scheduler_settings;
StateMachine state(default_scheduler_settings);
state.SetCanStart();
@@ -184,28 +335,65 @@ TEST(SchedulerStateMachineTest,
state.SetCanDraw(true);
state.SetNeedsRedraw(true);
EXPECT_TRUE(state.RedrawPending());
- EXPECT_TRUE(state.BeginImplFrameNeeded());
- state.OnBeginImplFrame(BeginFrameArgs::CreateForTesting());
+ EXPECT_TRUE(state.BeginFrameNeeded());
+ state.OnBeginImplFrame(CreateBeginFrameArgsForTesting());
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_ANIMATE);
EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
state.OnBeginImplFrameDeadline();
// We're drawing now.
EXPECT_ACTION_UPDATE_STATE(
SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE);
+ state.DidSwapBuffers();
+ state.DidSwapBuffersComplete();
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);
- state.OnBeginImplFrame(BeginFrameArgs::CreateForTesting());
+ state.DidDrawIfPossibleCompleted(DRAW_ABORTED_CHECKERBOARD_ANIMATIONS);
+ state.OnBeginImplFrame(CreateBeginFrameArgsForTesting());
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_ANIMATE);
EXPECT_ACTION_UPDATE_STATE(
SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME);
EXPECT_TRUE(state.RedrawPending());
EXPECT_TRUE(state.CommitPending());
}
+TEST(SchedulerStateMachineTest, TestFailedDrawForMissingHighResNeedsCommit) {
+ SchedulerSettings default_scheduler_settings;
+ StateMachine state(default_scheduler_settings);
+ state.SetCanStart();
+ state.UpdateState(state.NextAction());
+ state.CreateAndInitializeOutputSurfaceWithActivatedCommit();
+ state.SetVisible(true);
+ state.SetCanDraw(true);
+ state.SetNeedsRedraw(true);
+ EXPECT_TRUE(state.RedrawPending());
+ EXPECT_TRUE(state.BeginFrameNeeded());
+
+ state.OnBeginImplFrame(CreateBeginFrameArgsForTesting());
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_ANIMATE);
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
+ state.OnBeginImplFrameDeadline();
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE);
+ state.DidSwapBuffers();
+ state.DidSwapBuffersComplete();
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
+ EXPECT_FALSE(state.RedrawPending());
+ EXPECT_FALSE(state.CommitPending());
+
+ // Missing high res content requires a commit (but not a redraw)
+ state.DidDrawIfPossibleCompleted(DRAW_ABORTED_MISSING_HIGH_RES_CONTENT);
+ state.OnBeginImplFrame(CreateBeginFrameArgsForTesting());
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME);
+ EXPECT_FALSE(state.RedrawPending());
+ EXPECT_TRUE(state.CommitPending());
+}
+
TEST(SchedulerStateMachineTest,
TestsetNeedsRedrawDuringFailedDrawDoesNotRemoveNeedsRedraw) {
SchedulerSettings default_scheduler_settings;
@@ -218,14 +406,17 @@ TEST(SchedulerStateMachineTest,
state.SetCanDraw(true);
state.SetNeedsRedraw(true);
EXPECT_TRUE(state.RedrawPending());
- EXPECT_TRUE(state.BeginImplFrameNeeded());
- state.OnBeginImplFrame(BeginFrameArgs::CreateForTesting());
+ EXPECT_TRUE(state.BeginFrameNeeded());
+ state.OnBeginImplFrame(CreateBeginFrameArgsForTesting());
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_ANIMATE);
EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
state.OnBeginImplFrameDeadline();
// We're drawing now.
EXPECT_ACTION_UPDATE_STATE(
SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE);
+ state.DidSwapBuffers();
+ state.DidSwapBuffersComplete();
EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
EXPECT_FALSE(state.RedrawPending());
EXPECT_FALSE(state.CommitPending());
@@ -235,19 +426,21 @@ TEST(SchedulerStateMachineTest,
state.SetNeedsRedraw(true);
EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
- // Failing the draw makes us require a commit.
- state.DidDrawIfPossibleCompleted(false);
- state.OnBeginImplFrame(BeginFrameArgs::CreateForTesting());
+ // Failing the draw for animation checkerboards makes us require a commit.
+ state.DidDrawIfPossibleCompleted(DRAW_ABORTED_CHECKERBOARD_ANIMATIONS);
+ state.OnBeginImplFrame(CreateBeginFrameArgsForTesting());
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_ANIMATE);
EXPECT_ACTION_UPDATE_STATE(
SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME);
EXPECT_TRUE(state.RedrawPending());
}
-void TestFailedDrawsWillEventuallyForceADrawAfterTheNextCommit(
- bool deadline_scheduling_enabled) {
+void TestFailedDrawsEventuallyForceDrawAfterNextCommit(
+ bool main_frame_before_draw_enabled) {
SchedulerSettings scheduler_settings;
+ scheduler_settings.main_frame_before_draw_enabled =
+ main_frame_before_draw_enabled;
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());
@@ -257,69 +450,70 @@ void TestFailedDrawsWillEventuallyForceADrawAfterTheNextCommit(
// Start a commit.
state.SetNeedsCommit();
- if (!deadline_scheduling_enabled) {
- EXPECT_ACTION_UPDATE_STATE(
- SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME);
- }
EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
- state.OnBeginImplFrame(BeginFrameArgs::CreateForTesting());
- if (deadline_scheduling_enabled) {
- EXPECT_ACTION_UPDATE_STATE(
- SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME);
- }
+ state.OnBeginImplFrame(CreateBeginFrameArgsForTesting());
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME);
EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
EXPECT_TRUE(state.CommitPending());
// Then initiate a draw.
state.SetNeedsRedraw(true);
state.OnBeginImplFrameDeadline();
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_ANIMATE);
EXPECT_ACTION_UPDATE_STATE(
SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE);
// Fail the draw.
- state.DidDrawIfPossibleCompleted(false);
+ state.DidDrawIfPossibleCompleted(DRAW_ABORTED_CHECKERBOARD_ANIMATIONS);
EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
- EXPECT_TRUE(state.BeginImplFrameNeeded());
+ EXPECT_TRUE(state.BeginFrameNeeded());
EXPECT_TRUE(state.RedrawPending());
// But the commit is ongoing.
EXPECT_TRUE(state.CommitPending());
// Finish the commit. Note, we should not yet be forcing a draw, but should
// continue the commit as usual.
- state.FinishCommit();
+ state.NotifyBeginMainFrameStarted();
+ state.NotifyReadyToCommit();
EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_COMMIT);
EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
EXPECT_TRUE(state.RedrawPending());
// The redraw should be forced at the end of the next BeginImplFrame.
- state.OnBeginImplFrame(BeginFrameArgs::CreateForTesting());
+ state.OnBeginImplFrame(CreateBeginFrameArgsForTesting());
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_ANIMATE);
+ if (main_frame_before_draw_enabled) {
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME);
+ }
EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
state.OnBeginImplFrameDeadline();
EXPECT_ACTION_UPDATE_STATE(
SchedulerStateMachine::ACTION_DRAW_AND_SWAP_FORCED);
+ state.DidSwapBuffers();
+ state.DidSwapBuffersComplete();
}
TEST(SchedulerStateMachineTest,
- TestFailedDrawsWillEventuallyForceADrawAfterTheNextCommit) {
- bool deadline_scheduling_enabled = false;
- TestFailedDrawsWillEventuallyForceADrawAfterTheNextCommit(
- deadline_scheduling_enabled);
+ TestFailedDrawsEventuallyForceDrawAfterNextCommit) {
+ bool main_frame_before_draw_enabled = false;
+ TestFailedDrawsEventuallyForceDrawAfterNextCommit(
+ main_frame_before_draw_enabled);
}
TEST(SchedulerStateMachineTest,
- TestFailedDrawsWillEventuallyForceADrawAfterTheNextCommit_Deadline) {
- bool deadline_scheduling_enabled = true;
- TestFailedDrawsWillEventuallyForceADrawAfterTheNextCommit(
- deadline_scheduling_enabled);
+ TestFailedDrawsEventuallyForceDrawAfterNextCommit_CommitBeforeDraw) {
+ bool main_frame_before_draw_enabled = true;
+ TestFailedDrawsEventuallyForceDrawAfterNextCommit(
+ main_frame_before_draw_enabled);
}
-void TestFailedDrawsDoNotRestartForcedDraw(
- bool deadline_scheduling_enabled) {
+TEST(SchedulerStateMachineTest, TestFailedDrawsDoNotRestartForcedDraw) {
SchedulerSettings scheduler_settings;
- int drawLimit = 1;
+ int draw_limit = 1;
scheduler_settings.maximum_number_of_failed_draws_before_draw_is_forced_ =
- drawLimit;
- scheduler_settings.deadline_scheduling_enabled = deadline_scheduling_enabled;
+ draw_limit;
scheduler_settings.impl_side_painting = true;
StateMachine state(scheduler_settings);
state.SetCanStart();
@@ -330,39 +524,34 @@ void TestFailedDrawsDoNotRestartForcedDraw(
// Start a commit.
state.SetNeedsCommit();
- if (!deadline_scheduling_enabled) {
- EXPECT_ACTION_UPDATE_STATE(
- SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME);
- }
EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
- state.OnBeginImplFrame(BeginFrameArgs::CreateForTesting());
- if (deadline_scheduling_enabled) {
- EXPECT_ACTION_UPDATE_STATE(
- SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME);
- }
+ state.OnBeginImplFrame(CreateBeginFrameArgsForTesting());
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME);
EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
EXPECT_TRUE(state.CommitPending());
// Then initiate a draw.
state.SetNeedsRedraw(true);
state.OnBeginImplFrameDeadline();
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_ANIMATE);
EXPECT_ACTION_UPDATE_STATE(
SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE);
// Fail the draw enough times to force a redraw,
// then once more for good measure.
- for (int i = 0; i < drawLimit; ++i)
- state.DidDrawIfPossibleCompleted(false);
- state.DidDrawIfPossibleCompleted(false);
+ for (int i = 0; i < draw_limit + 1; ++i)
+ state.DidDrawIfPossibleCompleted(DRAW_ABORTED_CHECKERBOARD_ANIMATIONS);
EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
- EXPECT_TRUE(state.BeginImplFrameNeeded());
+ EXPECT_TRUE(state.BeginFrameNeeded());
EXPECT_TRUE(state.RedrawPending());
// But the commit is ongoing.
EXPECT_TRUE(state.CommitPending());
EXPECT_TRUE(state.ForcedRedrawState() ==
SchedulerStateMachine::FORCED_REDRAW_STATE_WAITING_FOR_COMMIT);
- state.FinishCommit();
+ state.NotifyBeginMainFrameStarted();
+ state.NotifyReadyToCommit();
EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_COMMIT);
EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
EXPECT_TRUE(state.RedrawPending());
@@ -374,28 +563,13 @@ void TestFailedDrawsDoNotRestartForcedDraw(
// After failing additional draws, we should still be in a forced
// redraw, but not back in WAITING_FOR_COMMIT.
- for (int i = 0; i < drawLimit; ++i)
- state.DidDrawIfPossibleCompleted(false);
- state.DidDrawIfPossibleCompleted(false);
+ for (int i = 0; i < draw_limit + 1; ++i)
+ state.DidDrawIfPossibleCompleted(DRAW_ABORTED_CHECKERBOARD_ANIMATIONS);
EXPECT_TRUE(state.RedrawPending());
EXPECT_TRUE(state.ForcedRedrawState() ==
SchedulerStateMachine::FORCED_REDRAW_STATE_WAITING_FOR_ACTIVATION);
}
-TEST(SchedulerStateMachineTest,
- TestFailedDrawsDoNotRestartForcedDraw) {
- bool deadline_scheduling_enabled = false;
- TestFailedDrawsDoNotRestartForcedDraw(
- deadline_scheduling_enabled);
-}
-
-TEST(SchedulerStateMachineTest,
- TestFailedDrawsDoNotRestartForcedDraw_Deadline) {
- bool deadline_scheduling_enabled = true;
- TestFailedDrawsDoNotRestartForcedDraw(
- deadline_scheduling_enabled);
-}
-
TEST(SchedulerStateMachineTest, TestFailedDrawIsRetriedInNextBeginImplFrame) {
SchedulerSettings default_scheduler_settings;
StateMachine state(default_scheduler_settings);
@@ -407,24 +581,26 @@ TEST(SchedulerStateMachineTest, TestFailedDrawIsRetriedInNextBeginImplFrame) {
// Start a draw.
state.SetNeedsRedraw(true);
- EXPECT_TRUE(state.BeginImplFrameNeeded());
- state.OnBeginImplFrame(BeginFrameArgs::CreateForTesting());
+ EXPECT_TRUE(state.BeginFrameNeeded());
+ state.OnBeginImplFrame(CreateBeginFrameArgsForTesting());
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_ANIMATE);
EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
state.OnBeginImplFrameDeadline();
EXPECT_TRUE(state.RedrawPending());
EXPECT_ACTION_UPDATE_STATE(
SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE);
- // Fail the draw
- state.DidDrawIfPossibleCompleted(false);
+ // Failing the draw for animation checkerboards makes us require a commit.
+ state.DidDrawIfPossibleCompleted(DRAW_ABORTED_CHECKERBOARD_ANIMATIONS);
EXPECT_ACTION_UPDATE_STATE(
SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME);
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_TRUE(state.BeginImplFrameNeeded());
- state.OnBeginImplFrame(BeginFrameArgs::CreateForTesting());
+ EXPECT_TRUE(state.BeginFrameNeeded());
+ state.OnBeginImplFrame(CreateBeginFrameArgsForTesting());
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_ANIMATE);
EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
// We should try to draw again at the end of the next BeginImplFrame on
@@ -432,6 +608,8 @@ TEST(SchedulerStateMachineTest, TestFailedDrawIsRetriedInNextBeginImplFrame) {
state.OnBeginImplFrameDeadline();
EXPECT_ACTION_UPDATE_STATE(
SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE);
+ state.DidSwapBuffers();
+ state.DidSwapBuffersComplete();
EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
}
@@ -446,14 +624,17 @@ TEST(SchedulerStateMachineTest, TestDoestDrawTwiceInSameFrame) {
state.SetNeedsRedraw(true);
// Draw the first frame.
- EXPECT_TRUE(state.BeginImplFrameNeeded());
- state.OnBeginImplFrame(BeginFrameArgs::CreateForTesting());
+ EXPECT_TRUE(state.BeginFrameNeeded());
+ state.OnBeginImplFrame(CreateBeginFrameArgsForTesting());
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_ANIMATE);
EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
state.OnBeginImplFrameDeadline();
EXPECT_ACTION_UPDATE_STATE(
SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE);
- state.DidDrawIfPossibleCompleted(true);
+ state.DidSwapBuffers();
+ state.DidDrawIfPossibleCompleted(DRAW_SUCCESS);
+ state.DidSwapBuffersComplete();
EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
// Before the next BeginImplFrame, set needs redraw again.
@@ -462,19 +643,22 @@ TEST(SchedulerStateMachineTest, TestDoestDrawTwiceInSameFrame) {
EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
// Move to another frame. This should now draw.
- EXPECT_TRUE(state.BeginImplFrameNeeded());
- state.OnBeginImplFrame(BeginFrameArgs::CreateForTesting());
+ EXPECT_TRUE(state.BeginFrameNeeded());
+ state.OnBeginImplFrame(CreateBeginFrameArgsForTesting());
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_ANIMATE);
EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
state.OnBeginImplFrameDeadline();
EXPECT_ACTION_UPDATE_STATE(
SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE);
- state.DidDrawIfPossibleCompleted(true);
+ state.DidSwapBuffers();
+ state.DidDrawIfPossibleCompleted(DRAW_SUCCESS);
+ state.DidSwapBuffersComplete();
EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
// We just swapped, so we should proactively request another BeginImplFrame.
- EXPECT_TRUE(state.BeginImplFrameNeeded());
+ EXPECT_TRUE(state.BeginFrameNeeded());
}
TEST(SchedulerStateMachineTest, TestNextActionDrawsOnBeginImplFrame) {
@@ -512,60 +696,38 @@ TEST(SchedulerStateMachineTest, TestNextActionDrawsOnBeginImplFrame) {
}
}
- // When in BeginImplFrame 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.
+ // When in BeginImplFrame deadline we should always draw for SetNeedsRedraw
+ // 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;
+ StateMachine state(default_scheduler_settings);
+ state.SetCanStart();
+ state.UpdateState(state.NextAction());
+ state.CreateAndInitializeOutputSurfaceWithActivatedCommit();
+ state.SetCanDraw(true);
+ state.SetCommitState(all_commit_states[i]);
+ state.SetBeginImplFrameState(
+ SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_INSIDE_DEADLINE);
- // Skip invalid states
- if (request_readback &&
- (SchedulerStateMachine::COMMIT_STATE_WAITING_FOR_FIRST_DRAW !=
- all_commit_states[i]))
- continue;
+ state.SetNeedsRedraw(true);
+ state.SetVisible(true);
- StateMachine state(default_scheduler_settings);
- state.SetCanStart();
+ SchedulerStateMachine::Action expected_action;
+ if (all_commit_states[i] ==
+ SchedulerStateMachine::COMMIT_STATE_READY_TO_COMMIT) {
+ expected_action = SchedulerStateMachine::ACTION_COMMIT;
+ } else {
+ expected_action = SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE;
+ EXPECT_EQ(state.NextAction(), SchedulerStateMachine::ACTION_ANIMATE)
+ << *state.AsValue();
state.UpdateState(state.NextAction());
- state.CreateAndInitializeOutputSurfaceWithActivatedCommit();
- state.SetCanDraw(true);
- state.SetCommitState(all_commit_states[i]);
- state.SetBeginImplFrameState(
- SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_INSIDE_DEADLINE);
- if (request_readback) {
- state.SetNeedsForcedRedrawForReadback();
- } else {
- state.SetNeedsRedraw(true);
- state.SetVisible(true);
- }
+ }
- SchedulerStateMachine::Action expected_action;
- if (all_commit_states[i] ==
- SchedulerStateMachine::COMMIT_STATE_READY_TO_COMMIT) {
- 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_EQ(state.NextAction(), expected_action) << *state.AsValue();
- // Case 1: needs_commit=false.
- EXPECT_NE(state.BeginImplFrameNeeded(), request_readback)
- << *state.AsValue();
- EXPECT_EQ(expected_action, state.NextAction()) << *state.AsValue();
-
- // Case 2: needs_commit=true.
- state.SetNeedsCommit();
- EXPECT_NE(state.BeginImplFrameNeeded(), request_readback)
- << *state.AsValue();
- EXPECT_EQ(expected_action, state.NextAction()) << *state.AsValue();
- }
+ // Case 2: needs_commit=true.
+ state.SetNeedsCommit();
+ EXPECT_EQ(state.NextAction(), expected_action) << *state.AsValue();
}
}
@@ -618,7 +780,7 @@ TEST(SchedulerStateMachineTest, TestCanRedraw_StopsDraw) {
state.SetVisible(false);
state.SetNeedsRedraw(true);
if (j == 1)
- state.OnBeginImplFrame(BeginFrameArgs::CreateForTesting());
+ state.OnBeginImplFrame(CreateBeginFrameArgsForTesting());
state.SetCanDraw(false);
EXPECT_NE(SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE,
@@ -635,28 +797,29 @@ TEST(SchedulerStateMachineTest,
state.UpdateState(state.NextAction());
state.CreateAndInitializeOutputSurfaceWithActivatedCommit();
- state.SetCommitState(
- SchedulerStateMachine::COMMIT_STATE_WAITING_FOR_FIRST_DRAW);
state.SetActiveTreeNeedsFirstDraw(true);
state.SetNeedsCommit();
state.SetNeedsRedraw(true);
state.SetVisible(true);
state.SetCanDraw(false);
- state.OnBeginImplFrame(BeginFrameArgs::CreateForTesting());
+ state.OnBeginImplFrame(CreateBeginFrameArgsForTesting());
EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_DRAW_AND_SWAP_ABORT);
EXPECT_ACTION_UPDATE_STATE(
SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME);
EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
- state.FinishCommit();
+ state.NotifyBeginMainFrameStarted();
+ state.NotifyReadyToCommit();
EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_COMMIT);
state.OnBeginImplFrameDeadline();
EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_DRAW_AND_SWAP_ABORT);
EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
}
-TEST(SchedulerStateMachineTest, TestsetNeedsCommitIsNotLost) {
- SchedulerSettings default_scheduler_settings;
- StateMachine state(default_scheduler_settings);
+void TestSetNeedsCommitIsNotLost(bool main_frame_before_draw_enabled) {
+ SchedulerSettings scheduler_settings;
+ scheduler_settings.main_frame_before_draw_enabled =
+ main_frame_before_draw_enabled;
+ StateMachine state(scheduler_settings);
state.SetCanStart();
state.UpdateState(state.NextAction());
state.CreateAndInitializeOutputSurfaceWithActivatedCommit();
@@ -664,13 +827,13 @@ TEST(SchedulerStateMachineTest, TestsetNeedsCommitIsNotLost) {
state.SetVisible(true);
state.SetCanDraw(true);
- EXPECT_TRUE(state.BeginImplFrameNeeded());
+ EXPECT_TRUE(state.BeginFrameNeeded());
// Begin the frame.
- state.OnBeginImplFrame(BeginFrameArgs::CreateForTesting());
+ state.OnBeginImplFrame(CreateBeginFrameArgsForTesting());
EXPECT_ACTION_UPDATE_STATE(
SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME);
- EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_FRAME_IN_PROGRESS,
+ EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_BEGIN_MAIN_FRAME_SENT,
state.CommitState());
// Now, while the frame is in progress, set another commit.
@@ -678,7 +841,8 @@ TEST(SchedulerStateMachineTest, TestsetNeedsCommitIsNotLost) {
EXPECT_TRUE(state.NeedsCommit());
// Let the frame finish.
- state.FinishCommit();
+ state.NotifyBeginMainFrameStarted();
+ state.NotifyReadyToCommit();
EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_READY_TO_COMMIT,
state.CommitState());
@@ -702,27 +866,46 @@ TEST(SchedulerStateMachineTest, TestsetNeedsCommitIsNotLost) {
state.begin_impl_frame_state());
EXPECT_EQ(SchedulerStateMachine::ACTION_COMMIT, state.NextAction());
- state.OnBeginImplFrame(BeginFrameArgs::CreateForTesting());
+ state.OnBeginImplFrame(CreateBeginFrameArgsForTesting());
EXPECT_EQ(SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_BEGIN_FRAME_STARTING,
state.begin_impl_frame_state());
EXPECT_EQ(SchedulerStateMachine::ACTION_COMMIT, state.NextAction());
- // Commit and make sure we draw on next BeginImplFrame
+ // Finish the commit, then make sure we start the next commit immediately
+ // and draw on the next BeginImplFrame.
EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_COMMIT);
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_ANIMATE);
+ if (main_frame_before_draw_enabled) {
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME);
+ }
EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
+
state.OnBeginImplFrameDeadline();
- EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_WAITING_FOR_FIRST_DRAW,
- state.CommitState());
- EXPECT_ACTION_UPDATE_STATE(
- SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE);
- state.DidDrawIfPossibleCompleted(true);
- // Verify that another commit will start immediately after draw.
+ EXPECT_TRUE(state.active_tree_needs_first_draw());
EXPECT_ACTION_UPDATE_STATE(
- SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME);
+ SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE);
+ state.DidSwapBuffers();
+ state.DidDrawIfPossibleCompleted(DRAW_SUCCESS);
+ state.DidSwapBuffersComplete();
+ if (!main_frame_before_draw_enabled) {
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME);
+ }
EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
}
+TEST(SchedulerStateMachineTest, TestSetNeedsCommitIsNotLost) {
+ bool main_frame_before_draw_enabled = false;
+ TestSetNeedsCommitIsNotLost(main_frame_before_draw_enabled);
+}
+
+TEST(SchedulerStateMachineTest, TestSetNeedsCommitIsNotLost_CommitBeforeDraw) {
+ bool main_frame_before_draw_enabled = true;
+ TestSetNeedsCommitIsNotLost(main_frame_before_draw_enabled);
+}
+
TEST(SchedulerStateMachineTest, TestFullCycle) {
SchedulerSettings default_scheduler_settings;
StateMachine state(default_scheduler_settings);
@@ -736,23 +919,23 @@ TEST(SchedulerStateMachineTest, TestFullCycle) {
state.SetNeedsCommit();
// Begin the frame.
- state.OnBeginImplFrame(BeginFrameArgs::CreateForTesting());
+ state.OnBeginImplFrame(CreateBeginFrameArgsForTesting());
EXPECT_ACTION_UPDATE_STATE(
SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME);
- EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_FRAME_IN_PROGRESS,
+ EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_BEGIN_MAIN_FRAME_SENT,
state.CommitState());
EXPECT_FALSE(state.NeedsCommit());
EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
// Tell the scheduler the frame finished.
- state.FinishCommit();
+ state.NotifyBeginMainFrameStarted();
+ state.NotifyReadyToCommit();
EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_READY_TO_COMMIT,
state.CommitState());
// Commit.
EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_COMMIT);
- EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_WAITING_FOR_FIRST_DRAW,
- state.CommitState());
+ EXPECT_TRUE(state.active_tree_needs_first_draw());
EXPECT_TRUE(state.needs_redraw());
// Expect to do nothing until BeginImplFrame deadline
@@ -760,9 +943,12 @@ TEST(SchedulerStateMachineTest, TestFullCycle) {
// At BeginImplFrame deadline, draw.
state.OnBeginImplFrameDeadline();
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_ANIMATE);
EXPECT_ACTION_UPDATE_STATE(
SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE);
- state.DidDrawIfPossibleCompleted(true);
+ state.DidSwapBuffers();
+ state.DidDrawIfPossibleCompleted(DRAW_SUCCESS);
+ state.DidSwapBuffersComplete();
// Should be synchronized, no draw needed, no action needed.
EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
@@ -783,10 +969,10 @@ TEST(SchedulerStateMachineTest, TestFullCycleWithCommitRequestInbetween) {
state.SetNeedsCommit();
// Begin the frame.
- state.OnBeginImplFrame(BeginFrameArgs::CreateForTesting());
+ state.OnBeginImplFrame(CreateBeginFrameArgsForTesting());
EXPECT_ACTION_UPDATE_STATE(
SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME);
- EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_FRAME_IN_PROGRESS,
+ EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_BEGIN_MAIN_FRAME_SENT,
state.CommitState());
EXPECT_FALSE(state.NeedsCommit());
EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
@@ -796,14 +982,14 @@ TEST(SchedulerStateMachineTest, TestFullCycleWithCommitRequestInbetween) {
EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
// Tell the scheduler the frame finished.
- state.FinishCommit();
+ state.NotifyBeginMainFrameStarted();
+ state.NotifyReadyToCommit();
EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_READY_TO_COMMIT,
state.CommitState());
// First commit.
EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_COMMIT);
- EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_WAITING_FOR_FIRST_DRAW,
- state.CommitState());
+ EXPECT_TRUE(state.active_tree_needs_first_draw());
EXPECT_TRUE(state.needs_redraw());
// Expect to do nothing until BeginImplFrame deadline.
@@ -811,9 +997,12 @@ TEST(SchedulerStateMachineTest, TestFullCycleWithCommitRequestInbetween) {
// At BeginImplFrame deadline, draw.
state.OnBeginImplFrameDeadline();
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_ANIMATE);
EXPECT_ACTION_UPDATE_STATE(
SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE);
- state.DidDrawIfPossibleCompleted(true);
+ state.DidSwapBuffers();
+ state.DidDrawIfPossibleCompleted(DRAW_SUCCESS);
+ state.DidSwapBuffersComplete();
// Should be synchronized, no draw needed, no action needed.
EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
@@ -821,7 +1010,7 @@ TEST(SchedulerStateMachineTest, TestFullCycleWithCommitRequestInbetween) {
EXPECT_FALSE(state.needs_redraw());
// Next BeginImplFrame should initiate second commit.
- state.OnBeginImplFrame(BeginFrameArgs::CreateForTesting());
+ state.OnBeginImplFrame(CreateBeginFrameArgsForTesting());
EXPECT_ACTION_UPDATE_STATE(
SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME);
}
@@ -849,10 +1038,10 @@ TEST(SchedulerStateMachineTest, TestGoesInvisibleBeforeFinishCommit) {
state.SetNeedsCommit();
// Begin the frame while visible.
- state.OnBeginImplFrame(BeginFrameArgs::CreateForTesting());
+ state.OnBeginImplFrame(CreateBeginFrameArgsForTesting());
EXPECT_ACTION_UPDATE_STATE(
SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME);
- EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_FRAME_IN_PROGRESS,
+ EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_BEGIN_MAIN_FRAME_SENT,
state.CommitState());
EXPECT_FALSE(state.NeedsCommit());
EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
@@ -879,12 +1068,12 @@ TEST(SchedulerStateMachineTest, TestGoesInvisibleBeforeFinishCommit) {
EXPECT_TRUE(state.NeedsCommit());
// Start a new frame.
- state.OnBeginImplFrame(BeginFrameArgs::CreateForTesting());
+ state.OnBeginImplFrame(CreateBeginFrameArgsForTesting());
EXPECT_ACTION_UPDATE_STATE(
SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME);
// We should be starting the commit now.
- EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_FRAME_IN_PROGRESS,
+ EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_BEGIN_MAIN_FRAME_SENT,
state.CommitState());
EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
}
@@ -903,7 +1092,7 @@ TEST(SchedulerStateMachineTest, AbortBeginMainFrameAndCancelCommit) {
EXPECT_ACTION_UPDATE_STATE(
SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME);
- EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_FRAME_IN_PROGRESS,
+ EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_BEGIN_MAIN_FRAME_SENT,
state.CommitState());
EXPECT_FALSE(state.NeedsCommit());
EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction());
@@ -918,11 +1107,14 @@ TEST(SchedulerStateMachineTest, AbortBeginMainFrameAndCancelCommit) {
// Start a new frame; draw because this is the first frame since output
// surface init'd.
- state.OnBeginImplFrame(BeginFrameArgs::CreateForTesting());
+ state.OnBeginImplFrame(CreateBeginFrameArgsForTesting());
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_ANIMATE);
EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
state.OnBeginImplFrameDeadline();
EXPECT_ACTION_UPDATE_STATE(
SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE);
+ state.DidSwapBuffers();
+ state.DidSwapBuffersComplete();
// Verify another commit doesn't start on another frame either.
EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_IDLE, state.CommitState());
@@ -936,6 +1128,152 @@ TEST(SchedulerStateMachineTest, AbortBeginMainFrameAndCancelCommit) {
state.NextAction());
}
+TEST(SchedulerStateMachineTest,
+ AbortBeginMainFrameAndCancelCommitWhenInvisible) {
+ SchedulerSettings default_scheduler_settings;
+ StateMachine state(default_scheduler_settings);
+ state.SetCanStart();
+ state.UpdateState(state.NextAction());
+ state.DidCreateAndInitializeOutputSurface();
+ state.SetVisible(true);
+ state.SetCanDraw(true);
+
+ // Get into a begin frame / commit state.
+ state.SetNeedsCommit();
+
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME);
+ EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_BEGIN_MAIN_FRAME_SENT,
+ state.CommitState());
+ EXPECT_FALSE(state.NeedsCommit());
+ EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction());
+
+ // Become invisible and abort BeginMainFrame.
+ state.SetVisible(false);
+ state.BeginMainFrameAborted(true);
+
+ // Verify that another commit doesn't start on the same frame.
+ EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_IDLE, state.CommitState());
+ EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction());
+ EXPECT_FALSE(state.NeedsCommit());
+
+ // Become visible and start a new frame.
+ state.SetVisible(true);
+ state.OnBeginImplFrame(CreateBeginFrameArgsForTesting());
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_ANIMATE);
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
+
+ // Draw because this is the first frame since output surface init'd.
+ state.OnBeginImplFrameDeadline();
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE);
+ state.DidSwapBuffers();
+ state.DidSwapBuffersComplete();
+
+ // Verify another commit doesn't start on another frame either.
+ EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_IDLE, state.CommitState());
+ EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction());
+ EXPECT_FALSE(state.NeedsCommit());
+
+ // Verify another commit can start if requested, though.
+ state.SetNeedsCommit();
+ EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_IDLE, state.CommitState());
+ EXPECT_EQ(SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME,
+ state.NextAction());
+}
+
+TEST(SchedulerStateMachineTest,
+ AbortBeginMainFrameAndRequestCommitWhenInvisible) {
+ SchedulerSettings default_scheduler_settings;
+ StateMachine state(default_scheduler_settings);
+ state.SetCanStart();
+ state.UpdateState(state.NextAction());
+ state.DidCreateAndInitializeOutputSurface();
+ state.SetVisible(true);
+ state.SetCanDraw(true);
+
+ // Get into a begin frame / commit state.
+ state.SetNeedsCommit();
+
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME);
+ EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_BEGIN_MAIN_FRAME_SENT,
+ state.CommitState());
+ EXPECT_FALSE(state.NeedsCommit());
+ EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction());
+
+ // Become invisible and abort BeginMainFrame.
+ state.SetVisible(false);
+ state.BeginMainFrameAborted(true);
+
+ // Verify that another commit doesn't start on the same frame.
+ EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_IDLE, state.CommitState());
+ EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction());
+ EXPECT_FALSE(state.NeedsCommit());
+
+ // Asking for a commit while not visible won't make it happen.
+ state.SetNeedsCommit();
+ EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_IDLE, state.CommitState());
+ EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction());
+ EXPECT_TRUE(state.NeedsCommit());
+
+ // Become visible but nothing happens until the next frame.
+ state.SetVisible(true);
+ EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_IDLE, state.CommitState());
+ EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction());
+ EXPECT_TRUE(state.NeedsCommit());
+
+ // We should get that commit when we begin the next frame.
+ state.OnBeginImplFrame(CreateBeginFrameArgsForTesting());
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_ANIMATE);
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME);
+}
+
+TEST(SchedulerStateMachineTest,
+ AbortBeginMainFrameAndRequestCommitAndBeginImplFrameWhenInvisible) {
+ SchedulerSettings default_scheduler_settings;
+ StateMachine state(default_scheduler_settings);
+ state.SetCanStart();
+ state.UpdateState(state.NextAction());
+ state.DidCreateAndInitializeOutputSurface();
+ state.SetVisible(true);
+ state.SetCanDraw(true);
+
+ // Get into a begin frame / commit state.
+ state.SetNeedsCommit();
+
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME);
+ EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_BEGIN_MAIN_FRAME_SENT,
+ state.CommitState());
+ EXPECT_FALSE(state.NeedsCommit());
+ EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction());
+
+ // Become invisible and abort BeginMainFrame.
+ state.SetVisible(false);
+ state.BeginMainFrameAborted(true);
+
+ // Asking for a commit while not visible won't make it happen.
+ state.SetNeedsCommit();
+ EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_IDLE, state.CommitState());
+ EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction());
+ EXPECT_TRUE(state.NeedsCommit());
+
+ // Begin a frame when not visible, the scheduler animates but does not commit.
+ state.OnBeginImplFrame(CreateBeginFrameArgsForTesting());
+ EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_IDLE, state.CommitState());
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_ANIMATE);
+ EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction());
+ EXPECT_TRUE(state.NeedsCommit());
+
+ // Become visible and the requested commit happens immediately.
+ state.SetVisible(true);
+ EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_IDLE, state.CommitState());
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME);
+}
+
TEST(SchedulerStateMachineTest, TestFirstContextCreation) {
SchedulerSettings default_scheduler_settings;
StateMachine state(default_scheduler_settings);
@@ -949,14 +1287,14 @@ TEST(SchedulerStateMachineTest, TestFirstContextCreation) {
EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
// Check that the first init does not SetNeedsCommit.
- state.OnBeginImplFrame(BeginFrameArgs::CreateForTesting());
+ state.OnBeginImplFrame(CreateBeginFrameArgsForTesting());
EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
state.OnBeginImplFrameDeadline();
EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
// Check that a needs commit initiates a BeginMainFrame.
state.SetNeedsCommit();
- state.OnBeginImplFrame(BeginFrameArgs::CreateForTesting());
+ state.OnBeginImplFrame(CreateBeginFrameArgsForTesting());
EXPECT_ACTION_UPDATE_STATE(
SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME);
}
@@ -986,7 +1324,7 @@ TEST(SchedulerStateMachineTest, TestContextLostWhenCompletelyIdle) {
state.CreateAndInitializeOutputSurfaceWithActivatedCommit();
// When the context is recreated, we should begin a commit.
- state.OnBeginImplFrame(BeginFrameArgs::CreateForTesting());
+ state.OnBeginImplFrame(CreateBeginFrameArgsForTesting());
EXPECT_ACTION_UPDATE_STATE(
SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME);
}
@@ -1010,14 +1348,14 @@ TEST(SchedulerStateMachineTest,
EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
// Once context recreation begins, nothing should happen.
- state.OnBeginImplFrame(BeginFrameArgs::CreateForTesting());
+ state.OnBeginImplFrame(CreateBeginFrameArgsForTesting());
EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
state.OnBeginImplFrameDeadline();
EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
// While context is recreating, commits shouldn't begin.
state.SetNeedsCommit();
- state.OnBeginImplFrame(BeginFrameArgs::CreateForTesting());
+ state.OnBeginImplFrame(CreateBeginFrameArgsForTesting());
EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
state.OnBeginImplFrameDeadline();
EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
@@ -1030,9 +1368,11 @@ TEST(SchedulerStateMachineTest,
EXPECT_ACTION_UPDATE_STATE(
SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME);
EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
- EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_FRAME_IN_PROGRESS,
+ EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_BEGIN_MAIN_FRAME_SENT,
state.CommitState());
- state.FinishCommit();
+
+ state.NotifyBeginMainFrameStarted();
+ state.NotifyReadyToCommit();
EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_COMMIT);
EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
// Finishing the first commit after initializing an output surface should
@@ -1041,7 +1381,8 @@ TEST(SchedulerStateMachineTest,
// Once the context is recreated, whether we draw should be based on
// SetCanDraw.
- state.OnBeginImplFrame(BeginFrameArgs::CreateForTesting());
+ state.OnBeginImplFrame(CreateBeginFrameArgsForTesting());
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_ANIMATE);
EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
state.OnBeginImplFrameDeadline();
EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE,
@@ -1054,9 +1395,8 @@ TEST(SchedulerStateMachineTest,
state.NextAction());
}
-void TestContextLostWhileCommitInProgress(bool deadline_scheduling_enabled) {
+TEST(SchedulerStateMachineTest, TestContextLostWhileCommitInProgress) {
SchedulerSettings scheduler_settings;
- scheduler_settings.deadline_scheduling_enabled = deadline_scheduling_enabled;
StateMachine state(scheduler_settings);
state.SetCanStart();
state.UpdateState(state.NextAction());
@@ -1066,22 +1406,19 @@ void TestContextLostWhileCommitInProgress(bool deadline_scheduling_enabled) {
// Get a commit in flight.
state.SetNeedsCommit();
- if (!deadline_scheduling_enabled) {
- EXPECT_ACTION_UPDATE_STATE(
- SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME);
- }
// Set damage and expect a draw.
state.SetNeedsRedraw(true);
- state.OnBeginImplFrame(BeginFrameArgs::CreateForTesting());
- if (deadline_scheduling_enabled) {
- EXPECT_ACTION_UPDATE_STATE(
- SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME);
- }
+ state.OnBeginImplFrame(CreateBeginFrameArgsForTesting());
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_ANIMATE);
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME);
EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
state.OnBeginImplFrameDeadline();
EXPECT_ACTION_UPDATE_STATE(
SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE);
+ state.DidSwapBuffers();
+ state.DidSwapBuffersComplete();
EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
// Cause a lost context while the BeginMainFrame is in flight.
@@ -1092,55 +1429,40 @@ void TestContextLostWhileCommitInProgress(bool deadline_scheduling_enabled) {
EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction());
// Finish the frame, and commit.
- state.FinishCommit();
+ state.NotifyBeginMainFrameStarted();
+ state.NotifyReadyToCommit();
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_TRUE(state.active_tree_needs_first_draw());
EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_DRAW_AND_SWAP_ABORT);
- // Expect to be told to begin context recreation, independent of
- // BeginImplFrame state.
+ // Expect to begin context recreation only in BEGIN_IMPL_FRAME_STATE_IDLE
EXPECT_EQ(SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_IDLE,
state.begin_impl_frame_state());
EXPECT_EQ(SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION,
state.NextAction());
- state.OnBeginImplFrame(BeginFrameArgs::CreateForTesting());
+ state.OnBeginImplFrame(CreateBeginFrameArgsForTesting());
EXPECT_EQ(SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_BEGIN_FRAME_STARTING,
state.begin_impl_frame_state());
- EXPECT_EQ(SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION,
- state.NextAction());
+ EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction());
state.OnBeginImplFrameDeadlinePending();
EXPECT_EQ(SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_INSIDE_BEGIN_FRAME,
state.begin_impl_frame_state());
- EXPECT_EQ(SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION,
- state.NextAction());
+ EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction());
state.OnBeginImplFrameDeadline();
EXPECT_EQ(SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_INSIDE_DEADLINE,
state.begin_impl_frame_state());
- EXPECT_EQ(SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION,
- state.NextAction());
-}
-
-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);
+ EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction());
}
-void TestContextLostWhileCommitInProgressAndAnotherCommitRequested(
- bool deadline_scheduling_enabled) {
+TEST(SchedulerStateMachineTest,
+ TestContextLostWhileCommitInProgressAndAnotherCommitRequested) {
SchedulerSettings scheduler_settings;
- scheduler_settings.deadline_scheduling_enabled = deadline_scheduling_enabled;
StateMachine state(scheduler_settings);
state.SetCanStart();
state.UpdateState(state.NextAction());
@@ -1150,23 +1472,20 @@ void TestContextLostWhileCommitInProgressAndAnotherCommitRequested(
// Get a commit in flight.
state.SetNeedsCommit();
- if (!deadline_scheduling_enabled) {
- EXPECT_ACTION_UPDATE_STATE(
- SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME);
- }
EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
// Set damage and expect a draw.
state.SetNeedsRedraw(true);
- state.OnBeginImplFrame(BeginFrameArgs::CreateForTesting());
- if (deadline_scheduling_enabled) {
- EXPECT_ACTION_UPDATE_STATE(
- SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME);
- }
+ state.OnBeginImplFrame(CreateBeginFrameArgsForTesting());
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_ANIMATE);
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME);
EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
state.OnBeginImplFrameDeadline();
EXPECT_ACTION_UPDATE_STATE(
SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE);
+ state.DidSwapBuffers();
+ state.DidSwapBuffersComplete();
EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
// Cause a lost context while the BeginMainFrame is in flight.
@@ -1178,116 +1497,55 @@ void TestContextLostWhileCommitInProgressAndAnotherCommitRequested(
EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
// Finish the frame, and commit.
- state.FinishCommit();
+ state.NotifyBeginMainFrameStarted();
+ state.NotifyReadyToCommit();
EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_COMMIT);
- EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_WAITING_FOR_FIRST_DRAW,
- state.CommitState());
+ EXPECT_TRUE(state.active_tree_needs_first_draw());
// 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
- // BeginImplFrame state
+ // Expect to begin context recreation only in BEGIN_IMPL_FRAME_STATE_IDLE
EXPECT_EQ(SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_IDLE,
state.begin_impl_frame_state());
EXPECT_EQ(SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION,
state.NextAction());
- state.OnBeginImplFrame(BeginFrameArgs::CreateForTesting());
+ state.OnBeginImplFrame(CreateBeginFrameArgsForTesting());
EXPECT_EQ(SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_BEGIN_FRAME_STARTING,
state.begin_impl_frame_state());
- EXPECT_EQ(SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION,
- state.NextAction());
+ EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction());
state.OnBeginImplFrameDeadlinePending();
EXPECT_EQ(SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_INSIDE_BEGIN_FRAME,
state.begin_impl_frame_state());
- EXPECT_EQ(SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION,
- state.NextAction());
+ EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction());
state.OnBeginImplFrameDeadline();
EXPECT_EQ(SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_INSIDE_DEADLINE,
state.begin_impl_frame_state());
- EXPECT_EQ(SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION,
- state.NextAction());
+ EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction());
- // After we get a new output surface, the commit flow should start.
+ state.OnBeginImplFrameIdle();
EXPECT_ACTION_UPDATE_STATE(
SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION);
+
+ // After we get a new output surface, the commit flow should start.
state.CreateAndInitializeOutputSurfaceWithActivatedCommit();
- state.OnBeginImplFrameIdle();
- state.OnBeginImplFrame(BeginFrameArgs::CreateForTesting());
+ state.OnBeginImplFrame(CreateBeginFrameArgsForTesting());
EXPECT_ACTION_UPDATE_STATE(
SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME);
EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
- state.FinishCommit();
+ state.NotifyBeginMainFrameStarted();
+ state.NotifyReadyToCommit();
EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_COMMIT);
EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
state.OnBeginImplFrameDeadline();
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_ANIMATE);
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) {
- SchedulerSettings default_scheduler_settings;
- StateMachine state(default_scheduler_settings);
- state.SetCanStart();
- state.UpdateState(state.NextAction());
- state.CreateAndInitializeOutputSurfaceWithActivatedCommit();
- state.SetVisible(true);
- state.SetCanDraw(true);
-
- // Cause a lost context lost.
- state.DidLoseOutputSurface();
-
- // Ask a forced redraw for readback and verify it ocurrs.
- state.SetCommitState(
- SchedulerStateMachine::COMMIT_STATE_WAITING_FOR_FIRST_DRAW);
- state.SetNeedsForcedRedrawForReadback();
- state.OnBeginImplFrame(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.
- EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_FRAME_IN_PROGRESS,
- state.CommitState());
- state.FinishCommit();
- EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_COMMIT);
-
- // We don't yet have an output surface, so we the draw and swap should abort.
- EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_DRAW_AND_SWAP_ABORT);
-
- // Expect to be told to begin context recreation, independent of
- // BeginImplFrame state
- EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_IDLE, state.CommitState());
- EXPECT_EQ(SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION,
- state.NextAction());
-
- state.OnBeginImplFrameDeadline();
- 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);
+ state.DidSwapBuffers();
+ state.DidSwapBuffersComplete();
EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
}
@@ -1310,83 +1568,69 @@ TEST(SchedulerStateMachineTest, DontDrawBeforeCommitAfterLostOutputSurface) {
state.DidCreateAndInitializeOutputSurface();
EXPECT_FALSE(state.RedrawPending());
- state.OnBeginImplFrame(BeginFrameArgs::CreateForTesting());
+ state.OnBeginImplFrame(CreateBeginFrameArgsForTesting());
EXPECT_EQ(SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME,
state.NextAction());
}
TEST(SchedulerStateMachineTest,
- TestSendBeginMainFrameWhenInvisibleAndForceCommit) {
- SchedulerSettings default_scheduler_settings;
- StateMachine state(default_scheduler_settings);
+ TestPendingActivationsShouldBeForcedAfterLostOutputSurface) {
+ SchedulerSettings settings;
+ settings.impl_side_painting = true;
+ StateMachine state(settings);
state.SetCanStart();
state.UpdateState(state.NextAction());
state.CreateAndInitializeOutputSurfaceWithActivatedCommit();
- state.SetVisible(false);
- state.SetNeedsCommit();
- state.SetNeedsForcedCommitForReadback();
- EXPECT_EQ(SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME,
- state.NextAction());
-}
-
-TEST(SchedulerStateMachineTest,
- TestSendBeginMainFrameWhenCanStartFalseAndForceCommit) {
- SchedulerSettings default_scheduler_settings;
- StateMachine state(default_scheduler_settings);
state.SetVisible(true);
state.SetCanDraw(true);
- state.SetNeedsCommit();
- state.SetNeedsForcedCommitForReadback();
- EXPECT_EQ(SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME,
- state.NextAction());
+
+ state.SetCommitState(
+ SchedulerStateMachine::COMMIT_STATE_BEGIN_MAIN_FRAME_SENT);
+
+ // Cause a lost context.
+ state.DidLoseOutputSurface();
+
+ state.NotifyBeginMainFrameStarted();
+ state.NotifyReadyToCommit();
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_COMMIT);
+
+ EXPECT_TRUE(state.PendingActivationsShouldBeForced());
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_ACTIVATE_PENDING_TREE);
+
+ EXPECT_TRUE(state.PendingDrawsShouldBeAborted());
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_DRAW_AND_SWAP_ABORT);
}
-TEST(SchedulerStateMachineTest, TestFinishCommitWhenCommitInProgress) {
+TEST(SchedulerStateMachineTest, TestNoBeginMainFrameWhenInvisible) {
SchedulerSettings default_scheduler_settings;
StateMachine state(default_scheduler_settings);
state.SetCanStart();
state.UpdateState(state.NextAction());
state.CreateAndInitializeOutputSurfaceWithActivatedCommit();
state.SetVisible(false);
- state.SetCommitState(SchedulerStateMachine::COMMIT_STATE_FRAME_IN_PROGRESS);
state.SetNeedsCommit();
-
- state.FinishCommit();
- EXPECT_EQ(SchedulerStateMachine::ACTION_COMMIT, state.NextAction());
- state.UpdateState(state.NextAction());
-
- EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_WAITING_FOR_FIRST_DRAW,
- state.CommitState());
- EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_DRAW_AND_SWAP_ABORT);
+ EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction());
}
-TEST(SchedulerStateMachineTest, TestFinishCommitWhenForcedCommitInProgress) {
+TEST(SchedulerStateMachineTest, TestFinishCommitWhenCommitInProgress) {
SchedulerSettings default_scheduler_settings;
StateMachine state(default_scheduler_settings);
state.SetCanStart();
state.UpdateState(state.NextAction());
state.CreateAndInitializeOutputSurfaceWithActivatedCommit();
state.SetVisible(false);
- state.SetCommitState(SchedulerStateMachine::COMMIT_STATE_FRAME_IN_PROGRESS);
+ state.SetCommitState(
+ SchedulerStateMachine::COMMIT_STATE_BEGIN_MAIN_FRAME_SENT);
state.SetNeedsCommit();
- state.SetNeedsForcedCommitForReadback();
- // The commit for readback interupts the normal commit.
- state.FinishCommit();
- EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_COMMIT);
-
- EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_WAITING_FOR_FIRST_DRAW,
- state.CommitState());
- EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_DRAW_AND_READBACK);
-
- // When the readback interrupts the normal commit, we should not get
- // another BeginMainFrame when the readback completes.
- EXPECT_NE(SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME,
- state.NextAction());
+ state.NotifyBeginMainFrameStarted();
+ state.NotifyReadyToCommit();
+ EXPECT_EQ(SchedulerStateMachine::ACTION_COMMIT, state.NextAction());
+ state.UpdateState(state.NextAction());
- // The normal commit can then proceed.
- state.FinishCommit();
- EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_COMMIT);
+ EXPECT_TRUE(state.active_tree_needs_first_draw());
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_DRAW_AND_SWAP_ABORT);
}
TEST(SchedulerStateMachineTest, TestInitialActionsWhenContextLost) {
@@ -1415,295 +1659,202 @@ TEST(SchedulerStateMachineTest, TestInitialActionsWhenContextLost) {
state.SetVisible(false);
EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction())
<< *state.AsValue();
-
- // If there is a forced commit, however, we could be blocking a readback
- // on the main thread, so we need to unblock it before we can get our
- // output surface, even if we are not visible.
- state.SetNeedsForcedCommitForReadback();
- EXPECT_EQ(
- SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME, state.NextAction())
- << *state.AsValue();
}
-TEST(SchedulerStateMachineTest, TestImmediateFinishCommit) {
+TEST(SchedulerStateMachineTest, ReportIfNotDrawing) {
SchedulerSettings default_scheduler_settings;
StateMachine state(default_scheduler_settings);
state.SetCanStart();
state.UpdateState(state.NextAction());
state.CreateAndInitializeOutputSurfaceWithActivatedCommit();
- state.SetVisible(true);
- state.SetCanDraw(true);
- // Schedule a readback, commit it, draw it.
- state.SetNeedsCommit();
- state.SetNeedsForcedCommitForReadback();
- EXPECT_ACTION_UPDATE_STATE(
- SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME);
- state.FinishCommit();
-
- EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_READY_TO_COMMIT,
- state.CommitState());
- EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_COMMIT);
+ state.SetCanDraw(true);
+ state.SetVisible(true);
+ EXPECT_FALSE(state.PendingDrawsShouldBeAborted());
- EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_WAITING_FOR_FIRST_DRAW,
- state.CommitState());
+ state.SetCanDraw(false);
+ state.SetVisible(true);
+ EXPECT_TRUE(state.PendingDrawsShouldBeAborted());
- EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_DRAW_AND_READBACK);
- state.DidDrawIfPossibleCompleted(true);
+ state.SetCanDraw(true);
+ state.SetVisible(false);
+ EXPECT_TRUE(state.PendingDrawsShouldBeAborted());
- EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
+ state.SetCanDraw(false);
+ state.SetVisible(false);
+ EXPECT_TRUE(state.PendingDrawsShouldBeAborted());
- // Should be waiting for the normal BeginMainFrame.
- EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_FRAME_IN_PROGRESS,
- state.CommitState());
+ state.SetCanDraw(true);
+ state.SetVisible(true);
+ EXPECT_FALSE(state.PendingDrawsShouldBeAborted());
}
-void TestImmediateFinishCommitDuringCommit(bool deadline_scheduling_enabled) {
- SchedulerSettings scheduler_settings;
- scheduler_settings.deadline_scheduling_enabled = deadline_scheduling_enabled;
- StateMachine state(scheduler_settings);
+TEST(SchedulerStateMachineTest, TestTriggerDeadlineEarlyAfterAbortedCommit) {
+ SchedulerSettings settings;
+ settings.impl_side_painting = true;
+ StateMachine state(settings);
state.SetCanStart();
state.UpdateState(state.NextAction());
state.CreateAndInitializeOutputSurfaceWithActivatedCommit();
state.SetVisible(true);
state.SetCanDraw(true);
- // Start a normal commit.
+ // This test mirrors what happens during the first frame of a scroll gesture.
+ // First we get the input event and a BeginFrame.
+ state.OnBeginImplFrame(CreateBeginFrameArgsForTesting());
+
+ // As a response the compositor requests a redraw and a commit to tell the
+ // main thread about the new scroll offset.
+ state.SetNeedsRedraw(true);
state.SetNeedsCommit();
- if (!deadline_scheduling_enabled) {
- EXPECT_ACTION_UPDATE_STATE(
- SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME);
- }
- EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
- // Schedule a readback, commit it, draw it.
- state.SetNeedsForcedCommitForReadback();
- if (deadline_scheduling_enabled) {
- EXPECT_ACTION_UPDATE_STATE(
- SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME);
- }
+ // We should start the commit normally.
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_ANIMATE);
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME);
EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
- state.FinishCommit();
- EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_READY_TO_COMMIT,
- state.CommitState());
- EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_COMMIT);
-
- EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_WAITING_FOR_FIRST_DRAW,
- state.CommitState());
- EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_DRAW_AND_READBACK);
- state.DidDrawIfPossibleCompleted(true);
- EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
+ // Since only the scroll offset changed, the main thread will abort the
+ // commit.
+ state.BeginMainFrameAborted(true);
- // Should be waiting for the normal BeginMainFrame.
- EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_FRAME_IN_PROGRESS,
- state.CommitState())
- << *state.AsValue();
+ // Since the commit was aborted, we should draw right away instead of waiting
+ // for the deadline.
+ EXPECT_TRUE(state.ShouldTriggerBeginImplFrameDeadlineEarly());
}
-TEST(SchedulerStateMachineTest, TestImmediateFinishCommitDuringCommit) {
- bool deadline_scheduling_enabled = false;
- TestImmediateFinishCommitDuringCommit(deadline_scheduling_enabled);
-}
+void FinishPreviousCommitAndDrawWithoutExitingDeadline(
+ StateMachine* state_ptr) {
+ // Gross, but allows us to use macros below.
+ StateMachine& state = *state_ptr;
-TEST(SchedulerStateMachineTest,
- TestImmediateFinishCommitDuringCommit_Deadline) {
- bool deadline_scheduling_enabled = true;
- TestImmediateFinishCommitDuringCommit(deadline_scheduling_enabled);
+ state.NotifyBeginMainFrameStarted();
+ state.NotifyReadyToCommit();
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_COMMIT);
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
+ state.NotifyReadyToActivate();
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_ACTIVATE_PENDING_TREE);
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
+
+ state.OnBeginImplFrame(CreateBeginFrameArgsForTesting());
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_ANIMATE);
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
+
+ EXPECT_TRUE(state.ShouldTriggerBeginImplFrameDeadlineEarly());
+ state.OnBeginImplFrameDeadline();
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE);
+ state.DidSwapBuffers();
}
-void ImmediateBeginMainFrameAbortedWhileInvisible(
- bool deadline_scheduling_enabled) {
- SchedulerSettings scheduler_settings;
- scheduler_settings.deadline_scheduling_enabled = deadline_scheduling_enabled;
- StateMachine state(scheduler_settings);
+TEST(SchedulerStateMachineTest, TestSmoothnessTakesPriority) {
+ SchedulerSettings settings;
+ settings.impl_side_painting = true;
+ StateMachine state(settings);
state.SetCanStart();
state.UpdateState(state.NextAction());
state.CreateAndInitializeOutputSurfaceWithActivatedCommit();
state.SetVisible(true);
state.SetCanDraw(true);
+ // This test ensures that impl-draws are prioritized over main thread updates
+ // in prefer smoothness mode.
+ state.SetNeedsRedraw(true);
state.SetNeedsCommit();
- if (!deadline_scheduling_enabled) {
- EXPECT_ACTION_UPDATE_STATE(
- SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME);
- }
+ state.OnBeginImplFrame(CreateBeginFrameArgsForTesting());
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_ANIMATE);
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME);
EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
- state.SetNeedsCommit();
- state.SetNeedsForcedCommitForReadback();
- if (deadline_scheduling_enabled) {
- EXPECT_ACTION_UPDATE_STATE(
- SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME);
- }
- state.FinishCommit();
-
- EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_READY_TO_COMMIT,
- state.CommitState());
- EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_COMMIT);
-
- EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_WAITING_FOR_FIRST_DRAW,
- state.CommitState());
+ // Verify the deadline is not triggered early until we enter
+ // prefer smoothness mode.
+ EXPECT_FALSE(state.ShouldTriggerBeginImplFrameDeadlineEarly());
+ state.SetSmoothnessTakesPriority(true);
+ EXPECT_TRUE(state.ShouldTriggerBeginImplFrameDeadlineEarly());
- EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_DRAW_AND_READBACK);
- state.DidDrawIfPossibleCompleted(true);
+ // Trigger the deadline.
+ state.OnBeginImplFrameDeadline();
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE);
+ state.DidSwapBuffers();
EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
+ state.DidSwapBuffersComplete();
- // Should be waiting for BeginMainFrame.
- EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_FRAME_IN_PROGRESS,
- state.CommitState())
- << *state.AsValue();
-
- // Become invisible and abort BeginMainFrame.
- state.SetVisible(false);
- state.BeginMainFrameAborted(false);
-
- // Should be back in the idle state, but needing a commit.
- EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_IDLE, state.CommitState());
- EXPECT_TRUE(state.NeedsCommit());
-}
-
-TEST(SchedulerStateMachineTest,
- ImmediateBeginMainFrameAbortedWhileInvisible) {
- bool deadline_scheduling_enabled = false;
- ImmediateBeginMainFrameAbortedWhileInvisible(
- deadline_scheduling_enabled);
-}
-
-TEST(SchedulerStateMachineTest,
- ImmediateBeginMainFrameAbortedWhileInvisible_Deadline) {
- bool deadline_scheduling_enabled = true;
- ImmediateBeginMainFrameAbortedWhileInvisible(
- deadline_scheduling_enabled);
-}
-
-TEST(SchedulerStateMachineTest, ImmediateFinishCommitWhileCantDraw) {
- SchedulerSettings default_scheduler_settings;
- StateMachine state(default_scheduler_settings);
- state.SetCanStart();
- state.UpdateState(state.NextAction());
- state.CreateAndInitializeOutputSurfaceWithActivatedCommit();
- state.SetVisible(true);
- state.SetCanDraw(false);
-
+ // Request a new commit and finish the previous one.
state.SetNeedsCommit();
- state.UpdateState(state.NextAction());
-
- state.SetNeedsCommit();
- state.SetNeedsForcedCommitForReadback();
- state.UpdateState(state.NextAction());
- state.FinishCommit();
-
- EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_READY_TO_COMMIT,
- state.CommitState());
- EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_COMMIT);
+ FinishPreviousCommitAndDrawWithoutExitingDeadline(&state);
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME);
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
+ state.DidSwapBuffersComplete();
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
- EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_WAITING_FOR_FIRST_DRAW,
- state.CommitState());
+ // Finish the previous commit and draw it.
+ FinishPreviousCommitAndDrawWithoutExitingDeadline(&state);
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
- EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_DRAW_AND_READBACK);
- state.DidDrawIfPossibleCompleted(true);
+ // Verify we do not send another BeginMainFrame if was are swap throttled
+ // and did not just swap.
+ state.SetNeedsCommit();
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
+ state.OnBeginImplFrame(CreateBeginFrameArgsForTesting());
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
+ EXPECT_FALSE(state.ShouldTriggerBeginImplFrameDeadlineEarly());
+ state.OnBeginImplFrameDeadline();
EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
}
-TEST(SchedulerStateMachineTest, ReportIfNotDrawing) {
+TEST(SchedulerStateMachineTest, TestTriggerDeadlineEarlyOnLostOutputSurface) {
SchedulerSettings default_scheduler_settings;
StateMachine state(default_scheduler_settings);
state.SetCanStart();
state.UpdateState(state.NextAction());
state.CreateAndInitializeOutputSurfaceWithActivatedCommit();
-
- state.SetCanDraw(true);
state.SetVisible(true);
- EXPECT_FALSE(state.PendingDrawsShouldBeAborted());
-
- state.SetCanDraw(false);
- state.SetVisible(true);
- EXPECT_TRUE(state.PendingDrawsShouldBeAborted());
-
- state.SetCanDraw(true);
- state.SetVisible(false);
- EXPECT_TRUE(state.PendingDrawsShouldBeAborted());
-
- state.SetCanDraw(false);
- state.SetVisible(false);
- EXPECT_TRUE(state.PendingDrawsShouldBeAborted());
-
- state.SetCanDraw(true);
- state.SetVisible(true);
- EXPECT_FALSE(state.PendingDrawsShouldBeAborted());
-}
-
-TEST(SchedulerStateMachineTest, ReportIfNotDrawingFromAcquiredTextures) {
- SchedulerSettings default_scheduler_settings;
- StateMachine state(default_scheduler_settings);
- state.SetCanStart();
- state.UpdateState(state.NextAction());
- state.CreateAndInitializeOutputSurfaceWithActivatedCommit();
state.SetCanDraw(true);
- state.SetVisible(true);
- EXPECT_FALSE(state.PendingDrawsShouldBeAborted());
-
- state.SetMainThreadNeedsLayerTextures();
- EXPECT_ACTION_UPDATE_STATE(
- SchedulerStateMachine::ACTION_ACQUIRE_LAYER_TEXTURES_FOR_MAIN_THREAD);
- EXPECT_TRUE(state.PendingDrawsShouldBeAborted());
- EXPECT_TRUE(state.PendingActivationsShouldBeForced());
state.SetNeedsCommit();
- state.OnBeginImplFrame(BeginFrameArgs::CreateForTesting());
+
+ state.OnBeginImplFrame(CreateBeginFrameArgsForTesting());
EXPECT_ACTION_UPDATE_STATE(
SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME);
- EXPECT_TRUE(state.PendingDrawsShouldBeAborted());
- EXPECT_TRUE(state.PendingActivationsShouldBeForced());
-
- EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction());
-
- state.FinishCommit();
- EXPECT_TRUE(state.PendingDrawsShouldBeAborted());
-
- EXPECT_EQ(SchedulerStateMachine::ACTION_COMMIT, state.NextAction());
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
+ EXPECT_FALSE(state.ShouldTriggerBeginImplFrameDeadlineEarly());
- state.UpdateState(state.NextAction());
- EXPECT_FALSE(state.PendingDrawsShouldBeAborted());
+ state.DidLoseOutputSurface();
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
+ // The deadline should be triggered immediately when output surface is lost.
+ EXPECT_TRUE(state.ShouldTriggerBeginImplFrameDeadlineEarly());
}
-TEST(SchedulerStateMachineTest, AcquireTexturesWithAbort) {
- SchedulerSettings default_scheduler_settings;
- StateMachine state(default_scheduler_settings);
+TEST(SchedulerStateMachineTest, TestSetNeedsAnimate) {
+ SchedulerSettings settings;
+ settings.impl_side_painting = true;
+ StateMachine state(settings);
state.SetCanStart();
state.UpdateState(state.NextAction());
- state.DidCreateAndInitializeOutputSurface();
- state.SetCanDraw(true);
+ state.CreateAndInitializeOutputSurfaceWithActivatedCommit();
state.SetVisible(true);
+ state.SetCanDraw(true);
- state.SetMainThreadNeedsLayerTextures();
- EXPECT_EQ(
- SchedulerStateMachine::ACTION_ACQUIRE_LAYER_TEXTURES_FOR_MAIN_THREAD,
- state.NextAction());
- state.UpdateState(state.NextAction());
- EXPECT_TRUE(state.PendingDrawsShouldBeAborted());
-
- EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction());
-
- state.SetNeedsCommit();
- EXPECT_EQ(SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME,
- state.NextAction());
- state.UpdateState(state.NextAction());
- EXPECT_TRUE(state.PendingDrawsShouldBeAborted());
-
- EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction());
+ // Test requesting an animation that, when run, causes us to draw.
+ state.SetNeedsAnimate();
+ EXPECT_TRUE(state.BeginFrameNeeded());
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
- state.BeginMainFrameAborted(true);
+ state.OnBeginImplFrame(CreateBeginFrameArgsForTesting());
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_ANIMATE);
- EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction());
- EXPECT_FALSE(state.PendingDrawsShouldBeAborted());
+ state.OnBeginImplFrameDeadlinePending();
+ state.OnBeginImplFrameDeadline();
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE);
}
-TEST(SchedulerStateMachineTest,
- TestTriggerDeadlineEarlyAfterAbortedCommit) {
+TEST(SchedulerStateMachineTest, TestAnimateBeforeCommit) {
SchedulerSettings settings;
- settings.deadline_scheduling_enabled = true;
settings.impl_side_painting = true;
StateMachine state(settings);
state.SetCanStart();
@@ -1712,32 +1863,26 @@ TEST(SchedulerStateMachineTest,
state.SetVisible(true);
state.SetCanDraw(true);
- // This test mirrors what happens during the first frame of a scroll gesture.
- // First we get the input event and a BeginFrame.
- state.OnBeginImplFrame(BeginFrameArgs::CreateForTesting());
-
- // As a response the compositor requests a redraw and a commit to tell the
- // main thread about the new scroll offset.
- state.SetNeedsRedraw(true);
+ // Check that animations are updated before we start a commit.
+ state.SetNeedsAnimate();
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
state.SetNeedsCommit();
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
+ EXPECT_TRUE(state.BeginFrameNeeded());
- // We should start the commit normally.
+ state.OnBeginImplFrame(CreateBeginFrameArgsForTesting());
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_ANIMATE);
EXPECT_ACTION_UPDATE_STATE(
SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME);
- EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
-
- // Since only the scroll offset changed, the main thread will abort the
- // commit.
- state.BeginMainFrameAborted(true);
- // Since the commit was aborted, we should draw right away instead of waiting
- // for the deadline.
- EXPECT_TRUE(state.ShouldTriggerBeginImplFrameDeadlineEarly());
+ state.OnBeginImplFrameDeadlinePending();
+ state.OnBeginImplFrameDeadline();
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE);
}
-TEST(SchedulerStateMachineTest, TestTriggerDeadlineEarlyForSmoothness) {
+TEST(SchedulerStateMachineTest, TestSetNeedsAnimateAfterAnimate) {
SchedulerSettings settings;
- settings.deadline_scheduling_enabled = true;
settings.impl_side_painting = true;
StateMachine state(settings);
state.SetCanStart();
@@ -1746,19 +1891,21 @@ TEST(SchedulerStateMachineTest, TestTriggerDeadlineEarlyForSmoothness) {
state.SetVisible(true);
state.SetCanDraw(true);
- // This test ensures that impl-draws are prioritized over main thread updates
- // in prefer smoothness mode.
- state.OnBeginImplFrame(BeginFrameArgs::CreateForTesting());
+ // Test requesting an animation after we have already animated during this
+ // frame.
state.SetNeedsRedraw(true);
- state.SetNeedsCommit();
- EXPECT_ACTION_UPDATE_STATE(
- SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME);
+ EXPECT_TRUE(state.BeginFrameNeeded());
EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
- // The deadline is not triggered early until we enter prefer smoothness mode.
- EXPECT_FALSE(state.ShouldTriggerBeginImplFrameDeadlineEarly());
- state.SetSmoothnessTakesPriority(true);
- EXPECT_TRUE(state.ShouldTriggerBeginImplFrameDeadlineEarly());
+ state.OnBeginImplFrame(CreateBeginFrameArgsForTesting());
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_ANIMATE);
+
+ state.SetNeedsAnimate();
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
+
+ state.OnBeginImplFrameDeadline();
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE);
}
} // namespace
diff --git a/chromium/cc/scheduler/scheduler_unittest.cc b/chromium/cc/scheduler/scheduler_unittest.cc
index 6f5eb2c2dcd..7d5a163dacc 100644
--- a/chromium/cc/scheduler/scheduler_unittest.cc
+++ b/chromium/cc/scheduler/scheduler_unittest.cc
@@ -11,43 +11,78 @@
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "base/time/time.h"
+#include "cc/test/begin_frame_args_test.h"
+#include "cc/test/ordered_simple_task_runner.h"
#include "cc/test/scheduler_test_common.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#define EXPECT_ACTION(action, client, action_index, expected_num_actions) \
- EXPECT_EQ(expected_num_actions, client.num_actions_()); \
- ASSERT_LT(action_index, client.num_actions_()); \
do { \
- EXPECT_STREQ(action, client.Action(action_index)); \
+ EXPECT_EQ(expected_num_actions, client.num_actions_()); \
+ if (action_index >= 0) { \
+ ASSERT_LT(action_index, client.num_actions_()) << scheduler; \
+ EXPECT_STREQ(action, client.Action(action_index)); \
+ } \
for (int i = expected_num_actions; i < client.num_actions_(); ++i) \
- ADD_FAILURE() << "Unexpected action: " << client.Action(i) << \
- " with state:\n" << client.StateForAction(action_index); \
+ ADD_FAILURE() << "Unexpected action: " << client.Action(i) \
+ << " with state:\n" << client.StateForAction(i); \
} while (false)
+#define EXPECT_NO_ACTION(client) EXPECT_ACTION("", client, -1, 0)
+
#define EXPECT_SINGLE_ACTION(action, client) \
EXPECT_ACTION(action, client, 0, 1)
namespace cc {
namespace {
-void InitializeOutputSurfaceAndFirstCommit(Scheduler* scheduler) {
- scheduler->DidCreateAndInitializeOutputSurface();
- scheduler->SetNeedsCommit();
- scheduler->FinishCommit();
- // Go through the motions to draw the commit.
- scheduler->BeginImplFrame(BeginFrameArgs::CreateForTesting());
- scheduler->OnBeginImplFrameDeadline();
- // We need another BeginImplFrame so Scheduler calls
- // SetNeedsBeginImplFrame(false).
- scheduler->BeginImplFrame(BeginFrameArgs::CreateForTesting());
- scheduler->OnBeginImplFrameDeadline();
-}
+class FakeSchedulerClient;
+
+void InitializeOutputSurfaceAndFirstCommit(Scheduler* scheduler,
+ FakeSchedulerClient* client);
+
+class TestScheduler : public Scheduler {
+ public:
+ static scoped_ptr<TestScheduler> Create(
+ SchedulerClient* client,
+ const SchedulerSettings& scheduler_settings,
+ int layer_tree_host_id,
+ const scoped_refptr<base::SingleThreadTaskRunner>& impl_task_runner) {
+ return make_scoped_ptr(new TestScheduler(
+ client, scheduler_settings, layer_tree_host_id, impl_task_runner));
+ }
+
+ virtual ~TestScheduler() {}
+
+ bool IsBeginRetroFrameArgsEmpty() const {
+ return begin_retro_frame_args_.empty();
+ }
+
+ bool IsSyntheticBeginFrameSourceActive() const {
+ return synthetic_begin_frame_source_->IsActive();
+ }
+
+ private:
+ TestScheduler(
+ SchedulerClient* client,
+ const SchedulerSettings& scheduler_settings,
+ int layer_tree_host_id,
+ const scoped_refptr<base::SingleThreadTaskRunner> & impl_task_runner)
+ : Scheduler(client,
+ scheduler_settings,
+ layer_tree_host_id,
+ impl_task_runner) {
+ }
+};
class FakeSchedulerClient : public SchedulerClient {
public:
FakeSchedulerClient()
- : needs_begin_impl_frame_(false) {
+ : needs_begin_frame_(false),
+ automatic_swap_ack_(true),
+ swap_contains_incomplete_tile_(false),
+ redraw_will_happen_if_update_visible_tiles_happens_(false) {
Reset();
}
@@ -60,8 +95,9 @@ class FakeSchedulerClient : public SchedulerClient {
log_anticipated_draw_time_change_ = false;
}
- Scheduler* CreateScheduler(const SchedulerSettings& settings) {
- scheduler_ = Scheduler::Create(this, settings, 0);
+ TestScheduler* CreateScheduler(const SchedulerSettings& settings) {
+ task_runner_ = new OrderedSimpleTaskRunner;
+ scheduler_ = TestScheduler::Create(this, settings, 0, task_runner_);
return scheduler_.get();
}
@@ -70,11 +106,16 @@ class FakeSchedulerClient : public SchedulerClient {
void set_log_anticipated_draw_time_change(bool log) {
log_anticipated_draw_time_change_ = log;
}
- bool needs_begin_impl_frame() { return needs_begin_impl_frame_; }
+ bool needs_begin_frame() { return needs_begin_frame_; }
int num_draws() const { return num_draws_; }
int num_actions_() const { return static_cast<int>(actions_.size()); }
const char* Action(int i) const { return actions_[i]; }
base::Value& StateForAction(int i) const { return *states_[i]; }
+ base::TimeTicks posted_begin_impl_frame_deadline() const {
+ return posted_begin_impl_frame_deadline_;
+ }
+
+ OrderedSimpleTaskRunner& task_runner() { return *task_runner_; }
int ActionIndex(const char* action) const {
for (size_t i = 0; i < actions_.size(); i++)
@@ -83,6 +124,10 @@ class FakeSchedulerClient : public SchedulerClient {
return -1;
}
+ void SetSwapContainsIncompleteTile(bool contain) {
+ swap_contains_incomplete_tile_ = contain;
+ }
+
bool HasAction(const char* action) const {
return ActionIndex(action) >= 0;
}
@@ -93,67 +138,78 @@ class FakeSchedulerClient : public SchedulerClient {
void SetSwapWillHappenIfDrawHappens(bool swap_will_happen_if_draw_happens) {
swap_will_happen_if_draw_happens_ = swap_will_happen_if_draw_happens;
}
-
+ void SetAutomaticSwapAck(bool automatic_swap_ack) {
+ automatic_swap_ack_ = automatic_swap_ack;
+ }
+ void SetRedrawWillHappenIfUpdateVisibleTilesHappens(bool redraw) {
+ redraw_will_happen_if_update_visible_tiles_happens_ = redraw;
+ }
// SchedulerClient implementation.
- virtual void SetNeedsBeginImplFrame(bool enable) OVERRIDE {
- actions_.push_back("SetNeedsBeginImplFrame");
- states_.push_back(scheduler_->StateAsValue().release());
- needs_begin_impl_frame_ = enable;
+ virtual void SetNeedsBeginFrame(bool enable) OVERRIDE {
+ actions_.push_back("SetNeedsBeginFrame");
+ states_.push_back(scheduler_->AsValue().release());
+ needs_begin_frame_ = enable;
+ }
+ virtual void WillBeginImplFrame(const BeginFrameArgs& args) OVERRIDE {
+ actions_.push_back("WillBeginImplFrame");
+ states_.push_back(scheduler_->AsValue().release());
}
virtual void ScheduledActionSendBeginMainFrame() OVERRIDE {
actions_.push_back("ScheduledActionSendBeginMainFrame");
- states_.push_back(scheduler_->StateAsValue().release());
+ states_.push_back(scheduler_->AsValue().release());
}
- virtual DrawSwapReadbackResult ScheduledActionDrawAndSwapIfPossible()
- OVERRIDE {
+ virtual void ScheduledActionAnimate() OVERRIDE {
+ actions_.push_back("ScheduledActionAnimate");
+ states_.push_back(scheduler_->AsValue().release());
+ }
+ virtual DrawResult ScheduledActionDrawAndSwapIfPossible() OVERRIDE {
actions_.push_back("ScheduledActionDrawAndSwapIfPossible");
- states_.push_back(scheduler_->StateAsValue().release());
+ states_.push_back(scheduler_->AsValue().release());
num_draws_++;
- bool did_readback = false;
- return DrawSwapReadbackResult(
- draw_will_happen_,
- draw_will_happen_ && swap_will_happen_if_draw_happens_,
- did_readback);
+ DrawResult result =
+ draw_will_happen_ ? DRAW_SUCCESS : DRAW_ABORTED_CHECKERBOARD_ANIMATIONS;
+ bool swap_will_happen =
+ draw_will_happen_ && swap_will_happen_if_draw_happens_;
+ if (swap_will_happen) {
+ scheduler_->DidSwapBuffers();
+ if (swap_contains_incomplete_tile_) {
+ scheduler_->SetSwapUsedIncompleteTile(true);
+ swap_contains_incomplete_tile_ = false;
+ } else {
+ scheduler_->SetSwapUsedIncompleteTile(false);
+ }
+
+ if (automatic_swap_ack_)
+ scheduler_->DidSwapBuffersComplete();
+ }
+ return result;
}
- virtual DrawSwapReadbackResult ScheduledActionDrawAndSwapForced() OVERRIDE {
+ virtual DrawResult ScheduledActionDrawAndSwapForced() OVERRIDE {
actions_.push_back("ScheduledActionDrawAndSwapForced");
- states_.push_back(scheduler_->StateAsValue().release());
- bool did_draw = true;
- bool did_swap = swap_will_happen_if_draw_happens_;
- bool did_readback = false;
- return DrawSwapReadbackResult(did_draw, did_swap, did_readback);
- }
- virtual DrawSwapReadbackResult ScheduledActionDrawAndReadback() OVERRIDE {
- actions_.push_back("ScheduledActionDrawAndReadback");
- states_.push_back(scheduler_->StateAsValue().release());
- bool did_draw = true;
- bool did_swap = false;
- bool did_readback = true;
- return DrawSwapReadbackResult(did_draw, did_swap, did_readback);
+ states_.push_back(scheduler_->AsValue().release());
+ return DRAW_SUCCESS;
}
virtual void ScheduledActionCommit() OVERRIDE {
actions_.push_back("ScheduledActionCommit");
- states_.push_back(scheduler_->StateAsValue().release());
+ states_.push_back(scheduler_->AsValue().release());
}
virtual void ScheduledActionUpdateVisibleTiles() OVERRIDE {
actions_.push_back("ScheduledActionUpdateVisibleTiles");
- states_.push_back(scheduler_->StateAsValue().release());
+ states_.push_back(scheduler_->AsValue().release());
+ if (redraw_will_happen_if_update_visible_tiles_happens_)
+ scheduler_->SetNeedsRedraw();
}
virtual void ScheduledActionActivatePendingTree() OVERRIDE {
actions_.push_back("ScheduledActionActivatePendingTree");
- states_.push_back(scheduler_->StateAsValue().release());
+ states_.push_back(scheduler_->AsValue().release());
}
virtual void ScheduledActionBeginOutputSurfaceCreation() OVERRIDE {
actions_.push_back("ScheduledActionBeginOutputSurfaceCreation");
- states_.push_back(scheduler_->StateAsValue().release());
- }
- virtual void ScheduledActionAcquireLayerTexturesForMainThread() OVERRIDE {
- actions_.push_back("ScheduledActionAcquireLayerTexturesForMainThread");
- states_.push_back(scheduler_->StateAsValue().release());
+ states_.push_back(scheduler_->AsValue().release());
}
virtual void ScheduledActionManageTiles() OVERRIDE {
actions_.push_back("ScheduledActionManageTiles");
- states_.push_back(scheduler_->StateAsValue().release());
+ states_.push_back(scheduler_->AsValue().release());
}
virtual void DidAnticipatedDrawTimeChange(base::TimeTicks) OVERRIDE {
if (log_anticipated_draw_time_change_)
@@ -169,29 +225,64 @@ class FakeSchedulerClient : public SchedulerClient {
return base::TimeDelta();
}
- virtual void PostBeginImplFrameDeadline(const base::Closure& closure,
- base::TimeTicks deadline) OVERRIDE {
- actions_.push_back("PostBeginImplFrameDeadlineTask");
- states_.push_back(scheduler_->StateAsValue().release());
- }
-
virtual void DidBeginImplFrameDeadline() OVERRIDE {}
protected:
- bool needs_begin_impl_frame_;
+ bool needs_begin_frame_;
bool draw_will_happen_;
bool swap_will_happen_if_draw_happens_;
+ bool automatic_swap_ack_;
int num_draws_;
bool log_anticipated_draw_time_change_;
+ bool swap_contains_incomplete_tile_;
+ bool redraw_will_happen_if_update_visible_tiles_happens_;
+ base::TimeTicks posted_begin_impl_frame_deadline_;
std::vector<const char*> actions_;
ScopedVector<base::Value> states_;
- scoped_ptr<Scheduler> scheduler_;
+ scoped_ptr<TestScheduler> scheduler_;
+ scoped_refptr<OrderedSimpleTaskRunner> task_runner_;
};
+void InitializeOutputSurfaceAndFirstCommit(Scheduler* scheduler,
+ FakeSchedulerClient* client) {
+ bool client_initiates_begin_frame =
+ scheduler->settings().begin_frame_scheduling_enabled &&
+ scheduler->settings().throttle_frame_production;
+
+ scheduler->DidCreateAndInitializeOutputSurface();
+ scheduler->SetNeedsCommit();
+ scheduler->NotifyBeginMainFrameStarted();
+ scheduler->NotifyReadyToCommit();
+ if (scheduler->settings().impl_side_painting)
+ scheduler->NotifyReadyToActivate();
+ // Go through the motions to draw the commit.
+ if (client_initiates_begin_frame)
+ scheduler->BeginFrame(CreateBeginFrameArgsForTesting());
+ else
+ client->task_runner().RunPendingTasks(); // Run posted BeginFrame.
+
+ // Run the posted deadline task.
+ EXPECT_TRUE(scheduler->BeginImplFrameDeadlinePending());
+ client->task_runner().RunPendingTasks();
+ EXPECT_FALSE(scheduler->BeginImplFrameDeadlinePending());
+
+ // We need another BeginImplFrame so Scheduler calls
+ // SetNeedsBeginFrame(false).
+ if (client_initiates_begin_frame)
+ scheduler->BeginFrame(CreateBeginFrameArgsForTesting());
+ else
+ client->task_runner().RunPendingTasks(); // Run posted BeginFrame.
+
+ // Run the posted deadline task.
+ EXPECT_TRUE(scheduler->BeginImplFrameDeadlinePending());
+ client->task_runner().RunPendingTasks();
+ EXPECT_FALSE(scheduler->BeginImplFrameDeadlinePending());
+}
+
TEST(SchedulerTest, InitializeOutputSurfaceDoesNotBeginImplFrame) {
FakeSchedulerClient client;
SchedulerSettings default_scheduler_settings;
- Scheduler* scheduler = client.CreateScheduler(default_scheduler_settings);
+ TestScheduler* scheduler = client.CreateScheduler(default_scheduler_settings);
scheduler->SetCanStart();
scheduler->SetVisible(true);
scheduler->SetCanDraw(true);
@@ -199,127 +290,99 @@ TEST(SchedulerTest, InitializeOutputSurfaceDoesNotBeginImplFrame) {
EXPECT_SINGLE_ACTION("ScheduledActionBeginOutputSurfaceCreation", client);
client.Reset();
scheduler->DidCreateAndInitializeOutputSurface();
- EXPECT_EQ(0, client.num_actions_());
+ EXPECT_NO_ACTION(client);
}
-void RequestCommit(bool deadline_scheduling_enabled) {
+TEST(SchedulerTest, RequestCommit) {
FakeSchedulerClient client;
SchedulerSettings scheduler_settings;
- scheduler_settings.deadline_scheduling_enabled = deadline_scheduling_enabled;
- Scheduler* scheduler = client.CreateScheduler(scheduler_settings);
+ TestScheduler* scheduler = client.CreateScheduler(scheduler_settings);
scheduler->SetCanStart();
scheduler->SetVisible(true);
scheduler->SetCanDraw(true);
EXPECT_SINGLE_ACTION("ScheduledActionBeginOutputSurfaceCreation", client);
- InitializeOutputSurfaceAndFirstCommit(scheduler);
+ InitializeOutputSurfaceAndFirstCommit(scheduler, &client);
// SetNeedsCommit should begin the frame on the next BeginImplFrame.
client.Reset();
scheduler->SetNeedsCommit();
- EXPECT_TRUE(client.needs_begin_impl_frame());
- if (deadline_scheduling_enabled) {
- EXPECT_SINGLE_ACTION("SetNeedsBeginImplFrame", client);
- } else {
- EXPECT_EQ(client.num_actions_(), 2);
- EXPECT_TRUE(client.HasAction("ScheduledActionSendBeginMainFrame"));
- EXPECT_TRUE(client.HasAction("SetNeedsBeginImplFrame"));
- }
+ EXPECT_TRUE(client.needs_begin_frame());
+ EXPECT_SINGLE_ACTION("SetNeedsBeginFrame", client);
client.Reset();
- scheduler->BeginImplFrame(BeginFrameArgs::CreateForTesting());
- if (deadline_scheduling_enabled) {
- EXPECT_ACTION("ScheduledActionSendBeginMainFrame", client, 0, 2);
- EXPECT_ACTION("PostBeginImplFrameDeadlineTask", client, 1, 2);
- } else {
- EXPECT_SINGLE_ACTION("PostBeginImplFrameDeadlineTask", client);
- }
- EXPECT_TRUE(client.needs_begin_impl_frame());
+ scheduler->BeginFrame(CreateBeginFrameArgsForTesting());
+ EXPECT_ACTION("WillBeginImplFrame", client, 0, 2);
+ EXPECT_ACTION("ScheduledActionSendBeginMainFrame", client, 1, 2);
+ EXPECT_TRUE(scheduler->BeginImplFrameDeadlinePending());
+ EXPECT_TRUE(client.needs_begin_frame());
client.Reset();
- // If we don't swap on the deadline, we need to request another
- // BeginImplFrame.
- scheduler->OnBeginImplFrameDeadline();
- EXPECT_SINGLE_ACTION("SetNeedsBeginImplFrame", client);
- EXPECT_TRUE(client.needs_begin_impl_frame());
+ // If we don't swap on the deadline, we wait for the next BeginFrame.
+ client.task_runner().RunPendingTasks(); // Run posted deadline.
+ EXPECT_NO_ACTION(client);
+ EXPECT_FALSE(scheduler->BeginImplFrameDeadlinePending());
+ EXPECT_TRUE(client.needs_begin_frame());
client.Reset();
- // FinishCommit should commit
- scheduler->FinishCommit();
+ // NotifyReadyToCommit should trigger the commit.
+ scheduler->NotifyBeginMainFrameStarted();
+ scheduler->NotifyReadyToCommit();
EXPECT_SINGLE_ACTION("ScheduledActionCommit", client);
- EXPECT_TRUE(client.needs_begin_impl_frame());
+ EXPECT_TRUE(client.needs_begin_frame());
client.Reset();
// BeginImplFrame should prepare the draw.
- scheduler->BeginImplFrame(BeginFrameArgs::CreateForTesting());
- EXPECT_SINGLE_ACTION("PostBeginImplFrameDeadlineTask", client);
- EXPECT_TRUE(client.needs_begin_impl_frame());
+ scheduler->BeginFrame(CreateBeginFrameArgsForTesting());
+ EXPECT_ACTION("WillBeginImplFrame", client, 0, 2);
+ EXPECT_ACTION("ScheduledActionAnimate", client, 1, 2);
+ EXPECT_TRUE(scheduler->BeginImplFrameDeadlinePending());
+ EXPECT_TRUE(client.needs_begin_frame());
client.Reset();
// BeginImplFrame deadline should draw.
- scheduler->OnBeginImplFrameDeadline();
- EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client, 0, 2);
- EXPECT_ACTION("SetNeedsBeginImplFrame", client, 1, 2);
- EXPECT_TRUE(client.needs_begin_impl_frame());
+ client.task_runner().RunPendingTasks(); // Run posted deadline.
+ EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client, 0, 1);
+ EXPECT_FALSE(scheduler->BeginImplFrameDeadlinePending());
+ EXPECT_TRUE(client.needs_begin_frame());
client.Reset();
- // The following BeginImplFrame deadline should SetNeedsBeginImplFrame(false)
+ // The following BeginImplFrame deadline should SetNeedsBeginFrame(false)
// to avoid excessive toggles.
- scheduler->BeginImplFrame(BeginFrameArgs::CreateForTesting());
- EXPECT_SINGLE_ACTION("PostBeginImplFrameDeadlineTask", client);
+ scheduler->BeginFrame(CreateBeginFrameArgsForTesting());
+ EXPECT_SINGLE_ACTION("WillBeginImplFrame", client);
+ EXPECT_TRUE(scheduler->BeginImplFrameDeadlinePending());
client.Reset();
- scheduler->OnBeginImplFrameDeadline();
- EXPECT_SINGLE_ACTION("SetNeedsBeginImplFrame", client);
- EXPECT_FALSE(client.needs_begin_impl_frame());
+ client.task_runner().RunPendingTasks(); // Run posted deadline.
+ EXPECT_SINGLE_ACTION("SetNeedsBeginFrame", client);
+ EXPECT_FALSE(client.needs_begin_frame());
client.Reset();
}
-TEST(SchedulerTest, RequestCommit) {
- bool deadline_scheduling_enabled = false;
- RequestCommit(deadline_scheduling_enabled);
-}
-
-TEST(SchedulerTest, RequestCommit_Deadline) {
- bool deadline_scheduling_enabled = true;
- RequestCommit(deadline_scheduling_enabled);
-}
-
-void RequestCommitAfterBeginMainFrameSent(
- bool deadline_scheduling_enabled) {
+TEST(SchedulerTest, RequestCommitAfterBeginMainFrameSent) {
FakeSchedulerClient client;
SchedulerSettings scheduler_settings;
- scheduler_settings.deadline_scheduling_enabled = deadline_scheduling_enabled;
- Scheduler* scheduler = client.CreateScheduler(scheduler_settings);
+ TestScheduler* scheduler = client.CreateScheduler(scheduler_settings);
scheduler->SetCanStart();
scheduler->SetVisible(true);
scheduler->SetCanDraw(true);
EXPECT_SINGLE_ACTION("ScheduledActionBeginOutputSurfaceCreation", client);
- InitializeOutputSurfaceAndFirstCommit(scheduler);
+ InitializeOutputSurfaceAndFirstCommit(scheduler, &client);
client.Reset();
// SetNeedsCommit should begin the frame.
scheduler->SetNeedsCommit();
- if (deadline_scheduling_enabled) {
- EXPECT_SINGLE_ACTION("SetNeedsBeginImplFrame", client);
- } else {
- EXPECT_EQ(client.num_actions_(), 2);
- EXPECT_TRUE(client.HasAction("SetNeedsBeginImplFrame"));
- EXPECT_TRUE(client.HasAction("ScheduledActionSendBeginMainFrame"));
- }
+ EXPECT_SINGLE_ACTION("SetNeedsBeginFrame", client);
client.Reset();
- scheduler->BeginImplFrame(BeginFrameArgs::CreateForTesting());
- if (deadline_scheduling_enabled) {
- EXPECT_EQ(client.num_actions_(), 2);
- EXPECT_TRUE(client.HasAction("ScheduledActionSendBeginMainFrame"));
- EXPECT_TRUE(client.HasAction("PostBeginImplFrameDeadlineTask"));
- } else {
- EXPECT_SINGLE_ACTION("PostBeginImplFrameDeadlineTask", client);
- }
+ scheduler->BeginFrame(CreateBeginFrameArgsForTesting());
+ EXPECT_ACTION("WillBeginImplFrame", client, 0, 2);
+ EXPECT_ACTION("ScheduledActionSendBeginMainFrame", client, 1, 2);
+ EXPECT_TRUE(scheduler->BeginImplFrameDeadlinePending());
- EXPECT_TRUE(client.needs_begin_impl_frame());
+ EXPECT_TRUE(client.needs_begin_frame());
client.Reset();
// Now SetNeedsCommit again. Calling here means we need a second commit.
@@ -328,358 +391,54 @@ void RequestCommitAfterBeginMainFrameSent(
client.Reset();
// Finish the first commit.
- scheduler->FinishCommit();
- EXPECT_ACTION("ScheduledActionCommit", client, 0, 2);
- EXPECT_ACTION("PostBeginImplFrameDeadlineTask", client, 1, 2);
- client.Reset();
- scheduler->OnBeginImplFrameDeadline();
- if (deadline_scheduling_enabled) {
- EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client, 0, 2);
- EXPECT_ACTION("SetNeedsBeginImplFrame", client, 1, 2);
- } else {
- EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client, 0, 3);
- EXPECT_ACTION("ScheduledActionSendBeginMainFrame", client, 1, 3);
- EXPECT_ACTION("SetNeedsBeginImplFrame", client, 2, 3);
- }
+ scheduler->NotifyBeginMainFrameStarted();
+ scheduler->NotifyReadyToCommit();
+ EXPECT_SINGLE_ACTION("ScheduledActionCommit", client);
+ EXPECT_TRUE(scheduler->BeginImplFrameDeadlinePending());
+ client.Reset();
+ client.task_runner().RunPendingTasks(); // Run posted deadline.
+ EXPECT_ACTION("ScheduledActionAnimate", client, 0, 2);
+ EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client, 1, 2);
+ EXPECT_FALSE(scheduler->BeginImplFrameDeadlinePending());
// Because we just swapped, the Scheduler should also request the next
// BeginImplFrame from the OutputSurface.
- EXPECT_TRUE(client.needs_begin_impl_frame());
+ EXPECT_TRUE(client.needs_begin_frame());
client.Reset();
-
// Since another commit is needed, the next BeginImplFrame should initiate
// the second commit.
- scheduler->BeginImplFrame(BeginFrameArgs::CreateForTesting());
- if (deadline_scheduling_enabled) {
- EXPECT_EQ(client.num_actions_(), 2);
- EXPECT_TRUE(client.HasAction("ScheduledActionSendBeginMainFrame"));
- EXPECT_TRUE(client.HasAction("PostBeginImplFrameDeadlineTask"));
- } else {
- EXPECT_SINGLE_ACTION("PostBeginImplFrameDeadlineTask", client);
- }
+ scheduler->BeginFrame(CreateBeginFrameArgsForTesting());
+ EXPECT_ACTION("WillBeginImplFrame", client, 0, 2);
+ EXPECT_ACTION("ScheduledActionSendBeginMainFrame", client, 1, 2);
+ EXPECT_TRUE(scheduler->BeginImplFrameDeadlinePending());
client.Reset();
// Finishing the commit before the deadline should post a new deadline task
// to trigger the deadline early.
- scheduler->FinishCommit();
- EXPECT_ACTION("ScheduledActionCommit", client, 0, 2);
- EXPECT_ACTION("PostBeginImplFrameDeadlineTask", client, 1, 2);
+ scheduler->NotifyBeginMainFrameStarted();
+ scheduler->NotifyReadyToCommit();
+ EXPECT_SINGLE_ACTION("ScheduledActionCommit", client);
+ EXPECT_TRUE(scheduler->BeginImplFrameDeadlinePending());
client.Reset();
- scheduler->OnBeginImplFrameDeadline();
- EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client, 0, 2);
- EXPECT_ACTION("SetNeedsBeginImplFrame", client, 1, 2);
- EXPECT_TRUE(client.needs_begin_impl_frame());
+ client.task_runner().RunPendingTasks(); // Run posted deadline.
+ EXPECT_ACTION("ScheduledActionAnimate", client, 0, 2);
+ EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client, 1, 2);
+ EXPECT_FALSE(scheduler->BeginImplFrameDeadlinePending());
+ EXPECT_TRUE(client.needs_begin_frame());
client.Reset();
// On the next BeginImplFrame, verify we go back to a quiescent state and
// no longer request BeginImplFrames.
- scheduler->BeginImplFrame(BeginFrameArgs::CreateForTesting());
- scheduler->OnBeginImplFrameDeadline();
- EXPECT_FALSE(client.needs_begin_impl_frame());
- client.Reset();
-}
-
-TEST(SchedulerTest, RequestCommitAfterBeginMainFrameSent) {
- bool deadline_scheduling_enabled = false;
- RequestCommitAfterBeginMainFrameSent(deadline_scheduling_enabled);
-}
-
-TEST(SchedulerTest, RequestCommitAfterBeginMainFrameSent_Deadline) {
- bool deadline_scheduling_enabled = true;
- RequestCommitAfterBeginMainFrameSent(deadline_scheduling_enabled);
-}
-
-void TextureAcquisitionCausesCommitInsteadOfDraw(
- bool deadline_scheduling_enabled) {
- FakeSchedulerClient client;
- SchedulerSettings scheduler_settings;
- scheduler_settings.deadline_scheduling_enabled = deadline_scheduling_enabled;
- Scheduler* scheduler = client.CreateScheduler(scheduler_settings);
- scheduler->SetCanStart();
- scheduler->SetVisible(true);
- scheduler->SetCanDraw(true);
- EXPECT_SINGLE_ACTION("ScheduledActionBeginOutputSurfaceCreation", client);
-
- InitializeOutputSurfaceAndFirstCommit(scheduler);
- client.Reset();
- scheduler->SetNeedsRedraw();
- EXPECT_TRUE(scheduler->RedrawPending());
- EXPECT_SINGLE_ACTION("SetNeedsBeginImplFrame", client);
- EXPECT_TRUE(client.needs_begin_impl_frame());
-
- client.Reset();
- scheduler->BeginImplFrame(BeginFrameArgs::CreateForTesting());
- EXPECT_SINGLE_ACTION("PostBeginImplFrameDeadlineTask", client);
- client.Reset();
- scheduler->OnBeginImplFrameDeadline();
- EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client, 0, 2);
- EXPECT_ACTION("SetNeedsBeginImplFrame", client, 1, 2);
- EXPECT_FALSE(scheduler->RedrawPending());
- EXPECT_TRUE(client.needs_begin_impl_frame());
-
- client.Reset();
- scheduler->BeginImplFrame(BeginFrameArgs::CreateForTesting());
- EXPECT_SINGLE_ACTION("PostBeginImplFrameDeadlineTask", client);
- client.Reset();
- scheduler->OnBeginImplFrameDeadline();
- EXPECT_SINGLE_ACTION("SetNeedsBeginImplFrame", client);
- EXPECT_FALSE(scheduler->RedrawPending());
- EXPECT_FALSE(client.needs_begin_impl_frame());
-
- client.Reset();
- scheduler->SetMainThreadNeedsLayerTextures();
- EXPECT_SINGLE_ACTION("ScheduledActionAcquireLayerTexturesForMainThread",
- client);
-
- // We should request a BeginImplFrame in anticipation of a draw.
- client.Reset();
- scheduler->SetNeedsRedraw();
- EXPECT_TRUE(scheduler->RedrawPending());
- EXPECT_SINGLE_ACTION("SetNeedsBeginImplFrame", client);
- EXPECT_TRUE(client.needs_begin_impl_frame());
-
- // No draw happens since the textures are acquired by the main thread.
- client.Reset();
- scheduler->BeginImplFrame(BeginFrameArgs::CreateForTesting());
- EXPECT_SINGLE_ACTION("PostBeginImplFrameDeadlineTask", client);
+ scheduler->BeginFrame(CreateBeginFrameArgsForTesting());
+ client.task_runner().RunPendingTasks(); // Run posted deadline.
+ EXPECT_FALSE(client.needs_begin_frame());
client.Reset();
- scheduler->OnBeginImplFrameDeadline();
- EXPECT_SINGLE_ACTION("SetNeedsBeginImplFrame", client);
- EXPECT_TRUE(scheduler->RedrawPending());
- EXPECT_TRUE(client.needs_begin_impl_frame());
-
- client.Reset();
- scheduler->SetNeedsCommit();
- if (deadline_scheduling_enabled) {
- EXPECT_EQ(0, client.num_actions_());
- } else {
- EXPECT_SINGLE_ACTION("ScheduledActionSendBeginMainFrame", client);
- }
-
- client.Reset();
- scheduler->BeginImplFrame(BeginFrameArgs::CreateForTesting());
- if (deadline_scheduling_enabled) {
- EXPECT_ACTION("ScheduledActionSendBeginMainFrame", client, 0, 2);
- EXPECT_ACTION("PostBeginImplFrameDeadlineTask", client, 1, 2);
- } else {
- EXPECT_SINGLE_ACTION("PostBeginImplFrameDeadlineTask", client);
- }
-
- // Commit will release the texture.
- client.Reset();
- scheduler->FinishCommit();
- EXPECT_ACTION("ScheduledActionCommit", client, 0, 2);
- EXPECT_ACTION("PostBeginImplFrameDeadlineTask", client, 1, 2);
- EXPECT_TRUE(scheduler->RedrawPending());
-
- // Now we can draw again after the commit happens.
- client.Reset();
- scheduler->OnBeginImplFrameDeadline();
- EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client, 0, 2);
- EXPECT_ACTION("SetNeedsBeginImplFrame", client, 1, 2);
- EXPECT_FALSE(scheduler->RedrawPending());
- EXPECT_TRUE(client.needs_begin_impl_frame());
-
- // Make sure we stop requesting BeginImplFrames if we don't swap.
- client.Reset();
- scheduler->BeginImplFrame(BeginFrameArgs::CreateForTesting());
- EXPECT_SINGLE_ACTION("PostBeginImplFrameDeadlineTask", client);
- client.Reset();
- scheduler->OnBeginImplFrameDeadline();
- EXPECT_SINGLE_ACTION("SetNeedsBeginImplFrame", client);
- EXPECT_FALSE(client.needs_begin_impl_frame());
-}
-
-TEST(SchedulerTest, TextureAcquisitionCausesCommitInsteadOfDraw) {
- bool deadline_scheduling_enabled = false;
- TextureAcquisitionCausesCommitInsteadOfDraw(deadline_scheduling_enabled);
-}
-
-TEST(SchedulerTest, TextureAcquisitionCausesCommitInsteadOfDraw_Deadline) {
- bool deadline_scheduling_enabled = true;
- TextureAcquisitionCausesCommitInsteadOfDraw(deadline_scheduling_enabled);
-}
-
-void TextureAcquisitionCollision(bool deadline_scheduling_enabled) {
- FakeSchedulerClient client;
- SchedulerSettings scheduler_settings;
- scheduler_settings.deadline_scheduling_enabled = deadline_scheduling_enabled;
- Scheduler* scheduler = client.CreateScheduler(scheduler_settings);
- scheduler->SetCanStart();
- scheduler->SetVisible(true);
- scheduler->SetCanDraw(true);
-
- EXPECT_SINGLE_ACTION("ScheduledActionBeginOutputSurfaceCreation", client);
- InitializeOutputSurfaceAndFirstCommit(scheduler);
-
- client.Reset();
- scheduler->SetNeedsCommit();
-if (deadline_scheduling_enabled) {
- EXPECT_SINGLE_ACTION("SetNeedsBeginImplFrame", client);
- } else {
- EXPECT_ACTION("ScheduledActionSendBeginMainFrame", client, 0, 2);
- EXPECT_ACTION("SetNeedsBeginImplFrame", client, 1, 2);
- }
-
- client.Reset();
- scheduler->SetMainThreadNeedsLayerTextures();
- EXPECT_SINGLE_ACTION(
- "ScheduledActionAcquireLayerTexturesForMainThread", client);
-
- client.Reset();
- scheduler->BeginImplFrame(BeginFrameArgs::CreateForTesting());
- if (deadline_scheduling_enabled) {
- EXPECT_ACTION("ScheduledActionSendBeginMainFrame", client, 0, 2);
- EXPECT_ACTION("PostBeginImplFrameDeadlineTask", client, 1, 2);
- } else {
- EXPECT_SINGLE_ACTION("PostBeginImplFrameDeadlineTask", client);
- }
-
- client.Reset();
- scheduler->OnBeginImplFrameDeadline();
- EXPECT_SINGLE_ACTION("SetNeedsBeginImplFrame", client);
-
- // Although the compositor cannot draw because textures are locked by main
- // thread, we continue requesting SetNeedsBeginImplFrame in anticipation of
- // the unlock.
- EXPECT_TRUE(client.needs_begin_impl_frame());
-
- // Trigger the commit
- scheduler->FinishCommit();
- EXPECT_TRUE(client.needs_begin_impl_frame());
-
- // Between commit and draw, texture acquisition for main thread delayed,
- // and main thread blocks.
- client.Reset();
- scheduler->SetMainThreadNeedsLayerTextures();
- EXPECT_EQ(0, client.num_actions_());
-
- // No implicit commit is expected.
- client.Reset();
- scheduler->BeginImplFrame(BeginFrameArgs::CreateForTesting());
- EXPECT_SINGLE_ACTION("PostBeginImplFrameDeadlineTask", client);
-
- client.Reset();
- scheduler->OnBeginImplFrameDeadline();
- EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client, 0, 3);
- EXPECT_ACTION(
- "ScheduledActionAcquireLayerTexturesForMainThread", client, 1, 3);
- EXPECT_ACTION("SetNeedsBeginImplFrame", client, 2, 3);
- EXPECT_TRUE(client.needs_begin_impl_frame());
-
- // The compositor should not draw because textures are locked by main
- // thread.
- client.Reset();
- scheduler->BeginImplFrame(BeginFrameArgs::CreateForTesting());
- EXPECT_SINGLE_ACTION("PostBeginImplFrameDeadlineTask", client);
- client.Reset();
- scheduler->OnBeginImplFrameDeadline();
- EXPECT_SINGLE_ACTION("SetNeedsBeginImplFrame", client);
- EXPECT_FALSE(client.needs_begin_impl_frame());
-
- // The impl thread need an explicit commit from the main thread to lock
- // the textures.
- client.Reset();
- scheduler->SetNeedsCommit();
- if (deadline_scheduling_enabled) {
- EXPECT_SINGLE_ACTION("SetNeedsBeginImplFrame", client);
- } else {
- EXPECT_ACTION("ScheduledActionSendBeginMainFrame", client, 0, 2);
- EXPECT_ACTION("SetNeedsBeginImplFrame", client, 1, 2);
- }
- EXPECT_TRUE(client.needs_begin_impl_frame());
-
- client.Reset();
- scheduler->BeginImplFrame(BeginFrameArgs::CreateForTesting());
- if (deadline_scheduling_enabled) {
- EXPECT_ACTION("ScheduledActionSendBeginMainFrame", client, 0, 2);
- EXPECT_ACTION("PostBeginImplFrameDeadlineTask", client, 1, 2);
- } else {
- EXPECT_SINGLE_ACTION("PostBeginImplFrameDeadlineTask", client);
- }
- client.Reset();
-
- // Trigger the commit, which will trigger the deadline task early.
- scheduler->FinishCommit();
- EXPECT_ACTION("ScheduledActionCommit", client, 0, 2);
- EXPECT_ACTION("PostBeginImplFrameDeadlineTask", client, 1, 2);
- EXPECT_TRUE(client.needs_begin_impl_frame());
- client.Reset();
-
- // Verify we draw on the next BeginImplFrame deadline
- scheduler->OnBeginImplFrameDeadline();
- EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client, 0, 2);
- EXPECT_ACTION("SetNeedsBeginImplFrame", client, 1, 2);
- EXPECT_TRUE(client.needs_begin_impl_frame());
- client.Reset();
-}
-
-TEST(SchedulerTest, TextureAcquisitionCollision) {
- bool deadline_scheduling_enabled = false;
- TextureAcquisitionCollision(deadline_scheduling_enabled);
-}
-
-TEST(SchedulerTest, TextureAcquisitionCollision_Deadline) {
- bool deadline_scheduling_enabled = true;
- TextureAcquisitionCollision(deadline_scheduling_enabled);
-}
-
-void VisibilitySwitchWithTextureAcquisition(bool deadline_scheduling_enabled) {
- FakeSchedulerClient client;
- SchedulerSettings scheduler_settings;
- scheduler_settings.deadline_scheduling_enabled = deadline_scheduling_enabled;
- Scheduler* scheduler = client.CreateScheduler(scheduler_settings);
- scheduler->SetCanStart();
- scheduler->SetVisible(true);
- scheduler->SetCanDraw(true);
-
- EXPECT_SINGLE_ACTION("ScheduledActionBeginOutputSurfaceCreation", client);
- client.Reset();
- scheduler->DidCreateAndInitializeOutputSurface();
-
- scheduler->SetNeedsCommit();
- if (deadline_scheduling_enabled) {
- scheduler->BeginImplFrame(BeginFrameArgs::CreateForTesting());
- scheduler->OnBeginImplFrameDeadline();
- }
- scheduler->FinishCommit();
- scheduler->SetMainThreadNeedsLayerTextures();
- scheduler->SetNeedsCommit();
- client.Reset();
- // Verify that pending texture acquisition fires when visibility
- // is lost in order to avoid a deadlock.
- scheduler->SetVisible(false);
- EXPECT_SINGLE_ACTION("ScheduledActionAcquireLayerTexturesForMainThread",
- client);
-
- client.Reset();
- scheduler->SetVisible(true);
- EXPECT_EQ(0, client.num_actions_());
- EXPECT_TRUE(client.needs_begin_impl_frame());
-
- // Regaining visibility with textures acquired by main thread while
- // compositor is waiting for first draw should result in a request
- // for a new frame in order to escape a deadlock.
- client.Reset();
- scheduler->BeginImplFrame(BeginFrameArgs::CreateForTesting());
- EXPECT_ACTION("ScheduledActionSendBeginMainFrame", client, 0, 2);
- EXPECT_ACTION("PostBeginImplFrameDeadlineTask", client, 1, 2);
-}
-
-TEST(SchedulerTest, VisibilitySwitchWithTextureAcquisition) {
- bool deadline_scheduling_enabled = false;
- VisibilitySwitchWithTextureAcquisition(deadline_scheduling_enabled);
-}
-
-TEST(SchedulerTest, VisibilitySwitchWithTextureAcquisition_Deadline) {
- bool deadline_scheduling_enabled = true;
- VisibilitySwitchWithTextureAcquisition(deadline_scheduling_enabled);
}
class SchedulerClientThatsetNeedsDrawInsideDraw : public FakeSchedulerClient {
public:
virtual void ScheduledActionSendBeginMainFrame() OVERRIDE {}
- virtual DrawSwapReadbackResult ScheduledActionDrawAndSwapIfPossible()
+ virtual DrawResult ScheduledActionDrawAndSwapIfPossible()
OVERRIDE {
// Only SetNeedsRedraw the first time this is called
if (!num_draws_)
@@ -687,12 +446,9 @@ class SchedulerClientThatsetNeedsDrawInsideDraw : public FakeSchedulerClient {
return FakeSchedulerClient::ScheduledActionDrawAndSwapIfPossible();
}
- virtual DrawSwapReadbackResult ScheduledActionDrawAndSwapForced() OVERRIDE {
+ virtual DrawResult ScheduledActionDrawAndSwapForced() OVERRIDE {
NOTREACHED();
- bool did_draw = true;
- bool did_swap = true;
- bool did_readback = false;
- return DrawSwapReadbackResult(did_draw, did_swap, did_readback);
+ return DRAW_SUCCESS;
}
virtual void ScheduledActionCommit() OVERRIDE {}
@@ -707,84 +463,84 @@ class SchedulerClientThatsetNeedsDrawInsideDraw : public FakeSchedulerClient {
TEST(SchedulerTest, RequestRedrawInsideDraw) {
SchedulerClientThatsetNeedsDrawInsideDraw client;
SchedulerSettings default_scheduler_settings;
- Scheduler* scheduler = client.CreateScheduler(default_scheduler_settings);
+ TestScheduler* scheduler = client.CreateScheduler(default_scheduler_settings);
scheduler->SetCanStart();
scheduler->SetVisible(true);
scheduler->SetCanDraw(true);
- InitializeOutputSurfaceAndFirstCommit(scheduler);
+ InitializeOutputSurfaceAndFirstCommit(scheduler, &client);
client.Reset();
scheduler->SetNeedsRedraw();
EXPECT_TRUE(scheduler->RedrawPending());
- EXPECT_TRUE(client.needs_begin_impl_frame());
+ EXPECT_TRUE(client.needs_begin_frame());
EXPECT_EQ(0, client.num_draws());
- scheduler->BeginImplFrame(BeginFrameArgs::CreateForTesting());
- scheduler->OnBeginImplFrameDeadline();
+ scheduler->BeginFrame(CreateBeginFrameArgsForTesting());
+ client.task_runner().RunPendingTasks(); // Run posted deadline.
EXPECT_EQ(1, client.num_draws());
EXPECT_TRUE(scheduler->RedrawPending());
- EXPECT_TRUE(client.needs_begin_impl_frame());
+ EXPECT_TRUE(client.needs_begin_frame());
- scheduler->BeginImplFrame(BeginFrameArgs::CreateForTesting());
- scheduler->OnBeginImplFrameDeadline();
+ scheduler->BeginFrame(CreateBeginFrameArgsForTesting());
+ client.task_runner().RunPendingTasks(); // Run posted deadline.
EXPECT_EQ(2, client.num_draws());
EXPECT_FALSE(scheduler->RedrawPending());
- EXPECT_TRUE(client.needs_begin_impl_frame());
+ EXPECT_TRUE(client.needs_begin_frame());
// We stop requesting BeginImplFrames after a BeginImplFrame where we don't
// swap.
- scheduler->BeginImplFrame(BeginFrameArgs::CreateForTesting());
- scheduler->OnBeginImplFrameDeadline();
+ scheduler->BeginFrame(CreateBeginFrameArgsForTesting());
+ client.task_runner().RunPendingTasks(); // Run posted deadline.
EXPECT_EQ(2, client.num_draws());
EXPECT_FALSE(scheduler->RedrawPending());
- EXPECT_FALSE(client.needs_begin_impl_frame());
+ EXPECT_FALSE(client.needs_begin_frame());
}
// Test that requesting redraw inside a failed draw doesn't lose the request.
TEST(SchedulerTest, RequestRedrawInsideFailedDraw) {
SchedulerClientThatsetNeedsDrawInsideDraw client;
SchedulerSettings default_scheduler_settings;
- Scheduler* scheduler = client.CreateScheduler(default_scheduler_settings);
+ TestScheduler* scheduler = client.CreateScheduler(default_scheduler_settings);
scheduler->SetCanStart();
scheduler->SetVisible(true);
scheduler->SetCanDraw(true);
- InitializeOutputSurfaceAndFirstCommit(scheduler);
+ InitializeOutputSurfaceAndFirstCommit(scheduler, &client);
client.Reset();
client.SetDrawWillHappen(false);
scheduler->SetNeedsRedraw();
EXPECT_TRUE(scheduler->RedrawPending());
- EXPECT_TRUE(client.needs_begin_impl_frame());
+ EXPECT_TRUE(client.needs_begin_frame());
EXPECT_EQ(0, client.num_draws());
// Fail the draw.
- scheduler->BeginImplFrame(BeginFrameArgs::CreateForTesting());
- scheduler->OnBeginImplFrameDeadline();
+ scheduler->BeginFrame(CreateBeginFrameArgsForTesting());
+ client.task_runner().RunPendingTasks(); // Run posted deadline.
EXPECT_EQ(1, client.num_draws());
// We have a commit pending and the draw failed, and we didn't lose the redraw
// request.
EXPECT_TRUE(scheduler->CommitPending());
EXPECT_TRUE(scheduler->RedrawPending());
- EXPECT_TRUE(client.needs_begin_impl_frame());
+ EXPECT_TRUE(client.needs_begin_frame());
// Fail the draw again.
- scheduler->BeginImplFrame(BeginFrameArgs::CreateForTesting());
- scheduler->OnBeginImplFrameDeadline();
+ scheduler->BeginFrame(CreateBeginFrameArgsForTesting());
+ client.task_runner().RunPendingTasks(); // Run posted deadline.
EXPECT_EQ(2, client.num_draws());
EXPECT_TRUE(scheduler->CommitPending());
EXPECT_TRUE(scheduler->RedrawPending());
- EXPECT_TRUE(client.needs_begin_impl_frame());
+ EXPECT_TRUE(client.needs_begin_frame());
// Draw successfully.
client.SetDrawWillHappen(true);
- scheduler->BeginImplFrame(BeginFrameArgs::CreateForTesting());
- scheduler->OnBeginImplFrameDeadline();
+ scheduler->BeginFrame(CreateBeginFrameArgsForTesting());
+ client.task_runner().RunPendingTasks(); // Run posted deadline.
EXPECT_EQ(3, client.num_draws());
EXPECT_TRUE(scheduler->CommitPending());
EXPECT_FALSE(scheduler->RedrawPending());
- EXPECT_TRUE(client.needs_begin_impl_frame());
+ EXPECT_TRUE(client.needs_begin_frame());
}
class SchedulerClientThatSetNeedsCommitInsideDraw : public FakeSchedulerClient {
@@ -793,7 +549,7 @@ class SchedulerClientThatSetNeedsCommitInsideDraw : public FakeSchedulerClient {
: set_needs_commit_on_next_draw_(false) {}
virtual void ScheduledActionSendBeginMainFrame() OVERRIDE {}
- virtual DrawSwapReadbackResult ScheduledActionDrawAndSwapIfPossible()
+ virtual DrawResult ScheduledActionDrawAndSwapIfPossible()
OVERRIDE {
// Only SetNeedsCommit the first time this is called
if (set_needs_commit_on_next_draw_) {
@@ -803,12 +559,9 @@ class SchedulerClientThatSetNeedsCommitInsideDraw : public FakeSchedulerClient {
return FakeSchedulerClient::ScheduledActionDrawAndSwapIfPossible();
}
- virtual DrawSwapReadbackResult ScheduledActionDrawAndSwapForced() OVERRIDE {
+ virtual DrawResult ScheduledActionDrawAndSwapForced() OVERRIDE {
NOTREACHED();
- bool did_draw = true;
- bool did_swap = false;
- bool did_readback = false;
- return DrawSwapReadbackResult(did_draw, did_swap, did_readback);
+ return DRAW_SUCCESS;
}
virtual void ScheduledActionCommit() OVERRIDE {}
@@ -826,172 +579,131 @@ class SchedulerClientThatSetNeedsCommitInsideDraw : public FakeSchedulerClient {
TEST(SchedulerTest, RequestCommitInsideDraw) {
SchedulerClientThatSetNeedsCommitInsideDraw client;
SchedulerSettings default_scheduler_settings;
- Scheduler* scheduler = client.CreateScheduler(default_scheduler_settings);
+ TestScheduler* scheduler = client.CreateScheduler(default_scheduler_settings);
scheduler->SetCanStart();
scheduler->SetVisible(true);
scheduler->SetCanDraw(true);
- InitializeOutputSurfaceAndFirstCommit(scheduler);
+ InitializeOutputSurfaceAndFirstCommit(scheduler, &client);
client.Reset();
- EXPECT_FALSE(client.needs_begin_impl_frame());
+ EXPECT_FALSE(client.needs_begin_frame());
scheduler->SetNeedsRedraw();
EXPECT_TRUE(scheduler->RedrawPending());
EXPECT_EQ(0, client.num_draws());
- EXPECT_TRUE(client.needs_begin_impl_frame());
+ EXPECT_TRUE(client.needs_begin_frame());
client.SetNeedsCommitOnNextDraw();
- scheduler->BeginImplFrame(BeginFrameArgs::CreateForTesting());
+ scheduler->BeginFrame(CreateBeginFrameArgsForTesting());
client.SetNeedsCommitOnNextDraw();
- scheduler->OnBeginImplFrameDeadline();
+ client.task_runner().RunPendingTasks(); // Run posted deadline.
EXPECT_EQ(1, client.num_draws());
EXPECT_TRUE(scheduler->CommitPending());
- EXPECT_TRUE(client.needs_begin_impl_frame());
- scheduler->FinishCommit();
+ EXPECT_TRUE(client.needs_begin_frame());
+ scheduler->NotifyBeginMainFrameStarted();
+ scheduler->NotifyReadyToCommit();
- scheduler->BeginImplFrame(BeginFrameArgs::CreateForTesting());
- scheduler->OnBeginImplFrameDeadline();
+ scheduler->BeginFrame(CreateBeginFrameArgsForTesting());
+ client.task_runner().RunPendingTasks(); // Run posted deadline.
EXPECT_EQ(2, client.num_draws());
EXPECT_FALSE(scheduler->RedrawPending());
EXPECT_FALSE(scheduler->CommitPending());
- EXPECT_TRUE(client.needs_begin_impl_frame());
+ EXPECT_TRUE(client.needs_begin_frame());
// We stop requesting BeginImplFrames after a BeginImplFrame where we don't
// swap.
- scheduler->BeginImplFrame(BeginFrameArgs::CreateForTesting());
- scheduler->OnBeginImplFrameDeadline();
+ scheduler->BeginFrame(CreateBeginFrameArgsForTesting());
+ client.task_runner().RunPendingTasks(); // Run posted deadline.
EXPECT_EQ(2, client.num_draws());
EXPECT_FALSE(scheduler->RedrawPending());
EXPECT_FALSE(scheduler->CommitPending());
- EXPECT_FALSE(client.needs_begin_impl_frame());
+ EXPECT_FALSE(client.needs_begin_frame());
}
// Tests that when a draw fails then the pending commit should not be dropped.
TEST(SchedulerTest, RequestCommitInsideFailedDraw) {
SchedulerClientThatsetNeedsDrawInsideDraw client;
SchedulerSettings default_scheduler_settings;
- Scheduler* scheduler = client.CreateScheduler(default_scheduler_settings);
+ TestScheduler* scheduler = client.CreateScheduler(default_scheduler_settings);
scheduler->SetCanStart();
scheduler->SetVisible(true);
scheduler->SetCanDraw(true);
- InitializeOutputSurfaceAndFirstCommit(scheduler);
+ InitializeOutputSurfaceAndFirstCommit(scheduler, &client);
client.Reset();
client.SetDrawWillHappen(false);
scheduler->SetNeedsRedraw();
EXPECT_TRUE(scheduler->RedrawPending());
- EXPECT_TRUE(client.needs_begin_impl_frame());
+ EXPECT_TRUE(client.needs_begin_frame());
EXPECT_EQ(0, client.num_draws());
// Fail the draw.
- scheduler->BeginImplFrame(BeginFrameArgs::CreateForTesting());
- scheduler->OnBeginImplFrameDeadline();
+ scheduler->BeginFrame(CreateBeginFrameArgsForTesting());
+ client.task_runner().RunPendingTasks(); // Run posted deadline.
EXPECT_EQ(1, client.num_draws());
// We have a commit pending and the draw failed, and we didn't lose the commit
// request.
EXPECT_TRUE(scheduler->CommitPending());
EXPECT_TRUE(scheduler->RedrawPending());
- EXPECT_TRUE(client.needs_begin_impl_frame());
+ EXPECT_TRUE(client.needs_begin_frame());
// Fail the draw again.
- scheduler->BeginImplFrame(BeginFrameArgs::CreateForTesting());
- scheduler->OnBeginImplFrameDeadline();
+ scheduler->BeginFrame(CreateBeginFrameArgsForTesting());
+
+ client.task_runner().RunPendingTasks(); // Run posted deadline.
EXPECT_EQ(2, client.num_draws());
EXPECT_TRUE(scheduler->CommitPending());
EXPECT_TRUE(scheduler->RedrawPending());
- EXPECT_TRUE(client.needs_begin_impl_frame());
+ EXPECT_TRUE(client.needs_begin_frame());
// Draw successfully.
client.SetDrawWillHappen(true);
- scheduler->BeginImplFrame(BeginFrameArgs::CreateForTesting());
- scheduler->OnBeginImplFrameDeadline();
+ scheduler->BeginFrame(CreateBeginFrameArgsForTesting());
+ client.task_runner().RunPendingTasks(); // Run posted deadline.
EXPECT_EQ(3, client.num_draws());
EXPECT_TRUE(scheduler->CommitPending());
EXPECT_FALSE(scheduler->RedrawPending());
- EXPECT_TRUE(client.needs_begin_impl_frame());
+ EXPECT_TRUE(client.needs_begin_frame());
}
TEST(SchedulerTest, NoSwapWhenDrawFails) {
SchedulerClientThatSetNeedsCommitInsideDraw client;
SchedulerSettings default_scheduler_settings;
- Scheduler* scheduler = client.CreateScheduler(default_scheduler_settings);
+ TestScheduler* scheduler = client.CreateScheduler(default_scheduler_settings);
scheduler->SetCanStart();
scheduler->SetVisible(true);
scheduler->SetCanDraw(true);
- InitializeOutputSurfaceAndFirstCommit(scheduler);
+ InitializeOutputSurfaceAndFirstCommit(scheduler, &client);
client.Reset();
scheduler->SetNeedsRedraw();
EXPECT_TRUE(scheduler->RedrawPending());
- EXPECT_TRUE(client.needs_begin_impl_frame());
+ EXPECT_TRUE(client.needs_begin_frame());
EXPECT_EQ(0, client.num_draws());
// Draw successfully, this starts a new frame.
client.SetNeedsCommitOnNextDraw();
- scheduler->BeginImplFrame(BeginFrameArgs::CreateForTesting());
- scheduler->OnBeginImplFrameDeadline();
+ scheduler->BeginFrame(CreateBeginFrameArgsForTesting());
+ client.task_runner().RunPendingTasks(); // Run posted deadline.
EXPECT_EQ(1, client.num_draws());
scheduler->SetNeedsRedraw();
EXPECT_TRUE(scheduler->RedrawPending());
- EXPECT_TRUE(client.needs_begin_impl_frame());
+ EXPECT_TRUE(client.needs_begin_frame());
// Fail to draw, this should not start a frame.
client.SetDrawWillHappen(false);
client.SetNeedsCommitOnNextDraw();
- scheduler->BeginImplFrame(BeginFrameArgs::CreateForTesting());
- scheduler->OnBeginImplFrameDeadline();
+ scheduler->BeginFrame(CreateBeginFrameArgsForTesting());
+ client.task_runner().RunPendingTasks(); // Run posted deadline.
EXPECT_EQ(2, client.num_draws());
}
-TEST(SchedulerTest, NoSwapWhenSwapFailsDuringForcedCommit) {
- FakeSchedulerClient client;
- SchedulerSettings default_scheduler_settings;
- Scheduler* scheduler = client.CreateScheduler(default_scheduler_settings);
-
- // Tell the client that it will fail to swap.
- client.SetDrawWillHappen(true);
- client.SetSwapWillHappenIfDrawHappens(false);
-
- // Get the compositor to do a ScheduledActionDrawAndReadback.
- scheduler->SetCanDraw(true);
- scheduler->SetNeedsRedraw();
- scheduler->SetNeedsForcedCommitForReadback();
- scheduler->FinishCommit();
- EXPECT_TRUE(client.HasAction("ScheduledActionDrawAndReadback"));
-}
-
-TEST(SchedulerTest, BackToBackReadbackAllowed) {
- // Some clients call readbacks twice in a row before the replacement
- // commit comes in. Make sure it is allowed.
- FakeSchedulerClient client;
- SchedulerSettings default_scheduler_settings;
- Scheduler* scheduler = client.CreateScheduler(default_scheduler_settings);
-
- // Get the compositor to do 2 ScheduledActionDrawAndReadbacks before
- // the replacement commit comes in.
- scheduler->SetCanDraw(true);
- scheduler->SetNeedsRedraw();
- scheduler->SetNeedsForcedCommitForReadback();
- scheduler->FinishCommit();
- EXPECT_TRUE(client.HasAction("ScheduledActionDrawAndReadback"));
-
- client.Reset();
- scheduler->SetNeedsForcedCommitForReadback();
- scheduler->FinishCommit();
- EXPECT_TRUE(client.HasAction("ScheduledActionDrawAndReadback"));
-
- // The replacement commit comes in after 2 readbacks.
- client.Reset();
- scheduler->FinishCommit();
-}
-
-
class SchedulerClientNeedsManageTilesInDraw : public FakeSchedulerClient {
public:
- virtual DrawSwapReadbackResult ScheduledActionDrawAndSwapIfPossible()
+ virtual DrawResult ScheduledActionDrawAndSwapIfPossible()
OVERRIDE {
scheduler_->SetNeedsManageTiles();
return FakeSchedulerClient::ScheduledActionDrawAndSwapIfPossible();
@@ -1002,11 +714,11 @@ class SchedulerClientNeedsManageTilesInDraw : public FakeSchedulerClient {
TEST(SchedulerTest, ManageTiles) {
SchedulerClientNeedsManageTilesInDraw client;
SchedulerSettings default_scheduler_settings;
- Scheduler* scheduler = client.CreateScheduler(default_scheduler_settings);
+ TestScheduler* scheduler = client.CreateScheduler(default_scheduler_settings);
scheduler->SetCanStart();
scheduler->SetVisible(true);
scheduler->SetCanDraw(true);
- InitializeOutputSurfaceAndFirstCommit(scheduler);
+ InitializeOutputSurfaceAndFirstCommit(scheduler, &client);
// Request both draw and manage tiles. ManageTiles shouldn't
// be trigged until BeginImplFrame.
@@ -1015,7 +727,7 @@ TEST(SchedulerTest, ManageTiles) {
scheduler->SetNeedsRedraw();
EXPECT_TRUE(scheduler->RedrawPending());
EXPECT_TRUE(scheduler->ManageTilesPending());
- EXPECT_TRUE(client.needs_begin_impl_frame());
+ EXPECT_TRUE(client.needs_begin_frame());
EXPECT_EQ(0, client.num_draws());
EXPECT_FALSE(client.HasAction("ScheduledActionManageTiles"));
EXPECT_FALSE(client.HasAction("ScheduledActionDrawAndSwapIfPossible"));
@@ -1023,12 +735,14 @@ TEST(SchedulerTest, ManageTiles) {
// We have no immediate actions to perform, so the BeginImplFrame should post
// the deadline task.
client.Reset();
- scheduler->BeginImplFrame(BeginFrameArgs::CreateForTesting());
- EXPECT_SINGLE_ACTION("PostBeginImplFrameDeadlineTask", client);
+ scheduler->BeginFrame(CreateBeginFrameArgsForTesting());
+ EXPECT_ACTION("WillBeginImplFrame", client, 0, 2);
+ EXPECT_ACTION("ScheduledActionAnimate", client, 1, 2);
+ EXPECT_TRUE(scheduler->BeginImplFrameDeadlinePending());
// On the deadline, he actions should have occured in the right order.
client.Reset();
- scheduler->OnBeginImplFrameDeadline();
+ client.task_runner().RunPendingTasks(); // Run posted deadline.
EXPECT_EQ(1, client.num_draws());
EXPECT_TRUE(client.HasAction("ScheduledActionDrawAndSwapIfPossible"));
EXPECT_TRUE(client.HasAction("ScheduledActionManageTiles"));
@@ -1036,26 +750,29 @@ TEST(SchedulerTest, ManageTiles) {
client.ActionIndex("ScheduledActionManageTiles"));
EXPECT_FALSE(scheduler->RedrawPending());
EXPECT_FALSE(scheduler->ManageTilesPending());
+ EXPECT_FALSE(scheduler->BeginImplFrameDeadlinePending());
// Request a draw. We don't need a ManageTiles yet.
client.Reset();
scheduler->SetNeedsRedraw();
EXPECT_TRUE(scheduler->RedrawPending());
EXPECT_FALSE(scheduler->ManageTilesPending());
- EXPECT_TRUE(client.needs_begin_impl_frame());
+ EXPECT_TRUE(client.needs_begin_frame());
EXPECT_EQ(0, client.num_draws());
// We have no immediate actions to perform, so the BeginImplFrame should post
// the deadline task.
client.Reset();
- scheduler->BeginImplFrame(BeginFrameArgs::CreateForTesting());
- EXPECT_SINGLE_ACTION("PostBeginImplFrameDeadlineTask", client);
+ scheduler->BeginFrame(CreateBeginFrameArgsForTesting());
+ EXPECT_ACTION("WillBeginImplFrame", client, 0, 2);
+ EXPECT_ACTION("ScheduledActionAnimate", client, 1, 2);
+ EXPECT_TRUE(scheduler->BeginImplFrameDeadlinePending());
// Draw. The draw will trigger SetNeedsManageTiles, and
// then the ManageTiles action will be triggered after the Draw.
// Afterwards, neither a draw nor ManageTiles are pending.
client.Reset();
- scheduler->OnBeginImplFrameDeadline();
+ client.task_runner().RunPendingTasks(); // Run posted deadline.
EXPECT_EQ(1, client.num_draws());
EXPECT_TRUE(client.HasAction("ScheduledActionDrawAndSwapIfPossible"));
EXPECT_TRUE(client.HasAction("ScheduledActionManageTiles"));
@@ -1063,75 +780,86 @@ TEST(SchedulerTest, ManageTiles) {
client.ActionIndex("ScheduledActionManageTiles"));
EXPECT_FALSE(scheduler->RedrawPending());
EXPECT_FALSE(scheduler->ManageTilesPending());
+ EXPECT_FALSE(scheduler->BeginImplFrameDeadlinePending());
// We need a BeginImplFrame where we don't swap to go idle.
client.Reset();
- scheduler->BeginImplFrame(BeginFrameArgs::CreateForTesting());
- EXPECT_SINGLE_ACTION("PostBeginImplFrameDeadlineTask", client);
+ scheduler->BeginFrame(CreateBeginFrameArgsForTesting());
+ EXPECT_SINGLE_ACTION("WillBeginImplFrame", client);
+ EXPECT_TRUE(scheduler->BeginImplFrameDeadlinePending());
client.Reset();
- scheduler->OnBeginImplFrameDeadline();
- EXPECT_SINGLE_ACTION("SetNeedsBeginImplFrame", client);;
+ client.task_runner().RunPendingTasks(); // Run posted deadline.
+ EXPECT_SINGLE_ACTION("SetNeedsBeginFrame", client);
+ EXPECT_FALSE(client.needs_begin_frame());
+ EXPECT_FALSE(scheduler->BeginImplFrameDeadlinePending());
EXPECT_EQ(0, client.num_draws());
// Now trigger a ManageTiles outside of a draw. We will then need
// a begin-frame for the ManageTiles, but we don't need a draw.
client.Reset();
- EXPECT_FALSE(client.needs_begin_impl_frame());
+ EXPECT_FALSE(client.needs_begin_frame());
scheduler->SetNeedsManageTiles();
- EXPECT_TRUE(client.needs_begin_impl_frame());
+ EXPECT_TRUE(client.needs_begin_frame());
EXPECT_TRUE(scheduler->ManageTilesPending());
EXPECT_FALSE(scheduler->RedrawPending());
// BeginImplFrame. There will be no draw, only ManageTiles.
client.Reset();
- scheduler->BeginImplFrame(BeginFrameArgs::CreateForTesting());
- EXPECT_SINGLE_ACTION("PostBeginImplFrameDeadlineTask", client);
+ scheduler->BeginFrame(CreateBeginFrameArgsForTesting());
+ EXPECT_SINGLE_ACTION("WillBeginImplFrame", client);
+ EXPECT_TRUE(scheduler->BeginImplFrameDeadlinePending());
client.Reset();
- scheduler->OnBeginImplFrameDeadline();
+ client.task_runner().RunPendingTasks(); // Run posted deadline.
EXPECT_EQ(0, client.num_draws());
EXPECT_FALSE(client.HasAction("ScheduledActionDrawAndSwapIfPossible"));
EXPECT_TRUE(client.HasAction("ScheduledActionManageTiles"));
+ EXPECT_FALSE(scheduler->BeginImplFrameDeadlinePending());
}
// Test that ManageTiles only happens once per frame. If an external caller
-// initiates it, then the state machine should not on that frame.
+// initiates it, then the state machine should not ManageTiles on that frame.
TEST(SchedulerTest, ManageTilesOncePerFrame) {
FakeSchedulerClient client;
SchedulerSettings default_scheduler_settings;
- Scheduler* scheduler = client.CreateScheduler(default_scheduler_settings);
+ TestScheduler* scheduler = client.CreateScheduler(default_scheduler_settings);
scheduler->SetCanStart();
scheduler->SetVisible(true);
scheduler->SetCanDraw(true);
- InitializeOutputSurfaceAndFirstCommit(scheduler);
+ InitializeOutputSurfaceAndFirstCommit(scheduler, &client);
// If DidManageTiles during a frame, then ManageTiles should not occur again.
scheduler->SetNeedsManageTiles();
scheduler->SetNeedsRedraw();
client.Reset();
- scheduler->BeginImplFrame(BeginFrameArgs::CreateForTesting());
- EXPECT_SINGLE_ACTION("PostBeginImplFrameDeadlineTask", client);
+ scheduler->BeginFrame(CreateBeginFrameArgsForTesting());
+ EXPECT_ACTION("WillBeginImplFrame", client, 0, 2);
+ EXPECT_ACTION("ScheduledActionAnimate", client, 1, 2);
+ EXPECT_TRUE(scheduler->BeginImplFrameDeadlinePending());
EXPECT_TRUE(scheduler->ManageTilesPending());
- scheduler->DidManageTiles();
+ scheduler->DidManageTiles(); // An explicit ManageTiles.
EXPECT_FALSE(scheduler->ManageTilesPending());
client.Reset();
- scheduler->OnBeginImplFrameDeadline();
+ client.task_runner().RunPendingTasks(); // Run posted deadline.
EXPECT_EQ(1, client.num_draws());
EXPECT_TRUE(client.HasAction("ScheduledActionDrawAndSwapIfPossible"));
EXPECT_FALSE(client.HasAction("ScheduledActionManageTiles"));
EXPECT_FALSE(scheduler->RedrawPending());
EXPECT_FALSE(scheduler->ManageTilesPending());
+ EXPECT_FALSE(scheduler->BeginImplFrameDeadlinePending());
// Next frame without DidManageTiles should ManageTiles with draw.
scheduler->SetNeedsManageTiles();
scheduler->SetNeedsRedraw();
client.Reset();
- scheduler->BeginImplFrame(BeginFrameArgs::CreateForTesting());
- EXPECT_SINGLE_ACTION("PostBeginImplFrameDeadlineTask", client);
+ scheduler->BeginFrame(CreateBeginFrameArgsForTesting());
+ EXPECT_ACTION("WillBeginImplFrame", client, 0, 2);
+ EXPECT_ACTION("ScheduledActionAnimate", client, 1, 2);
+ EXPECT_TRUE(scheduler->BeginImplFrameDeadlinePending());
client.Reset();
- scheduler->OnBeginImplFrameDeadline();
+ client.task_runner().RunPendingTasks(); // Run posted deadline.
EXPECT_EQ(1, client.num_draws());
EXPECT_TRUE(client.HasAction("ScheduledActionDrawAndSwapIfPossible"));
EXPECT_TRUE(client.HasAction("ScheduledActionManageTiles"));
@@ -1139,6 +867,153 @@ TEST(SchedulerTest, ManageTilesOncePerFrame) {
client.ActionIndex("ScheduledActionManageTiles"));
EXPECT_FALSE(scheduler->RedrawPending());
EXPECT_FALSE(scheduler->ManageTilesPending());
+ EXPECT_FALSE(scheduler->BeginImplFrameDeadlinePending());
+ scheduler->DidManageTiles(); // Corresponds to ScheduledActionManageTiles
+
+ // If we get another DidManageTiles within the same frame, we should
+ // not ManageTiles on the next frame.
+ scheduler->DidManageTiles(); // An explicit ManageTiles.
+ scheduler->SetNeedsManageTiles();
+ scheduler->SetNeedsRedraw();
+ client.Reset();
+ scheduler->BeginFrame(CreateBeginFrameArgsForTesting());
+ EXPECT_ACTION("WillBeginImplFrame", client, 0, 2);
+ EXPECT_ACTION("ScheduledActionAnimate", client, 1, 2);
+ EXPECT_TRUE(scheduler->BeginImplFrameDeadlinePending());
+
+ EXPECT_TRUE(scheduler->ManageTilesPending());
+
+ client.Reset();
+ client.task_runner().RunPendingTasks(); // Run posted deadline.
+ EXPECT_EQ(1, client.num_draws());
+ EXPECT_TRUE(client.HasAction("ScheduledActionDrawAndSwapIfPossible"));
+ EXPECT_FALSE(client.HasAction("ScheduledActionManageTiles"));
+ EXPECT_FALSE(scheduler->RedrawPending());
+ EXPECT_FALSE(scheduler->BeginImplFrameDeadlinePending());
+
+ // If we get another DidManageTiles, we should not ManageTiles on the next
+ // frame. This verifies we don't alternate calling ManageTiles once and twice.
+ EXPECT_TRUE(scheduler->ManageTilesPending());
+ scheduler->DidManageTiles(); // An explicit ManageTiles.
+ EXPECT_FALSE(scheduler->ManageTilesPending());
+ scheduler->SetNeedsManageTiles();
+ scheduler->SetNeedsRedraw();
+ client.Reset();
+ scheduler->BeginFrame(CreateBeginFrameArgsForTesting());
+ EXPECT_ACTION("WillBeginImplFrame", client, 0, 2);
+ EXPECT_ACTION("ScheduledActionAnimate", client, 1, 2);
+ EXPECT_TRUE(scheduler->BeginImplFrameDeadlinePending());
+
+ EXPECT_TRUE(scheduler->ManageTilesPending());
+
+ client.Reset();
+ client.task_runner().RunPendingTasks(); // Run posted deadline.
+ EXPECT_EQ(1, client.num_draws());
+ EXPECT_TRUE(client.HasAction("ScheduledActionDrawAndSwapIfPossible"));
+ EXPECT_FALSE(client.HasAction("ScheduledActionManageTiles"));
+ EXPECT_FALSE(scheduler->RedrawPending());
+ EXPECT_FALSE(scheduler->BeginImplFrameDeadlinePending());
+
+ // Next frame without DidManageTiles should ManageTiles with draw.
+ scheduler->SetNeedsManageTiles();
+ scheduler->SetNeedsRedraw();
+ client.Reset();
+ scheduler->BeginFrame(CreateBeginFrameArgsForTesting());
+ EXPECT_ACTION("WillBeginImplFrame", client, 0, 2);
+ EXPECT_ACTION("ScheduledActionAnimate", client, 1, 2);
+ EXPECT_TRUE(scheduler->BeginImplFrameDeadlinePending());
+
+ client.Reset();
+ client.task_runner().RunPendingTasks(); // Run posted deadline.
+ EXPECT_EQ(1, client.num_draws());
+ EXPECT_TRUE(client.HasAction("ScheduledActionDrawAndSwapIfPossible"));
+ EXPECT_TRUE(client.HasAction("ScheduledActionManageTiles"));
+ EXPECT_LT(client.ActionIndex("ScheduledActionDrawAndSwapIfPossible"),
+ client.ActionIndex("ScheduledActionManageTiles"));
+ EXPECT_FALSE(scheduler->RedrawPending());
+ EXPECT_FALSE(scheduler->ManageTilesPending());
+ EXPECT_FALSE(scheduler->BeginImplFrameDeadlinePending());
+ scheduler->DidManageTiles(); // Corresponds to ScheduledActionManageTiles
+}
+
+TEST(SchedulerTest, ShouldUpdateVisibleTiles) {
+ FakeSchedulerClient client;
+ SchedulerSettings scheduler_settings;
+ scheduler_settings.impl_side_painting = true;
+ TestScheduler* scheduler = client.CreateScheduler(scheduler_settings);
+ scheduler->SetCanStart();
+ scheduler->SetVisible(true);
+ scheduler->SetCanDraw(true);
+ InitializeOutputSurfaceAndFirstCommit(scheduler, &client);
+
+ client.SetRedrawWillHappenIfUpdateVisibleTilesHappens(true);
+
+ // SetNeedsCommit should begin the frame.
+ client.Reset();
+ scheduler->SetNeedsCommit();
+ EXPECT_SINGLE_ACTION("SetNeedsBeginFrame", client);
+
+ client.Reset();
+ scheduler->BeginFrame(CreateBeginFrameArgsForTesting());
+ EXPECT_ACTION("WillBeginImplFrame", client, 0, 2);
+ EXPECT_ACTION("ScheduledActionSendBeginMainFrame", client, 1, 2);
+ EXPECT_TRUE(scheduler->BeginImplFrameDeadlinePending());
+
+ client.Reset();
+ scheduler->NotifyBeginMainFrameStarted();
+ scheduler->NotifyReadyToCommit();
+ EXPECT_SINGLE_ACTION("ScheduledActionCommit", client);
+
+ client.Reset();
+ scheduler->NotifyReadyToActivate();
+ EXPECT_SINGLE_ACTION("ScheduledActionActivatePendingTree", client);
+
+ client.Reset();
+ client.SetSwapContainsIncompleteTile(true);
+ client.task_runner().RunPendingTasks(); // Run posted deadline.
+ EXPECT_ACTION("ScheduledActionAnimate", client, 0, 2);
+ EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client, 1, 2);
+ EXPECT_FALSE(scheduler->RedrawPending());
+
+ client.Reset();
+ scheduler->BeginFrame(CreateBeginFrameArgsForTesting());
+ EXPECT_SINGLE_ACTION("WillBeginImplFrame", client);
+ EXPECT_TRUE(scheduler->BeginImplFrameDeadlinePending());
+
+ client.Reset();
+ client.task_runner().RunPendingTasks(); // Run posted deadline.
+ EXPECT_ACTION("ScheduledActionUpdateVisibleTiles", client, 0, 3);
+ EXPECT_ACTION("ScheduledActionAnimate", client, 1, 3);
+ EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client, 2, 3);
+
+ client.Reset();
+ scheduler->BeginFrame(CreateBeginFrameArgsForTesting());
+ EXPECT_SINGLE_ACTION("WillBeginImplFrame", client);
+ EXPECT_TRUE(scheduler->BeginImplFrameDeadlinePending());
+
+ // No more UpdateVisibleTiles().
+ client.Reset();
+ client.task_runner().RunPendingTasks(); // Run posted deadline.
+ EXPECT_SINGLE_ACTION("SetNeedsBeginFrame", client);
+ EXPECT_FALSE(client.needs_begin_frame());
+}
+
+TEST(SchedulerTest, TriggerBeginFrameDeadlineEarly) {
+ SchedulerClientNeedsManageTilesInDraw client;
+ SchedulerSettings default_scheduler_settings;
+ TestScheduler* scheduler = client.CreateScheduler(default_scheduler_settings);
+ scheduler->SetCanStart();
+ scheduler->SetVisible(true);
+ scheduler->SetCanDraw(true);
+ InitializeOutputSurfaceAndFirstCommit(scheduler, &client);
+
+ client.Reset();
+ scheduler->SetNeedsRedraw();
+ scheduler->BeginFrame(CreateBeginFrameArgsForTesting());
+
+ // The deadline should be zero since there is no work other than drawing
+ // pending.
+ EXPECT_EQ(base::TimeTicks(), client.posted_begin_impl_frame_deadline());
}
class SchedulerClientWithFixedEstimates : public FakeSchedulerClient {
@@ -1170,6 +1045,7 @@ class SchedulerClientWithFixedEstimates : public FakeSchedulerClient {
void MainFrameInHighLatencyMode(int64 begin_main_frame_to_commit_estimate_in_ms,
int64 commit_to_activate_estimate_in_ms,
+ bool smoothness_takes_priority,
bool should_send_begin_main_frame) {
// Set up client with specified estimates (draw duration is set to 1).
SchedulerClientWithFixedEstimates client(
@@ -1177,33 +1053,33 @@ void MainFrameInHighLatencyMode(int64 begin_main_frame_to_commit_estimate_in_ms,
base::TimeDelta::FromMilliseconds(
begin_main_frame_to_commit_estimate_in_ms),
base::TimeDelta::FromMilliseconds(commit_to_activate_estimate_in_ms));
- SchedulerSettings scheduler_settings;
- scheduler_settings.deadline_scheduling_enabled = true;
- scheduler_settings.switch_to_low_latency_if_possible = true;
- Scheduler* scheduler = client.CreateScheduler(scheduler_settings);
+ SchedulerSettings default_scheduler_settings;
+ TestScheduler* scheduler = client.CreateScheduler(default_scheduler_settings);
scheduler->SetCanStart();
scheduler->SetVisible(true);
scheduler->SetCanDraw(true);
- InitializeOutputSurfaceAndFirstCommit(scheduler);
+ scheduler->SetSmoothnessTakesPriority(smoothness_takes_priority);
+ InitializeOutputSurfaceAndFirstCommit(scheduler, &client);
// Impl thread hits deadline before commit finishes.
client.Reset();
scheduler->SetNeedsCommit();
EXPECT_FALSE(scheduler->MainThreadIsInHighLatencyMode());
- scheduler->BeginImplFrame(BeginFrameArgs::CreateForTesting());
+ scheduler->BeginFrame(CreateBeginFrameArgsForTesting());
EXPECT_FALSE(scheduler->MainThreadIsInHighLatencyMode());
- scheduler->OnBeginImplFrameDeadline();
+ client.task_runner().RunPendingTasks(); // Run posted deadline.
EXPECT_TRUE(scheduler->MainThreadIsInHighLatencyMode());
- scheduler->FinishCommit();
+ scheduler->NotifyBeginMainFrameStarted();
+ scheduler->NotifyReadyToCommit();
EXPECT_TRUE(scheduler->MainThreadIsInHighLatencyMode());
EXPECT_TRUE(client.HasAction("ScheduledActionSendBeginMainFrame"));
client.Reset();
scheduler->SetNeedsCommit();
EXPECT_TRUE(scheduler->MainThreadIsInHighLatencyMode());
- scheduler->BeginImplFrame(BeginFrameArgs::CreateForTesting());
+ scheduler->BeginFrame(CreateBeginFrameArgsForTesting());
EXPECT_TRUE(scheduler->MainThreadIsInHighLatencyMode());
- scheduler->OnBeginImplFrameDeadline();
+ client.task_runner().RunPendingTasks(); // Run posted deadline.
EXPECT_EQ(scheduler->MainThreadIsInHighLatencyMode(),
should_send_begin_main_frame);
EXPECT_EQ(client.HasAction("ScheduledActionSendBeginMainFrame"),
@@ -1214,36 +1090,38 @@ TEST(SchedulerTest,
SkipMainFrameIfHighLatencyAndCanCommitAndActivateBeforeDeadline) {
// Set up client so that estimates indicate that we can commit and activate
// before the deadline (~8ms by default).
- MainFrameInHighLatencyMode(1, 1, false);
+ MainFrameInHighLatencyMode(1, 1, false, false);
}
TEST(SchedulerTest, NotSkipMainFrameIfHighLatencyAndCanCommitTooLong) {
// Set up client so that estimates indicate that the commit cannot finish
// before the deadline (~8ms by default).
- MainFrameInHighLatencyMode(10, 1, true);
+ MainFrameInHighLatencyMode(10, 1, false, true);
}
TEST(SchedulerTest, NotSkipMainFrameIfHighLatencyAndCanActivateTooLong) {
// Set up client so that estimates indicate that the activate cannot finish
// before the deadline (~8ms by default).
- MainFrameInHighLatencyMode(1, 10, true);
+ MainFrameInHighLatencyMode(1, 10, false, true);
}
-void SpinForMillis(int millis) {
- base::RunLoop run_loop;
- base::MessageLoop::current()->PostDelayedTask(
- FROM_HERE,
- run_loop.QuitClosure(),
- base::TimeDelta::FromMilliseconds(millis));
- run_loop.Run();
+TEST(SchedulerTest, NotSkipMainFrameInPreferSmoothnessMode) {
+ // Set up client so that estimates indicate that we can commit and activate
+ // before the deadline (~8ms by default), but also enable smoothness takes
+ // priority mode.
+ MainFrameInHighLatencyMode(1, 1, true, true);
}
TEST(SchedulerTest, PollForCommitCompletion) {
- FakeSchedulerClient client;
+ // Since we are simulating a long commit, set up a client with draw duration
+ // estimates that prevent skipping main frames to get to low latency mode.
+ SchedulerClientWithFixedEstimates client(
+ base::TimeDelta::FromMilliseconds(1),
+ base::TimeDelta::FromMilliseconds(32),
+ base::TimeDelta::FromMilliseconds(32));
client.set_log_anticipated_draw_time_change(true);
- SchedulerSettings settings = SchedulerSettings();
- settings.throttle_frame_production = false;
- Scheduler* scheduler = client.CreateScheduler(settings);
+ SchedulerSettings default_scheduler_settings;
+ TestScheduler* scheduler = client.CreateScheduler(default_scheduler_settings);
scheduler->SetCanDraw(true);
scheduler->SetCanStart();
@@ -1252,34 +1130,789 @@ TEST(SchedulerTest, PollForCommitCompletion) {
scheduler->SetNeedsCommit();
EXPECT_TRUE(scheduler->CommitPending());
- scheduler->FinishCommit();
+ scheduler->NotifyBeginMainFrameStarted();
+ scheduler->NotifyReadyToCommit();
scheduler->SetNeedsRedraw();
- BeginFrameArgs impl_frame_args = BeginFrameArgs::CreateForTesting();
- const int interval = 1;
- impl_frame_args.interval = base::TimeDelta::FromMilliseconds(interval);
- scheduler->BeginImplFrame(impl_frame_args);
- scheduler->OnBeginImplFrameDeadline();
-
- // At this point, we've drawn a frame. Start another commit, but hold off on
- // the FinishCommit for now.
+
+ BeginFrameArgs frame_args = CreateBeginFrameArgsForTesting();
+ frame_args.interval = base::TimeDelta::FromMilliseconds(1000);
+ scheduler->BeginFrame(frame_args);
+
+ EXPECT_TRUE(scheduler->BeginImplFrameDeadlinePending());
+ client.task_runner().RunPendingTasks(); // Run posted deadline.
+ EXPECT_FALSE(scheduler->BeginImplFrameDeadlinePending());
+
+ scheduler->DidSwapBuffers();
+ scheduler->DidSwapBuffersComplete();
+
+ // At this point, we've drawn a frame. Start another commit, but hold off on
+ // the NotifyReadyToCommit for now.
EXPECT_FALSE(scheduler->CommitPending());
scheduler->SetNeedsCommit();
+ scheduler->BeginFrame(frame_args);
EXPECT_TRUE(scheduler->CommitPending());
+ // Draw and swap the frame, but don't ack the swap to simulate the Browser
+ // blocking on the renderer.
+ EXPECT_TRUE(scheduler->BeginImplFrameDeadlinePending());
+ client.task_runner().RunPendingTasks(); // Run posted deadline.
+ EXPECT_FALSE(scheduler->BeginImplFrameDeadlinePending());
+ scheduler->DidSwapBuffers();
+
// Spin the event loop a few times and make sure we get more
// DidAnticipateDrawTimeChange calls every time.
int actions_so_far = client.num_actions_();
// Does three iterations to make sure that the timer is properly repeating.
for (int i = 0; i < 3; ++i) {
- // Wait for 2x the frame interval to match
- // Scheduler::advance_commit_state_timer_'s rate.
- SpinForMillis(interval * 2);
+ EXPECT_EQ((frame_args.interval * 2).InMicroseconds(),
+ client.task_runner().NextPendingTaskDelay().InMicroseconds())
+ << *scheduler->AsValue();
+ client.task_runner().RunPendingTasks();
EXPECT_GT(client.num_actions_(), actions_so_far);
EXPECT_STREQ(client.Action(client.num_actions_() - 1),
"DidAnticipatedDrawTimeChange");
actions_so_far = client.num_actions_();
}
+
+ // Do the same thing after BeginMainFrame starts but still before activation.
+ scheduler->NotifyBeginMainFrameStarted();
+ for (int i = 0; i < 3; ++i) {
+ EXPECT_EQ((frame_args.interval * 2).InMicroseconds(),
+ client.task_runner().NextPendingTaskDelay().InMicroseconds())
+ << *scheduler->AsValue();
+ client.task_runner().RunPendingTasks();
+ EXPECT_GT(client.num_actions_(), actions_so_far);
+ EXPECT_STREQ(client.Action(client.num_actions_() - 1),
+ "DidAnticipatedDrawTimeChange");
+ actions_so_far = client.num_actions_();
+ }
+}
+
+TEST(SchedulerTest, BeginRetroFrame) {
+ FakeSchedulerClient client;
+ SchedulerSettings scheduler_settings;
+ TestScheduler* scheduler = client.CreateScheduler(scheduler_settings);
+ scheduler->SetCanStart();
+ scheduler->SetVisible(true);
+ scheduler->SetCanDraw(true);
+ InitializeOutputSurfaceAndFirstCommit(scheduler, &client);
+
+ // SetNeedsCommit should begin the frame on the next BeginImplFrame.
+ client.Reset();
+ scheduler->SetNeedsCommit();
+ EXPECT_TRUE(client.needs_begin_frame());
+ EXPECT_SINGLE_ACTION("SetNeedsBeginFrame", client);
+ client.Reset();
+
+ // Create a BeginFrame with a long deadline to avoid race conditions.
+ // This is the first BeginFrame, which will be handled immediately.
+ BeginFrameArgs args = CreateBeginFrameArgsForTesting();
+ args.deadline += base::TimeDelta::FromHours(1);
+ scheduler->BeginFrame(args);
+ EXPECT_ACTION("WillBeginImplFrame", client, 0, 2);
+ EXPECT_ACTION("ScheduledActionSendBeginMainFrame", client, 1, 2);
+ EXPECT_TRUE(scheduler->BeginImplFrameDeadlinePending());
+ EXPECT_TRUE(client.needs_begin_frame());
+ client.Reset();
+
+ // Queue BeginFrames while we are still handling the previous BeginFrame.
+ args.frame_time += base::TimeDelta::FromSeconds(1);
+ scheduler->BeginFrame(args);
+ args.frame_time += base::TimeDelta::FromSeconds(1);
+ scheduler->BeginFrame(args);
+
+ // If we don't swap on the deadline, we wait for the next BeginImplFrame.
+ client.task_runner().RunPendingTasks(); // Run posted deadline.
+ EXPECT_NO_ACTION(client);
+ EXPECT_FALSE(scheduler->BeginImplFrameDeadlinePending());
+ EXPECT_TRUE(client.needs_begin_frame());
+ client.Reset();
+
+ // NotifyReadyToCommit should trigger the commit.
+ scheduler->NotifyBeginMainFrameStarted();
+ scheduler->NotifyReadyToCommit();
+ EXPECT_SINGLE_ACTION("ScheduledActionCommit", client);
+ EXPECT_TRUE(client.needs_begin_frame());
+ client.Reset();
+
+ // BeginImplFrame should prepare the draw.
+ client.task_runner().RunPendingTasks(); // Run posted BeginRetroFrame.
+ EXPECT_ACTION("WillBeginImplFrame", client, 0, 2);
+ EXPECT_ACTION("ScheduledActionAnimate", client, 1, 2);
+ EXPECT_TRUE(scheduler->BeginImplFrameDeadlinePending());
+ EXPECT_TRUE(client.needs_begin_frame());
+ client.Reset();
+
+ // BeginImplFrame deadline should draw.
+ client.task_runner().RunPendingTasks(); // Run posted deadline.
+ EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client, 0, 1);
+ EXPECT_FALSE(scheduler->BeginImplFrameDeadlinePending());
+ EXPECT_TRUE(client.needs_begin_frame());
+ client.Reset();
+
+ // The following BeginImplFrame deadline should SetNeedsBeginFrame(false)
+ // to avoid excessive toggles.
+ client.task_runner().RunPendingTasks(); // Run posted BeginRetroFrame.
+ EXPECT_SINGLE_ACTION("WillBeginImplFrame", client);
+ EXPECT_TRUE(scheduler->BeginImplFrameDeadlinePending());
+ client.Reset();
+
+ client.task_runner().RunPendingTasks(); // Run posted deadline.
+ EXPECT_SINGLE_ACTION("SetNeedsBeginFrame", client);
+ EXPECT_FALSE(client.needs_begin_frame());
+ client.Reset();
+}
+
+TEST(SchedulerTest, BeginRetroFrame_SwapThrottled) {
+ FakeSchedulerClient client;
+ SchedulerSettings scheduler_settings;
+ TestScheduler* scheduler = client.CreateScheduler(scheduler_settings);
+ scheduler->SetCanStart();
+ scheduler->SetVisible(true);
+ scheduler->SetCanDraw(true);
+ InitializeOutputSurfaceAndFirstCommit(scheduler, &client);
+
+ // To test swap ack throttling, this test disables automatic swap acks.
+ scheduler->SetMaxSwapsPending(1);
+ client.SetAutomaticSwapAck(false);
+
+ // SetNeedsCommit should begin the frame on the next BeginImplFrame.
+ client.Reset();
+ scheduler->SetNeedsCommit();
+ EXPECT_TRUE(client.needs_begin_frame());
+ EXPECT_SINGLE_ACTION("SetNeedsBeginFrame", client);
+ client.Reset();
+
+ // Create a BeginFrame with a long deadline to avoid race conditions.
+ // This is the first BeginFrame, which will be handled immediately.
+ BeginFrameArgs args = CreateBeginFrameArgsForTesting();
+ args.deadline += base::TimeDelta::FromHours(1);
+ scheduler->BeginFrame(args);
+ EXPECT_ACTION("WillBeginImplFrame", client, 0, 2);
+ EXPECT_ACTION("ScheduledActionSendBeginMainFrame", client, 1, 2);
+ EXPECT_TRUE(scheduler->BeginImplFrameDeadlinePending());
+ EXPECT_TRUE(client.needs_begin_frame());
+ client.Reset();
+
+ // Queue BeginFrame while we are still handling the previous BeginFrame.
+ EXPECT_TRUE(scheduler->BeginImplFrameDeadlinePending());
+ args.frame_time += base::TimeDelta::FromSeconds(1);
+ scheduler->BeginFrame(args);
+ EXPECT_NO_ACTION(client);
+ EXPECT_TRUE(scheduler->BeginImplFrameDeadlinePending());
+ client.Reset();
+
+ // NotifyReadyToCommit should trigger the pending commit and draw.
+ scheduler->NotifyBeginMainFrameStarted();
+ scheduler->NotifyReadyToCommit();
+ EXPECT_SINGLE_ACTION("ScheduledActionCommit", client);
+ EXPECT_TRUE(client.needs_begin_frame());
+ client.Reset();
+
+ // Swapping will put us into a swap throttled state.
+ client.task_runner().RunPendingTasks(); // Run posted deadline.
+ EXPECT_ACTION("ScheduledActionAnimate", client, 0, 2);
+ EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client, 1, 2);
+ EXPECT_FALSE(scheduler->BeginImplFrameDeadlinePending());
+ EXPECT_TRUE(client.needs_begin_frame());
+ client.Reset();
+
+ // While swap throttled, BeginRetroFrames should trigger BeginImplFrames
+ // but not a BeginMainFrame or draw.
+ scheduler->SetNeedsCommit();
+ client.task_runner().RunPendingTasks(); // Run posted BeginRetroFrame.
+ EXPECT_ACTION("WillBeginImplFrame", client, 0, 1);
+ EXPECT_TRUE(scheduler->BeginImplFrameDeadlinePending());
+ EXPECT_TRUE(client.needs_begin_frame());
+ client.Reset();
+
+ // Queue BeginFrame while we are still handling the previous BeginFrame.
+ args.frame_time += base::TimeDelta::FromSeconds(1);
+ scheduler->BeginFrame(args);
+ EXPECT_NO_ACTION(client);
+ EXPECT_TRUE(scheduler->BeginImplFrameDeadlinePending());
+ EXPECT_TRUE(client.needs_begin_frame());
+ client.Reset();
+
+ // Take us out of a swap throttled state.
+ scheduler->DidSwapBuffersComplete();
+ EXPECT_ACTION("ScheduledActionSendBeginMainFrame", client, 0, 1);
+ EXPECT_TRUE(scheduler->BeginImplFrameDeadlinePending());
+ EXPECT_TRUE(client.needs_begin_frame());
+ client.Reset();
+
+ // BeginImplFrame deadline should draw.
+ scheduler->SetNeedsRedraw();
+ client.task_runner().RunPendingTasks(); // Run posted deadline.
+ EXPECT_ACTION("ScheduledActionAnimate", client, 0, 2);
+ EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client, 1, 2);
+ EXPECT_FALSE(scheduler->BeginImplFrameDeadlinePending());
+ EXPECT_TRUE(client.needs_begin_frame());
+ client.Reset();
+}
+
+void BeginFramesNotFromClient(bool begin_frame_scheduling_enabled,
+ bool throttle_frame_production) {
+ FakeSchedulerClient client;
+ SchedulerSettings scheduler_settings;
+ scheduler_settings.begin_frame_scheduling_enabled =
+ begin_frame_scheduling_enabled;
+ scheduler_settings.throttle_frame_production = throttle_frame_production;
+ TestScheduler* scheduler = client.CreateScheduler(scheduler_settings);
+ scheduler->SetCanStart();
+ scheduler->SetVisible(true);
+ scheduler->SetCanDraw(true);
+ InitializeOutputSurfaceAndFirstCommit(scheduler, &client);
+
+ // SetNeedsCommit should begin the frame on the next BeginImplFrame
+ // without calling SetNeedsBeginFrame.
+ client.Reset();
+ scheduler->SetNeedsCommit();
+ EXPECT_FALSE(client.needs_begin_frame());
+ EXPECT_NO_ACTION(client);
+ client.Reset();
+
+ // When the client-driven BeginFrame are disabled, the scheduler posts it's
+ // own BeginFrame tasks.
+ client.task_runner().RunPendingTasks(); // Run posted BeginFrame.
+ EXPECT_ACTION("WillBeginImplFrame", client, 0, 2);
+ EXPECT_ACTION("ScheduledActionSendBeginMainFrame", client, 1, 2);
+ EXPECT_TRUE(scheduler->BeginImplFrameDeadlinePending());
+ EXPECT_FALSE(client.needs_begin_frame());
+ client.Reset();
+
+ // If we don't swap on the deadline, we wait for the next BeginFrame.
+ client.task_runner().RunPendingTasks(); // Run posted deadline.
+ EXPECT_NO_ACTION(client);
+ EXPECT_FALSE(scheduler->BeginImplFrameDeadlinePending());
+ EXPECT_FALSE(client.needs_begin_frame());
+ client.Reset();
+
+ // NotifyReadyToCommit should trigger the commit.
+ scheduler->NotifyBeginMainFrameStarted();
+ scheduler->NotifyReadyToCommit();
+ EXPECT_SINGLE_ACTION("ScheduledActionCommit", client);
+ EXPECT_FALSE(client.needs_begin_frame());
+ client.Reset();
+
+ // BeginImplFrame should prepare the draw.
+ client.task_runner().RunPendingTasks(); // Run posted BeginFrame.
+ EXPECT_ACTION("WillBeginImplFrame", client, 0, 2);
+ EXPECT_ACTION("ScheduledActionAnimate", client, 1, 2);
+ EXPECT_TRUE(scheduler->BeginImplFrameDeadlinePending());
+ EXPECT_FALSE(client.needs_begin_frame());
+ client.Reset();
+
+ // BeginImplFrame deadline should draw.
+ client.task_runner().RunPendingTasks(); // Run posted deadline.
+ EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client, 0, 1);
+ EXPECT_FALSE(scheduler->BeginImplFrameDeadlinePending());
+ EXPECT_FALSE(client.needs_begin_frame());
+ client.Reset();
+
+ // The following BeginImplFrame deadline should SetNeedsBeginFrame(false)
+ // to avoid excessive toggles.
+ client.task_runner().RunPendingTasks(); // Run posted BeginFrame.
+ EXPECT_SINGLE_ACTION("WillBeginImplFrame", client);
+ EXPECT_TRUE(scheduler->BeginImplFrameDeadlinePending());
+ client.Reset();
+
+ // Make sure SetNeedsBeginFrame isn't called on the client
+ // when the BeginFrame is no longer needed.
+ client.task_runner().RunPendingTasks(); // Run posted deadline.
+ EXPECT_NO_ACTION(client);
+ EXPECT_FALSE(client.needs_begin_frame());
+ client.Reset();
+}
+
+TEST(SchedulerTest, SyntheticBeginFrames) {
+ bool begin_frame_scheduling_enabled = false;
+ bool throttle_frame_production = true;
+ BeginFramesNotFromClient(begin_frame_scheduling_enabled,
+ throttle_frame_production);
+}
+
+TEST(SchedulerTest, VSyncThrottlingDisabled) {
+ bool begin_frame_scheduling_enabled = true;
+ bool throttle_frame_production = false;
+ BeginFramesNotFromClient(begin_frame_scheduling_enabled,
+ throttle_frame_production);
+}
+
+TEST(SchedulerTest, SyntheticBeginFrames_And_VSyncThrottlingDisabled) {
+ bool begin_frame_scheduling_enabled = false;
+ bool throttle_frame_production = false;
+ BeginFramesNotFromClient(begin_frame_scheduling_enabled,
+ throttle_frame_production);
+}
+
+void BeginFramesNotFromClient_SwapThrottled(bool begin_frame_scheduling_enabled,
+ bool throttle_frame_production) {
+ FakeSchedulerClient client;
+ SchedulerSettings scheduler_settings;
+ scheduler_settings.begin_frame_scheduling_enabled =
+ begin_frame_scheduling_enabled;
+ scheduler_settings.throttle_frame_production = throttle_frame_production;
+ TestScheduler* scheduler = client.CreateScheduler(scheduler_settings);
+ scheduler->SetCanStart();
+ scheduler->SetVisible(true);
+ scheduler->SetCanDraw(true);
+ InitializeOutputSurfaceAndFirstCommit(scheduler, &client);
+
+ // To test swap ack throttling, this test disables automatic swap acks.
+ scheduler->SetMaxSwapsPending(1);
+ client.SetAutomaticSwapAck(false);
+
+ // SetNeedsCommit should begin the frame on the next BeginImplFrame.
+ client.Reset();
+ scheduler->SetNeedsCommit();
+ EXPECT_FALSE(client.needs_begin_frame());
+ EXPECT_NO_ACTION(client);
+ client.Reset();
+
+ // Trigger the first BeginImplFrame and BeginMainFrame
+ client.task_runner().RunPendingTasks(); // Run posted BeginFrame.
+ EXPECT_ACTION("WillBeginImplFrame", client, 0, 2);
+ EXPECT_ACTION("ScheduledActionSendBeginMainFrame", client, 1, 2);
+ EXPECT_TRUE(scheduler->BeginImplFrameDeadlinePending());
+ EXPECT_FALSE(client.needs_begin_frame());
+ client.Reset();
+
+ // NotifyReadyToCommit should trigger the pending commit and draw.
+ scheduler->NotifyBeginMainFrameStarted();
+ scheduler->NotifyReadyToCommit();
+ EXPECT_SINGLE_ACTION("ScheduledActionCommit", client);
+ EXPECT_FALSE(client.needs_begin_frame());
+ client.Reset();
+
+ // Swapping will put us into a swap throttled state.
+ client.task_runner().RunPendingTasks(); // Run posted deadline.
+ EXPECT_ACTION("ScheduledActionAnimate", client, 0, 2);
+ EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client, 1, 2);
+ EXPECT_FALSE(scheduler->BeginImplFrameDeadlinePending());
+ EXPECT_FALSE(client.needs_begin_frame());
+ client.Reset();
+
+ // While swap throttled, BeginFrames should trigger BeginImplFrames,
+ // but not a BeginMainFrame or draw.
+ scheduler->SetNeedsCommit();
+ client.task_runner().RunPendingTasks(); // Run posted BeginFrame.
+ EXPECT_ACTION("WillBeginImplFrame", client, 0, 1);
+ EXPECT_TRUE(scheduler->BeginImplFrameDeadlinePending());
+ EXPECT_FALSE(client.needs_begin_frame());
+ client.Reset();
+
+ // Take us out of a swap throttled state.
+ scheduler->DidSwapBuffersComplete();
+ EXPECT_ACTION("ScheduledActionSendBeginMainFrame", client, 0, 1);
+ EXPECT_TRUE(scheduler->BeginImplFrameDeadlinePending());
+ EXPECT_FALSE(client.needs_begin_frame());
+ client.Reset();
+
+ // BeginImplFrame deadline should draw.
+ scheduler->SetNeedsRedraw();
+ client.task_runner().RunPendingTasks(); // Run posted deadline.
+ EXPECT_ACTION("ScheduledActionAnimate", client, 0, 2);
+ EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client, 1, 2);
+ EXPECT_FALSE(scheduler->BeginImplFrameDeadlinePending());
+ EXPECT_FALSE(client.needs_begin_frame());
+ client.Reset();
+}
+
+TEST(SchedulerTest, SyntheticBeginFrames_SwapThrottled) {
+ bool begin_frame_scheduling_enabled = false;
+ bool throttle_frame_production = true;
+ BeginFramesNotFromClient_SwapThrottled(begin_frame_scheduling_enabled,
+ throttle_frame_production);
+}
+
+TEST(SchedulerTest, VSyncThrottlingDisabled_SwapThrottled) {
+ bool begin_frame_scheduling_enabled = true;
+ bool throttle_frame_production = false;
+ BeginFramesNotFromClient_SwapThrottled(begin_frame_scheduling_enabled,
+ throttle_frame_production);
+}
+
+TEST(SchedulerTest,
+ SyntheticBeginFrames_And_VSyncThrottlingDisabled_SwapThrottled) {
+ bool begin_frame_scheduling_enabled = false;
+ bool throttle_frame_production = false;
+ BeginFramesNotFromClient_SwapThrottled(begin_frame_scheduling_enabled,
+ throttle_frame_production);
+}
+
+TEST(SchedulerTest, DidLoseOutputSurfaceAfterOutputSurfaceIsInitialized) {
+ FakeSchedulerClient client;
+ SchedulerSettings scheduler_settings;
+ TestScheduler* scheduler = client.CreateScheduler(scheduler_settings);
+ scheduler->SetCanStart();
+ scheduler->SetVisible(true);
+ scheduler->SetCanDraw(true);
+
+ EXPECT_SINGLE_ACTION("ScheduledActionBeginOutputSurfaceCreation", client);
+ client.Reset();
+ scheduler->DidCreateAndInitializeOutputSurface();
+ EXPECT_NO_ACTION(client);
+
+ scheduler->DidLoseOutputSurface();
+ EXPECT_SINGLE_ACTION("ScheduledActionBeginOutputSurfaceCreation", client);
+}
+
+TEST(SchedulerTest, DidLoseOutputSurfaceAfterBeginFrameStarted) {
+ FakeSchedulerClient client;
+ SchedulerSettings scheduler_settings;
+ TestScheduler* scheduler = client.CreateScheduler(scheduler_settings);
+ scheduler->SetCanStart();
+ scheduler->SetVisible(true);
+ scheduler->SetCanDraw(true);
+
+ EXPECT_SINGLE_ACTION("ScheduledActionBeginOutputSurfaceCreation", client);
+ InitializeOutputSurfaceAndFirstCommit(scheduler, &client);
+ // SetNeedsCommit should begin the frame.
+ client.Reset();
+ scheduler->SetNeedsCommit();
+ EXPECT_SINGLE_ACTION("SetNeedsBeginFrame", client);
+
+ client.Reset();
+ scheduler->BeginFrame(CreateBeginFrameArgsForTesting());
+ EXPECT_ACTION("WillBeginImplFrame", client, 0, 2);
+ EXPECT_ACTION("ScheduledActionSendBeginMainFrame", client, 1, 2);
+ EXPECT_TRUE(scheduler->BeginImplFrameDeadlinePending());
+
+ client.Reset();
+ scheduler->DidLoseOutputSurface();
+ // Do nothing when impl frame is in deadine pending state.
+ EXPECT_NO_ACTION(client);
+
+ client.Reset();
+ scheduler->NotifyBeginMainFrameStarted();
+ scheduler->NotifyReadyToCommit();
+ EXPECT_ACTION("ScheduledActionCommit", client, 0, 1);
+
+ client.Reset();
+ client.task_runner().RunPendingTasks(); // Run posted deadline.
+ EXPECT_SINGLE_ACTION("ScheduledActionBeginOutputSurfaceCreation", client);
+}
+
+void DidLoseOutputSurfaceAfterBeginFrameStartedWithHighLatency(
+ bool impl_side_painting) {
+ FakeSchedulerClient client;
+ SchedulerSettings scheduler_settings;
+ scheduler_settings.impl_side_painting = impl_side_painting;
+ TestScheduler* scheduler = client.CreateScheduler(scheduler_settings);
+ scheduler->SetCanStart();
+ scheduler->SetVisible(true);
+ scheduler->SetCanDraw(true);
+
+ EXPECT_SINGLE_ACTION("ScheduledActionBeginOutputSurfaceCreation", client);
+ InitializeOutputSurfaceAndFirstCommit(scheduler, &client);
+
+ // SetNeedsCommit should begin the frame.
+ client.Reset();
+ scheduler->SetNeedsCommit();
+ EXPECT_SINGLE_ACTION("SetNeedsBeginFrame", client);
+
+ client.Reset();
+ scheduler->BeginFrame(CreateBeginFrameArgsForTesting());
+ EXPECT_ACTION("WillBeginImplFrame", client, 0, 2);
+ EXPECT_ACTION("ScheduledActionSendBeginMainFrame", client, 1, 2);
+ EXPECT_TRUE(scheduler->BeginImplFrameDeadlinePending());
+
+ client.Reset();
+ scheduler->DidLoseOutputSurface();
+ // Do nothing when impl frame is in deadine pending state.
+ EXPECT_NO_ACTION(client);
+
+ client.Reset();
+ client.task_runner().RunPendingTasks(); // Run posted deadline.
+ // OnBeginImplFrameDeadline didn't schedule any actions because main frame is
+ // not yet completed.
+ EXPECT_NO_ACTION(client);
+
+ // BeginImplFrame is not started.
+ scheduler->BeginFrame(CreateBeginFrameArgsForTesting());
+ EXPECT_NO_ACTION(client);
+ EXPECT_FALSE(scheduler->BeginImplFrameDeadlinePending());
+
+ client.Reset();
+ scheduler->NotifyBeginMainFrameStarted();
+ scheduler->NotifyReadyToCommit();
+ if (impl_side_painting) {
+ EXPECT_ACTION("ScheduledActionCommit", client, 0, 3);
+ EXPECT_ACTION("ScheduledActionActivatePendingTree", client, 1, 3);
+ EXPECT_ACTION("ScheduledActionBeginOutputSurfaceCreation", client, 2, 3);
+ } else {
+ EXPECT_ACTION("ScheduledActionCommit", client, 0, 2);
+ EXPECT_ACTION("ScheduledActionBeginOutputSurfaceCreation", client, 1, 2);
+ }
+}
+
+TEST(SchedulerTest, DidLoseOutputSurfaceAfterBeginFrameStartedWithHighLatency) {
+ bool impl_side_painting = false;
+ DidLoseOutputSurfaceAfterBeginFrameStartedWithHighLatency(impl_side_painting);
+}
+
+TEST(SchedulerTest,
+ DidLoseOutputSurfaceAfterBeginFrameStartedWithHighLatencyWithImplPaint) {
+ bool impl_side_painting = true;
+ DidLoseOutputSurfaceAfterBeginFrameStartedWithHighLatency(impl_side_painting);
+}
+
+void DidLoseOutputSurfaceAfterReadyToCommit(bool impl_side_painting) {
+ FakeSchedulerClient client;
+ SchedulerSettings scheduler_settings;
+ scheduler_settings.impl_side_painting = impl_side_painting;
+ TestScheduler* scheduler = client.CreateScheduler(scheduler_settings);
+ scheduler->SetCanStart();
+ scheduler->SetVisible(true);
+ scheduler->SetCanDraw(true);
+
+ EXPECT_SINGLE_ACTION("ScheduledActionBeginOutputSurfaceCreation", client);
+ InitializeOutputSurfaceAndFirstCommit(scheduler, &client);
+
+ // SetNeedsCommit should begin the frame.
+ client.Reset();
+ scheduler->SetNeedsCommit();
+ EXPECT_SINGLE_ACTION("SetNeedsBeginFrame", client);
+
+ client.Reset();
+ scheduler->BeginFrame(CreateBeginFrameArgsForTesting());
+ EXPECT_ACTION("WillBeginImplFrame", client, 0, 2);
+ EXPECT_ACTION("ScheduledActionSendBeginMainFrame", client, 1, 2);
+ EXPECT_TRUE(scheduler->BeginImplFrameDeadlinePending());
+
+ client.Reset();
+ scheduler->NotifyBeginMainFrameStarted();
+ scheduler->NotifyReadyToCommit();
+ EXPECT_SINGLE_ACTION("ScheduledActionCommit", client);
+
+ client.Reset();
+ scheduler->DidLoseOutputSurface();
+ if (impl_side_painting) {
+ // Pending tree should be forced to activate.
+ EXPECT_SINGLE_ACTION("ScheduledActionActivatePendingTree", client);
+ } else {
+ // Do nothing when impl frame is in deadine pending state.
+ EXPECT_NO_ACTION(client);
+ }
+
+ client.Reset();
+ client.task_runner().RunPendingTasks(); // Run posted deadline.
+ EXPECT_SINGLE_ACTION("ScheduledActionBeginOutputSurfaceCreation", client);
+}
+
+TEST(SchedulerTest, DidLoseOutputSurfaceAfterReadyToCommit) {
+ DidLoseOutputSurfaceAfterReadyToCommit(false);
+}
+
+TEST(SchedulerTest, DidLoseOutputSurfaceAfterReadyToCommitWithImplPainting) {
+ DidLoseOutputSurfaceAfterReadyToCommit(true);
+}
+
+TEST(SchedulerTest, DidLoseOutputSurfaceAfterSetNeedsManageTiles) {
+ FakeSchedulerClient client;
+ SchedulerSettings scheduler_settings;
+ TestScheduler* scheduler = client.CreateScheduler(scheduler_settings);
+ scheduler->SetCanStart();
+ scheduler->SetVisible(true);
+ scheduler->SetCanDraw(true);
+ InitializeOutputSurfaceAndFirstCommit(scheduler, &client);
+
+ client.Reset();
+ scheduler->SetNeedsManageTiles();
+ scheduler->SetNeedsRedraw();
+ EXPECT_SINGLE_ACTION("SetNeedsBeginFrame", client);
+ EXPECT_TRUE(client.needs_begin_frame());
+
+ client.Reset();
+ scheduler->BeginFrame(CreateBeginFrameArgsForTesting());
+ EXPECT_ACTION("WillBeginImplFrame", client, 0, 2);
+ EXPECT_ACTION("ScheduledActionAnimate", client, 1, 2);
+ EXPECT_TRUE(scheduler->BeginImplFrameDeadlinePending());
+
+ client.Reset();
+ scheduler->DidLoseOutputSurface();
+ EXPECT_NO_ACTION(client);
+
+ client.Reset();
+ client.task_runner().RunPendingTasks(); // Run posted deadline.
+ EXPECT_ACTION("ScheduledActionManageTiles", client, 0, 2);
+ EXPECT_ACTION("ScheduledActionBeginOutputSurfaceCreation", client, 1, 2);
+}
+
+TEST(SchedulerTest, DidLoseOutputSurfaceAfterBeginRetroFramePosted) {
+ FakeSchedulerClient client;
+ SchedulerSettings scheduler_settings;
+ TestScheduler* scheduler = client.CreateScheduler(scheduler_settings);
+ scheduler->SetCanStart();
+ scheduler->SetVisible(true);
+ scheduler->SetCanDraw(true);
+ InitializeOutputSurfaceAndFirstCommit(scheduler, &client);
+
+ // SetNeedsCommit should begin the frame on the next BeginImplFrame.
+ client.Reset();
+ scheduler->SetNeedsCommit();
+ EXPECT_TRUE(client.needs_begin_frame());
+ EXPECT_SINGLE_ACTION("SetNeedsBeginFrame", client);
+
+ // Create a BeginFrame with a long deadline to avoid race conditions.
+ // This is the first BeginFrame, which will be handled immediately.
+ client.Reset();
+ BeginFrameArgs args = CreateBeginFrameArgsForTesting();
+ args.deadline += base::TimeDelta::FromHours(1);
+ scheduler->BeginFrame(args);
+ EXPECT_ACTION("WillBeginImplFrame", client, 0, 2);
+ EXPECT_ACTION("ScheduledActionSendBeginMainFrame", client, 1, 2);
+ EXPECT_TRUE(scheduler->BeginImplFrameDeadlinePending());
+ EXPECT_TRUE(client.needs_begin_frame());
+
+ // Queue BeginFrames while we are still handling the previous BeginFrame.
+ args.frame_time += base::TimeDelta::FromSeconds(1);
+ scheduler->BeginFrame(args);
+ args.frame_time += base::TimeDelta::FromSeconds(1);
+ scheduler->BeginFrame(args);
+
+ // If we don't swap on the deadline, we wait for the next BeginImplFrame.
+ client.Reset();
+ client.task_runner().RunPendingTasks(); // Run posted deadline.
+ EXPECT_NO_ACTION(client);
+ EXPECT_FALSE(scheduler->BeginImplFrameDeadlinePending());
+ EXPECT_TRUE(client.needs_begin_frame());
+
+ // NotifyReadyToCommit should trigger the commit.
+ client.Reset();
+ scheduler->NotifyBeginMainFrameStarted();
+ scheduler->NotifyReadyToCommit();
+ EXPECT_SINGLE_ACTION("ScheduledActionCommit", client);
+ EXPECT_TRUE(client.needs_begin_frame());
+
+ client.Reset();
+ EXPECT_FALSE(scheduler->IsBeginRetroFrameArgsEmpty());
+ scheduler->DidLoseOutputSurface();
+ EXPECT_SINGLE_ACTION("ScheduledActionBeginOutputSurfaceCreation", client);
+ EXPECT_TRUE(client.needs_begin_frame());
+ EXPECT_TRUE(scheduler->IsBeginRetroFrameArgsEmpty());
+
+ // Posted BeginRetroFrame is aborted.
+ client.Reset();
+ client.task_runner().RunPendingTasks();
+ EXPECT_NO_ACTION(client);
+}
+
+TEST(SchedulerTest, DidLoseOutputSurfaceDuringBeginRetroFrameRunning) {
+ FakeSchedulerClient client;
+ SchedulerSettings scheduler_settings;
+ TestScheduler* scheduler = client.CreateScheduler(scheduler_settings);
+ scheduler->SetCanStart();
+ scheduler->SetVisible(true);
+ scheduler->SetCanDraw(true);
+ InitializeOutputSurfaceAndFirstCommit(scheduler, &client);
+
+ // SetNeedsCommit should begin the frame on the next BeginImplFrame.
+ client.Reset();
+ scheduler->SetNeedsCommit();
+ EXPECT_TRUE(client.needs_begin_frame());
+ EXPECT_SINGLE_ACTION("SetNeedsBeginFrame", client);
+
+ // Create a BeginFrame with a long deadline to avoid race conditions.
+ // This is the first BeginFrame, which will be handled immediately.
+ client.Reset();
+ BeginFrameArgs args = CreateBeginFrameArgsForTesting();
+ args.deadline += base::TimeDelta::FromHours(1);
+ scheduler->BeginFrame(args);
+ EXPECT_ACTION("WillBeginImplFrame", client, 0, 2);
+ EXPECT_ACTION("ScheduledActionSendBeginMainFrame", client, 1, 2);
+ EXPECT_TRUE(scheduler->BeginImplFrameDeadlinePending());
+ EXPECT_TRUE(client.needs_begin_frame());
+
+ // Queue BeginFrames while we are still handling the previous BeginFrame.
+ args.frame_time += base::TimeDelta::FromSeconds(1);
+ scheduler->BeginFrame(args);
+ args.frame_time += base::TimeDelta::FromSeconds(1);
+ scheduler->BeginFrame(args);
+
+ // If we don't swap on the deadline, we wait for the next BeginImplFrame.
+ client.Reset();
+ client.task_runner().RunPendingTasks(); // Run posted deadline.
+ EXPECT_NO_ACTION(client);
+ EXPECT_FALSE(scheduler->BeginImplFrameDeadlinePending());
+ EXPECT_TRUE(client.needs_begin_frame());
+
+ // NotifyReadyToCommit should trigger the commit.
+ client.Reset();
+ scheduler->NotifyBeginMainFrameStarted();
+ scheduler->NotifyReadyToCommit();
+ EXPECT_SINGLE_ACTION("ScheduledActionCommit", client);
+ EXPECT_TRUE(client.needs_begin_frame());
+
+ // BeginImplFrame should prepare the draw.
+ client.Reset();
+ client.task_runner().RunPendingTasks(); // Run posted BeginRetroFrame.
+ EXPECT_ACTION("WillBeginImplFrame", client, 0, 2);
+ EXPECT_ACTION("ScheduledActionAnimate", client, 1, 2);
+ EXPECT_TRUE(scheduler->BeginImplFrameDeadlinePending());
+ EXPECT_TRUE(client.needs_begin_frame());
+
+ client.Reset();
+ EXPECT_FALSE(scheduler->IsBeginRetroFrameArgsEmpty());
+ scheduler->DidLoseOutputSurface();
+ EXPECT_NO_ACTION(client);
+ EXPECT_TRUE(scheduler->IsBeginRetroFrameArgsEmpty());
+
+ // BeginImplFrame deadline should abort drawing.
+ client.Reset();
+ client.task_runner().RunPendingTasks(); // Run posted deadline.
+ EXPECT_SINGLE_ACTION("ScheduledActionBeginOutputSurfaceCreation", client);
+ EXPECT_FALSE(scheduler->BeginImplFrameDeadlinePending());
+ EXPECT_TRUE(client.needs_begin_frame());
+
+ // No more BeginRetroFrame because BeginRetroFrame queue is cleared.
+ client.Reset();
+ client.task_runner().RunPendingTasks();
+ EXPECT_NO_ACTION(client);
+}
+
+TEST(SchedulerTest,
+ StopBeginFrameAfterDidLoseOutputSurfaceWithSyntheticBeginFrameSource) {
+ FakeSchedulerClient client;
+ SchedulerSettings scheduler_settings;
+ scheduler_settings.begin_frame_scheduling_enabled = false;
+ TestScheduler* scheduler = client.CreateScheduler(scheduler_settings);
+ scheduler->SetCanStart();
+ scheduler->SetVisible(true);
+ scheduler->SetCanDraw(true);
+ InitializeOutputSurfaceAndFirstCommit(scheduler, &client);
+
+ // SetNeedsCommit should begin the frame on the next BeginImplFrame.
+ client.Reset();
+ EXPECT_FALSE(scheduler->IsSyntheticBeginFrameSourceActive());
+ scheduler->SetNeedsCommit();
+ EXPECT_TRUE(scheduler->IsSyntheticBeginFrameSourceActive());
+
+ client.Reset();
+ client.task_runner().RunPendingTasks(); // Run posted Tick.
+ EXPECT_ACTION("WillBeginImplFrame", client, 0, 2);
+ EXPECT_ACTION("ScheduledActionSendBeginMainFrame", client, 1, 2);
+ EXPECT_TRUE(scheduler->BeginImplFrameDeadlinePending());
+ EXPECT_TRUE(scheduler->IsSyntheticBeginFrameSourceActive());
+
+ // NotifyReadyToCommit should trigger the commit.
+ client.Reset();
+ scheduler->NotifyBeginMainFrameStarted();
+ scheduler->NotifyReadyToCommit();
+ EXPECT_SINGLE_ACTION("ScheduledActionCommit", client);
+ EXPECT_TRUE(scheduler->IsSyntheticBeginFrameSourceActive());
+
+ client.Reset();
+ scheduler->DidLoseOutputSurface();
+ EXPECT_EQ(0, client.num_actions_());
+ EXPECT_FALSE(scheduler->IsSyntheticBeginFrameSourceActive());
+
+ client.Reset();
+ client.task_runner().RunPendingTasks(); // Run posted deadline.
+ EXPECT_SINGLE_ACTION("ScheduledActionBeginOutputSurfaceCreation", client);
+ EXPECT_FALSE(scheduler->IsSyntheticBeginFrameSourceActive());
}
} // namespace
diff --git a/chromium/cc/scheduler/texture_uploader.cc b/chromium/cc/scheduler/texture_uploader.cc
deleted file mode 100644
index 6758f11de2d..00000000000
--- a/chromium/cc/scheduler/texture_uploader.cc
+++ /dev/null
@@ -1,333 +0,0 @@
-// Copyright 2012 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/texture_uploader.h"
-
-#include <algorithm>
-#include <vector>
-
-#include "base/debug/trace_event.h"
-#include "base/metrics/histogram.h"
-#include "cc/base/util.h"
-#include "cc/resources/prioritized_resource.h"
-#include "cc/resources/resource.h"
-#include "gpu/GLES2/gl2extchromium.h"
-#include "gpu/command_buffer/client/gles2_interface.h"
-#include "third_party/khronos/GLES2/gl2.h"
-#include "third_party/khronos/GLES2/gl2ext.h"
-#include "ui/gfx/rect.h"
-#include "ui/gfx/vector2d.h"
-
-using gpu::gles2::GLES2Interface;
-
-namespace {
-
-// How many previous uploads to use when predicting future throughput.
-static const size_t kUploadHistorySizeMax = 1000;
-static const size_t kUploadHistorySizeInitial = 100;
-
-// Global estimated number of textures per second to maintain estimates across
-// subsequent instances of TextureUploader.
-// More than one thread will not access this variable, so we do not need to
-// synchronize access.
-static const double kDefaultEstimatedTexturesPerSecond = 48.0 * 60.0;
-
-// Flush interval when performing texture uploads.
-static const size_t kTextureUploadFlushPeriod = 4;
-
-} // anonymous namespace
-
-namespace cc {
-
-TextureUploader::Query::Query(GLES2Interface* gl)
- : gl_(gl),
- query_id_(0),
- value_(0),
- has_value_(false),
- is_non_blocking_(false) {
- gl_->GenQueriesEXT(1, &query_id_);
-}
-
-TextureUploader::Query::~Query() { gl_->DeleteQueriesEXT(1, &query_id_); }
-
-void TextureUploader::Query::Begin() {
- has_value_ = false;
- is_non_blocking_ = false;
- gl_->BeginQueryEXT(GL_COMMANDS_ISSUED_CHROMIUM, query_id_);
-}
-
-void TextureUploader::Query::End() {
- gl_->EndQueryEXT(GL_COMMANDS_ISSUED_CHROMIUM);
-}
-
-bool TextureUploader::Query::IsPending() {
- unsigned available = 1;
- gl_->GetQueryObjectuivEXT(
- query_id_, GL_QUERY_RESULT_AVAILABLE_EXT, &available);
- return !available;
-}
-
-unsigned TextureUploader::Query::Value() {
- if (!has_value_) {
- gl_->GetQueryObjectuivEXT(query_id_, GL_QUERY_RESULT_EXT, &value_);
- has_value_ = true;
- }
- return value_;
-}
-
-TextureUploader::TextureUploader(GLES2Interface* gl)
- : gl_(gl),
- num_blocking_texture_uploads_(0),
- sub_image_size_(0),
- num_texture_uploads_since_last_flush_(0) {
- for (size_t i = kUploadHistorySizeInitial; i > 0; i--)
- textures_per_second_history_.insert(kDefaultEstimatedTexturesPerSecond);
-}
-
-TextureUploader::~TextureUploader() {}
-
-size_t TextureUploader::NumBlockingUploads() {
- ProcessQueries();
- return num_blocking_texture_uploads_;
-}
-
-void TextureUploader::MarkPendingUploadsAsNonBlocking() {
- for (ScopedPtrDeque<Query>::iterator it = pending_queries_.begin();
- it != pending_queries_.end();
- ++it) {
- if ((*it)->is_non_blocking())
- continue;
-
- num_blocking_texture_uploads_--;
- (*it)->mark_as_non_blocking();
- }
-
- DCHECK(!num_blocking_texture_uploads_);
-}
-
-double TextureUploader::EstimatedTexturesPerSecond() {
- ProcessQueries();
-
- // Use the median as our estimate.
- std::multiset<double>::iterator median = textures_per_second_history_.begin();
- std::advance(median, textures_per_second_history_.size() / 2);
- return *median;
-}
-
-void TextureUploader::BeginQuery() {
- if (available_queries_.empty())
- available_queries_.push_back(Query::Create(gl_));
-
- available_queries_.front()->Begin();
-}
-
-void TextureUploader::EndQuery() {
- available_queries_.front()->End();
- pending_queries_.push_back(available_queries_.take_front());
- num_blocking_texture_uploads_++;
-}
-
-void TextureUploader::Upload(const uint8* image,
- gfx::Rect image_rect,
- gfx::Rect source_rect,
- gfx::Vector2d dest_offset,
- ResourceFormat format,
- gfx::Size size) {
- CHECK(image_rect.Contains(source_rect));
-
- bool is_full_upload = dest_offset.IsZero() && source_rect.size() == size;
-
- if (is_full_upload)
- BeginQuery();
-
- if (format == ETC1) {
- // ETC1 does not support subimage uploads.
- DCHECK(is_full_upload);
- UploadWithTexImageETC1(image, size);
- } else {
- UploadWithMapTexSubImage(
- image, image_rect, source_rect, dest_offset, format);
- }
-
- if (is_full_upload)
- EndQuery();
-
- num_texture_uploads_since_last_flush_++;
- if (num_texture_uploads_since_last_flush_ >= kTextureUploadFlushPeriod)
- Flush();
-}
-
-void TextureUploader::Flush() {
- if (!num_texture_uploads_since_last_flush_)
- return;
-
- gl_->ShallowFlushCHROMIUM();
-
- num_texture_uploads_since_last_flush_ = 0;
-}
-
-void TextureUploader::ReleaseCachedQueries() {
- ProcessQueries();
- available_queries_.clear();
-}
-
-void TextureUploader::UploadWithTexSubImage(const uint8* image,
- gfx::Rect image_rect,
- gfx::Rect source_rect,
- gfx::Vector2d dest_offset,
- ResourceFormat format) {
- TRACE_EVENT0("cc", "TextureUploader::UploadWithTexSubImage");
-
- // Early-out if this is a no-op, and assert that |image| be valid if this is
- // not a no-op.
- if (source_rect.IsEmpty())
- return;
- DCHECK(image);
-
- // Offset from image-rect to source-rect.
- gfx::Vector2d offset(source_rect.origin() - image_rect.origin());
-
- const uint8* pixel_source;
- unsigned bytes_per_pixel = BitsPerPixel(format) / 8;
- // Use 4-byte row alignment (OpenGL default) for upload performance.
- // Assuming that GL_UNPACK_ALIGNMENT has not changed from default.
- unsigned upload_image_stride =
- RoundUp(bytes_per_pixel * source_rect.width(), 4u);
-
- if (upload_image_stride == image_rect.width() * bytes_per_pixel &&
- !offset.x()) {
- pixel_source = &image[image_rect.width() * bytes_per_pixel * offset.y()];
- } else {
- size_t needed_size = upload_image_stride * source_rect.height();
- if (sub_image_size_ < needed_size) {
- sub_image_.reset(new uint8[needed_size]);
- sub_image_size_ = needed_size;
- }
- // Strides not equal, so do a row-by-row memcpy from the
- // paint results into a temp buffer for uploading.
- for (int row = 0; row < source_rect.height(); ++row)
- memcpy(&sub_image_[upload_image_stride * row],
- &image[bytes_per_pixel *
- (offset.x() + (offset.y() + row) * image_rect.width())],
- source_rect.width() * bytes_per_pixel);
-
- pixel_source = &sub_image_[0];
- }
-
- gl_->TexSubImage2D(GL_TEXTURE_2D,
- 0,
- dest_offset.x(),
- dest_offset.y(),
- source_rect.width(),
- source_rect.height(),
- GLDataFormat(format),
- GLDataType(format),
- pixel_source);
-}
-
-void TextureUploader::UploadWithMapTexSubImage(const uint8* image,
- gfx::Rect image_rect,
- gfx::Rect source_rect,
- gfx::Vector2d dest_offset,
- ResourceFormat format) {
- TRACE_EVENT0("cc", "TextureUploader::UploadWithMapTexSubImage");
-
- // Early-out if this is a no-op, and assert that |image| be valid if this is
- // not a no-op.
- if (source_rect.IsEmpty())
- return;
- DCHECK(image);
- // Compressed textures have no implementation of mapTexSubImage.
- DCHECK_NE(ETC1, format);
-
- // Offset from image-rect to source-rect.
- gfx::Vector2d offset(source_rect.origin() - image_rect.origin());
-
- unsigned bytes_per_pixel = BitsPerPixel(format) / 8;
- // Use 4-byte row alignment (OpenGL default) for upload performance.
- // Assuming that GL_UNPACK_ALIGNMENT has not changed from default.
- unsigned upload_image_stride =
- RoundUp(bytes_per_pixel * source_rect.width(), 4u);
-
- // Upload tile data via a mapped transfer buffer
- uint8* pixel_dest =
- static_cast<uint8*>(gl_->MapTexSubImage2DCHROMIUM(GL_TEXTURE_2D,
- 0,
- dest_offset.x(),
- dest_offset.y(),
- source_rect.width(),
- source_rect.height(),
- GLDataFormat(format),
- GLDataType(format),
- GL_WRITE_ONLY));
-
- if (!pixel_dest) {
- UploadWithTexSubImage(image, image_rect, source_rect, dest_offset, format);
- return;
- }
-
- if (upload_image_stride == image_rect.width() * bytes_per_pixel &&
- !offset.x()) {
- memcpy(pixel_dest,
- &image[image_rect.width() * bytes_per_pixel * offset.y()],
- source_rect.height() * image_rect.width() * bytes_per_pixel);
- } else {
- // Strides not equal, so do a row-by-row memcpy from the
- // paint results into the pixel_dest.
- for (int row = 0; row < source_rect.height(); ++row) {
- memcpy(&pixel_dest[upload_image_stride * row],
- &image[bytes_per_pixel *
- (offset.x() + (offset.y() + row) * image_rect.width())],
- source_rect.width() * bytes_per_pixel);
- }
- }
-
- gl_->UnmapTexSubImage2DCHROMIUM(pixel_dest);
-}
-
-void TextureUploader::UploadWithTexImageETC1(const uint8* image,
- gfx::Size size) {
- TRACE_EVENT0("cc", "TextureUploader::UploadWithTexImageETC1");
- DCHECK_EQ(0, size.width() % 4);
- DCHECK_EQ(0, size.height() % 4);
-
- gl_->CompressedTexImage2D(GL_TEXTURE_2D,
- 0,
- GLInternalFormat(ETC1),
- size.width(),
- size.height(),
- 0,
- Resource::MemorySizeBytes(size, ETC1),
- image);
-}
-
-void TextureUploader::ProcessQueries() {
- while (!pending_queries_.empty()) {
- if (pending_queries_.front()->IsPending())
- break;
-
- unsigned us_elapsed = pending_queries_.front()->Value();
- UMA_HISTOGRAM_CUSTOM_COUNTS(
- "Renderer4.TextureGpuUploadTimeUS", us_elapsed, 0, 100000, 50);
-
- // Clamp the queries to saner values in case the queries fail.
- us_elapsed = std::max(1u, us_elapsed);
- us_elapsed = std::min(15000u, us_elapsed);
-
- if (!pending_queries_.front()->is_non_blocking())
- num_blocking_texture_uploads_--;
-
- // Remove the min and max value from our history and insert the new one.
- double textures_per_second = 1.0 / (us_elapsed * 1e-6);
- if (textures_per_second_history_.size() >= kUploadHistorySizeMax) {
- textures_per_second_history_.erase(textures_per_second_history_.begin());
- textures_per_second_history_.erase(--textures_per_second_history_.end());
- }
- textures_per_second_history_.insert(textures_per_second);
-
- available_queries_.push_back(pending_queries_.take_front());
- }
-}
-
-} // namespace cc
diff --git a/chromium/cc/scheduler/texture_uploader.h b/chromium/cc/scheduler/texture_uploader.h
deleted file mode 100644
index 815282e2c4b..00000000000
--- a/chromium/cc/scheduler/texture_uploader.h
+++ /dev/null
@@ -1,122 +0,0 @@
-// Copyright 2012 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.
-
-#ifndef CC_SCHEDULER_TEXTURE_UPLOADER_H_
-#define CC_SCHEDULER_TEXTURE_UPLOADER_H_
-
-#include <set>
-
-#include "base/basictypes.h"
-#include "base/memory/scoped_ptr.h"
-#include "cc/base/cc_export.h"
-#include "cc/base/scoped_ptr_deque.h"
-#include "cc/resources/resource_provider.h"
-
-namespace gfx {
-class Rect;
-class Size;
-class Vector2d;
-}
-
-namespace gpu {
-namespace gles2 {
-class GLES2Interface;
-}
-}
-
-namespace cc {
-
-class CC_EXPORT TextureUploader {
- public:
- static scoped_ptr<TextureUploader> Create(gpu::gles2::GLES2Interface* gl) {
- return make_scoped_ptr(new TextureUploader(gl));
- }
- ~TextureUploader();
-
- size_t NumBlockingUploads();
- void MarkPendingUploadsAsNonBlocking();
- double EstimatedTexturesPerSecond();
-
- // Let content_rect be a rectangle, and let content_rect be a sub-rectangle of
- // content_rect, expressed in the same coordinate system as content_rect. Let
- // image be a buffer for content_rect. This function will copy the region
- // corresponding to source_rect to dest_offset in this sub-image.
- void Upload(const uint8* image,
- gfx::Rect content_rect,
- gfx::Rect source_rect,
- gfx::Vector2d dest_offset,
- ResourceFormat format,
- gfx::Size size);
-
- void Flush();
- void ReleaseCachedQueries();
-
- private:
- class Query {
- public:
- static scoped_ptr<Query> Create(gpu::gles2::GLES2Interface* gl) {
- return make_scoped_ptr(new Query(gl));
- }
-
- virtual ~Query();
-
- void Begin();
- void End();
- bool IsPending();
- unsigned Value();
- size_t TexturesUploaded();
- void mark_as_non_blocking() {
- is_non_blocking_ = true;
- }
- bool is_non_blocking() const {
- return is_non_blocking_;
- }
-
- private:
- explicit Query(gpu::gles2::GLES2Interface* gl);
-
- gpu::gles2::GLES2Interface* gl_;
- unsigned query_id_;
- unsigned value_;
- bool has_value_;
- bool is_non_blocking_;
-
- DISALLOW_COPY_AND_ASSIGN(Query);
- };
-
- explicit TextureUploader(gpu::gles2::GLES2Interface* gl);
-
- void BeginQuery();
- void EndQuery();
- void ProcessQueries();
-
- void UploadWithTexSubImage(const uint8* image,
- gfx::Rect image_rect,
- gfx::Rect source_rect,
- gfx::Vector2d dest_offset,
- ResourceFormat format);
- void UploadWithMapTexSubImage(const uint8* image,
- gfx::Rect image_rect,
- gfx::Rect source_rect,
- gfx::Vector2d dest_offset,
- ResourceFormat format);
- void UploadWithTexImageETC1(const uint8* image, gfx::Size size);
-
- gpu::gles2::GLES2Interface* gl_;
- ScopedPtrDeque<Query> pending_queries_;
- ScopedPtrDeque<Query> available_queries_;
- std::multiset<double> textures_per_second_history_;
- size_t num_blocking_texture_uploads_;
-
- size_t sub_image_size_;
- scoped_ptr<uint8[]> sub_image_;
-
- size_t num_texture_uploads_since_last_flush_;
-
- DISALLOW_COPY_AND_ASSIGN(TextureUploader);
-};
-
-} // namespace cc
-
-#endif // CC_SCHEDULER_TEXTURE_UPLOADER_H_
diff --git a/chromium/cc/scheduler/texture_uploader_unittest.cc b/chromium/cc/scheduler/texture_uploader_unittest.cc
deleted file mode 100644
index 792216cf081..00000000000
--- a/chromium/cc/scheduler/texture_uploader_unittest.cc
+++ /dev/null
@@ -1,235 +0,0 @@
-// Copyright 2012 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/texture_uploader.h"
-
-#include "cc/base/util.h"
-#include "cc/resources/prioritized_resource.h"
-#include "gpu/command_buffer/client/gles2_interface_stub.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/khronos/GLES2/gl2.h"
-#include "third_party/khronos/GLES2/gl2ext.h"
-
-namespace cc {
-namespace {
-
-class TextureUploadTestContext : public gpu::gles2::GLES2InterfaceStub {
- public:
- TextureUploadTestContext() : result_available_(0), unpack_alignment_(4) {}
-
- virtual void PixelStorei(GLenum pname, GLint param) OVERRIDE {
- switch (pname) {
- case GL_UNPACK_ALIGNMENT:
- // Param should be a power of two <= 8.
- EXPECT_EQ(0, param & (param - 1));
- EXPECT_GE(8, param);
- switch (param) {
- case 1:
- case 2:
- case 4:
- case 8:
- unpack_alignment_ = param;
- break;
- default:
- break;
- }
- break;
- default:
- break;
- }
- }
-
- virtual void GetQueryObjectuivEXT(GLuint,
- GLenum type,
- GLuint* value) OVERRIDE {
- switch (type) {
- case GL_QUERY_RESULT_AVAILABLE_EXT:
- *value = result_available_;
- break;
- default:
- *value = 0;
- break;
- }
- }
-
- virtual void TexSubImage2D(GLenum target,
- GLint level,
- GLint xoffset,
- GLint yoffset,
- GLsizei width,
- GLsizei height,
- GLenum format,
- GLenum type,
- const void* pixels) OVERRIDE {
- EXPECT_EQ(static_cast<unsigned>(GL_TEXTURE_2D), target);
- EXPECT_EQ(0, level);
- EXPECT_LE(0, width);
- EXPECT_LE(0, height);
- EXPECT_LE(0, xoffset);
- EXPECT_LE(0, yoffset);
- EXPECT_LE(0, width);
- EXPECT_LE(0, height);
-
- // Check for allowed format/type combination.
- unsigned int bytes_per_pixel = 0;
- switch (format) {
- case GL_ALPHA:
- EXPECT_EQ(static_cast<unsigned>(GL_UNSIGNED_BYTE), type);
- bytes_per_pixel = 1;
- break;
- case GL_RGB:
- EXPECT_NE(static_cast<unsigned>(GL_UNSIGNED_SHORT_4_4_4_4), type);
- EXPECT_NE(static_cast<unsigned>(GL_UNSIGNED_SHORT_5_5_5_1), type);
- switch (type) {
- case GL_UNSIGNED_BYTE:
- bytes_per_pixel = 3;
- break;
- case GL_UNSIGNED_SHORT_5_6_5:
- bytes_per_pixel = 2;
- break;
- }
- break;
- case GL_RGBA:
- EXPECT_NE(static_cast<unsigned>(GL_UNSIGNED_SHORT_5_6_5), type);
- switch (type) {
- case GL_UNSIGNED_BYTE:
- bytes_per_pixel = 4;
- break;
- case GL_UNSIGNED_SHORT_4_4_4_4:
- bytes_per_pixel = 2;
- break;
- case GL_UNSIGNED_SHORT_5_5_5_1:
- bytes_per_pixel = 2;
- break;
- }
- break;
- case GL_LUMINANCE:
- EXPECT_EQ(static_cast<unsigned>(GL_UNSIGNED_BYTE), type);
- bytes_per_pixel = 1;
- break;
- case GL_LUMINANCE_ALPHA:
- EXPECT_EQ(static_cast<unsigned>(GL_UNSIGNED_BYTE), type);
- bytes_per_pixel = 2;
- break;
- }
-
- // If NULL, we aren't checking texture contents.
- if (pixels == NULL)
- return;
-
- const uint8* bytes = static_cast<const uint8*>(pixels);
- // We'll expect the first byte of every row to be 0x1, and the last byte to
- // be 0x2.
- const unsigned int stride =
- RoundUp(bytes_per_pixel * width, unpack_alignment_);
- for (GLsizei row = 0; row < height; ++row) {
- const uint8* row_bytes =
- bytes + (xoffset * bytes_per_pixel + (yoffset + row) * stride);
- EXPECT_EQ(0x1, row_bytes[0]);
- EXPECT_EQ(0x2, row_bytes[width * bytes_per_pixel - 1]);
- }
- }
-
- void SetResultAvailable(unsigned result_available) {
- result_available_ = result_available;
- }
-
- private:
- unsigned result_available_;
- unsigned unpack_alignment_;
-
- DISALLOW_COPY_AND_ASSIGN(TextureUploadTestContext);
-};
-
-void UploadTexture(TextureUploader* uploader,
- ResourceFormat format,
- gfx::Size size,
- const uint8* data) {
- uploader->Upload(data,
- gfx::Rect(size),
- gfx::Rect(size),
- gfx::Vector2d(),
- format,
- size);
-}
-
-TEST(TextureUploaderTest, NumBlockingUploads) {
- TextureUploadTestContext context;
- scoped_ptr<TextureUploader> uploader = TextureUploader::Create(&context);
-
- context.SetResultAvailable(0);
- EXPECT_EQ(0u, uploader->NumBlockingUploads());
- UploadTexture(uploader.get(), RGBA_8888, gfx::Size(), NULL);
- EXPECT_EQ(1u, uploader->NumBlockingUploads());
- UploadTexture(uploader.get(), RGBA_8888, gfx::Size(), NULL);
- EXPECT_EQ(2u, uploader->NumBlockingUploads());
-
- context.SetResultAvailable(1);
- EXPECT_EQ(0u, uploader->NumBlockingUploads());
- UploadTexture(uploader.get(), RGBA_8888, gfx::Size(), NULL);
- EXPECT_EQ(0u, uploader->NumBlockingUploads());
- UploadTexture(uploader.get(), RGBA_8888, gfx::Size(), NULL);
- UploadTexture(uploader.get(), RGBA_8888, gfx::Size(), NULL);
- EXPECT_EQ(0u, uploader->NumBlockingUploads());
-}
-
-TEST(TextureUploaderTest, MarkPendingUploadsAsNonBlocking) {
- TextureUploadTestContext context;
- scoped_ptr<TextureUploader> uploader = TextureUploader::Create(&context);
-
- context.SetResultAvailable(0);
- EXPECT_EQ(0u, uploader->NumBlockingUploads());
- UploadTexture(uploader.get(), RGBA_8888, gfx::Size(), NULL);
- UploadTexture(uploader.get(), RGBA_8888, gfx::Size(), NULL);
- EXPECT_EQ(2u, uploader->NumBlockingUploads());
-
- uploader->MarkPendingUploadsAsNonBlocking();
- EXPECT_EQ(0u, uploader->NumBlockingUploads());
- UploadTexture(uploader.get(), RGBA_8888, gfx::Size(), NULL);
- EXPECT_EQ(1u, uploader->NumBlockingUploads());
-
- context.SetResultAvailable(1);
- EXPECT_EQ(0u, uploader->NumBlockingUploads());
- UploadTexture(uploader.get(), RGBA_8888, gfx::Size(), NULL);
- uploader->MarkPendingUploadsAsNonBlocking();
- EXPECT_EQ(0u, uploader->NumBlockingUploads());
-}
-
-TEST(TextureUploaderTest, UploadContentsTest) {
- TextureUploadTestContext context;
- scoped_ptr<TextureUploader> uploader = TextureUploader::Create(&context);
-
- uint8 buffer[256 * 256 * 4];
-
- // Upload a tightly packed 256x256 RGBA texture.
- memset(buffer, 0, sizeof(buffer));
- for (int i = 0; i < 256; ++i) {
- // Mark the beginning and end of each row, for the test.
- buffer[i * 4 * 256] = 0x1;
- buffer[(i + 1) * 4 * 256 - 1] = 0x2;
- }
- UploadTexture(uploader.get(), RGBA_8888, gfx::Size(256, 256), buffer);
-
- // Upload a tightly packed 41x43 RGBA texture.
- memset(buffer, 0, sizeof(buffer));
- for (int i = 0; i < 43; ++i) {
- // Mark the beginning and end of each row, for the test.
- buffer[i * 4 * 41] = 0x1;
- buffer[(i + 1) * 4 * 41 - 1] = 0x2;
- }
- UploadTexture(uploader.get(), RGBA_8888, gfx::Size(41, 43), buffer);
-
- // Upload a tightly packed 82x86 LUMINANCE texture.
- memset(buffer, 0, sizeof(buffer));
- for (int i = 0; i < 86; ++i) {
- // Mark the beginning and end of each row, for the test.
- buffer[i * 1 * 82] = 0x1;
- buffer[(i + 1) * 82 - 1] = 0x2;
- }
- UploadTexture(uploader.get(), LUMINANCE_8, gfx::Size(82, 86), buffer);
-}
-
-} // namespace
-} // namespace cc
diff --git a/chromium/cc/scheduler/time_source.h b/chromium/cc/scheduler/time_source.h
deleted file mode 100644
index e45ffbb95aa..00000000000
--- a/chromium/cc/scheduler/time_source.h
+++ /dev/null
@@ -1,51 +0,0 @@
-// 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.
-
-#ifndef CC_SCHEDULER_TIME_SOURCE_H_
-#define CC_SCHEDULER_TIME_SOURCE_H_
-
-#include "base/memory/ref_counted.h"
-#include "base/time/time.h"
-#include "cc/base/cc_export.h"
-
-namespace cc {
-
-class TimeSourceClient {
- public:
- virtual void OnTimerTick() = 0;
-
- protected:
- virtual ~TimeSourceClient() {}
-};
-
-// An generic interface for getting a reliably-ticking timesource of
-// a specified rate.
-//
-// Be sure to call SetActive(false) before releasing your reference to the
-// timer, or it will keep on ticking!
-class CC_EXPORT TimeSource : public base::RefCounted<TimeSource> {
- public:
- virtual void SetClient(TimeSourceClient* client) = 0;
-
- // If transitioning from not active to active, SetActive will return the
- // timestamp of the most recenly missed tick that did not have OnTimerTick
- // called.
- virtual base::TimeTicks SetActive(bool active) = 0;
-
- virtual bool Active() const = 0;
- virtual void SetTimebaseAndInterval(base::TimeTicks timebase,
- base::TimeDelta interval) = 0;
- virtual base::TimeTicks LastTickTime() = 0;
- virtual base::TimeTicks NextTickTime() = 0;
-
- protected:
- virtual ~TimeSource() {}
-
- private:
- friend class base::RefCounted<TimeSource>;
-};
-
-} // namespace cc
-
-#endif // CC_SCHEDULER_TIME_SOURCE_H_