diff options
author | Jocelyn Turcotte <jocelyn.turcotte@digia.com> | 2014-08-08 14:30:41 +0200 |
---|---|---|
committer | Jocelyn Turcotte <jocelyn.turcotte@digia.com> | 2014-08-12 13:49:54 +0200 |
commit | ab0a50979b9eb4dfa3320eff7e187e41efedf7a9 (patch) | |
tree | 498dfb8a97ff3361a9f7486863a52bb4e26bb898 /chromium/cc/scheduler | |
parent | 4ce69f7403811819800e7c5ae1318b2647e778d1 (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')
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_ |