diff options
author | Allan Sandfeld Jensen <allan.jensen@theqtcompany.com> | 2015-06-18 14:10:49 +0200 |
---|---|---|
committer | Oswald Buddenhagen <oswald.buddenhagen@theqtcompany.com> | 2015-06-18 13:53:24 +0000 |
commit | 813fbf95af77a531c57a8c497345ad2c61d475b3 (patch) | |
tree | 821b2c8de8365f21b6c9ba17a236fb3006a1d506 /chromium/cc/scheduler | |
parent | af6588f8d723931a298c995fa97259bb7f7deb55 (diff) | |
download | qtwebengine-chromium-813fbf95af77a531c57a8c497345ad2c61d475b3.tar.gz |
BASELINE: Update chromium to 44.0.2403.47
Change-Id: Ie056fedba95cf5e5c76b30c4b2c80fca4764aa2f
Reviewed-by: Oswald Buddenhagen <oswald.buddenhagen@theqtcompany.com>
Diffstat (limited to 'chromium/cc/scheduler')
-rw-r--r-- | chromium/cc/scheduler/begin_frame_source.cc | 72 | ||||
-rw-r--r-- | chromium/cc/scheduler/begin_frame_source.h | 40 | ||||
-rw-r--r-- | chromium/cc/scheduler/begin_frame_source_unittest.cc | 144 | ||||
-rw-r--r-- | chromium/cc/scheduler/commit_earlyout_reason.h | 40 | ||||
-rw-r--r-- | chromium/cc/scheduler/delay_based_time_source.cc | 39 | ||||
-rw-r--r-- | chromium/cc/scheduler/delay_based_time_source.h | 8 | ||||
-rw-r--r-- | chromium/cc/scheduler/delay_based_time_source_unittest.cc | 17 | ||||
-rw-r--r-- | chromium/cc/scheduler/scheduler.cc | 599 | ||||
-rw-r--r-- | chromium/cc/scheduler/scheduler.h | 93 | ||||
-rw-r--r-- | chromium/cc/scheduler/scheduler_settings.cc | 40 | ||||
-rw-r--r-- | chromium/cc/scheduler/scheduler_settings.h | 18 | ||||
-rw-r--r-- | chromium/cc/scheduler/scheduler_state_machine.cc | 655 | ||||
-rw-r--r-- | chromium/cc/scheduler/scheduler_state_machine.h | 152 | ||||
-rw-r--r-- | chromium/cc/scheduler/scheduler_state_machine_unittest.cc | 1100 | ||||
-rw-r--r-- | chromium/cc/scheduler/scheduler_unittest.cc | 3649 | ||||
-rw-r--r-- | chromium/cc/scheduler/video_frame_controller.h | 40 |
16 files changed, 3794 insertions, 2912 deletions
diff --git a/chromium/cc/scheduler/begin_frame_source.cc b/chromium/cc/scheduler/begin_frame_source.cc index 4a9f1844083..48d834359f4 100644 --- a/chromium/cc/scheduler/begin_frame_source.cc +++ b/chromium/cc/scheduler/begin_frame_source.cc @@ -5,10 +5,11 @@ #include "cc/scheduler/begin_frame_source.h" #include "base/auto_reset.h" -#include "base/debug/trace_event.h" -#include "base/debug/trace_event_argument.h" +#include "base/location.h" #include "base/logging.h" #include "base/strings/string_number_conversions.h" +#include "base/trace_event/trace_event.h" +#include "base/trace_event/trace_event_argument.h" #include "cc/scheduler/delay_based_time_source.h" #include "cc/scheduler/scheduler.h" #include "ui/gfx/frame_time.h" @@ -52,7 +53,7 @@ void BeginFrameObserverMixIn::OnBeginFrame(const BeginFrameArgs& args) { } void BeginFrameObserverMixIn::AsValueInto( - base::debug::TracedValue* dict) const { + base::trace_event::TracedValue* dict) const { dict->BeginDictionary("last_begin_frame_args_"); last_begin_frame_args_.AsValueInto(dict); dict->EndDictionary(); @@ -79,9 +80,9 @@ void BeginFrameSourceMixIn::SetNeedsBeginFrames(bool needs_begin_frames) { "new state", needs_begin_frames); if (needs_begin_frames_ != needs_begin_frames) { + needs_begin_frames_ = needs_begin_frames; OnNeedsBeginFramesChange(needs_begin_frames); } - needs_begin_frames_ = needs_begin_frames; } void BeginFrameSourceMixIn::AddObserver(BeginFrameObserver* obs) { @@ -116,7 +117,8 @@ void BeginFrameSourceMixIn::CallOnBeginFrame(const BeginFrameArgs& args) { } // Tracing support -void BeginFrameSourceMixIn::AsValueInto(base::debug::TracedValue* dict) const { +void BeginFrameSourceMixIn::AsValueInto( + base::trace_event::TracedValue* dict) const { // As the observer might try to trace the source, prevent an infinte loop // from occuring. if (inside_as_value_into_) { @@ -145,9 +147,9 @@ scoped_ptr<BackToBackBeginFrameSource> BackToBackBeginFrameSource::Create( BackToBackBeginFrameSource::BackToBackBeginFrameSource( base::SingleThreadTaskRunner* task_runner) : BeginFrameSourceMixIn(), - weak_factory_(this), task_runner_(task_runner), - send_begin_frame_posted_(false) { + send_begin_frame_posted_(false), + weak_factory_(this) { DCHECK(task_runner); DCHECK_EQ(needs_begin_frames_, false); DCHECK_EQ(send_begin_frame_posted_, false); @@ -181,10 +183,9 @@ void BackToBackBeginFrameSource::BeginFrame() { return; base::TimeTicks now = Now(); - BeginFrameArgs args = - BeginFrameArgs::Create(now, - now + BeginFrameArgs::DefaultInterval(), - BeginFrameArgs::DefaultInterval()); + BeginFrameArgs args = BeginFrameArgs::Create( + BEGINFRAME_FROM_HERE, now, now + BeginFrameArgs::DefaultInterval(), + BeginFrameArgs::DefaultInterval(), BeginFrameArgs::NORMAL); CallOnBeginFrame(args); } @@ -198,7 +199,7 @@ void BackToBackBeginFrameSource::DidFinishFrame(size_t remaining_frames) { // Tracing support void BackToBackBeginFrameSource::AsValueInto( - base::debug::TracedValue* dict) const { + base::trace_event::TracedValue* dict) const { dict->SetString("type", "BackToBackBeginFrameSource"); BeginFrameSourceMixIn::AsValueInto(dict); dict->SetBoolean("send_begin_frame_posted_", send_begin_frame_posted_); @@ -243,8 +244,8 @@ BeginFrameArgs SyntheticBeginFrameSource::CreateBeginFrameArgs( base::TimeTicks frame_time, BeginFrameArgs::BeginFrameArgsType type) { base::TimeTicks deadline = time_source_->NextTickTime(); - return BeginFrameArgs::CreateTyped( - frame_time, deadline, time_source_->Interval(), type); + return BeginFrameArgs::Create(BEGINFRAME_FROM_HERE, frame_time, deadline, + time_source_->Interval(), type); } // TimeSourceClient support @@ -264,13 +265,9 @@ void SyntheticBeginFrameSource::OnNeedsBeginFramesChange( } } -bool SyntheticBeginFrameSource::NeedsBeginFrames() const { - return time_source_->Active(); -} - // Tracing support void SyntheticBeginFrameSource::AsValueInto( - base::debug::TracedValue* dict) const { + base::trace_event::TracedValue* dict) const { dict->SetString("type", "SyntheticBeginFrameSource"); BeginFrameSourceMixIn::AsValueInto(dict); @@ -300,6 +297,10 @@ BeginFrameSourceMultiplexer::BeginFrameSourceMultiplexer( } BeginFrameSourceMultiplexer::~BeginFrameSourceMultiplexer() { + if (active_source_) { + active_source_->SetNeedsBeginFrames(false); + active_source_->RemoveObserver(this); + } } void BeginFrameSourceMultiplexer::SetMinimumInterval( @@ -314,11 +315,8 @@ void BeginFrameSourceMultiplexer::SetMinimumInterval( } void BeginFrameSourceMultiplexer::AddSource(BeginFrameSource* new_source) { - DEBUG_FRAMES("BeginFrameSourceMultiplexer::AddSource", - "current active", - active_source_, - "source to remove", - new_source); + DEBUG_FRAMES("BeginFrameSourceMultiplexer::AddSource", "current active", + active_source_, "source to be added", new_source); DCHECK(new_source); DCHECK(!HasSource(new_source)); @@ -331,11 +329,8 @@ void BeginFrameSourceMultiplexer::AddSource(BeginFrameSource* new_source) { void BeginFrameSourceMultiplexer::RemoveSource( BeginFrameSource* existing_source) { - DEBUG_FRAMES("BeginFrameSourceMultiplexer::RemoveSource", - "current active", - active_source_, - "source to remove", - existing_source); + DEBUG_FRAMES("BeginFrameSourceMultiplexer::RemoveSource", "current active", + active_source_, "source to be removed", existing_source); DCHECK(existing_source); DCHECK(HasSource(existing_source)); DCHECK_NE(existing_source, active_source_); @@ -406,19 +401,10 @@ const BeginFrameArgs BeginFrameSourceMultiplexer::LastUsedBeginFrameArgs() } // BeginFrameSource support -bool BeginFrameSourceMultiplexer::NeedsBeginFrames() const { - if (active_source_) { - return active_source_->NeedsBeginFrames(); - } else { - return false; - } -} - -void BeginFrameSourceMultiplexer::SetNeedsBeginFrames(bool needs_begin_frames) { - DEBUG_FRAMES("BeginFrameSourceMultiplexer::SetNeedsBeginFrames", - "active_source", - active_source_, - "needs_begin_frames", +void BeginFrameSourceMultiplexer::OnNeedsBeginFramesChange( + bool needs_begin_frames) { + DEBUG_FRAMES("BeginFrameSourceMultiplexer::OnNeedsBeginFramesChange", + "active_source", active_source_, "needs_begin_frames", needs_begin_frames); if (active_source_) { active_source_->SetNeedsBeginFrames(needs_begin_frames); @@ -440,7 +426,7 @@ void BeginFrameSourceMultiplexer::DidFinishFrame(size_t remaining_frames) { // Tracing support void BeginFrameSourceMultiplexer::AsValueInto( - base::debug::TracedValue* dict) const { + base::trace_event::TracedValue* dict) const { dict->SetString("type", "BeginFrameSourceMultiplexer"); dict->SetInteger("minimum_interval_us", minimum_interval_.InMicroseconds()); diff --git a/chromium/cc/scheduler/begin_frame_source.h b/chromium/cc/scheduler/begin_frame_source.h index 3d849a40d63..d845dacd8e4 100644 --- a/chromium/cc/scheduler/begin_frame_source.h +++ b/chromium/cc/scheduler/begin_frame_source.h @@ -8,8 +8,8 @@ #include <set> #include <string> -#include "base/debug/trace_event.h" #include "base/logging.h" +#include "base/trace_event/trace_event.h" #include "cc/output/begin_frame_args.h" #include "cc/output/vsync_parameter_observer.h" #include "cc/scheduler/delay_based_time_source.h" @@ -50,7 +50,7 @@ class CC_EXPORT BeginFrameObserver { virtual const BeginFrameArgs LastUsedBeginFrameArgs() const = 0; // Tracing support - virtual void AsValueInto(base::debug::TracedValue* dict) const = 0; + virtual void AsValueInto(base::trace_event::TracedValue* dict) const = 0; }; // Simple mix in which implements a BeginFrameObserver which checks the @@ -75,7 +75,7 @@ class CC_EXPORT BeginFrameObserverMixIn : public BeginFrameObserver { const BeginFrameArgs LastUsedBeginFrameArgs() const override; // Outputs last_begin_frame_args_ - void AsValueInto(base::debug::TracedValue* dict) const override; + void AsValueInto(base::trace_event::TracedValue* dict) const override; protected: // Subclasses should override this method! @@ -117,9 +117,12 @@ class CC_EXPORT BeginFrameSource { virtual void AddObserver(BeginFrameObserver* obs) = 0; virtual void RemoveObserver(BeginFrameObserver* obs) = 0; + // Tells the Source that client is ready to handle BeginFrames messages. + virtual void SetClientReady() = 0; + // Tracing support - Recommend (but not required) to call this implementation // in any override. - virtual void AsValueInto(base::debug::TracedValue* dict) const = 0; + virtual void AsValueInto(base::trace_event::TracedValue* dict) const = 0; }; // Simple mix in which implements a BeginFrameSource. @@ -134,15 +137,16 @@ class CC_EXPORT BeginFrameSourceMixIn : public BeginFrameSource { ~BeginFrameSourceMixIn() override {} // BeginFrameSource - bool NeedsBeginFrames() const override; - void SetNeedsBeginFrames(bool needs_begin_frames) override; + bool NeedsBeginFrames() const final; + void SetNeedsBeginFrames(bool needs_begin_frames) final; void DidFinishFrame(size_t remaining_frames) override {} - void AddObserver(BeginFrameObserver* obs) override; - void RemoveObserver(BeginFrameObserver* obs) override; + void AddObserver(BeginFrameObserver* obs) final; + void RemoveObserver(BeginFrameObserver* obs) final; + void SetClientReady() override {} // Tracing support - Recommend (but not required) to call this implementation // in any override. - void AsValueInto(base::debug::TracedValue* dict) const override; + void AsValueInto(base::trace_event::TracedValue* dict) const override; protected: BeginFrameSourceMixIn(); @@ -174,14 +178,13 @@ class CC_EXPORT BackToBackBeginFrameSource : public BeginFrameSourceMixIn { void DidFinishFrame(size_t remaining_frames) override; // Tracing - void AsValueInto(base::debug::TracedValue* dict) const override; + void AsValueInto(base::trace_event::TracedValue* dict) const override; protected: explicit BackToBackBeginFrameSource( base::SingleThreadTaskRunner* task_runner); virtual base::TimeTicks Now(); // Now overridable for testing - base::WeakPtrFactory<BackToBackBeginFrameSource> weak_factory_; base::SingleThreadTaskRunner* task_runner_; bool send_begin_frame_posted_; @@ -190,6 +193,9 @@ class CC_EXPORT BackToBackBeginFrameSource : public BeginFrameSourceMixIn { void OnNeedsBeginFramesChange(bool needs_begin_frames) override; void BeginFrame(); + + private: + base::WeakPtrFactory<BackToBackBeginFrameSource> weak_factory_; }; // A frame source which is locked to an external parameters provides from a @@ -204,11 +210,8 @@ class CC_EXPORT SyntheticBeginFrameSource : public BeginFrameSourceMixIn, base::TimeDelta initial_vsync_interval); ~SyntheticBeginFrameSource() override; - // BeginFrameSource - bool NeedsBeginFrames() const override; - // Tracing - void AsValueInto(base::debug::TracedValue* dict) const override; + void AsValueInto(base::trace_event::TracedValue* dict) const override; // VSyncParameterObserver void OnUpdateVSyncParameters(base::TimeTicks new_vsync_timebase, @@ -254,12 +257,13 @@ class CC_EXPORT BeginFrameSourceMultiplexer : public BeginFrameSourceMixIn, const BeginFrameArgs LastUsedBeginFrameArgs() const override; // BeginFrameSource - bool NeedsBeginFrames() const override; - void SetNeedsBeginFrames(bool needs_begin_frames) override; void DidFinishFrame(size_t remaining_frames) override; + // BeginFrameSourceMixIn + void OnNeedsBeginFramesChange(bool needs_begin_frames) override; + // Tracing - void AsValueInto(base::debug::TracedValue* dict) const override; + void AsValueInto(base::trace_event::TracedValue* dict) const override; protected: BeginFrameSourceMultiplexer(); diff --git a/chromium/cc/scheduler/begin_frame_source_unittest.cc b/chromium/cc/scheduler/begin_frame_source_unittest.cc index b741e3581a9..22126bead45 100644 --- a/chromium/cc/scheduler/begin_frame_source_unittest.cc +++ b/chromium/cc/scheduler/begin_frame_source_unittest.cc @@ -15,19 +15,18 @@ #include "testing/gtest/include/gtest/gtest.h" // Macros to help set up expected calls on the MockBeginFrameObserver. -#define EXPECT_BEGIN_FRAME_DROP(obs, frame_time, deadline, interval) \ - { \ - ::testing::Expectation exp = \ - EXPECT_CALL((obs), \ - OnBeginFrame(CreateBeginFrameArgsForTesting( \ - frame_time, deadline, interval))) \ - .InSequence((obs).sequence); \ +#define EXPECT_BEGIN_FRAME_DROP(obs, frame_time, deadline, interval) \ + { \ + ::testing::Expectation exp = \ + EXPECT_CALL((obs), OnBeginFrame(CreateBeginFrameArgsForTesting( \ + BEGINFRAME_FROM_HERE, frame_time, deadline, \ + interval))).InSequence((obs).sequence); \ } #define EXPECT_BEGIN_FRAME_USED(obs, frame_time, deadline, interval) \ { \ - BeginFrameArgs args = \ - CreateBeginFrameArgsForTesting(frame_time, deadline, interval); \ + BeginFrameArgs args = CreateBeginFrameArgsForTesting( \ + BEGINFRAME_FROM_HERE, frame_time, deadline, interval); \ ::testing::Expectation exp = \ EXPECT_CALL((obs), OnBeginFrame(args)).InSequence((obs).sequence); \ EXPECT_CALL((obs), LastUsedBeginFrameArgs()) \ @@ -38,15 +37,15 @@ // Macros to send BeginFrameArgs on a FakeBeginFrameSink (and verify resulting // observer behaviour). -#define SEND_BEGIN_FRAME( \ - args_equal_to, source, frame_time, deadline, interval) \ - { \ - BeginFrameArgs old_args = (source).TestLastUsedBeginFrameArgs(); \ - BeginFrameArgs new_args = \ - CreateBeginFrameArgsForTesting(frame_time, deadline, interval); \ - ASSERT_TRUE(!(old_args == new_args)); \ - (source).TestOnBeginFrame(new_args); \ - EXPECT_EQ(args_equal_to, (source).TestLastUsedBeginFrameArgs()); \ +#define SEND_BEGIN_FRAME(args_equal_to, source, frame_time, deadline, \ + interval) \ + { \ + BeginFrameArgs old_args = (source).TestLastUsedBeginFrameArgs(); \ + BeginFrameArgs new_args = CreateBeginFrameArgsForTesting( \ + BEGINFRAME_FROM_HERE, frame_time, deadline, interval); \ + ASSERT_FALSE(old_args == new_args); \ + (source).TestOnBeginFrame(new_args); \ + EXPECT_EQ(args_equal_to, (source).TestLastUsedBeginFrameArgs()); \ } // When dropping LastUsedBeginFrameArgs **shouldn't** change. @@ -65,7 +64,7 @@ class MockBeginFrameObserver : public BeginFrameObserver { MOCK_METHOD1(OnBeginFrame, void(const BeginFrameArgs&)); MOCK_CONST_METHOD0(LastUsedBeginFrameArgs, const BeginFrameArgs()); - virtual void AsValueInto(base::debug::TracedValue* dict) const { + virtual void AsValueInto(base::trace_event::TracedValue* dict) const { dict->SetString("type", "MockBeginFrameObserver"); dict->BeginDictionary("last_begin_frame_args"); LastUsedBeginFrameArgs().AsValueInto(dict); @@ -99,19 +98,25 @@ TEST(MockBeginFrameObserverTest, ExpectOnBeginFrame) { MockBeginFrameObserver::kDefaultBeginFrameArgs); obs.OnBeginFrame(CreateBeginFrameArgsForTesting( - 100, 200, 300)); // One call to LastUsedBeginFrameArgs - EXPECT_EQ(obs.LastUsedBeginFrameArgs(), - CreateBeginFrameArgsForTesting(100, 200, 300)); + BEGINFRAME_FROM_HERE, 100, 200, + 300)); // One call to LastUsedBeginFrameArgs + EXPECT_EQ( + obs.LastUsedBeginFrameArgs(), + CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 100, 200, 300)); obs.OnBeginFrame(CreateBeginFrameArgsForTesting( - 400, 600, 300)); // Multiple calls to LastUsedBeginFrameArgs - EXPECT_EQ(obs.LastUsedBeginFrameArgs(), - CreateBeginFrameArgsForTesting(400, 600, 300)); - EXPECT_EQ(obs.LastUsedBeginFrameArgs(), - CreateBeginFrameArgsForTesting(400, 600, 300)); + BEGINFRAME_FROM_HERE, 400, 600, + 300)); // Multiple calls to LastUsedBeginFrameArgs + EXPECT_EQ( + obs.LastUsedBeginFrameArgs(), + CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 400, 600, 300)); + EXPECT_EQ( + obs.LastUsedBeginFrameArgs(), + CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 400, 600, 300)); obs.OnBeginFrame(CreateBeginFrameArgsForTesting( - 700, 900, 300)); // No calls to LastUsedBeginFrameArgs + BEGINFRAME_FROM_HERE, 700, 900, + 300)); // No calls to LastUsedBeginFrameArgs } TEST(MockBeginFrameObserverTest, ExpectOnBeginFrameStatus) { @@ -125,28 +130,45 @@ TEST(MockBeginFrameObserverTest, ExpectOnBeginFrameStatus) { MockBeginFrameObserver::kDefaultBeginFrameArgs); // Used - obs.OnBeginFrame(CreateBeginFrameArgsForTesting(100, 200, 300)); - EXPECT_EQ(obs.LastUsedBeginFrameArgs(), - CreateBeginFrameArgsForTesting(100, 200, 300)); + obs.OnBeginFrame( + CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 100, 200, 300)); + EXPECT_EQ( + obs.LastUsedBeginFrameArgs(), + CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 100, 200, 300)); // Dropped - obs.OnBeginFrame(CreateBeginFrameArgsForTesting(400, 600, 300)); - EXPECT_EQ(obs.LastUsedBeginFrameArgs(), - CreateBeginFrameArgsForTesting(100, 200, 300)); + obs.OnBeginFrame( + CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 400, 600, 300)); + EXPECT_EQ( + obs.LastUsedBeginFrameArgs(), + CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 100, 200, 300)); // Dropped - obs.OnBeginFrame(CreateBeginFrameArgsForTesting(450, 650, 300)); - EXPECT_EQ(obs.LastUsedBeginFrameArgs(), - CreateBeginFrameArgsForTesting(100, 200, 300)); + obs.OnBeginFrame( + CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 450, 650, 300)); + EXPECT_EQ( + obs.LastUsedBeginFrameArgs(), + CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 100, 200, 300)); // Used - obs.OnBeginFrame(CreateBeginFrameArgsForTesting(700, 900, 300)); - EXPECT_EQ(obs.LastUsedBeginFrameArgs(), - CreateBeginFrameArgsForTesting(700, 900, 300)); + obs.OnBeginFrame( + CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 700, 900, 300)); + EXPECT_EQ( + obs.LastUsedBeginFrameArgs(), + CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 700, 900, 300)); } const BeginFrameArgs MockBeginFrameObserver::kDefaultBeginFrameArgs = - CreateBeginFrameArgsForTesting(-1, -1, -1); + CreateBeginFrameArgsForTesting( +#ifdef NDEBUG + nullptr, +#else + FROM_HERE_WITH_EXPLICIT_FUNCTION( + "MockBeginFrameObserver::kDefaultBeginFrameArgs"), +#endif + -1, + -1, + -1); // BeginFrameObserverMixIn testing --------------------------------------- class MockMinimalBeginFrameObserverMixIn : public BeginFrameObserverMixIn { @@ -168,25 +190,31 @@ TEST(BeginFrameObserverMixInTest, OnBeginFrameImplementation) { EXPECT_DEATH({ obs.OnBeginFrame(BeginFrameArgs()); }, ""); #endif - BeginFrameArgs args1 = CreateBeginFrameArgsForTesting(100, 200, 300); + BeginFrameArgs args1 = + CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 100, 200, 300); EXPECT_CALL(obs, OnBeginFrameMixInDelegate(args1)).WillOnce(Return(true)); obs.OnBeginFrame(args1); EXPECT_EQ(args1, obs.LastUsedBeginFrameArgs()); EXPECT_EQ(0, obs.dropped_begin_frame_args()); #ifndef NDEBUG - EXPECT_DEATH( - { obs.OnBeginFrame(CreateBeginFrameArgsForTesting(50, 200, 300)); }, ""); + EXPECT_DEATH({ + obs.OnBeginFrame(CreateBeginFrameArgsForTesting( + BEGINFRAME_FROM_HERE, 50, 200, 300)); + }, + ""); #endif // Returning false shouldn't update the LastUsedBeginFrameArgs value. - BeginFrameArgs args2 = CreateBeginFrameArgsForTesting(200, 300, 400); + BeginFrameArgs args2 = + CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 200, 300, 400); EXPECT_CALL(obs, OnBeginFrameMixInDelegate(args2)).WillOnce(Return(false)); obs.OnBeginFrame(args2); EXPECT_EQ(args1, obs.LastUsedBeginFrameArgs()); EXPECT_EQ(1, obs.dropped_begin_frame_args()); - BeginFrameArgs args3 = CreateBeginFrameArgsForTesting(150, 300, 400); + BeginFrameArgs args3 = + CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 150, 300, 400); EXPECT_CALL(obs, OnBeginFrameMixInDelegate(args3)).WillOnce(Return(true)); obs.OnBeginFrame(args3); EXPECT_EQ(args3, obs.LastUsedBeginFrameArgs()); @@ -256,7 +284,7 @@ class LoopingBeginFrameObserver : public BeginFrameObserverMixIn { public: BeginFrameSource* source_; - void AsValueInto(base::debug::TracedValue* dict) const override { + void AsValueInto(base::trace_event::TracedValue* dict) const override { dict->SetString("type", "LoopingBeginFrameObserver"); dict->BeginDictionary("source"); source_->AsValueInto(dict); @@ -277,8 +305,8 @@ TEST(BeginFrameSourceMixInTest, DetectAsValueIntoLoop) { obs.source_ = &source; source.AddObserver(&obs); - scoped_refptr<base::debug::TracedValue> state = - new base::debug::TracedValue(); + scoped_refptr<base::trace_event::TracedValue> state = + new base::trace_event::TracedValue(); source.AsValueInto(state.get()); } @@ -312,7 +340,7 @@ class BackToBackBeginFrameSourceTest : public ::testing::Test { scoped_ptr<TestBackToBackBeginFrameSource> source_; scoped_ptr<MockBeginFrameObserver> obs_; - virtual void SetUp() override { + void SetUp() override { now_src_ = TestNowSource::Create(1000); task_runner_ = make_scoped_refptr(new OrderedSimpleTaskRunner(now_src_, false)); @@ -323,7 +351,7 @@ class BackToBackBeginFrameSourceTest : public ::testing::Test { source_->AddObserver(obs_.get()); } - virtual void TearDown() override { obs_.reset(); } + void TearDown() override { obs_.reset(); } }; const int64_t BackToBackBeginFrameSourceTest::kDeadline = @@ -478,7 +506,7 @@ class SyntheticBeginFrameSourceTest : public ::testing::Test { scoped_ptr<TestSyntheticBeginFrameSource> source_; scoped_ptr<MockBeginFrameObserver> obs_; - virtual void SetUp() override { + void SetUp() override { now_src_ = TestNowSource::Create(1000); task_runner_ = make_scoped_refptr(new OrderedSimpleTaskRunner(now_src_, false)); @@ -488,15 +516,15 @@ class SyntheticBeginFrameSourceTest : public ::testing::Test { source_->AddObserver(obs_.get()); } - virtual void TearDown() override { obs_.reset(); } + void TearDown() override { obs_.reset(); } }; TEST_F(SyntheticBeginFrameSourceTest, SetNeedsBeginFramesCallsOnBeginFrameWithMissedTick) { now_src_->SetNowMicroseconds(10010); - EXPECT_CALL((*obs_), - OnBeginFrame(CreateTypedBeginFrameArgsForTesting( - 10000, 20000, 10000, BeginFrameArgs::MISSED))); + EXPECT_CALL((*obs_), OnBeginFrame(CreateBeginFrameArgsForTesting( + BEGINFRAME_FROM_HERE, 10000, 20000, 10000, + BeginFrameArgs::MISSED))); source_->SetNeedsBeginFrames(true); // Should cause the last tick to be sent // No tasks should need to be run for this to occur. } @@ -547,7 +575,7 @@ TEST_F(SyntheticBeginFrameSourceTest, VSyncChanges) { // BeginFrameSourceMultiplexer testing ----------------------------------- class BeginFrameSourceMultiplexerTest : public ::testing::Test { protected: - virtual void SetUp() override { + void SetUp() override { mux_ = BeginFrameSourceMultiplexer::Create(); source1_store_ = make_scoped_ptr(new FakeBeginFrameSource()); @@ -559,7 +587,7 @@ class BeginFrameSourceMultiplexerTest : public ::testing::Test { source3_ = source3_store_.get(); } - virtual void TearDown() override { + void TearDown() override { // Make sure the mux is torn down before the sources. mux_.reset(); } diff --git a/chromium/cc/scheduler/commit_earlyout_reason.h b/chromium/cc/scheduler/commit_earlyout_reason.h new file mode 100644 index 00000000000..14aaeb3b7d9 --- /dev/null +++ b/chromium/cc/scheduler/commit_earlyout_reason.h @@ -0,0 +1,40 @@ +// 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_COMMIT_EARLYOUT_REASON_H_ +#define CC_SCHEDULER_COMMIT_EARLYOUT_REASON_H_ + +#include "base/logging.h" + +namespace cc { + +enum class CommitEarlyOutReason { + ABORTED_OUTPUT_SURFACE_LOST, + ABORTED_NOT_VISIBLE, + ABORTED_DEFERRED_COMMIT, + FINISHED_NO_UPDATES, +}; + +inline const char* CommitEarlyOutReasonToString(CommitEarlyOutReason reason) { + switch (reason) { + case CommitEarlyOutReason::ABORTED_OUTPUT_SURFACE_LOST: + return "CommitEarlyOutReason::ABORTED_OUTPUT_SURFACE_LOST"; + case CommitEarlyOutReason::ABORTED_NOT_VISIBLE: + return "CommitEarlyOutReason::ABORTED_NOT_VISIBLE"; + case CommitEarlyOutReason::ABORTED_DEFERRED_COMMIT: + return "CommitEarlyOutReason::ABORTED_DEFERRED_COMMIT"; + case CommitEarlyOutReason::FINISHED_NO_UPDATES: + return "CommitEarlyOutReason::FINISHED_NO_UPDATES"; + } + NOTREACHED(); + return "???"; +} + +inline bool CommitEarlyOutHandledCommit(CommitEarlyOutReason reason) { + return reason == CommitEarlyOutReason::FINISHED_NO_UPDATES; +} + +} // namespace cc + +#endif // CC_SCHEDULER_COMMIT_EARLYOUT_REASON_H_ diff --git a/chromium/cc/scheduler/delay_based_time_source.cc b/chromium/cc/scheduler/delay_based_time_source.cc index cd214fd98d7..ef43524a878 100644 --- a/chromium/cc/scheduler/delay_based_time_source.cc +++ b/chromium/cc/scheduler/delay_based_time_source.cc @@ -9,11 +9,11 @@ #include <string> #include "base/bind.h" -#include "base/debug/trace_event.h" -#include "base/debug/trace_event_argument.h" #include "base/location.h" #include "base/logging.h" #include "base/single_thread_task_runner.h" +#include "base/trace_event/trace_event.h" +#include "base/trace_event/trace_event_argument.h" namespace cc { @@ -36,7 +36,7 @@ static const double kPhaseChangeThreshold = 0.25; } // namespace // The following methods correspond to the DelayBasedTimeSource that uses -// the base::TimeTicks::HighResNow as the timebase. +// the base::TimeTicks::Now as the timebase. scoped_refptr<DelayBasedTimeSourceHighRes> DelayBasedTimeSourceHighRes::Create( base::TimeDelta interval, base::SingleThreadTaskRunner* task_runner) { @@ -53,7 +53,7 @@ DelayBasedTimeSourceHighRes::DelayBasedTimeSourceHighRes( DelayBasedTimeSourceHighRes::~DelayBasedTimeSourceHighRes() {} base::TimeTicks DelayBasedTimeSourceHighRes::Now() const { - return base::TimeTicks::HighResNow(); + return base::TimeTicks::Now(); } // The following methods correspond to the DelayBasedTimeSource that uses @@ -234,34 +234,20 @@ base::TimeTicks DelayBasedTimeSource::Now() const { // now=37 tick_target=16.667 new_target=50.000 --> // tick(), PostDelayedTask(floor(50.000-37)) --> PostDelayedTask(13) base::TimeTicks DelayBasedTimeSource::NextTickTarget(base::TimeTicks now) { - base::TimeDelta new_interval = next_parameters_.interval; - - // |interval_offset| is the offset from |now| to the next multiple of - // |interval| after |tick_target|, possibly negative if in the past. - base::TimeDelta interval_offset = base::TimeDelta::FromInternalValue( - (next_parameters_.tick_target - now).ToInternalValue() % - new_interval.ToInternalValue()); - // If |now| is exactly on the interval (i.e. offset==0), don't adjust. - // Otherwise, if |tick_target| was in the past, adjust forward to the next - // tick after |now|. - if (interval_offset.ToInternalValue() != 0 && - next_parameters_.tick_target < now) { - interval_offset += new_interval; - } - - base::TimeTicks new_tick_target = now + interval_offset; + base::TimeTicks new_tick_target = now.SnappedToNextTick( + next_parameters_.tick_target, next_parameters_.interval); DCHECK(now <= new_tick_target) << "now = " << now.ToInternalValue() << "; new_tick_target = " << new_tick_target.ToInternalValue() - << "; new_interval = " << new_interval.InMicroseconds() - << "; tick_target = " << next_parameters_.tick_target.ToInternalValue() - << "; interval_offset = " << interval_offset.ToInternalValue(); + << "; new_interval = " << next_parameters_.interval.InMicroseconds() + << "; tick_target = " << next_parameters_.tick_target.ToInternalValue(); // Avoid double ticks when: // 1) Turning off the timer and turning it right back on. // 2) Jittery data is passed to SetTimebaseAndInterval(). - if (new_tick_target - last_tick_time_ <= new_interval / kDoubleTickDivisor) - new_tick_target += new_interval; + if (new_tick_target - last_tick_time_ <= + next_parameters_.interval / kDoubleTickDivisor) + new_tick_target += next_parameters_.interval; return new_tick_target; } @@ -290,7 +276,8 @@ std::string DelayBasedTimeSourceHighRes::TypeString() const { return "DelayBasedTimeSourceHighRes"; } -void DelayBasedTimeSource::AsValueInto(base::debug::TracedValue* state) const { +void DelayBasedTimeSource::AsValueInto( + base::trace_event::TracedValue* state) const { state->SetString("type", TypeString()); state->SetDouble("last_tick_time_us", LastTickTime().ToInternalValue()); state->SetDouble("next_tick_time_us", NextTickTime().ToInternalValue()); diff --git a/chromium/cc/scheduler/delay_based_time_source.h b/chromium/cc/scheduler/delay_based_time_source.h index 9f670d1f8ff..4d7276c97c8 100644 --- a/chromium/cc/scheduler/delay_based_time_source.h +++ b/chromium/cc/scheduler/delay_based_time_source.h @@ -12,7 +12,7 @@ #include "cc/base/cc_export.h" namespace base { -namespace debug { +namespace trace_event { class TracedValue; } class SingleThreadTaskRunner; @@ -55,7 +55,7 @@ class CC_EXPORT DelayBasedTimeSource // Virtual for testing. virtual base::TimeTicks Now() const; - virtual void AsValueInto(base::debug::TracedValue* dict) const; + virtual void AsValueInto(base::trace_event::TracedValue* dict) const; protected: DelayBasedTimeSource(base::TimeDelta interval, @@ -95,7 +95,9 @@ class CC_EXPORT DelayBasedTimeSource DISALLOW_COPY_AND_ASSIGN(DelayBasedTimeSource); }; -// DelayBasedTimeSource uses base::TimeTicks::HighResNow as its timebase. +// DelayBasedTimeSource that once used base::TimeTicks::HighResNow as its time +// source, but is now a no-op. +// TODO(brianderson): Remove along with gfx::/FrameTime.http://crbug.com/447329 class DelayBasedTimeSourceHighRes : public DelayBasedTimeSource { public: static scoped_refptr<DelayBasedTimeSourceHighRes> Create( diff --git a/chromium/cc/scheduler/delay_based_time_source_unittest.cc b/chromium/cc/scheduler/delay_based_time_source_unittest.cc index 0af8b02f4b9..f721b11ff7f 100644 --- a/chromium/cc/scheduler/delay_based_time_source_unittest.cc +++ b/chromium/cc/scheduler/delay_based_time_source_unittest.cc @@ -508,23 +508,6 @@ TEST(DelayBasedTimeSourceTest, TestDeactivateAndReactivateAfterNextTickTime) { EXPECT_EQ(13, task_runner->NextPendingTaskDelay().InMilliseconds()); } -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); - base::TimeTicks big_now = base::TimeTicks::FromInternalValue(8635916564000); - - 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->SetNow(big_now); - timer->SetActive(true); - EXPECT_EQ(0, task_runner->NextPendingTaskDelay().InMilliseconds()); -} - TEST(DelayBasedTimeSourceTest, TestReturnValueWhenTimerIsDeActivated) { scoped_refptr<base::TestSimpleTaskRunner> task_runner = new base::TestSimpleTaskRunner; diff --git a/chromium/cc/scheduler/scheduler.cc b/chromium/cc/scheduler/scheduler.cc index 88c27c103e3..d2520335c0c 100644 --- a/chromium/cc/scheduler/scheduler.cc +++ b/chromium/cc/scheduler/scheduler.cc @@ -5,11 +5,13 @@ #include "cc/scheduler/scheduler.h" #include <algorithm> + #include "base/auto_reset.h" -#include "base/debug/trace_event.h" -#include "base/debug/trace_event_argument.h" #include "base/logging.h" +#include "base/profiler/scoped_tracker.h" #include "base/single_thread_task_runner.h" +#include "base/trace_event/trace_event.h" +#include "base/trace_event/trace_event_argument.h" #include "cc/debug/devtools_instrumentation.h" #include "cc/debug/traced_value.h" #include "cc/scheduler/delay_based_time_source.h" @@ -19,21 +21,14 @@ namespace cc { BeginFrameSource* SchedulerFrameSourcesConstructor::ConstructPrimaryFrameSource( Scheduler* scheduler) { - if (!scheduler->settings_.throttle_frame_production) { + if (scheduler->settings_.use_external_begin_frame_source) { TRACE_EVENT1("cc", "Scheduler::Scheduler()", "PrimaryFrameSource", - "BackToBackBeginFrameSource"); - DCHECK(!scheduler->primary_frame_source_internal_); - scheduler->primary_frame_source_internal_ = - BackToBackBeginFrameSource::Create(scheduler->task_runner_.get()); + "ExternalBeginFrameSource"); + DCHECK(scheduler->primary_frame_source_internal_) + << "Need external BeginFrameSource"; return scheduler->primary_frame_source_internal_.get(); - } else if (scheduler->settings_.begin_frame_scheduling_enabled) { - TRACE_EVENT1("cc", - "Scheduler::Scheduler()", - "PrimaryFrameSource", - "SchedulerClient"); - return scheduler->client_->ExternalBeginFrameSource(); } else { TRACE_EVENT1("cc", "Scheduler::Scheduler()", @@ -54,18 +49,14 @@ BeginFrameSource* SchedulerFrameSourcesConstructor::ConstructPrimaryFrameSource( } BeginFrameSource* -SchedulerFrameSourcesConstructor::ConstructBackgroundFrameSource( +SchedulerFrameSourcesConstructor::ConstructUnthrottledFrameSource( Scheduler* scheduler) { - TRACE_EVENT1("cc", - "Scheduler::Scheduler()", - "BackgroundFrameSource", - "SyntheticBeginFrameSource"); - DCHECK(!(scheduler->background_frame_source_internal_)); - scheduler->background_frame_source_internal_ = - SyntheticBeginFrameSource::Create(scheduler->task_runner_.get(), - scheduler->Now(), - base::TimeDelta::FromSeconds(1)); - return scheduler->background_frame_source_internal_.get(); + TRACE_EVENT1("cc", "Scheduler::Scheduler()", "UnthrottledFrameSource", + "BackToBackBeginFrameSource"); + DCHECK(!scheduler->unthrottled_frame_source_internal_); + scheduler->unthrottled_frame_source_internal_ = + BackToBackBeginFrameSource::Create(scheduler->task_runner_.get()); + return scheduler->unthrottled_frame_source_internal_.get(); } Scheduler::Scheduler( @@ -73,20 +64,21 @@ Scheduler::Scheduler( const SchedulerSettings& scheduler_settings, int layer_tree_host_id, const scoped_refptr<base::SingleThreadTaskRunner>& task_runner, - base::PowerMonitor* power_monitor, + scoped_ptr<BeginFrameSource> external_begin_frame_source, SchedulerFrameSourcesConstructor* frame_sources_constructor) : frame_source_(), primary_frame_source_(NULL), - background_frame_source_(NULL), - primary_frame_source_internal_(), - background_frame_source_internal_(), + primary_frame_source_internal_(external_begin_frame_source.Pass()), vsync_observer_(NULL), + authoritative_vsync_interval_(base::TimeDelta()), + last_vsync_timebase_(base::TimeTicks()), + throttle_frame_production_(false), settings_(scheduler_settings), client_(client), layer_tree_host_id_(layer_tree_host_id), task_runner_(task_runner), - power_monitor_(power_monitor), - begin_retro_frame_posted_(false), + begin_impl_frame_deadline_mode_( + SchedulerStateMachine::BEGIN_IMPL_FRAME_DEADLINE_MODE_NONE), state_machine_(scheduler_settings), inside_process_scheduled_actions_(false), inside_action_(SchedulerStateMachine::ACTION_NONE), @@ -102,8 +94,6 @@ Scheduler::Scheduler( base::Bind(&Scheduler::BeginRetroFrame, 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()); @@ -114,17 +104,20 @@ Scheduler::Scheduler( primary_frame_source_ = frame_sources_constructor->ConstructPrimaryFrameSource(this); frame_source_->AddSource(primary_frame_source_); + primary_frame_source_->SetClientReady(); - // Background ticking frame source - background_frame_source_ = - frame_sources_constructor->ConstructBackgroundFrameSource(this); - frame_source_->AddSource(background_frame_source_); + // Unthrottled frame source + unthrottled_frame_source_ = + frame_sources_constructor->ConstructUnthrottledFrameSource(this); + frame_source_->AddSource(unthrottled_frame_source_); - SetupPowerMonitoring(); + SetThrottleFrameProduction(scheduler_settings.throttle_frame_production); } Scheduler::~Scheduler() { - TeardownPowerMonitoring(); + if (frame_source_->NeedsBeginFrames()) + frame_source_->SetNeedsBeginFrames(false); + frame_source_->SetActiveSource(nullptr); } base::TimeTicks Scheduler::Now() const { @@ -136,32 +129,16 @@ base::TimeTicks Scheduler::Now() const { return now; } -void Scheduler::SetupPowerMonitoring() { - if (settings_.disable_hi_res_timer_tasks_on_battery) { - DCHECK(power_monitor_); - power_monitor_->AddObserver(this); - state_machine_.SetImplLatencyTakesPriorityOnBattery( - power_monitor_->IsOnBatteryPower()); - } -} - -void Scheduler::TeardownPowerMonitoring() { - if (settings_.disable_hi_res_timer_tasks_on_battery) { - DCHECK(power_monitor_); - power_monitor_->RemoveObserver(this); - } -} - -void Scheduler::OnPowerStateChange(bool on_battery_power) { - DCHECK(settings_.disable_hi_res_timer_tasks_on_battery); - state_machine_.SetImplLatencyTakesPriorityOnBattery(on_battery_power); -} - void Scheduler::CommitVSyncParameters(base::TimeTicks timebase, base::TimeDelta interval) { - // TODO(brianderson): We should not be receiving 0 intervals. - if (interval == base::TimeDelta()) + if (authoritative_vsync_interval_ != base::TimeDelta()) { + interval = authoritative_vsync_interval_; + } else if (interval == base::TimeDelta()) { + // TODO(brianderson): We should not be receiving 0 intervals. interval = BeginFrameArgs::DefaultInterval(); + } + + last_vsync_timebase_ = timebase; if (vsync_observer_) vsync_observer_->OnUpdateVSyncParameters(timebase, interval); @@ -179,11 +156,6 @@ void Scheduler::SetCanStart() { void Scheduler::SetVisible(bool visible) { state_machine_.SetVisible(visible); - if (visible) { - frame_source_->SetActiveSource(primary_frame_source_); - } else { - frame_source_->SetActiveSource(background_frame_source_); - } ProcessScheduledActions(); } @@ -197,6 +169,22 @@ void Scheduler::NotifyReadyToActivate() { ProcessScheduledActions(); } +void Scheduler::NotifyReadyToDraw() { + // Future work might still needed for crbug.com/352894. + state_machine_.NotifyReadyToDraw(); + ProcessScheduledActions(); +} + +void Scheduler::SetThrottleFrameProduction(bool throttle) { + throttle_frame_production_ = throttle; + if (throttle) { + frame_source_->SetActiveSource(primary_frame_source_); + } else { + frame_source_->SetActiveSource(unthrottled_frame_source_); + } + ProcessScheduledActions(); +} + void Scheduler::SetNeedsCommit() { state_machine_.SetNeedsCommit(); ProcessScheduledActions(); @@ -212,9 +200,14 @@ void Scheduler::SetNeedsAnimate() { ProcessScheduledActions(); } -void Scheduler::SetNeedsManageTiles() { - DCHECK(!IsInsideAction(SchedulerStateMachine::ACTION_MANAGE_TILES)); - state_machine_.SetNeedsManageTiles(); +void Scheduler::SetNeedsPrepareTiles() { + DCHECK(!IsInsideAction(SchedulerStateMachine::ACTION_PREPARE_TILES)); + state_machine_.SetNeedsPrepareTiles(); + ProcessScheduledActions(); +} + +void Scheduler::SetWaitForReadyToDraw() { + state_machine_.SetWaitForReadyToDraw(); ProcessScheduledActions(); } @@ -232,11 +225,6 @@ void Scheduler::DidSwapBuffers() { } } -void Scheduler::SetSwapUsedIncompleteTile(bool used_incomplete_tile) { - state_machine_.SetSwapUsedIncompleteTile(used_incomplete_tile); - ProcessScheduledActions(); -} - void Scheduler::DidSwapBuffersComplete() { state_machine_.DidSwapBuffersComplete(); ProcessScheduledActions(); @@ -253,22 +241,22 @@ void Scheduler::NotifyReadyToCommit() { ProcessScheduledActions(); } -void Scheduler::BeginMainFrameAborted(bool did_handle) { - TRACE_EVENT0("cc", "Scheduler::BeginMainFrameAborted"); - state_machine_.BeginMainFrameAborted(did_handle); +void Scheduler::BeginMainFrameAborted(CommitEarlyOutReason reason) { + TRACE_EVENT1("cc", "Scheduler::BeginMainFrameAborted", "reason", + CommitEarlyOutReasonToString(reason)); + state_machine_.BeginMainFrameAborted(reason); ProcessScheduledActions(); } -void Scheduler::DidManageTiles() { - state_machine_.DidManageTiles(); +void Scheduler::DidPrepareTiles() { + state_machine_.DidPrepareTiles(); } void Scheduler::DidLoseOutputSurface() { TRACE_EVENT0("cc", "Scheduler::DidLoseOutputSurface"); - state_machine_.DidLoseOutputSurface(); - if (frame_source_->NeedsBeginFrames()) - frame_source_->SetNeedsBeginFrames(false); begin_retro_frame_args_.clear(); + begin_retro_frame_task_.Cancel(); + state_machine_.DidLoseOutputSurface(); ProcessScheduledActions(); } @@ -302,69 +290,35 @@ base::TimeTicks Scheduler::LastBeginImplFrameTime() { } void Scheduler::SetupNextBeginFrameIfNeeded() { - if (!task_runner_.get()) - return; - - bool needs_begin_frame = state_machine_.BeginFrameNeeded(); - - bool at_end_of_deadline = - (state_machine_.begin_impl_frame_state() == - SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_INSIDE_DEADLINE); - - bool should_call_set_needs_begin_frame = - // Always request the BeginFrame immediately if it wasn't needed before. - (needs_begin_frame && !frame_source_->NeedsBeginFrames()) || - // Only stop requesting BeginFrames after a deadline. - (!needs_begin_frame && frame_source_->NeedsBeginFrames() && - at_end_of_deadline); - - if (should_call_set_needs_begin_frame) { - frame_source_->SetNeedsBeginFrames(needs_begin_frame); - } - - if (at_end_of_deadline) { - frame_source_->DidFinishFrame(begin_retro_frame_args_.size()); + // Never call SetNeedsBeginFrames if the frame source already has the right + // value. + if (frame_source_->NeedsBeginFrames() != state_machine_.BeginFrameNeeded()) { + if (state_machine_.BeginFrameNeeded()) { + // Call SetNeedsBeginFrames(true) as soon as possible. + frame_source_->SetNeedsBeginFrames(true); + } else if (state_machine_.begin_impl_frame_state() == + SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_IDLE) { + // Call SetNeedsBeginFrames(false) in between frames only. + frame_source_->SetNeedsBeginFrames(false); + client_->SendBeginMainFrameNotExpectedSoon(); + } } PostBeginRetroFrameIfNeeded(); - SetupPollingMechanisms(needs_begin_frame); } // 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 BeginFrames. This should only be needed by - // the synchronous compositor when BeginFrameNeeded is false. - if (state_machine_.ShouldPollForAnticipatedDrawTriggers()) { - 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(); - task_runner_->PostDelayedTask( - FROM_HERE, poll_for_draw_triggers_task_.callback(), delay); - } - } else { - 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 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 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) { +void Scheduler::SetupPollingMechanisms() { + // 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 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 BeginFrame. Synchronous compositor does not use + // frame rate controller or have the circular wait in the bug. + if (IsBeginMainFrameSentOrStarted() && + !settings_.using_synchronous_renderer_compositor) { if (advance_commit_state_task_.IsCancelled() && begin_impl_frame_args_.IsValid()) { // Since we'd rather get a BeginImplFrame by the normal mechanism, we @@ -384,31 +338,38 @@ void Scheduler::SetupPollingMechanisms(bool needs_begin_frame) { // If the scheduler is busy, we queue the BeginFrame to be handled later as // a BeginRetroFrame. bool Scheduler::OnBeginFrameMixInDelegate(const BeginFrameArgs& args) { - TRACE_EVENT1("cc", "Scheduler::BeginFrame", "args", args.AsValue()); + TRACE_EVENT1("cc,benchmark", "Scheduler::BeginFrame", "args", args.AsValue()); + + // TODO(brianderson): Adjust deadline in the DisplayScheduler. + BeginFrameArgs adjusted_args(args); + adjusted_args.deadline -= EstimatedParentDrawTime(); + + // Deliver BeginFrames to children. + // TODO(brianderson): Move this responsibility to the DisplayScheduler. + if (state_machine_.children_need_begin_frames()) + client_->SendBeginFramesToChildren(adjusted_args); + + if (settings_.using_synchronous_renderer_compositor) { + BeginImplFrameSynchronous(adjusted_args); + return true; + } // We have just called SetNeedsBeginFrame(true) and the BeginFrameSource has // sent us the last BeginFrame we have missed. As we might not be able to // actually make rendering for this call, handle it like a "retro frame". // TODO(brainderson): Add a test for this functionality ASAP! - if (args.type == BeginFrameArgs::MISSED) { - begin_retro_frame_args_.push_back(args); + if (adjusted_args.type == BeginFrameArgs::MISSED) { + begin_retro_frame_args_.push_back(adjusted_args); PostBeginRetroFrameIfNeeded(); return true; } - 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_ || - !frame_source_->NeedsBeginFrames() || - (state_machine_.begin_impl_frame_state() != - SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_IDLE); - } + bool should_defer_begin_frame = + !begin_retro_frame_args_.empty() || + !begin_retro_frame_task_.IsCancelled() || + !frame_source_->NeedsBeginFrames() || + (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); @@ -416,23 +377,51 @@ bool Scheduler::OnBeginFrameMixInDelegate(const BeginFrameArgs& args) { "cc", "Scheduler::BeginFrame deferred", TRACE_EVENT_SCOPE_THREAD); // Queuing the frame counts as "using it", so we need to return true. } else { - BeginImplFrame(adjusted_args); + BeginImplFrameWithDeadline(adjusted_args); } return true; } +void Scheduler::SetChildrenNeedBeginFrames(bool children_need_begin_frames) { + state_machine_.SetChildrenNeedBeginFrames(children_need_begin_frames); + ProcessScheduledActions(); +} + +void Scheduler::SetAuthoritativeVSyncInterval(const base::TimeDelta& interval) { + authoritative_vsync_interval_ = interval; + if (vsync_observer_) + vsync_observer_->OnUpdateVSyncParameters(last_vsync_timebase_, interval); +} + +void Scheduler::SetVideoNeedsBeginFrames(bool video_needs_begin_frames) { + state_machine_.SetVideoNeedsBeginFrames(video_needs_begin_frames); + ProcessScheduledActions(); +} + +void Scheduler::OnDrawForOutputSurface() { + DCHECK(settings_.using_synchronous_renderer_compositor); + DCHECK_EQ(state_machine_.begin_impl_frame_state(), + SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_IDLE); + DCHECK(!BeginImplFrameDeadlinePending()); + + state_machine_.OnBeginImplFrameDeadline(); + ProcessScheduledActions(); + + state_machine_.OnBeginImplFrameIdle(); + ProcessScheduledActions(); +} + // 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"); + TRACE_EVENT0("cc,benchmark", "Scheduler::BeginRetroFrame"); DCHECK(!settings_.using_synchronous_renderer_compositor); - DCHECK(begin_retro_frame_posted_); - begin_retro_frame_posted_ = false; + DCHECK(!begin_retro_frame_args_.empty()); + DCHECK(!begin_retro_frame_task_.IsCancelled()); + DCHECK_EQ(state_machine_.begin_impl_frame_state(), + SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_IDLE); - // If there aren't any retroactive BeginFrames, then we've lost the - // OutputSurface and should abort. - if (begin_retro_frame_args_.empty()) - return; + begin_retro_frame_task_.Cancel(); // Discard expired BeginRetroFrames // Today, we should always end up with at most one un-expired BeginRetroFrame @@ -442,20 +431,16 @@ void Scheduler::BeginRetroFrame() { // 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 = Now(); - base::TimeDelta draw_duration_estimate = client_->DrawDurationEstimate(); + while (!begin_retro_frame_args_.empty()) { - base::TimeTicks adjusted_deadline = AdjustedBeginImplFrameDeadline( - begin_retro_frame_args_.front(), draw_duration_estimate); - if (now <= adjusted_deadline) + const BeginFrameArgs& args = begin_retro_frame_args_.front(); + base::TimeTicks expiration_time = args.frame_time + args.interval; + if (now <= expiration_time) break; - - TRACE_EVENT_INSTANT2("cc", - "Scheduler::BeginRetroFrame discarding", - TRACE_EVENT_SCOPE_THREAD, - "deadline - now", - (adjusted_deadline - now).InMicroseconds(), - "BeginFrameArgs", - begin_retro_frame_args_.front().AsValue()); + TRACE_EVENT_INSTANT2( + "cc", "Scheduler::BeginRetroFrame discarding", TRACE_EVENT_SCOPE_THREAD, + "expiration_time - now", (expiration_time - now).InMillisecondsF(), + "BeginFrameArgs", begin_retro_frame_args_.front().AsValue()); begin_retro_frame_args_.pop_front(); frame_source_->DidFinishFrame(begin_retro_frame_args_.size()); } @@ -465,8 +450,9 @@ void Scheduler::BeginRetroFrame() { "Scheduler::BeginRetroFrames all expired", TRACE_EVENT_SCOPE_THREAD); } else { - BeginImplFrame(begin_retro_frame_args_.front()); + BeginFrameArgs front = begin_retro_frame_args_.front(); begin_retro_frame_args_.pop_front(); + BeginImplFrameWithDeadline(front); } } @@ -482,7 +468,7 @@ void Scheduler::PostBeginRetroFrameIfNeeded() { if (!frame_source_->NeedsBeginFrames()) return; - if (begin_retro_frame_args_.empty() || begin_retro_frame_posted_) + if (begin_retro_frame_args_.empty() || !begin_retro_frame_task_.IsCancelled()) return; // begin_retro_frame_args_ should always be empty for the @@ -493,34 +479,24 @@ void Scheduler::PostBeginRetroFrameIfNeeded() { SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_IDLE) return; - begin_retro_frame_posted_ = true; - task_runner_->PostTask(FROM_HERE, begin_retro_frame_closure_); + begin_retro_frame_task_.Reset(begin_retro_frame_closure_); + + task_runner_->PostTask(FROM_HERE, begin_retro_frame_task_.callback()); } -// 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) { +void Scheduler::BeginImplFrameWithDeadline(const BeginFrameArgs& args) { bool main_thread_is_in_high_latency_mode = state_machine_.MainThreadIsInHighLatencyMode(); - TRACE_EVENT2("cc", - "Scheduler::BeginImplFrame", - "args", - args.AsValue(), - "main_thread_is_high_latency", + TRACE_EVENT2("cc,benchmark", "Scheduler::BeginImplFrame", "args", + args.AsValue(), "main_thread_is_high_latency", main_thread_is_in_high_latency_mode); TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("cc.debug.scheduler"), - "MainThreadLatency", - main_thread_is_in_high_latency_mode); - DCHECK_EQ(state_machine_.begin_impl_frame_state(), - SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_IDLE); - DCHECK(state_machine_.HasInitializedOutputSurface()); + "MainThreadLatency", main_thread_is_in_high_latency_mode); 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; + begin_impl_frame_args_.deadline -= client_->DrawDurationEstimate(); if (!state_machine_.impl_latency_takes_priority() && main_thread_is_in_high_latency_mode && @@ -528,68 +504,115 @@ void Scheduler::BeginImplFrame(const BeginFrameArgs& args) { state_machine_.SetSkipNextBeginMainFrameToReduceLatency(); } - client_->WillBeginImplFrame(begin_impl_frame_args_); - state_machine_.OnBeginImplFrame(begin_impl_frame_args_); - devtools_instrumentation::DidBeginFrame(layer_tree_host_id_); + BeginImplFrame(); + // The deadline will be scheduled in ProcessScheduledActions. + state_machine_.OnBeginImplFrameDeadlinePending(); ProcessScheduledActions(); +} - state_machine_.OnBeginImplFrameDeadlinePending(); - ScheduleBeginImplFrameDeadline( - AdjustedBeginImplFrameDeadline(args, draw_duration_estimate)); +void Scheduler::BeginImplFrameSynchronous(const BeginFrameArgs& args) { + TRACE_EVENT1("cc,benchmark", "Scheduler::BeginImplFrame", "args", + args.AsValue()); + begin_impl_frame_args_ = args; + BeginImplFrame(); + FinishImplFrame(); } -base::TimeTicks Scheduler::AdjustedBeginImplFrameDeadline( - const BeginFrameArgs& args, - base::TimeDelta draw_duration_estimate) const { - if (settings_.using_synchronous_renderer_compositor) { - // 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. - 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. - 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 - // expected BeginImplFrame start. This allows us to draw immediately when - // there is a new active tree, instead of waiting for the next - // BeginImplFrame. - // TODO(brianderson): Handle long deadlines (that are past the next frame's - // frame time) properly instead of using this hack. - return args.frame_time + args.interval; - } +void Scheduler::FinishImplFrame() { + state_machine_.OnBeginImplFrameIdle(); + ProcessScheduledActions(); + + client_->DidFinishImplFrame(); + frame_source_->DidFinishFrame(begin_retro_frame_args_.size()); } -void Scheduler::ScheduleBeginImplFrameDeadline(base::TimeTicks deadline) { - TRACE_EVENT1( - "cc", "Scheduler::ScheduleBeginImplFrameDeadline", "deadline", 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; - } +// 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() { + DCHECK_EQ(state_machine_.begin_impl_frame_state(), + SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_IDLE); + DCHECK(!BeginImplFrameDeadlinePending()); + DCHECK(state_machine_.HasInitializedOutputSurface()); + DCHECK(advance_commit_state_task_.IsCancelled()); + + state_machine_.OnBeginImplFrame(); + devtools_instrumentation::DidBeginFrame(layer_tree_host_id_); + client_->WillBeginImplFrame(begin_impl_frame_args_); + + ProcessScheduledActions(); +} + +void Scheduler::ScheduleBeginImplFrameDeadline() { + // The synchronous compositor does not post a deadline task. + DCHECK(!settings_.using_synchronous_renderer_compositor); + begin_impl_frame_deadline_task_.Cancel(); begin_impl_frame_deadline_task_.Reset(begin_impl_frame_deadline_closure_); - base::TimeDelta delta = deadline - Now(); - if (delta <= base::TimeDelta()) - delta = base::TimeDelta(); + begin_impl_frame_deadline_mode_ = + state_machine_.CurrentBeginImplFrameDeadlineMode(); + + base::TimeTicks deadline; + switch (begin_impl_frame_deadline_mode_) { + case SchedulerStateMachine::BEGIN_IMPL_FRAME_DEADLINE_MODE_NONE: + // No deadline. + return; + case SchedulerStateMachine::BEGIN_IMPL_FRAME_DEADLINE_MODE_IMMEDIATE: + // We are ready to draw a new active tree immediately. + // We don't use Now() here because it's somewhat expensive to call. + deadline = base::TimeTicks(); + break; + case SchedulerStateMachine::BEGIN_IMPL_FRAME_DEADLINE_MODE_REGULAR: + // We are animating on the impl thread but we can wait for some time. + deadline = begin_impl_frame_args_.deadline; + break; + case SchedulerStateMachine::BEGIN_IMPL_FRAME_DEADLINE_MODE_LATE: + // We are blocked for one reason or another and we should wait. + // TODO(brianderson): Handle long deadlines (that are past the next + // frame's frame time) properly instead of using this hack. + deadline = + begin_impl_frame_args_.frame_time + begin_impl_frame_args_.interval; + break; + case SchedulerStateMachine:: + BEGIN_IMPL_FRAME_DEADLINE_MODE_BLOCKED_ON_READY_TO_DRAW: + // We are blocked because we are waiting for ReadyToDraw signal. We would + // post deadline after we received ReadyToDraw singal. + TRACE_EVENT1("cc", "Scheduler::ScheduleBeginImplFrameDeadline", + "deadline_mode", "blocked_on_ready_to_draw"); + return; + } + + TRACE_EVENT2("cc", "Scheduler::ScheduleBeginImplFrameDeadline", "mode", + SchedulerStateMachine::BeginImplFrameDeadlineModeToString( + begin_impl_frame_deadline_mode_), + "deadline", deadline); + + base::TimeDelta delta = std::max(deadline - Now(), base::TimeDelta()); task_runner_->PostDelayedTask( FROM_HERE, begin_impl_frame_deadline_task_.callback(), delta); } +void Scheduler::ScheduleBeginImplFrameDeadlineIfNeeded() { + if (settings_.using_synchronous_renderer_compositor) + return; + + if (state_machine_.begin_impl_frame_state() != + SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_INSIDE_BEGIN_FRAME) + return; + + if (begin_impl_frame_deadline_mode_ == + state_machine_.CurrentBeginImplFrameDeadlineMode() && + BeginImplFrameDeadlinePending()) + return; + + ScheduleBeginImplFrameDeadline(); +} + void Scheduler::OnBeginImplFrameDeadline() { - TRACE_EVENT0("cc", "Scheduler::OnBeginImplFrameDeadline"); + TRACE_EVENT0("cc,benchmark", "Scheduler::OnBeginImplFrameDeadline"); 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: @@ -597,21 +620,16 @@ void Scheduler::OnBeginImplFrameDeadline() { // 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. + + // TODO(robliao): Remove ScopedTracker below once crbug.com/461509 is fixed. + tracked_objects::ScopedTracker tracking_profile1( + FROM_HERE_WITH_EXPLICIT_FUNCTION( + "461509 Scheduler::OnBeginImplFrameDeadline1")); state_machine_.OnBeginImplFrameDeadline(); ProcessScheduledActions(); - state_machine_.OnBeginImplFrameIdle(); - ProcessScheduledActions(); - - client_->DidBeginImplFrameDeadline(); + FinishImplFrame(); } -void Scheduler::PollForAnticipatedDrawTriggers() { - TRACE_EVENT0("cc", "Scheduler::PollForAnticipatedDrawTriggers"); - poll_for_draw_triggers_task_.Cancel(); - state_machine_.DidEnterPollForAnticipatedDrawTriggers(); - ProcessScheduledActions(); - state_machine_.DidLeavePollForAnticipatedDrawTriggers(); -} void Scheduler::PollToAdvanceCommitState() { TRACE_EVENT0("cc", "Scheduler::PollToAdvanceCommitState"); @@ -624,6 +642,14 @@ void Scheduler::DrawAndSwapIfPossible() { state_machine_.DidDrawIfPossibleCompleted(result); } +void Scheduler::SetDeferCommits(bool defer_commits) { + TRACE_EVENT1("cc", "Scheduler::SetDeferCommits", + "defer_commits", + defer_commits); + state_machine_.SetDeferCommits(defer_commits); + ProcessScheduledActions(); +} + void Scheduler::ProcessScheduledActions() { // We do not allow ProcessScheduledActions to be recursive. // The top-level call will iteratively execute the next action for us anyway. @@ -654,18 +680,27 @@ void Scheduler::ProcessScheduledActions() { case SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME: client_->ScheduledActionSendBeginMainFrame(); break; - case SchedulerStateMachine::ACTION_COMMIT: + case SchedulerStateMachine::ACTION_COMMIT: { + // TODO(robliao): Remove ScopedTracker below once crbug.com/461509 is + // fixed. + tracked_objects::ScopedTracker tracking_profile4( + FROM_HERE_WITH_EXPLICIT_FUNCTION( + "461509 Scheduler::ProcessScheduledActions4")); client_->ScheduledActionCommit(); break; - case SchedulerStateMachine::ACTION_UPDATE_VISIBLE_TILES: - client_->ScheduledActionUpdateVisibleTiles(); - break; + } case SchedulerStateMachine::ACTION_ACTIVATE_SYNC_TREE: client_->ScheduledActionActivateSyncTree(); break; - case SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE: + case SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE: { + // TODO(robliao): Remove ScopedTracker below once crbug.com/461509 is + // fixed. + tracked_objects::ScopedTracker tracking_profile6( + FROM_HERE_WITH_EXPLICIT_FUNCTION( + "461509 Scheduler::ProcessScheduledActions6")); DrawAndSwapIfPossible(); break; + } case SchedulerStateMachine::ACTION_DRAW_AND_SWAP_FORCED: client_->ScheduledActionDrawAndSwapForced(); break; @@ -676,36 +711,36 @@ void Scheduler::ProcessScheduledActions() { case SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION: client_->ScheduledActionBeginOutputSurfaceCreation(); break; - case SchedulerStateMachine::ACTION_MANAGE_TILES: - client_->ScheduledActionManageTiles(); + case SchedulerStateMachine::ACTION_PREPARE_TILES: + client_->ScheduledActionPrepareTiles(); + break; + case SchedulerStateMachine::ACTION_INVALIDATE_OUTPUT_SURFACE: { + client_->ScheduledActionInvalidateOutputSurface(); break; + } } } while (action != SchedulerStateMachine::ACTION_NONE); - SetupNextBeginFrameIfNeeded(); + SetupPollingMechanisms(); + client_->DidAnticipatedDrawTimeChange(AnticipatedDrawTime()); - if (state_machine_.ShouldTriggerBeginImplFrameDeadlineEarly()) { - DCHECK(!settings_.using_synchronous_renderer_compositor); - ScheduleBeginImplFrameDeadline(base::TimeTicks()); - } -} + ScheduleBeginImplFrameDeadlineIfNeeded(); -bool Scheduler::WillDrawIfNeeded() const { - return !state_machine_.PendingDrawsShouldBeAborted(); + SetupNextBeginFrameIfNeeded(); } -scoped_refptr<base::debug::ConvertableToTraceFormat> Scheduler::AsValue() +scoped_refptr<base::trace_event::ConvertableToTraceFormat> Scheduler::AsValue() const { - scoped_refptr<base::debug::TracedValue> state = - new base::debug::TracedValue(); + scoped_refptr<base::trace_event::TracedValue> state = + new base::trace_event::TracedValue(); AsValueInto(state.get()); return state; } -void Scheduler::AsValueInto(base::debug::TracedValue* state) const { +void Scheduler::AsValueInto(base::trace_event::TracedValue* state) const { state->BeginDictionary("state_machine"); - state_machine_.AsValueInto(state, Now()); + state_machine_.AsValueInto(state); state->EndDictionary(); // Only trace frame sources when explicitly enabled - http://crbug.com/420607 @@ -726,18 +761,34 @@ void Scheduler::AsValueInto(base::debug::TracedValue* state) const { estimated_parent_draw_time_.InMillisecondsF()); state->SetBoolean("last_set_needs_begin_frame_", frame_source_->NeedsBeginFrames()); - state->SetBoolean("begin_retro_frame_posted_", begin_retro_frame_posted_); state->SetInteger("begin_retro_frame_args_", begin_retro_frame_args_.size()); + state->SetBoolean("begin_retro_frame_task_", + !begin_retro_frame_task_.IsCancelled()); state->SetBoolean("begin_impl_frame_deadline_task_", !begin_impl_frame_deadline_task_.IsCancelled()); - state->SetBoolean("poll_for_draw_triggers_task_", - !poll_for_draw_triggers_task_.IsCancelled()); state->SetBoolean("advance_commit_state_task_", !advance_commit_state_task_.IsCancelled()); state->BeginDictionary("begin_impl_frame_args"); begin_impl_frame_args_.AsValueInto(state); state->EndDictionary(); + base::TimeTicks now = Now(); + base::TimeTicks frame_time = begin_impl_frame_args_.frame_time; + base::TimeTicks deadline = begin_impl_frame_args_.deadline; + base::TimeDelta interval = begin_impl_frame_args_.interval; + state->BeginDictionary("major_timestamps_in_ms"); + state->SetDouble("0_interval", interval.InMillisecondsF()); + state->SetDouble("1_now_to_deadline", (deadline - now).InMillisecondsF()); + state->SetDouble("2_frame_time_to_now", (now - frame_time).InMillisecondsF()); + state->SetDouble("3_frame_time_to_deadline", + (deadline - frame_time).InMillisecondsF()); + state->SetDouble("4_now", (now - base::TimeTicks()).InMillisecondsF()); + state->SetDouble("5_frame_time", + (frame_time - base::TimeTicks()).InMillisecondsF()); + state->SetDouble("6_deadline", + (deadline - base::TimeTicks()).InMillisecondsF()); + state->EndDictionary(); + state->EndDictionary(); state->BeginDictionary("client_state"); diff --git a/chromium/cc/scheduler/scheduler.h b/chromium/cc/scheduler/scheduler.h index f470d17a9e7..13daf008cc3 100644 --- a/chromium/cc/scheduler/scheduler.h +++ b/chromium/cc/scheduler/scheduler.h @@ -11,8 +11,6 @@ #include "base/basictypes.h" #include "base/cancelable_callback.h" #include "base/memory/scoped_ptr.h" -#include "base/power_monitor/power_monitor.h" -#include "base/power_monitor/power_observer.h" #include "base/time/time.h" #include "cc/base/cc_export.h" #include "cc/output/begin_frame_args.h" @@ -24,7 +22,7 @@ #include "cc/scheduler/scheduler_state_machine.h" namespace base { -namespace debug { +namespace trace_event { class ConvertableToTraceFormat; } class SingleThreadTaskRunner; @@ -34,22 +32,23 @@ namespace cc { class SchedulerClient { public: - virtual BeginFrameSource* ExternalBeginFrameSource() = 0; virtual void WillBeginImplFrame(const BeginFrameArgs& args) = 0; virtual void ScheduledActionSendBeginMainFrame() = 0; virtual DrawResult ScheduledActionDrawAndSwapIfPossible() = 0; virtual DrawResult ScheduledActionDrawAndSwapForced() = 0; virtual void ScheduledActionAnimate() = 0; virtual void ScheduledActionCommit() = 0; - virtual void ScheduledActionUpdateVisibleTiles() = 0; virtual void ScheduledActionActivateSyncTree() = 0; virtual void ScheduledActionBeginOutputSurfaceCreation() = 0; - virtual void ScheduledActionManageTiles() = 0; + virtual void ScheduledActionPrepareTiles() = 0; + virtual void ScheduledActionInvalidateOutputSurface() = 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 DidBeginImplFrameDeadline() = 0; + virtual void DidFinishImplFrame() = 0; + virtual void SendBeginFramesToChildren(const BeginFrameArgs& args) = 0; + virtual void SendBeginMainFrameNotExpectedSoon() = 0; protected: virtual ~SchedulerClient() {} @@ -64,7 +63,7 @@ class CC_EXPORT SchedulerFrameSourcesConstructor { public: virtual ~SchedulerFrameSourcesConstructor() {} virtual BeginFrameSource* ConstructPrimaryFrameSource(Scheduler* scheduler); - virtual BeginFrameSource* ConstructBackgroundFrameSource( + virtual BeginFrameSource* ConstructUnthrottledFrameSource( Scheduler* scheduler); protected: @@ -73,28 +72,29 @@ class CC_EXPORT SchedulerFrameSourcesConstructor { friend class Scheduler; }; -class CC_EXPORT Scheduler : public BeginFrameObserverMixIn, - public base::PowerObserver { +class CC_EXPORT Scheduler : public BeginFrameObserverMixIn { public: static scoped_ptr<Scheduler> Create( SchedulerClient* client, const SchedulerSettings& scheduler_settings, int layer_tree_host_id, const scoped_refptr<base::SingleThreadTaskRunner>& task_runner, - base::PowerMonitor* power_monitor) { + scoped_ptr<BeginFrameSource> external_begin_frame_source) { SchedulerFrameSourcesConstructor frame_sources_constructor; return make_scoped_ptr(new Scheduler(client, scheduler_settings, layer_tree_host_id, task_runner, - power_monitor, + external_begin_frame_source.Pass(), &frame_sources_constructor)); } ~Scheduler() override; - // base::PowerObserver method. - void OnPowerStateChange(bool on_battery_power) override; + // BeginFrameObserverMixin + bool OnBeginFrameMixInDelegate(const BeginFrameArgs& args) override; + + void OnDrawForOutputSurface(); const SchedulerSettings& settings() const { return settings_; } @@ -107,6 +107,8 @@ class CC_EXPORT Scheduler : public BeginFrameObserverMixIn, void SetVisible(bool visible); void SetCanDraw(bool can_draw); void NotifyReadyToActivate(); + void NotifyReadyToDraw(); + void SetThrottleFrameProduction(bool throttle); void SetNeedsCommit(); @@ -114,19 +116,20 @@ class CC_EXPORT Scheduler : public BeginFrameObserverMixIn, void SetNeedsAnimate(); - void SetNeedsManageTiles(); + void SetNeedsPrepareTiles(); + + void SetWaitForReadyToDraw(); void SetMaxSwapsPending(int max); void DidSwapBuffers(); - void SetSwapUsedIncompleteTile(bool used_incomplete_tile); void DidSwapBuffersComplete(); void SetImplLatencyTakesPriority(bool impl_latency_takes_priority); void NotifyReadyToCommit(); - void BeginMainFrameAborted(bool did_handle); + void BeginMainFrameAborted(CommitEarlyOutReason reason); - void DidManageTiles(); + void DidPrepareTiles(); void DidLoseOutputSurface(); void DidCreateAndInitializeOutputSurface(); @@ -139,8 +142,8 @@ class CC_EXPORT Scheduler : public BeginFrameObserverMixIn, bool CommitPending() const { return state_machine_.CommitPending(); } bool RedrawPending() const { return state_machine_.RedrawPending(); } - bool ManageTilesPending() const { - return state_machine_.ManageTilesPending(); + bool PrepareTilesPending() const { + return state_machine_.PrepareTilesPending(); } bool MainThreadIsInHighLatencyMode() const { return state_machine_.MainThreadIsInHighLatencyMode(); @@ -149,30 +152,32 @@ class CC_EXPORT Scheduler : public BeginFrameObserverMixIn, return !begin_impl_frame_deadline_task_.IsCancelled(); } - bool WillDrawIfNeeded() const; - base::TimeTicks AnticipatedDrawTime() const; void NotifyBeginMainFrameStarted(); base::TimeTicks LastBeginImplFrameTime(); - scoped_refptr<base::debug::ConvertableToTraceFormat> AsValue() const; - void AsValueInto(base::debug::TracedValue* value) const override; + void SetDeferCommits(bool defer_commits); + + scoped_refptr<base::trace_event::ConvertableToTraceFormat> AsValue() const; + void AsValueInto(base::trace_event::TracedValue* value) const override; void SetContinuousPainting(bool continuous_painting) { state_machine_.SetContinuousPainting(continuous_painting); } - // BeginFrameObserverMixin - bool OnBeginFrameMixInDelegate(const BeginFrameArgs& args) override; + void SetChildrenNeedBeginFrames(bool children_need_begin_frames); + void SetVideoNeedsBeginFrames(bool video_needs_begin_frames); + + void SetAuthoritativeVSyncInterval(const base::TimeDelta& interval); protected: Scheduler(SchedulerClient* client, const SchedulerSettings& scheduler_settings, int layer_tree_host_id, const scoped_refptr<base::SingleThreadTaskRunner>& task_runner, - base::PowerMonitor* power_monitor, + scoped_ptr<BeginFrameSource> external_begin_frame_source, SchedulerFrameSourcesConstructor* frame_sources_constructor); // virtual for testing - Don't call these in the constructor or @@ -181,35 +186,35 @@ class CC_EXPORT Scheduler : public BeginFrameObserverMixIn, scoped_ptr<BeginFrameSourceMultiplexer> frame_source_; BeginFrameSource* primary_frame_source_; - BeginFrameSource* background_frame_source_; + BeginFrameSource* unthrottled_frame_source_; // Storage when frame sources are internal scoped_ptr<BeginFrameSource> primary_frame_source_internal_; - scoped_ptr<SyntheticBeginFrameSource> background_frame_source_internal_; + scoped_ptr<BeginFrameSource> unthrottled_frame_source_internal_; VSyncParameterObserver* vsync_observer_; + base::TimeDelta authoritative_vsync_interval_; + base::TimeTicks last_vsync_timebase_; + + bool throttle_frame_production_; const SchedulerSettings settings_; SchedulerClient* client_; int layer_tree_host_id_; scoped_refptr<base::SingleThreadTaskRunner> task_runner_; - base::PowerMonitor* power_monitor_; - base::TimeDelta estimated_parent_draw_time_; - bool begin_retro_frame_posted_; std::deque<BeginFrameArgs> begin_retro_frame_args_; BeginFrameArgs begin_impl_frame_args_; + SchedulerStateMachine::BeginImplFrameDeadlineMode + begin_impl_frame_deadline_mode_; 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_retro_frame_task_; base::CancelableClosure begin_impl_frame_deadline_task_; - base::CancelableClosure poll_for_draw_triggers_task_; base::CancelableClosure advance_commit_state_task_; SchedulerStateMachine state_machine_; @@ -217,25 +222,23 @@ class CC_EXPORT Scheduler : public BeginFrameObserverMixIn, SchedulerStateMachine::Action inside_action_; private: - base::TimeTicks AdjustedBeginImplFrameDeadline( - const BeginFrameArgs& args, - base::TimeDelta draw_duration_estimate) const; - void ScheduleBeginImplFrameDeadline(base::TimeTicks deadline); + void ScheduleBeginImplFrameDeadline(); + void ScheduleBeginImplFrameDeadlineIfNeeded(); void SetupNextBeginFrameIfNeeded(); void PostBeginRetroFrameIfNeeded(); - void SetupPollingMechanisms(bool needs_begin_frame); + void SetupPollingMechanisms(); void DrawAndSwapIfPossible(); void ProcessScheduledActions(); bool CanCommitAndActivateBeforeDeadline() const; void AdvanceCommitStateIfPossible(); bool IsBeginMainFrameSentOrStarted() const; void BeginRetroFrame(); - void BeginImplFrame(const BeginFrameArgs& args); + void BeginImplFrameWithDeadline(const BeginFrameArgs& args); + void BeginImplFrameSynchronous(const BeginFrameArgs& args); + void BeginImplFrame(); + void FinishImplFrame(); void OnBeginImplFrameDeadline(); - void PollForAnticipatedDrawTriggers(); void PollToAdvanceCommitState(); - void SetupPowerMonitoring(); - void TeardownPowerMonitoring(); base::TimeDelta EstimatedParentDrawTime() { return estimated_parent_draw_time_; diff --git a/chromium/cc/scheduler/scheduler_settings.cc b/chromium/cc/scheduler/scheduler_settings.cc index ab9add087bd..c6c8e8eb2db 100644 --- a/chromium/cc/scheduler/scheduler_settings.cc +++ b/chromium/cc/scheduler/scheduler_settings.cc @@ -4,46 +4,30 @@ #include "cc/scheduler/scheduler_settings.h" -#include "base/debug/trace_event_argument.h" -#include "cc/trees/layer_tree_settings.h" +#include "base/trace_event/trace_event_argument.h" namespace cc { SchedulerSettings::SchedulerSettings() - : begin_frame_scheduling_enabled(true), + : use_external_begin_frame_source(false), 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), - disable_hi_res_timer_tasks_on_battery(false) { -} - -SchedulerSettings::SchedulerSettings(const LayerTreeSettings& settings) - : begin_frame_scheduling_enabled(settings.begin_frame_scheduling_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), - disable_hi_res_timer_tasks_on_battery( - settings.disable_hi_res_timer_tasks_on_battery) { + main_thread_should_always_be_low_latency(false), + background_frame_interval(base::TimeDelta::FromSeconds(1)) { } SchedulerSettings::~SchedulerSettings() {} -scoped_refptr<base::debug::ConvertableToTraceFormat> +scoped_refptr<base::trace_event::ConvertableToTraceFormat> SchedulerSettings::AsValue() const { - scoped_refptr<base::debug::TracedValue> state = - new base::debug::TracedValue(); - state->SetBoolean("begin_frame_scheduling_enabled", - begin_frame_scheduling_enabled); + scoped_refptr<base::trace_event::TracedValue> state = + new base::trace_event::TracedValue(); + state->SetBoolean("use_external_begin_frame_source", + use_external_begin_frame_source); state->SetBoolean("main_frame_before_activation_enabled", main_frame_before_activation_enabled); state->SetBoolean("impl_side_painting", impl_side_painting); @@ -54,8 +38,10 @@ SchedulerSettings::AsValue() const { state->SetBoolean("using_synchronous_renderer_compositor", using_synchronous_renderer_compositor); state->SetBoolean("throttle_frame_production", throttle_frame_production); - state->SetBoolean("disable_hi_res_timer_tasks_on_battery", - disable_hi_res_timer_tasks_on_battery); + state->SetBoolean("main_thread_should_always_be_low_latency", + main_thread_should_always_be_low_latency); + state->SetInteger("background_frame_interval", + background_frame_interval.InMicroseconds()); return state; } diff --git a/chromium/cc/scheduler/scheduler_settings.h b/chromium/cc/scheduler/scheduler_settings.h index 8607991914e..421409ab4dc 100644 --- a/chromium/cc/scheduler/scheduler_settings.h +++ b/chromium/cc/scheduler/scheduler_settings.h @@ -6,34 +6,40 @@ #define CC_SCHEDULER_SCHEDULER_SETTINGS_H_ #include "base/memory/ref_counted.h" +#include "base/time/time.h" #include "base/values.h" #include "cc/base/cc_export.h" namespace base { -namespace debug { +namespace trace_event { class ConvertableToTraceFormat; } } namespace cc { -class LayerTreeSettings; class CC_EXPORT SchedulerSettings { public: SchedulerSettings(); - explicit SchedulerSettings(const LayerTreeSettings& settings); ~SchedulerSettings(); - bool begin_frame_scheduling_enabled; + bool use_external_begin_frame_source; 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 disable_hi_res_timer_tasks_on_battery; - scoped_refptr<base::debug::ConvertableToTraceFormat> AsValue() const; + // In main thread low latency mode the entire + // BeginMainFrame->Commit->Activation->Draw cycle should complete before + // starting the next cycle. Additionally, BeginMainFrame and Commit are + // completed atomically with no other tasks or actions occuring between them. + bool main_thread_should_always_be_low_latency; + + base::TimeDelta background_frame_interval; + + scoped_refptr<base::trace_event::ConvertableToTraceFormat> AsValue() const; }; } // namespace cc diff --git a/chromium/cc/scheduler/scheduler_state_machine.cc b/chromium/cc/scheduler/scheduler_state_machine.cc index 2b8210d2d2f..e303d3ab48a 100644 --- a/chromium/cc/scheduler/scheduler_state_machine.cc +++ b/chromium/cc/scheduler/scheduler_state_machine.cc @@ -4,11 +4,11 @@ #include "cc/scheduler/scheduler_state_machine.h" -#include "base/debug/trace_event.h" -#include "base/debug/trace_event_argument.h" #include "base/format_macros.h" #include "base/logging.h" #include "base/strings/stringprintf.h" +#include "base/trace_event/trace_event.h" +#include "base/trace_event/trace_event_argument.h" #include "base/values.h" #include "ui/gfx/frame_time.h" @@ -26,30 +26,37 @@ SchedulerStateMachine::SchedulerStateMachine(const SchedulerSettings& settings) 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), - manage_tiles_funnel_(0), + last_frame_number_invalidate_output_surface_performed_(-1), + animate_funnel_(false), + request_swap_funnel_(false), + send_begin_main_frame_funnel_(false), + invalidate_output_surface_funnel_(false), + prepare_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_prepare_tiles_(false), needs_commit_(false), - inside_poll_for_anticipated_draw_triggers_(false), visible_(false), can_start_(false), can_draw_(false), has_pending_tree_(false), pending_tree_is_ready_for_activation_(false), active_tree_needs_first_draw_(false), - did_commit_after_animating_(false), did_create_and_initialize_first_output_surface_(false), impl_latency_takes_priority_(false), skip_next_begin_main_frame_to_reduce_latency_(false), skip_begin_main_frame_to_reduce_latency_(false), continuous_painting_(false), - impl_latency_takes_priority_on_battery_(false) { + children_need_begin_frames_(false), + defer_commits_(false), + video_needs_begin_frames_(false), + last_commit_had_no_updates_(false), + wait_for_active_tree_ready_to_draw_(false), + did_request_swap_in_last_frame_(false), + did_perform_swap_in_last_draw_(false) { } const char* SchedulerStateMachine::OutputSurfaceStateToString( @@ -86,6 +93,24 @@ const char* SchedulerStateMachine::BeginImplFrameStateToString( return "???"; } +const char* SchedulerStateMachine::BeginImplFrameDeadlineModeToString( + BeginImplFrameDeadlineMode mode) { + switch (mode) { + case BEGIN_IMPL_FRAME_DEADLINE_MODE_NONE: + return "BEGIN_IMPL_FRAME_DEADLINE_MODE_NONE"; + case BEGIN_IMPL_FRAME_DEADLINE_MODE_IMMEDIATE: + return "BEGIN_IMPL_FRAME_DEADLINE_MODE_IMMEDIATE"; + case BEGIN_IMPL_FRAME_DEADLINE_MODE_REGULAR: + return "BEGIN_IMPL_FRAME_DEADLINE_MODE_REGULAR"; + case BEGIN_IMPL_FRAME_DEADLINE_MODE_LATE: + return "BEGIN_IMPL_FRAME_DEADLINE_MODE_LATE"; + case BEGIN_IMPL_FRAME_DEADLINE_MODE_BLOCKED_ON_READY_TO_DRAW: + return "BEGIN_IMPL_FRAME_DEADLINE_MODE_BLOCKED_ON_READY_TO_DRAW"; + } + NOTREACHED(); + return "???"; +} + const char* SchedulerStateMachine::CommitStateToString(CommitState state) { switch (state) { case COMMIT_STATE_IDLE: @@ -98,6 +123,8 @@ const char* SchedulerStateMachine::CommitStateToString(CommitState state) { return "COMMIT_STATE_READY_TO_COMMIT"; case COMMIT_STATE_WAITING_FOR_ACTIVATION: return "COMMIT_STATE_WAITING_FOR_ACTIVATION"; + case COMMIT_STATE_WAITING_FOR_DRAW: + return "COMMIT_STATE_WAITING_FOR_DRAW"; } NOTREACHED(); return "???"; @@ -129,8 +156,6 @@ const char* SchedulerStateMachine::ActionToString(Action action) { return "ACTION_SEND_BEGIN_MAIN_FRAME"; case ACTION_COMMIT: return "ACTION_COMMIT"; - case ACTION_UPDATE_VISIBLE_TILES: - return "ACTION_UPDATE_VISIBLE_TILES"; case ACTION_ACTIVATE_SYNC_TREE: return "ACTION_ACTIVATE_SYNC_TREE"; case ACTION_DRAW_AND_SWAP_IF_POSSIBLE: @@ -141,23 +166,25 @@ const char* SchedulerStateMachine::ActionToString(Action action) { return "ACTION_DRAW_AND_SWAP_ABORT"; case ACTION_BEGIN_OUTPUT_SURFACE_CREATION: return "ACTION_BEGIN_OUTPUT_SURFACE_CREATION"; - case ACTION_MANAGE_TILES: - return "ACTION_MANAGE_TILES"; + case ACTION_PREPARE_TILES: + return "ACTION_PREPARE_TILES"; + case ACTION_INVALIDATE_OUTPUT_SURFACE: + return "ACTION_INVALIDATE_OUTPUT_SURFACE"; } NOTREACHED(); return "???"; } -scoped_refptr<base::debug::ConvertableToTraceFormat> +scoped_refptr<base::trace_event::ConvertableToTraceFormat> SchedulerStateMachine::AsValue() const { - scoped_refptr<base::debug::TracedValue> state = - new base::debug::TracedValue(); - AsValueInto(state.get(), gfx::FrameTime::Now()); + scoped_refptr<base::trace_event::TracedValue> state = + new base::trace_event::TracedValue(); + AsValueInto(state.get()); return state; } -void SchedulerStateMachine::AsValueInto(base::debug::TracedValue* state, - base::TimeTicks now) const { +void SchedulerStateMachine::AsValueInto( + base::trace_event::TracedValue* state) const { state->BeginDictionary("major_state"); state->SetString("next_action", ActionToString(NextAction())); state->SetString("begin_impl_frame_state", @@ -169,35 +196,9 @@ void SchedulerStateMachine::AsValueInto(base::debug::TracedValue* state, ForcedRedrawOnTimeoutStateToString(forced_redraw_state_)); state->EndDictionary(); - state->BeginDictionary("major_timestamps_in_ms"); - state->SetDouble("0_interval", - begin_impl_frame_args_.interval.InMicroseconds() / 1000.0L); - state->SetDouble( - "1_now_to_deadline", - (begin_impl_frame_args_.deadline - now).InMicroseconds() / 1000.0L); - state->SetDouble( - "2_frame_time_to_now", - (now - begin_impl_frame_args_.frame_time).InMicroseconds() / 1000.0L); - state->SetDouble("3_frame_time_to_deadline", - (begin_impl_frame_args_.deadline - - begin_impl_frame_args_.frame_time).InMicroseconds() / - 1000.0L); - state->SetDouble("4_now", - (now - base::TimeTicks()).InMicroseconds() / 1000.0L); - state->SetDouble( - "5_frame_time", - (begin_impl_frame_args_.frame_time - base::TimeTicks()).InMicroseconds() / - 1000.0L); - state->SetDouble( - "6_deadline", - (begin_impl_frame_args_.deadline - base::TimeTicks()).InMicroseconds() / - 1000.0L); - state->EndDictionary(); - state->BeginDictionary("minor_state"); state->SetInteger("commit_count", commit_count_); state->SetInteger("current_frame_number", current_frame_number_); - state->SetInteger("last_frame_number_animate_performed", last_frame_number_animate_performed_); state->SetInteger("last_frame_number_swap_performed", @@ -206,18 +207,20 @@ void SchedulerStateMachine::AsValueInto(base::debug::TracedValue* state, last_frame_number_swap_requested_); state->SetInteger("last_frame_number_begin_main_frame_sent", last_frame_number_begin_main_frame_sent_); - state->SetInteger("last_frame_number_update_visible_tiles_was_called", - last_frame_number_update_visible_tiles_was_called_); - - state->SetInteger("manage_tiles_funnel", manage_tiles_funnel_); + state->SetBoolean("funnel: animate_funnel", animate_funnel_); + state->SetBoolean("funnel: request_swap_funnel", request_swap_funnel_); + state->SetBoolean("funnel: send_begin_main_frame_funnel", + send_begin_main_frame_funnel_); + state->SetInteger("funnel: prepare_tiles_funnel", prepare_tiles_funnel_); + state->SetBoolean("funnel: invalidate_output_surface_funnel", + invalidate_output_surface_funnel_); state->SetInteger("consecutive_checkerboard_animations", consecutive_checkerboard_animations_); state->SetInteger("max_pending_swaps_", max_pending_swaps_); state->SetInteger("pending_swaps_", pending_swaps_); state->SetBoolean("needs_redraw", needs_redraw_); state->SetBoolean("needs_animate_", needs_animate_); - state->SetBoolean("needs_manage_tiles", needs_manage_tiles_); - state->SetBoolean("swap_used_incomplete_tile", swap_used_incomplete_tile_); + state->SetBoolean("needs_prepare_tiles", needs_prepare_tiles_); state->SetBoolean("needs_commit", needs_commit_); state->SetBoolean("visible", visible_); state->SetBoolean("can_start", can_start_); @@ -227,7 +230,8 @@ void SchedulerStateMachine::AsValueInto(base::debug::TracedValue* state, pending_tree_is_ready_for_activation_); state->SetBoolean("active_tree_needs_first_draw", active_tree_needs_first_draw_); - state->SetBoolean("did_commit_after_animating", did_commit_after_animating_); + state->SetBoolean("wait_for_active_tree_ready_to_draw", + wait_for_active_tree_ready_to_draw_); state->SetBoolean("did_create_and_initialize_first_output_surface", did_create_and_initialize_first_output_surface_); state->SetBoolean("impl_latency_takes_priority", @@ -239,45 +243,17 @@ void SchedulerStateMachine::AsValueInto(base::debug::TracedValue* state, state->SetBoolean("skip_next_begin_main_frame_to_reduce_latency", skip_next_begin_main_frame_to_reduce_latency_); state->SetBoolean("continuous_painting", continuous_painting_); - state->SetBoolean("impl_latency_takes_priority_on_battery", - impl_latency_takes_priority_on_battery_); + state->SetBoolean("children_need_begin_frames", children_need_begin_frames_); + state->SetBoolean("video_needs_begin_frames", video_needs_begin_frames_); + state->SetBoolean("defer_commits", defer_commits_); + state->SetBoolean("last_commit_had_no_updates", last_commit_had_no_updates_); + state->SetBoolean("did_request_swap_in_last_frame", + did_request_swap_in_last_frame_); + state->SetBoolean("did_perform_swap_in_last_draw", + did_perform_swap_in_last_draw_); state->EndDictionary(); } -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::HasAnimatedThisFrame() const { - return last_frame_number_animate_performed_ == current_frame_number_; -} - -bool SchedulerStateMachine::HasSentBeginMainFrameThisFrame() const { - return current_frame_number_ == - last_frame_number_begin_main_frame_sent_; -} - -bool SchedulerStateMachine::HasUpdatedVisibleTilesThisFrame() const { - return current_frame_number_ == - last_frame_number_update_visible_tiles_was_called_; -} - -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, @@ -329,7 +305,7 @@ bool SchedulerStateMachine::ShouldBeginOutputSurfaceCreation() const { if (begin_impl_frame_state_ != BEGIN_IMPL_FRAME_STATE_IDLE) return false; - // We want to clear the pipline of any pending draws and activations + // We want to clear the pipeline 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. @@ -344,19 +320,20 @@ bool SchedulerStateMachine::ShouldBeginOutputSurfaceCreation() const { bool SchedulerStateMachine::ShouldDraw() const { // 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 + // from occurring. If we are waiting for the first draw, then perform the // aborted draw to keep things moving. If we are not waiting for the first // draw however, we don't want to abort for no reason. if (PendingDrawsShouldBeAborted()) return active_tree_needs_first_draw_; - // If a commit has occurred after the animate call, we need to call animate - // again before we should draw. - if (did_commit_after_animating_) + // Do not draw too many times in a single frame. It's okay that we don't check + // this before checking for aborted draws because aborted draws do not request + // a swap. + if (request_swap_funnel_) return false; - // After this line, we only want to send a swap request once per frame. - if (HasRequestedSwapThisFrame()) + // Don't draw if we are waiting on the first commit after a surface. + if (output_surface_state_ != OUTPUT_SURFACE_ACTIVE) return false; // Do not queue too many swaps. @@ -394,40 +371,13 @@ bool SchedulerStateMachine::ShouldActivatePendingTree() const { return pending_tree_is_ready_for_activation_; } -bool SchedulerStateMachine::ShouldUpdateVisibleTiles() const { - if (!settings_.impl_side_painting) - return false; - 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; - - // We should not check for visible tiles until we've entered the deadline so - // we check as late as possible and give the tiles more time to initialize. - if (begin_impl_frame_state_ != BEGIN_IMPL_FRAME_STATE_INSIDE_DEADLINE) - return false; - - // If the last swap drew with checkerboard or missing tiles, we should - // poll for any new visible tiles so we can be notified to draw again - // when there are. - if (swap_used_incomplete_tile_) - return true; - - return false; -} - bool SchedulerStateMachine::ShouldAnimate() const { - if (!can_draw_) + // Do not animate too many times in a single frame. + if (animate_funnel_) return false; - // If a commit occurred after our last call, we need to do animation again. - if (HasAnimatedThisFrame() && !did_commit_after_animating_) + // Don't animate if we are waiting on the first commit after a surface. + if (output_surface_state_ != OUTPUT_SURFACE_ACTIVE) return false; if (begin_impl_frame_state_ != BEGIN_IMPL_FRAME_STATE_BEGIN_FRAME_STARTING && @@ -445,6 +395,10 @@ bool SchedulerStateMachine::CouldSendBeginMainFrame() const { if (!visible_) return false; + // Do not make a new commits when it is deferred. + if (defer_commits_) + return false; + return true; } @@ -452,6 +406,10 @@ bool SchedulerStateMachine::ShouldSendBeginMainFrame() const { if (!CouldSendBeginMainFrame()) return false; + // Do not send begin main frame too many times in a single frame. + if (send_begin_main_frame_funnel_) + return false; + // Only send BeginMainFrame when there isn't another commit pending already. if (commit_state_ != COMMIT_STATE_IDLE) return false; @@ -463,10 +421,6 @@ bool SchedulerStateMachine::ShouldSendBeginMainFrame() const { return false; } - // We want to start the first commit after we get a new output surface ASAP. - if (output_surface_state_ == OUTPUT_SURFACE_WAITING_FOR_FIRST_COMMIT) - return true; - // We should not send BeginMainFrame while we are in // BEGIN_IMPL_FRAME_STATE_IDLE since we might have new // user input arriving soon. @@ -481,10 +435,6 @@ bool SchedulerStateMachine::ShouldSendBeginMainFrame() const { if (forced_redraw_state_ == FORCED_REDRAW_STATE_WAITING_FOR_COMMIT) return true; - // After this point, we only start a commit once per frame. - if (HasSentBeginMainFrameThisFrame()) - return false; - // We shouldn't normally accept commits if there isn't an OutputSurface. if (!HasInitializedOutputSurface()) return false; @@ -493,7 +443,7 @@ bool SchedulerStateMachine::ShouldSendBeginMainFrame() const { // TODO(brianderson): Remove this restriction to improve throughput. bool just_swapped_in_deadline = begin_impl_frame_state_ == BEGIN_IMPL_FRAME_STATE_INSIDE_DEADLINE && - HasSwappedThisFrame(); + did_perform_swap_in_last_draw_; if (pending_swaps_ >= max_pending_swaps_ && !just_swapped_in_deadline) return false; @@ -520,25 +470,41 @@ bool SchedulerStateMachine::ShouldCommit() const { return true; } -bool SchedulerStateMachine::ShouldManageTiles() const { - // ManageTiles only really needs to be called immediately after commit +bool SchedulerStateMachine::ShouldPrepareTiles() const { + // PrepareTiles only really needs to be called immediately after commit // 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) + // one PrepareTiles per BeginImplFrame in the long run. + if (prepare_tiles_funnel_ > 0) return false; // Limiting to once per-frame is not enough, since we only want to - // manage tiles _after_ draws. Polling for draw triggers and - // begin-frame are mutually exclusive, so we limit to these two cases. - if (begin_impl_frame_state_ != BEGIN_IMPL_FRAME_STATE_INSIDE_DEADLINE && - !inside_poll_for_anticipated_draw_triggers_) + // prepare tiles _after_ draws. + if (begin_impl_frame_state_ != BEGIN_IMPL_FRAME_STATE_INSIDE_DEADLINE) return false; - return needs_manage_tiles_; + + return needs_prepare_tiles_; +} + +bool SchedulerStateMachine::ShouldInvalidateOutputSurface() const { + // Do not invalidate too many times in a frame. + if (invalidate_output_surface_funnel_) + return false; + + // Only the synchronous compositor requires invalidations. + if (!settings_.using_synchronous_renderer_compositor) + return false; + + // Invalidations are only performed inside a BeginFrame. + if (begin_impl_frame_state_ != BEGIN_IMPL_FRAME_STATE_BEGIN_FRAME_STARTING) + return false; + + // TODO(sunnyps): needs_prepare_tiles_ is needed here because PrepareTiles is + // called only inside the deadline / draw phase. We could remove this if we + // allowed PrepareTiles to happen in OnBeginImplFrame. + return needs_redraw_ || needs_prepare_tiles_; } SchedulerStateMachine::Action SchedulerStateMachine::NextAction() const { - if (ShouldUpdateVisibleTiles()) - return ACTION_UPDATE_VISIBLE_TILES; if (ShouldActivatePendingTree()) return ACTION_ACTIVATE_SYNC_TREE; if (ShouldCommit()) @@ -553,10 +519,12 @@ SchedulerStateMachine::Action SchedulerStateMachine::NextAction() const { else return ACTION_DRAW_AND_SWAP_IF_POSSIBLE; } - if (ShouldManageTiles()) - return ACTION_MANAGE_TILES; + if (ShouldPrepareTiles()) + return ACTION_PREPARE_TILES; if (ShouldSendBeginMainFrame()) return ACTION_SEND_BEGIN_MAIN_FRAME; + if (ShouldInvalidateOutputSurface()) + return ACTION_INVALIDATE_OUTPUT_SURFACE; if (ShouldBeginOutputSurfaceCreation()) return ACTION_BEGIN_OUTPUT_SURFACE_CREATION; return ACTION_NONE; @@ -567,37 +535,21 @@ void SchedulerStateMachine::UpdateState(Action action) { case ACTION_NONE: return; - case ACTION_UPDATE_VISIBLE_TILES: - last_frame_number_update_visible_tiles_was_called_ = - current_frame_number_; - return; - case ACTION_ACTIVATE_SYNC_TREE: UpdateStateOnActivation(); return; case ACTION_ANIMATE: - last_frame_number_animate_performed_ = current_frame_number_; - needs_animate_ = false; - did_commit_after_animating_ = false; - // TODO(skyostil): Instead of assuming this, require the client to tell - // us. - SetNeedsRedraw(); + UpdateStateOnAnimate(); return; case ACTION_SEND_BEGIN_MAIN_FRAME: - DCHECK(!has_pending_tree_ || - settings_.main_frame_before_activation_enabled); - DCHECK(visible_); - commit_state_ = COMMIT_STATE_BEGIN_MAIN_FRAME_SENT; - needs_commit_ = false; - last_frame_number_begin_main_frame_sent_ = - current_frame_number_; + UpdateStateOnSendBeginMainFrame(); return; case ACTION_COMMIT: { - bool commit_was_aborted = false; - UpdateStateOnCommit(commit_was_aborted); + bool commit_has_no_updates = false; + UpdateStateOnCommit(commit_has_no_updates); return; } @@ -615,40 +567,58 @@ void SchedulerStateMachine::UpdateState(Action action) { } case ACTION_BEGIN_OUTPUT_SURFACE_CREATION: - DCHECK_EQ(output_surface_state_, OUTPUT_SURFACE_LOST); - output_surface_state_ = OUTPUT_SURFACE_CREATING; - - // The following DCHECKs make sure we are in the proper quiescent state. - // The pipeline should be flushed entirely before we start output - // surface creation to avoid complicated corner cases. - DCHECK_EQ(commit_state_, COMMIT_STATE_IDLE); - DCHECK(!has_pending_tree_); - DCHECK(!active_tree_needs_first_draw_); + UpdateStateOnBeginOutputSurfaceCreation(); + return; + + case ACTION_PREPARE_TILES: + UpdateStateOnPrepareTiles(); return; - case ACTION_MANAGE_TILES: - UpdateStateOnManageTiles(); + case ACTION_INVALIDATE_OUTPUT_SURFACE: + UpdateStateOnInvalidateOutputSurface(); return; } } -void SchedulerStateMachine::UpdateStateOnCommit(bool commit_was_aborted) { +void SchedulerStateMachine::UpdateStateOnAnimate() { + DCHECK(!animate_funnel_); + last_frame_number_animate_performed_ = current_frame_number_; + animate_funnel_ = true; + needs_animate_ = false; + // TODO(skyostil): Instead of assuming this, require the client to tell us. + SetNeedsRedraw(); +} + +void SchedulerStateMachine::UpdateStateOnSendBeginMainFrame() { + DCHECK(!has_pending_tree_ || settings_.main_frame_before_activation_enabled); + DCHECK(visible_); + DCHECK(!send_begin_main_frame_funnel_); + commit_state_ = COMMIT_STATE_BEGIN_MAIN_FRAME_SENT; + needs_commit_ = false; + send_begin_main_frame_funnel_ = true; + last_frame_number_begin_main_frame_sent_ = current_frame_number_; +} + +void SchedulerStateMachine::UpdateStateOnCommit(bool commit_has_no_updates) { commit_count_++; - if (!commit_was_aborted && HasAnimatedThisFrame()) - did_commit_after_animating_ = true; + // Animate after commit even if we've already animated. + if (!commit_has_no_updates) + animate_funnel_ = false; - if (commit_was_aborted || settings_.main_frame_before_activation_enabled) { + if (commit_has_no_updates || settings_.main_frame_before_activation_enabled) { commit_state_ = COMMIT_STATE_IDLE; + } else if (settings_.impl_side_painting) { + commit_state_ = COMMIT_STATE_WAITING_FOR_ACTIVATION; } else { - commit_state_ = settings_.impl_side_painting - ? COMMIT_STATE_WAITING_FOR_ACTIVATION + commit_state_ = settings_.main_thread_should_always_be_low_latency + ? COMMIT_STATE_WAITING_FOR_DRAW : COMMIT_STATE_IDLE; } // 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; + has_pending_tree_ = settings_.impl_side_painting && !commit_has_no_updates; // Update state related to forced draws. if (forced_redraw_state_ == FORCED_REDRAW_STATE_WAITING_FOR_COMMIT) { @@ -671,7 +641,7 @@ void SchedulerStateMachine::UpdateStateOnCommit(bool commit_was_aborted) { // Update state if we have a new active tree to draw, or if the active tree // was unchanged but we need to do a forced draw. if (!has_pending_tree_ && - (!commit_was_aborted || + (!commit_has_no_updates || forced_redraw_state_ == FORCED_REDRAW_STATE_WAITING_FOR_DRAW)) { needs_redraw_ = true; active_tree_needs_first_draw_ = true; @@ -682,11 +652,15 @@ void SchedulerStateMachine::UpdateStateOnCommit(bool commit_was_aborted) { if (continuous_painting_) needs_commit_ = true; + last_commit_had_no_updates_ = commit_has_no_updates; } void SchedulerStateMachine::UpdateStateOnActivation() { - if (commit_state_ == COMMIT_STATE_WAITING_FOR_ACTIVATION) - commit_state_ = COMMIT_STATE_IDLE; + if (commit_state_ == COMMIT_STATE_WAITING_FOR_ACTIVATION) { + commit_state_ = settings_.main_thread_should_always_be_low_latency + ? COMMIT_STATE_WAITING_FOR_DRAW + : COMMIT_STATE_IDLE; + } if (output_surface_state_ == OUTPUT_SURFACE_WAITING_FOR_FIRST_ACTIVATION) output_surface_state_ = OUTPUT_SURFACE_ACTIVE; @@ -704,15 +678,46 @@ void SchedulerStateMachine::UpdateStateOnDraw(bool did_request_swap) { if (forced_redraw_state_ == FORCED_REDRAW_STATE_WAITING_FOR_DRAW) forced_redraw_state_ = FORCED_REDRAW_STATE_IDLE; + if (commit_state_ == COMMIT_STATE_WAITING_FOR_DRAW) + commit_state_ = COMMIT_STATE_IDLE; + needs_redraw_ = false; active_tree_needs_first_draw_ = false; - if (did_request_swap) + if (did_request_swap) { + DCHECK(!request_swap_funnel_); + request_swap_funnel_ = true; + did_request_swap_in_last_frame_ = true; last_frame_number_swap_requested_ = current_frame_number_; + } +} + +void SchedulerStateMachine::UpdateStateOnPrepareTiles() { + needs_prepare_tiles_ = false; +} + +void SchedulerStateMachine::UpdateStateOnBeginOutputSurfaceCreation() { + DCHECK_EQ(output_surface_state_, OUTPUT_SURFACE_LOST); + output_surface_state_ = OUTPUT_SURFACE_CREATING; + + // The following DCHECKs make sure we are in the proper quiescent state. + // The pipeline should be flushed entirely before we start output + // surface creation to avoid complicated corner cases. + DCHECK_EQ(commit_state_, COMMIT_STATE_IDLE); + DCHECK(!has_pending_tree_); + DCHECK(!active_tree_needs_first_draw_); } -void SchedulerStateMachine::UpdateStateOnManageTiles() { - needs_manage_tiles_ = false; +void SchedulerStateMachine::UpdateStateOnInvalidateOutputSurface() { + DCHECK(!invalidate_output_surface_funnel_); + invalidate_output_surface_funnel_ = true; + last_frame_number_invalidate_output_surface_performed_ = + current_frame_number_; + + // The synchronous compositor makes no guarantees about a draw coming in after + // an invalidate so clear any flags that would cause the compositor's pipeline + // to stall. + active_tree_needs_first_draw_ = false; // blocks commit if true } void SchedulerStateMachine::SetSkipNextBeginMainFrameToReduceLatency() { @@ -722,91 +727,69 @@ void SchedulerStateMachine::SetSkipNextBeginMainFrameToReduceLatency() { skip_next_begin_main_frame_to_reduce_latency_ = true; } -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 (!SupportsProactiveBeginFrame()) - return BeginFrameNeededToAnimateOrDraw(); - - return BeginFrameNeededToAnimateOrDraw() || ProactiveBeginFrameWanted(); -} - -bool SchedulerStateMachine::ShouldPollForAnticipatedDrawTriggers() const { - // ShouldPollForAnticipatedDrawTriggers is what we use in place of - // ProactiveBeginFrameWanted when we are using the synchronous - // compositor. - if (!SupportsProactiveBeginFrame()) { - return !BeginFrameNeededToAnimateOrDraw() && ProactiveBeginFrameWanted(); - } - - // Non synchronous compositors should rely on - // ProactiveBeginFrameWanted to poll for state instead. - return false; +bool SchedulerStateMachine::BeginFrameRequiredForChildren() const { + return children_need_begin_frames_; } - -// 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; +bool SchedulerStateMachine::BeginFrameNeededForVideo() const { + return video_needs_begin_frames_; } -// These are the cases where we definitely (or almost definitely) have a -// 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. +bool SchedulerStateMachine::BeginFrameNeeded() const { + // We can't handle BeginFrames when output surface isn't initialized. + // TODO(brianderson): Support output surface creation inside a BeginFrame. if (!HasInitializedOutputSurface()) return false; - // If we can't draw, don't tick until we are notified that we can draw again. - if (!can_draw_) + // If we are not visible, we don't need BeginFrame messages. + if (!visible_) return false; - // The forced draw respects our normal draw scheduling, so we need to - // request a BeginImplFrame for it. - if (forced_redraw_state_ == FORCED_REDRAW_STATE_WAITING_FOR_DRAW) - return true; + return (BeginFrameRequiredForAction() || BeginFrameRequiredForChildren() || + BeginFrameNeededForVideo() || ProactiveBeginFrameWanted()); +} - // There's no need to produce frames if we are not visible. - if (!visible_) - return false; +void SchedulerStateMachine::SetChildrenNeedBeginFrames( + bool children_need_begin_frames) { + children_need_begin_frames_ = children_need_begin_frames; +} - // We need to draw a more complete frame than we did the last BeginImplFrame, - // so request another BeginImplFrame in anticipation that we will have - // additional visible tiles. - if (swap_used_incomplete_tile_) - return true; +void SchedulerStateMachine::SetVideoNeedsBeginFrames( + bool video_needs_begin_frames) { + video_needs_begin_frames_ = video_needs_begin_frames; +} + +void SchedulerStateMachine::SetDeferCommits(bool defer_commits) { + defer_commits_ = defer_commits; +} - if (needs_animate_) +// These are the cases where we require a BeginFrame message to make progress +// on requested actions. +bool SchedulerStateMachine::BeginFrameRequiredForAction() const { + // The forced draw respects our normal draw scheduling, so we need to + // request a BeginImplFrame for it. + if (forced_redraw_state_ == FORCED_REDRAW_STATE_WAITING_FOR_DRAW) return true; - return needs_redraw_; + return needs_animate_ || needs_redraw_ || (needs_commit_ && !defer_commits_); } -// 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 SetNeedsBeginFrame request that has to go to the Browser. +// These are cases where we are very likely want a BeginFrame message in the +// near future. Proactively requesting the BeginImplFrame helps hide the round +// trip latency of the SetNeedsBeginFrame request that has to go to the +// Browser. +// This includes things like drawing soon, but might not actually have a new +// frame to draw when we receive the next BeginImplFrame. 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()) - return false; - // Do not be proactive when invisible. if (!visible_) return false; // We should proactively request a BeginImplFrame if a commit is pending - // because we will want to draw if the commit completes quickly. - if (needs_commit_ || commit_state_ != COMMIT_STATE_IDLE) + // because we will want to draw if the commit completes quickly. Do not + // request frames when commits are disabled, because the frame requests will + // not provide the needed commit (and will wake up the process when it could + // stay idle). + if ((commit_state_ != COMMIT_STATE_IDLE) && !defer_commits_) return true; // If the pending tree activates quickly, we'll want a BeginImplFrame soon @@ -816,7 +799,7 @@ bool SchedulerStateMachine::ProactiveBeginFrameWanted() const { // Changing priorities may allow us to activate (given the new priorities), // which may result in a new frame. - if (needs_manage_tiles_) + if (needs_prepare_tiles_) return true; // If we just sent a swap request, it's likely that we are going to produce @@ -824,48 +807,93 @@ bool SchedulerStateMachine::ProactiveBeginFrameWanted() const { // SetNeedsBeginFrame requests, which may propagate to the BeginImplFrame // provider and get sampled at an inopportune time, delaying the next // BeginImplFrame. - if (HasRequestedSwapThisFrame()) + if (did_request_swap_in_last_frame_) + return true; + + // If the last commit was aborted because of early out (no updates), we should + // still want a begin frame in case there is a commit coming again. + if (last_commit_had_no_updates_) return true; return false; } -void SchedulerStateMachine::OnBeginImplFrame(const BeginFrameArgs& args) { - AdvanceCurrentFrameNumber(); - begin_impl_frame_args_ = args; - DCHECK_EQ(begin_impl_frame_state_, BEGIN_IMPL_FRAME_STATE_IDLE) - << AsValue()->ToString(); +void SchedulerStateMachine::OnBeginImplFrame() { begin_impl_frame_state_ = BEGIN_IMPL_FRAME_STATE_BEGIN_FRAME_STARTING; + current_frame_number_++; + + last_commit_had_no_updates_ = false; + did_request_swap_in_last_frame_ = false; + + // Clear funnels for any actions we perform during the frame. + animate_funnel_ = false; + send_begin_main_frame_funnel_ = false; + invalidate_output_surface_funnel_ = false; + + // "Drain" the PrepareTiles funnel. + if (prepare_tiles_funnel_ > 0) + prepare_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; } void SchedulerStateMachine::OnBeginImplFrameDeadlinePending() { - DCHECK_EQ(begin_impl_frame_state_, - BEGIN_IMPL_FRAME_STATE_BEGIN_FRAME_STARTING) - << AsValue()->ToString(); begin_impl_frame_state_ = BEGIN_IMPL_FRAME_STATE_INSIDE_BEGIN_FRAME; } void SchedulerStateMachine::OnBeginImplFrameDeadline() { - DCHECK_EQ(begin_impl_frame_state_, BEGIN_IMPL_FRAME_STATE_INSIDE_BEGIN_FRAME) - << AsValue()->ToString(); begin_impl_frame_state_ = BEGIN_IMPL_FRAME_STATE_INSIDE_DEADLINE; + + did_perform_swap_in_last_draw_ = false; + + // Clear funnels for any actions we perform during the deadline. + request_swap_funnel_ = false; + + // Allow one PrepareTiles per draw for synchronous compositor. + if (settings_.using_synchronous_renderer_compositor) { + if (prepare_tiles_funnel_ > 0) + prepare_tiles_funnel_--; + } } void SchedulerStateMachine::OnBeginImplFrameIdle() { - DCHECK_EQ(begin_impl_frame_state_, BEGIN_IMPL_FRAME_STATE_INSIDE_DEADLINE) - << AsValue()->ToString(); begin_impl_frame_state_ = BEGIN_IMPL_FRAME_STATE_IDLE; } -bool SchedulerStateMachine::ShouldTriggerBeginImplFrameDeadlineEarly() const { - // TODO(brianderson): This should take into account multiple commit sources. +SchedulerStateMachine::BeginImplFrameDeadlineMode +SchedulerStateMachine::CurrentBeginImplFrameDeadlineMode() const { + if (settings_.using_synchronous_renderer_compositor) { + // No deadline for synchronous compositor. + return BEGIN_IMPL_FRAME_DEADLINE_MODE_NONE; + } else if (wait_for_active_tree_ready_to_draw_) { + // When we are waiting for ready to draw signal, we do not wait to post a + // deadline yet. + return BEGIN_IMPL_FRAME_DEADLINE_MODE_BLOCKED_ON_READY_TO_DRAW; + } else if (ShouldTriggerBeginImplFrameDeadlineImmediately()) { + return BEGIN_IMPL_FRAME_DEADLINE_MODE_IMMEDIATE; + } else if (needs_redraw_ && pending_swaps_ < max_pending_swaps_) { + // 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. + // If we are swap throttled we should wait until we are unblocked. + return BEGIN_IMPL_FRAME_DEADLINE_MODE_REGULAR; + } else { + // The impl thread doesn't have anything it wants to draw and we are just + // waiting for a new active tree or we are swap throttled. In short we are + // blocked. + return BEGIN_IMPL_FRAME_DEADLINE_MODE_LATE; + } +} +bool SchedulerStateMachine::ShouldTriggerBeginImplFrameDeadlineImmediately() + const { + // TODO(brianderson): This should take into account multiple commit sources. if (begin_impl_frame_state_ != BEGIN_IMPL_FRAME_STATE_INSIDE_BEGIN_FRAME) return false; - // 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) + // If we just forced activation, we should end the deadline right now. + if (PendingActivationsShouldBeForced() && !has_pending_tree_) return true; // SwapAck throttle the deadline since we wont draw and swap anyway. @@ -890,11 +918,6 @@ bool SchedulerStateMachine::ShouldTriggerBeginImplFrameDeadlineEarly() const { if (impl_latency_takes_priority_) return true; - // If we are on battery power and want to prioritize impl latency because - // we don't trust deadline tasks to execute at the right time. - if (impl_latency_takes_priority_on_battery_) - return true; - return false; } @@ -906,7 +929,7 @@ bool SchedulerStateMachine::MainThreadIsInHighLatencyMode() const { // If we just sent a BeginMainFrame and haven't hit the deadline yet, the main // thread is in a low latency mode. - if (HasSentBeginMainFrameThisFrame() && + if (send_begin_main_frame_funnel_ && (begin_impl_frame_state_ == BEGIN_IMPL_FRAME_STATE_BEGIN_FRAME_STARTING || begin_impl_frame_state_ == BEGIN_IMPL_FRAME_STATE_INSIDE_BEGIN_FRAME)) return false; @@ -930,8 +953,8 @@ bool SchedulerStateMachine::MainThreadIsInHighLatencyMode() const { // Even if there's a new active tree to draw at the deadline or we've just // 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_ || HasSwappedThisFrame()) && - !HasSentBeginMainFrameThisFrame(); + return (active_tree_needs_first_draw_ || did_perform_swap_in_last_draw_) && + !send_begin_main_frame_funnel_; } // If the active tree needs its first draw in any other state, we know the @@ -939,15 +962,6 @@ bool SchedulerStateMachine::MainThreadIsInHighLatencyMode() const { return active_tree_needs_first_draw_; } -void SchedulerStateMachine::DidEnterPollForAnticipatedDrawTriggers() { - AdvanceCurrentFrameNumber(); - inside_poll_for_anticipated_draw_triggers_ = true; -} - -void SchedulerStateMachine::DidLeavePollForAnticipatedDrawTriggers() { - inside_poll_for_anticipated_draw_triggers_ = false; -} - void SchedulerStateMachine::SetVisible(bool visible) { visible_ = visible; } void SchedulerStateMachine::SetCanDraw(bool can_draw) { can_draw_ = can_draw; } @@ -958,11 +972,15 @@ void SchedulerStateMachine::SetNeedsAnimate() { needs_animate_ = true; } -void SchedulerStateMachine::SetNeedsManageTiles() { - if (!needs_manage_tiles_) { - TRACE_EVENT0("cc", - "SchedulerStateMachine::SetNeedsManageTiles"); - needs_manage_tiles_ = true; +void SchedulerStateMachine::SetWaitForReadyToDraw() { + DCHECK(settings_.impl_side_painting); + wait_for_active_tree_ready_to_draw_ = true; +} + +void SchedulerStateMachine::SetNeedsPrepareTiles() { + if (!needs_prepare_tiles_) { + TRACE_EVENT0("cc", "SchedulerStateMachine::SetNeedsPrepareTiles"); + needs_prepare_tiles_ = true; } } @@ -974,14 +992,10 @@ void SchedulerStateMachine::DidSwapBuffers() { pending_swaps_++; DCHECK_LE(pending_swaps_, max_pending_swaps_); + did_perform_swap_in_last_draw_ = true; 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_--; @@ -1043,23 +1057,33 @@ void SchedulerStateMachine::NotifyReadyToCommit() { DCHECK(commit_state_ == COMMIT_STATE_BEGIN_MAIN_FRAME_STARTED) << AsValue()->ToString(); commit_state_ = COMMIT_STATE_READY_TO_COMMIT; + // In main thread low latency mode, commit should happen right after + // BeginFrame, meaning when this function is called, next action should be + // commit. + if (settings_.main_thread_should_always_be_low_latency) + DCHECK(ShouldCommit()); } -void SchedulerStateMachine::BeginMainFrameAborted(bool did_handle) { +void SchedulerStateMachine::BeginMainFrameAborted(CommitEarlyOutReason reason) { DCHECK_EQ(commit_state_, COMMIT_STATE_BEGIN_MAIN_FRAME_SENT); - if (did_handle) { - bool commit_was_aborted = true; - UpdateStateOnCommit(commit_was_aborted); - } else { - commit_state_ = COMMIT_STATE_IDLE; - SetNeedsCommit(); + switch (reason) { + case CommitEarlyOutReason::ABORTED_OUTPUT_SURFACE_LOST: + case CommitEarlyOutReason::ABORTED_NOT_VISIBLE: + case CommitEarlyOutReason::ABORTED_DEFERRED_COMMIT: + commit_state_ = COMMIT_STATE_IDLE; + SetNeedsCommit(); + return; + case CommitEarlyOutReason::FINISHED_NO_UPDATES: + bool commit_has_no_updates = true; + UpdateStateOnCommit(commit_has_no_updates); + return; } } -void SchedulerStateMachine::DidManageTiles() { - needs_manage_tiles_ = false; - // "Fill" the ManageTiles funnel. - manage_tiles_funnel_++; +void SchedulerStateMachine::DidPrepareTiles() { + needs_prepare_tiles_ = false; + // "Fill" the PrepareTiles funnel. + prepare_tiles_funnel_++; } void SchedulerStateMachine::DidLoseOutputSurface() { @@ -1068,6 +1092,7 @@ void SchedulerStateMachine::DidLoseOutputSurface() { return; output_surface_state_ = OUTPUT_SURFACE_LOST; needs_redraw_ = false; + wait_for_active_tree_ready_to_draw_ = false; } void SchedulerStateMachine::NotifyReadyToActivate() { @@ -1075,6 +1100,10 @@ void SchedulerStateMachine::NotifyReadyToActivate() { pending_tree_is_ready_for_activation_ = true; } +void SchedulerStateMachine::NotifyReadyToDraw() { + wait_for_active_tree_ready_to_draw_ = false; +} + void SchedulerStateMachine::DidCreateAndInitializeOutputSurface() { DCHECK_EQ(output_surface_state_, OUTPUT_SURFACE_CREATING); output_surface_state_ = OUTPUT_SURFACE_WAITING_FOR_FIRST_COMMIT; diff --git a/chromium/cc/scheduler/scheduler_state_machine.h b/chromium/cc/scheduler/scheduler_state_machine.h index a63a683e907..cf29ca9b6a5 100644 --- a/chromium/cc/scheduler/scheduler_state_machine.h +++ b/chromium/cc/scheduler/scheduler_state_machine.h @@ -9,14 +9,14 @@ #include "base/basictypes.h" #include "base/memory/scoped_ptr.h" -#include "base/time/time.h" #include "cc/base/cc_export.h" #include "cc/output/begin_frame_args.h" +#include "cc/scheduler/commit_earlyout_reason.h" #include "cc/scheduler/draw_result.h" #include "cc/scheduler/scheduler_settings.h" namespace base { -namespace debug { +namespace trace_event { class ConvertableToTraceFormat; class TracedValue; } @@ -50,10 +50,10 @@ class CC_EXPORT SchedulerStateMachine { }; static const char* OutputSurfaceStateToString(OutputSurfaceState state); - // Note: BeginImplFrameState will always cycle through all the states in - // order. Whether or not it actually waits or draws, it will at least try to - // wait in BEGIN_IMPL_FRAME_STATE_INSIDE_BEGIN_FRAME and try to draw in - // BEGIN_IMPL_FRAME_STATE_INSIDE_DEADLINE + // Note: BeginImplFrameState does not cycle through these states in a fixed + // order on all platforms. It's up to the scheduler to set these correctly. + // TODO(sunnyps): Rename the states to IDLE, ANIMATE, WAITING_FOR_DEADLINE and + // DRAW. enum BeginImplFrameState { BEGIN_IMPL_FRAME_STATE_IDLE, BEGIN_IMPL_FRAME_STATE_BEGIN_FRAME_STARTING, @@ -62,12 +62,23 @@ class CC_EXPORT SchedulerStateMachine { }; static const char* BeginImplFrameStateToString(BeginImplFrameState state); + enum BeginImplFrameDeadlineMode { + BEGIN_IMPL_FRAME_DEADLINE_MODE_NONE, + BEGIN_IMPL_FRAME_DEADLINE_MODE_IMMEDIATE, + BEGIN_IMPL_FRAME_DEADLINE_MODE_REGULAR, + BEGIN_IMPL_FRAME_DEADLINE_MODE_LATE, + BEGIN_IMPL_FRAME_DEADLINE_MODE_BLOCKED_ON_READY_TO_DRAW, + }; + static const char* BeginImplFrameDeadlineModeToString( + BeginImplFrameDeadlineMode mode); + enum CommitState { COMMIT_STATE_IDLE, 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_DRAW, }; static const char* CommitStateToString(CommitState state); @@ -88,25 +99,25 @@ class CC_EXPORT SchedulerStateMachine { CommitState commit_state() const { return commit_state_; } bool RedrawPending() const { return needs_redraw_; } - bool ManageTilesPending() const { return needs_manage_tiles_; } + bool PrepareTilesPending() const { return needs_prepare_tiles_; } enum Action { ACTION_NONE, ACTION_ANIMATE, ACTION_SEND_BEGIN_MAIN_FRAME, ACTION_COMMIT, - ACTION_UPDATE_VISIBLE_TILES, ACTION_ACTIVATE_SYNC_TREE, ACTION_DRAW_AND_SWAP_IF_POSSIBLE, ACTION_DRAW_AND_SWAP_FORCED, ACTION_DRAW_AND_SWAP_ABORT, ACTION_BEGIN_OUTPUT_SURFACE_CREATION, - ACTION_MANAGE_TILES, + ACTION_PREPARE_TILES, + ACTION_INVALIDATE_OUTPUT_SURFACE, }; static const char* ActionToString(Action action); - scoped_refptr<base::debug::ConvertableToTraceFormat> AsValue() const; - void AsValueInto(base::debug::TracedValue* dict, base::TimeTicks now) const; + scoped_refptr<base::trace_event::ConvertableToTraceFormat> AsValue() const; + void AsValueInto(base::trace_event::TracedValue* dict) const; Action NextAction() const; void UpdateState(Action action); @@ -115,39 +126,28 @@ class CC_EXPORT SchedulerStateMachine { // to make progress. 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 - // drawing repeat frames with the synchronous compositor without dropping - // necessary actions on the floor. - bool ShouldPollForAnticipatedDrawTriggers() const; - // Indicates that the system has entered and left a BeginImplFrame callback. // The scheduler will not draw more than once in a given BeginImplFrame // callback nor send more than one BeginMainFrame message. - void OnBeginImplFrame(const BeginFrameArgs& args); + void OnBeginImplFrame(); void OnBeginImplFrameDeadlinePending(); + // Indicates that the scheduler has entered the draw phase. The scheduler + // will not draw more than once in a single draw phase. + // TODO(sunnyps): Rename OnBeginImplFrameDeadline to OnDraw or similar. void OnBeginImplFrameDeadline(); void OnBeginImplFrameIdle(); - bool ShouldTriggerBeginImplFrameDeadlineEarly() const; BeginImplFrameState begin_impl_frame_state() const { return begin_impl_frame_state_; } + BeginImplFrameDeadlineMode CurrentBeginImplFrameDeadlineMode() const; // If the main thread didn't manage to produce a new frame in time for the // impl thread to draw, it is in a high latency mode. bool MainThreadIsInHighLatencyMode() const; - // PollForAnticipatedDrawTriggers is used by the synchronous compositor to - // avoid requesting BeginImplFrames when we won't actually draw but still - // need to advance our state at vsync intervals. - void DidEnterPollForAnticipatedDrawTriggers(); - void DidLeavePollForAnticipatedDrawTriggers(); - bool inside_poll_for_anticipated_draw_triggers() const { - return inside_poll_for_anticipated_draw_triggers_; - } - // Indicates whether the LayerTreeHostImpl is visible. void SetVisible(bool visible); + bool visible() const { return visible_; } // Indicates that a redraw is required, either due to the impl tree changing // or the screen being damaged and simply needing redisplay. @@ -157,9 +157,12 @@ class CC_EXPORT SchedulerStateMachine { 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(); + // Indicates that prepare-tiles is required. This guarantees another + // PrepareTiles will occur shortly (even if no redraw is required). + void SetNeedsPrepareTiles(); + + // Make deadline wait for ReadyToDraw signal. + void SetWaitForReadyToDraw(); // Sets how many swaps can be pending to the OutputSurface. void SetMaxSwapsPending(int max); @@ -198,11 +201,12 @@ class CC_EXPORT SchedulerStateMachine { // 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); + void BeginMainFrameAborted(CommitEarlyOutReason reason); // Set that we can create the first OutputSurface and start the scheduler. void SetCanStart() { can_start_ = true; } + // Allow access of the can_start_ state in tests. + bool CanStartForTesting() const { return can_start_; } void SetSkipNextBeginMainFrameToReduceLatency(); @@ -217,12 +221,15 @@ class CC_EXPORT SchedulerStateMachine { // Indicates that the pending tree is ready for activation. void NotifyReadyToActivate(); + // Indicates the active tree's visible tiles are ready to be drawn. + void NotifyReadyToDraw(); + 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 DidPrepareTiles(); void DidLoseOutputSurface(); void DidCreateAndInitializeOutputSurface(); bool HasInitializedOutputSurface() const; @@ -230,52 +237,54 @@ class CC_EXPORT SchedulerStateMachine { // True if we need to abort draws to make forward progress. bool PendingDrawsShouldBeAborted() const; - bool SupportsProactiveBeginFrame() const; - void SetContinuousPainting(bool continuous_painting) { continuous_painting_ = continuous_painting; } bool CouldSendBeginMainFrame() const; - void SetImplLatencyTakesPriorityOnBattery( - bool impl_latency_takes_priority_on_battery) { - impl_latency_takes_priority_on_battery_ = - impl_latency_takes_priority_on_battery; - } + void SetDeferCommits(bool defer_commits); // TODO(zmo): This is temporary for debugging crbug.com/393331. // We should remove it afterwards. std::string GetStatesForDebugging() const; + void SetChildrenNeedBeginFrames(bool children_need_begin_frames); + bool children_need_begin_frames() const { + return children_need_begin_frames_; + } + + void SetVideoNeedsBeginFrames(bool video_needs_begin_frames); + bool video_needs_begin_frames() const { return video_needs_begin_frames_; } + protected: - bool BeginFrameNeededToAnimateOrDraw() const; + bool BeginFrameRequiredForAction() const; + bool BeginFrameRequiredForChildren() const; + bool BeginFrameNeededForVideo() const; bool ProactiveBeginFrameWanted() const; + bool ShouldTriggerBeginImplFrameDeadlineImmediately() 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 ShouldUpdateVisibleTiles() const; bool ShouldSendBeginMainFrame() const; bool ShouldCommit() const; - bool ShouldManageTiles() const; + bool ShouldPrepareTiles() const; + bool ShouldInvalidateOutputSurface() const; - void AdvanceCurrentFrameNumber(); - bool HasAnimatedThisFrame() const; - bool HasSentBeginMainFrameThisFrame() const; - bool HasUpdatedVisibleTilesThisFrame() const; - bool HasRequestedSwapThisFrame() const; - bool HasSwappedThisFrame() const; - - void UpdateStateOnCommit(bool commit_was_aborted); + void UpdateStateOnAnimate(); + void UpdateStateOnSendBeginMainFrame(); + void UpdateStateOnCommit(bool commit_had_no_updates); void UpdateStateOnActivation(); void UpdateStateOnDraw(bool did_request_swap); - void UpdateStateOnManageTiles(); + void UpdateStateOnBeginOutputSurfaceCreation(); + void UpdateStateOnPrepareTiles(); + void UpdateStateOnInvalidateOutputSurface(); const SchedulerSettings settings_; @@ -284,43 +293,52 @@ class CC_EXPORT SchedulerStateMachine { CommitState commit_state_; ForcedRedrawOnTimeoutState forced_redraw_state_; - BeginFrameArgs begin_impl_frame_args_; - + // These are used for tracing only. 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_; - - // manage_tiles_funnel_ is "filled" each time ManageTiles is called + int last_frame_number_invalidate_output_surface_performed_; + + // These are used to ensure that an action only happens once per frame, + // deadline, etc. + bool animate_funnel_; + bool request_swap_funnel_; + bool send_begin_main_frame_funnel_; + bool invalidate_output_surface_funnel_; + // prepare_tiles_funnel_ is "filled" each time PrepareTiles 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_; + // we start throttling ACTION_PREPARE_TILES such that we average one + // PrepareTiles per BeginImplFrame. + int prepare_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_prepare_tiles_; bool needs_commit_; - bool inside_poll_for_anticipated_draw_triggers_; bool visible_; bool can_start_; bool can_draw_; bool has_pending_tree_; bool pending_tree_is_ready_for_activation_; bool active_tree_needs_first_draw_; - bool did_commit_after_animating_; bool did_create_and_initialize_first_output_surface_; bool impl_latency_takes_priority_; bool skip_next_begin_main_frame_to_reduce_latency_; bool skip_begin_main_frame_to_reduce_latency_; bool continuous_painting_; - bool impl_latency_takes_priority_on_battery_; + bool children_need_begin_frames_; + bool defer_commits_; + bool video_needs_begin_frames_; + bool last_commit_had_no_updates_; + bool wait_for_active_tree_ready_to_draw_; + bool did_request_swap_in_last_frame_; + bool did_perform_swap_in_last_draw_; 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 e3530c877f4..7b332a3fbf3 100644 --- a/chromium/cc/scheduler/scheduler_state_machine_unittest.cc +++ b/chromium/cc/scheduler/scheduler_state_machine_unittest.cc @@ -4,31 +4,58 @@ #include "cc/scheduler/scheduler_state_machine.h" -#include "base/debug/trace_event.h" +#include "base/trace_event/trace_event.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_STREQ(SchedulerStateMachine::ActionToString(action), \ - SchedulerStateMachine::ActionToString(state.NextAction())) \ - << state.AsValue()->ToString(); \ - if (action == SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE || \ - action == SchedulerStateMachine::ACTION_DRAW_AND_SWAP_FORCED) { \ - EXPECT_EQ(SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_INSIDE_DEADLINE, \ - state.begin_impl_frame_state()) \ - << state.AsValue()->ToString(); \ - } \ - state.UpdateState(action); \ - if (action == SchedulerStateMachine::ACTION_NONE) { \ - if (state.begin_impl_frame_state() == \ - SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_BEGIN_FRAME_STARTING) \ - state.OnBeginImplFrameDeadlinePending(); \ - if (state.begin_impl_frame_state() == \ - SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_INSIDE_DEADLINE) \ - state.OnBeginImplFrameIdle(); \ +// Macro to compare two enum values and get nice output. +// Without: +// Value of: actual() Actual: 7 +// Expected: expected() Which is: 0 +// With: +// Value of: actual() Actual: "ACTION_ANIMATE" +// Expected: expected() Which is: "ACTION_NONE" +#define EXPECT_ENUM_EQ(enum_tostring, expected, actual) \ + EXPECT_STREQ(SchedulerStateMachine::enum_tostring(expected), \ + SchedulerStateMachine::enum_tostring(actual)) + +#define EXPECT_IMPL_FRAME_STATE(expected) \ + EXPECT_ENUM_EQ(BeginImplFrameStateToString, expected, \ + state.begin_impl_frame_state()) \ + << state.AsValue()->ToString() + +#define EXPECT_COMMIT_STATE(expected) \ + EXPECT_ENUM_EQ(CommitStateToString, expected, state.CommitState()) + +#define EXPECT_ACTION(expected) \ + EXPECT_ENUM_EQ(ActionToString, expected, state.NextAction()) \ + << state.AsValue()->ToString() + +#define EXPECT_ACTION_UPDATE_STATE(action) \ + EXPECT_ACTION(action); \ + if (action == SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE || \ + action == SchedulerStateMachine::ACTION_DRAW_AND_SWAP_FORCED) { \ + EXPECT_IMPL_FRAME_STATE( \ + SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_INSIDE_DEADLINE); \ + } \ + state.UpdateState(action); \ + if (action == SchedulerStateMachine::ACTION_NONE) { \ + if (state.begin_impl_frame_state() == \ + SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_BEGIN_FRAME_STARTING) \ + state.OnBeginImplFrameDeadlinePending(); \ + if (state.begin_impl_frame_state() == \ + SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_INSIDE_DEADLINE) \ + state.OnBeginImplFrameIdle(); \ } +#define SET_UP_STATE(state) \ + state.SetCanStart(); \ + state.UpdateState(state.NextAction()); \ + state.CreateAndInitializeOutputSurfaceWithActivatedCommit(); \ + state.SetVisible(true); \ + state.SetCanDraw(true); + namespace cc { namespace { @@ -44,7 +71,8 @@ const SchedulerStateMachine::CommitState all_commit_states[] = { 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_ACTIVATION}; + SchedulerStateMachine::COMMIT_STATE_WAITING_FOR_ACTIVATION, + SchedulerStateMachine::COMMIT_STATE_WAITING_FOR_DRAW}; // Exposes the protected state fields of the SchedulerStateMachine for testing class StateMachine : public SchedulerStateMachine { @@ -76,9 +104,17 @@ class StateMachine : public SchedulerStateMachine { return output_surface_state_; } + void SetNeedsCommitForTest(bool needs_commit) { + needs_commit_ = needs_commit; + } + bool NeedsCommit() const { return needs_commit_; } - void SetNeedsRedraw(bool b) { needs_redraw_ = b; } + void SetNeedsAnimateForTest(bool needs_animate) { + needs_animate_ = needs_animate; + } + + void SetNeedsRedraw(bool needs_redraw) { needs_redraw_ = needs_redraw; } void SetNeedsForcedRedrawForTimeout(bool b) { forced_redraw_state_ = FORCED_REDRAW_STATE_WAITING_FOR_COMMIT; @@ -102,8 +138,56 @@ class StateMachine : public SchedulerStateMachine { void SetHasPendingTree(bool has_pending_tree) { has_pending_tree_ = has_pending_tree; } + + using SchedulerStateMachine::ShouldTriggerBeginImplFrameDeadlineImmediately; + using SchedulerStateMachine::ProactiveBeginFrameWanted; + using SchedulerStateMachine::UpdateStateOnCommit; }; +TEST(SchedulerStateMachineTest, BeginFrameNeeded) { + SchedulerSettings default_scheduler_settings; + StateMachine state(default_scheduler_settings); + state.SetCanStart(); + EXPECT_ACTION_UPDATE_STATE( + SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION) + state.CreateAndInitializeOutputSurfaceWithActivatedCommit(); + state.SetCommitState(SchedulerStateMachine::COMMIT_STATE_IDLE); + + // Don't request BeginFrames if we are idle. + state.SetVisible(true); + state.SetNeedsRedraw(false); + state.SetNeedsAnimateForTest(false); + EXPECT_FALSE(state.BeginFrameNeeded()); + + // Request BeginFrames if we are ready to draw. + state.SetVisible(true); + state.SetNeedsRedraw(true); + state.SetNeedsAnimateForTest(false); + EXPECT_TRUE(state.BeginFrameNeeded()); + + // Don't background tick for needs_redraw. + state.SetVisible(false); + state.SetNeedsRedraw(true); + state.SetNeedsAnimateForTest(false); + EXPECT_FALSE(state.BeginFrameNeeded()); + + // Proactively request BeginFrames when commit is pending. + state.SetVisible(true); + state.SetNeedsRedraw(false); + state.SetNeedsAnimateForTest(false); + state.SetNeedsCommitForTest(true); + EXPECT_TRUE(state.BeginFrameNeeded()); + + // Don't request BeginFrames when commit is pending if + // we are currently deferring commits. + state.SetVisible(true); + state.SetNeedsRedraw(false); + state.SetNeedsAnimateForTest(false); + state.SetNeedsCommitForTest(true); + state.SetDeferCommits(true); + EXPECT_FALSE(state.BeginFrameNeeded()); +} + TEST(SchedulerStateMachineTest, TestNextActionBeginsMainFrameIfNeeded) { SchedulerSettings default_scheduler_settings; @@ -122,7 +206,7 @@ TEST(SchedulerStateMachineTest, TestNextActionBeginsMainFrameIfNeeded) { EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); EXPECT_FALSE(state.BeginFrameNeeded()); - state.OnBeginImplFrame(CreateBeginFrameArgsForTesting()); + state.OnBeginImplFrame(); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); state.OnBeginImplFrameDeadline(); @@ -140,7 +224,7 @@ TEST(SchedulerStateMachineTest, TestNextActionBeginsMainFrameIfNeeded) { EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); EXPECT_FALSE(state.BeginFrameNeeded()); - state.OnBeginImplFrame(CreateBeginFrameArgsForTesting()); + state.OnBeginImplFrame(); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); state.OnBeginImplFrameDeadline(); } @@ -158,7 +242,36 @@ TEST(SchedulerStateMachineTest, TestNextActionBeginsMainFrameIfNeeded) { EXPECT_TRUE(state.BeginFrameNeeded()); - state.OnBeginImplFrame(CreateBeginFrameArgsForTesting()); + // Expect nothing to happen until after OnBeginImplFrame. + EXPECT_COMMIT_STATE(SchedulerStateMachine::COMMIT_STATE_IDLE); + EXPECT_IMPL_FRAME_STATE(SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_IDLE); + EXPECT_ACTION(SchedulerStateMachine::ACTION_NONE); + + state.OnBeginImplFrame(); + EXPECT_ACTION_UPDATE_STATE( + SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME); + } + + // If commit requested and can't draw, still begin a main frame. + { + 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); + state.SetNeedsCommit(); + state.SetCanDraw(false); + + EXPECT_TRUE(state.BeginFrameNeeded()); + + // Expect nothing to happen until after OnBeginImplFrame. + EXPECT_COMMIT_STATE(SchedulerStateMachine::COMMIT_STATE_IDLE); + EXPECT_IMPL_FRAME_STATE(SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_IDLE); + EXPECT_ACTION(SchedulerStateMachine::ACTION_NONE); + + state.OnBeginImplFrame(); EXPECT_ACTION_UPDATE_STATE( SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME); } @@ -171,8 +284,8 @@ TEST(SchedulerStateMachineTest, TestNextActionBeginsMainFrameIfNeeded) { state.CreateAndInitializeOutputSurfaceWithActivatedCommit(); state.SetVisible(true); state.UpdateState(SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME); - EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_BEGIN_MAIN_FRAME_SENT, - state.CommitState()); + EXPECT_COMMIT_STATE( + SchedulerStateMachine::COMMIT_STATE_BEGIN_MAIN_FRAME_SENT); EXPECT_FALSE(state.NeedsCommit()); } } @@ -184,18 +297,14 @@ TEST(SchedulerStateMachineTest, MainFrameBeforeActivationEnabled) { 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(); + SET_UP_STATE(state) state.SetNeedsRedraw(false); - state.SetVisible(true); - state.SetCanDraw(true); state.SetNeedsCommit(); EXPECT_TRUE(state.BeginFrameNeeded()); // Commit to the pending tree. - state.OnBeginImplFrame(CreateBeginFrameArgsForTesting()); + state.OnBeginImplFrame(); EXPECT_ACTION_UPDATE_STATE( SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); @@ -204,14 +313,14 @@ TEST(SchedulerStateMachineTest, MainFrameBeforeActivationEnabled) { state.NotifyReadyToCommit(); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_COMMIT); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); - EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_IDLE, state.CommitState()); + EXPECT_COMMIT_STATE(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()); + state.OnBeginImplFrame(); EXPECT_ACTION_UPDATE_STATE( SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); @@ -228,7 +337,7 @@ TEST(SchedulerStateMachineTest, MainFrameBeforeActivationEnabled) { EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_ACTIVATE_SYNC_TREE); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); - EXPECT_TRUE(state.ShouldTriggerBeginImplFrameDeadlineEarly()); + EXPECT_TRUE(state.ShouldTriggerBeginImplFrameDeadlineImmediately()); state.OnBeginImplFrameDeadline(); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_ANIMATE); EXPECT_ACTION_UPDATE_STATE( @@ -237,22 +346,18 @@ TEST(SchedulerStateMachineTest, MainFrameBeforeActivationEnabled) { state.DidSwapBuffersComplete(); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_COMMIT); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); - EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_IDLE, state.CommitState()); + EXPECT_COMMIT_STATE(SchedulerStateMachine::COMMIT_STATE_IDLE); } TEST(SchedulerStateMachineTest, TestFailedDrawForAnimationCheckerboardSetsNeedsCommitAndDoesNotDrawAgain) { SchedulerSettings default_scheduler_settings; StateMachine state(default_scheduler_settings); - state.SetCanStart(); - state.UpdateState(state.NextAction()); - state.CreateAndInitializeOutputSurfaceWithActivatedCommit(); - state.SetVisible(true); - state.SetCanDraw(true); + SET_UP_STATE(state) state.SetNeedsRedraw(true); EXPECT_TRUE(state.RedrawPending()); EXPECT_TRUE(state.BeginFrameNeeded()); - state.OnBeginImplFrame(CreateBeginFrameArgsForTesting()); + state.OnBeginImplFrame(); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_ANIMATE); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); state.OnBeginImplFrameDeadline(); @@ -269,7 +374,7 @@ TEST(SchedulerStateMachineTest, // Failing the draw makes us require a commit. state.DidDrawIfPossibleCompleted(DRAW_ABORTED_CHECKERBOARD_ANIMATIONS); - state.OnBeginImplFrame(CreateBeginFrameArgsForTesting()); + state.OnBeginImplFrame(); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_ANIMATE); EXPECT_ACTION_UPDATE_STATE( SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME); @@ -280,16 +385,12 @@ TEST(SchedulerStateMachineTest, 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); + SET_UP_STATE(state) state.SetNeedsRedraw(true); EXPECT_TRUE(state.RedrawPending()); EXPECT_TRUE(state.BeginFrameNeeded()); - state.OnBeginImplFrame(CreateBeginFrameArgsForTesting()); + state.OnBeginImplFrame(); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_ANIMATE); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); state.OnBeginImplFrameDeadline(); @@ -303,7 +404,7 @@ TEST(SchedulerStateMachineTest, TestFailedDrawForMissingHighResNeedsCommit) { // Missing high res content requires a commit (but not a redraw) state.DidDrawIfPossibleCompleted(DRAW_ABORTED_MISSING_HIGH_RES_CONTENT); - state.OnBeginImplFrame(CreateBeginFrameArgsForTesting()); + state.OnBeginImplFrame(); EXPECT_ACTION_UPDATE_STATE( SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME); EXPECT_FALSE(state.RedrawPending()); @@ -314,16 +415,11 @@ TEST(SchedulerStateMachineTest, TestsetNeedsRedrawDuringFailedDrawDoesNotRemoveNeedsRedraw) { SchedulerSettings default_scheduler_settings; StateMachine state(default_scheduler_settings); - state.SetCanStart(); - state.UpdateState(state.NextAction()); - state.CreateAndInitializeOutputSurfaceWithActivatedCommit(); - - state.SetVisible(true); - state.SetCanDraw(true); + SET_UP_STATE(state) state.SetNeedsRedraw(true); EXPECT_TRUE(state.RedrawPending()); EXPECT_TRUE(state.BeginFrameNeeded()); - state.OnBeginImplFrame(CreateBeginFrameArgsForTesting()); + state.OnBeginImplFrame(); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_ANIMATE); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); state.OnBeginImplFrameDeadline(); @@ -344,7 +440,7 @@ TEST(SchedulerStateMachineTest, // Failing the draw for animation checkerboards makes us require a commit. state.DidDrawIfPossibleCompleted(DRAW_ABORTED_CHECKERBOARD_ANIMATIONS); - state.OnBeginImplFrame(CreateBeginFrameArgsForTesting()); + state.OnBeginImplFrame(); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_ANIMATE); EXPECT_ACTION_UPDATE_STATE( SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME); @@ -356,16 +452,12 @@ TEST(SchedulerStateMachineTest, SchedulerSettings scheduler_settings; scheduler_settings.maximum_number_of_failed_draws_before_draw_is_forced_ = 1; StateMachine state(scheduler_settings); - state.SetCanStart(); - state.UpdateState(state.NextAction()); - state.CreateAndInitializeOutputSurfaceWithActivatedCommit(); - state.SetVisible(true); - state.SetCanDraw(true); + SET_UP_STATE(state) // Start a commit. state.SetNeedsCommit(); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); - state.OnBeginImplFrame(CreateBeginFrameArgsForTesting()); + state.OnBeginImplFrame(); EXPECT_ACTION_UPDATE_STATE( SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); @@ -395,7 +487,7 @@ TEST(SchedulerStateMachineTest, EXPECT_TRUE(state.RedrawPending()); // The redraw should be forced at the end of the next BeginImplFrame. - state.OnBeginImplFrame(CreateBeginFrameArgsForTesting()); + state.OnBeginImplFrame(); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_ANIMATE); EXPECT_ACTION_UPDATE_STATE( SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME); @@ -414,16 +506,12 @@ TEST(SchedulerStateMachineTest, TestFailedDrawsDoNotRestartForcedDraw) { draw_limit; scheduler_settings.impl_side_painting = true; StateMachine state(scheduler_settings); - state.SetCanStart(); - state.UpdateState(state.NextAction()); - state.CreateAndInitializeOutputSurfaceWithActivatedCommit(); - state.SetVisible(true); - state.SetCanDraw(true); + SET_UP_STATE(state) // Start a commit. state.SetNeedsCommit(); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); - state.OnBeginImplFrame(CreateBeginFrameArgsForTesting()); + state.OnBeginImplFrame(); EXPECT_ACTION_UPDATE_STATE( SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); @@ -471,16 +559,12 @@ TEST(SchedulerStateMachineTest, TestFailedDrawsDoNotRestartForcedDraw) { TEST(SchedulerStateMachineTest, TestFailedDrawIsRetriedInNextBeginImplFrame) { SchedulerSettings default_scheduler_settings; StateMachine state(default_scheduler_settings); - state.SetCanStart(); - state.UpdateState(state.NextAction()); - state.CreateAndInitializeOutputSurfaceWithActivatedCommit(); - state.SetVisible(true); - state.SetCanDraw(true); + SET_UP_STATE(state) // Start a draw. state.SetNeedsRedraw(true); EXPECT_TRUE(state.BeginFrameNeeded()); - state.OnBeginImplFrame(CreateBeginFrameArgsForTesting()); + state.OnBeginImplFrame(); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_ANIMATE); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); state.OnBeginImplFrameDeadline(); @@ -497,7 +581,7 @@ TEST(SchedulerStateMachineTest, TestFailedDrawIsRetriedInNextBeginImplFrame) { // We should not be trying to draw again now, but we have a commit pending. EXPECT_TRUE(state.BeginFrameNeeded()); - state.OnBeginImplFrame(CreateBeginFrameArgsForTesting()); + state.OnBeginImplFrame(); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_ANIMATE); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); @@ -514,16 +598,12 @@ TEST(SchedulerStateMachineTest, TestFailedDrawIsRetriedInNextBeginImplFrame) { TEST(SchedulerStateMachineTest, TestDoestDrawTwiceInSameFrame) { SchedulerSettings default_scheduler_settings; StateMachine state(default_scheduler_settings); - state.SetCanStart(); - state.UpdateState(state.NextAction()); - state.CreateAndInitializeOutputSurfaceWithActivatedCommit(); - state.SetVisible(true); - state.SetCanDraw(true); + SET_UP_STATE(state) state.SetNeedsRedraw(true); // Draw the first frame. EXPECT_TRUE(state.BeginFrameNeeded()); - state.OnBeginImplFrame(CreateBeginFrameArgsForTesting()); + state.OnBeginImplFrame(); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_ANIMATE); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); @@ -542,7 +622,7 @@ TEST(SchedulerStateMachineTest, TestDoestDrawTwiceInSameFrame) { // Move to another frame. This should now draw. EXPECT_TRUE(state.BeginFrameNeeded()); - state.OnBeginImplFrame(CreateBeginFrameArgsForTesting()); + state.OnBeginImplFrame(); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_ANIMATE); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); @@ -615,19 +695,16 @@ TEST(SchedulerStateMachineTest, TestNextActionDrawsOnBeginImplFrame) { expected_action = SchedulerStateMachine::ACTION_COMMIT; } else { expected_action = SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE; - EXPECT_EQ(state.NextAction(), SchedulerStateMachine::ACTION_ANIMATE) - << state.AsValue()->ToString(); + EXPECT_ACTION(SchedulerStateMachine::ACTION_ANIMATE); state.UpdateState(state.NextAction()); } // Case 1: needs_commit=false. - EXPECT_EQ(state.NextAction(), expected_action) - << state.AsValue()->ToString(); + EXPECT_ACTION(expected_action); // Case 2: needs_commit=true. state.SetNeedsCommit(); - EXPECT_EQ(state.NextAction(), expected_action) - << state.AsValue()->ToString(); + EXPECT_ACTION(expected_action); } } @@ -680,7 +757,7 @@ TEST(SchedulerStateMachineTest, TestCanRedraw_StopsDraw) { state.SetVisible(false); state.SetNeedsRedraw(true); if (j == 1) - state.OnBeginImplFrame(CreateBeginFrameArgsForTesting()); + state.OnBeginImplFrame(); state.SetCanDraw(false); EXPECT_NE(SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE, @@ -702,7 +779,8 @@ TEST(SchedulerStateMachineTest, state.SetNeedsRedraw(true); state.SetVisible(true); state.SetCanDraw(false); - state.OnBeginImplFrame(CreateBeginFrameArgsForTesting()); + state.OnBeginImplFrame(); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_ANIMATE); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_DRAW_AND_SWAP_ABORT); EXPECT_ACTION_UPDATE_STATE( SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME); @@ -711,6 +789,7 @@ TEST(SchedulerStateMachineTest, state.NotifyReadyToCommit(); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_COMMIT); state.OnBeginImplFrameDeadline(); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_ANIMATE); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_DRAW_AND_SWAP_ABORT); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); } @@ -718,21 +797,17 @@ TEST(SchedulerStateMachineTest, TEST(SchedulerStateMachineTest, TestSetNeedsCommitIsNotLost) { SchedulerSettings scheduler_settings; StateMachine state(scheduler_settings); - state.SetCanStart(); - state.UpdateState(state.NextAction()); - state.CreateAndInitializeOutputSurfaceWithActivatedCommit(); + SET_UP_STATE(state) state.SetNeedsCommit(); - state.SetVisible(true); - state.SetCanDraw(true); EXPECT_TRUE(state.BeginFrameNeeded()); // Begin the frame. - state.OnBeginImplFrame(CreateBeginFrameArgsForTesting()); + state.OnBeginImplFrame(); EXPECT_ACTION_UPDATE_STATE( SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME); - EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_BEGIN_MAIN_FRAME_SENT, - state.CommitState()); + EXPECT_COMMIT_STATE( + SchedulerStateMachine::COMMIT_STATE_BEGIN_MAIN_FRAME_SENT); // Now, while the frame is in progress, set another commit. state.SetNeedsCommit(); @@ -741,33 +816,31 @@ TEST(SchedulerStateMachineTest, TestSetNeedsCommitIsNotLost) { // Let the frame finish. state.NotifyBeginMainFrameStarted(); state.NotifyReadyToCommit(); - EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_READY_TO_COMMIT, - state.CommitState()); + EXPECT_COMMIT_STATE(SchedulerStateMachine::COMMIT_STATE_READY_TO_COMMIT); // Expect to commit regardless of BeginImplFrame state. - EXPECT_EQ(SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_BEGIN_FRAME_STARTING, - state.begin_impl_frame_state()); - EXPECT_EQ(SchedulerStateMachine::ACTION_COMMIT, state.NextAction()); + EXPECT_IMPL_FRAME_STATE( + SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_BEGIN_FRAME_STARTING); + EXPECT_ACTION(SchedulerStateMachine::ACTION_COMMIT); state.OnBeginImplFrameDeadlinePending(); - EXPECT_EQ(SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_INSIDE_BEGIN_FRAME, - state.begin_impl_frame_state()); - EXPECT_EQ(SchedulerStateMachine::ACTION_COMMIT, state.NextAction()); + EXPECT_IMPL_FRAME_STATE( + SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_INSIDE_BEGIN_FRAME); + EXPECT_ACTION(SchedulerStateMachine::ACTION_COMMIT); state.OnBeginImplFrameDeadline(); - EXPECT_EQ(SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_INSIDE_DEADLINE, - state.begin_impl_frame_state()); - EXPECT_EQ(SchedulerStateMachine::ACTION_COMMIT, state.NextAction()); + EXPECT_IMPL_FRAME_STATE( + SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_INSIDE_DEADLINE); + EXPECT_ACTION(SchedulerStateMachine::ACTION_COMMIT); state.OnBeginImplFrameIdle(); - EXPECT_EQ(SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_IDLE, - state.begin_impl_frame_state()); - EXPECT_EQ(SchedulerStateMachine::ACTION_COMMIT, state.NextAction()); + EXPECT_IMPL_FRAME_STATE(SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_IDLE); + EXPECT_ACTION(SchedulerStateMachine::ACTION_COMMIT); - 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()); + state.OnBeginImplFrame(); + EXPECT_IMPL_FRAME_STATE( + SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_BEGIN_FRAME_STARTING); + EXPECT_ACTION(SchedulerStateMachine::ACTION_COMMIT); // Finish the commit, then make sure we start the next commit immediately // and draw on the next BeginImplFrame. @@ -791,29 +864,24 @@ TEST(SchedulerStateMachineTest, TestSetNeedsCommitIsNotLost) { TEST(SchedulerStateMachineTest, TestFullCycle) { SchedulerSettings default_scheduler_settings; StateMachine state(default_scheduler_settings); - state.SetCanStart(); - state.UpdateState(state.NextAction()); - state.CreateAndInitializeOutputSurfaceWithActivatedCommit(); - state.SetVisible(true); - state.SetCanDraw(true); + SET_UP_STATE(state) // Start clean and set commit. state.SetNeedsCommit(); // Begin the frame. - state.OnBeginImplFrame(CreateBeginFrameArgsForTesting()); + state.OnBeginImplFrame(); EXPECT_ACTION_UPDATE_STATE( SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME); - EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_BEGIN_MAIN_FRAME_SENT, - state.CommitState()); + EXPECT_COMMIT_STATE( + SchedulerStateMachine::COMMIT_STATE_BEGIN_MAIN_FRAME_SENT); EXPECT_FALSE(state.NeedsCommit()); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); // Tell the scheduler the frame finished. state.NotifyBeginMainFrameStarted(); state.NotifyReadyToCommit(); - EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_READY_TO_COMMIT, - state.CommitState()); + EXPECT_COMMIT_STATE(SchedulerStateMachine::COMMIT_STATE_READY_TO_COMMIT); // Commit. EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_COMMIT); @@ -834,28 +902,166 @@ TEST(SchedulerStateMachineTest, TestFullCycle) { // Should be synchronized, no draw needed, no action needed. EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); - EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_IDLE, state.CommitState()); + EXPECT_COMMIT_STATE(SchedulerStateMachine::COMMIT_STATE_IDLE); EXPECT_FALSE(state.needs_redraw()); } +TEST(SchedulerStateMachineTest, TestFullCycleWithMainThreadLowLatencyMode) { + SchedulerSettings scheduler_settings; + scheduler_settings.main_thread_should_always_be_low_latency = true; + StateMachine state(scheduler_settings); + SET_UP_STATE(state) + + // Start clean and set commit. + state.SetNeedsCommit(); + + // Begin the frame. + state.OnBeginImplFrame(); + EXPECT_ACTION_UPDATE_STATE( + SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME); + EXPECT_COMMIT_STATE( + SchedulerStateMachine::COMMIT_STATE_BEGIN_MAIN_FRAME_SENT); + EXPECT_FALSE(state.NeedsCommit()); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); + + // Tell the scheduler the frame finished. + state.NotifyBeginMainFrameStarted(); + state.NotifyReadyToCommit(); + EXPECT_COMMIT_STATE(SchedulerStateMachine::COMMIT_STATE_READY_TO_COMMIT); + + // Commit. + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_COMMIT); + EXPECT_TRUE(state.active_tree_needs_first_draw()); + EXPECT_TRUE(state.needs_redraw()); + + // Now commit should wait for draw. + EXPECT_COMMIT_STATE(SchedulerStateMachine::COMMIT_STATE_WAITING_FOR_DRAW); + + // Swap throttled. Do not draw. + state.DidSwapBuffers(); + state.OnBeginImplFrameDeadline(); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_ANIMATE); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); + state.DidSwapBuffersComplete(); + + // Haven't draw since last commit, do not begin new main frame. + state.SetNeedsCommit(); + state.OnBeginImplFrame(); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_ANIMATE); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); + + // At BeginImplFrame deadline, draw. + state.OnBeginImplFrameDeadline(); + EXPECT_ACTION_UPDATE_STATE( + SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE); + state.DidSwapBuffers(); + state.DidDrawIfPossibleCompleted(DRAW_SUCCESS); + state.DidSwapBuffersComplete(); + + // Now will be able to start main frame. + EXPECT_COMMIT_STATE(SchedulerStateMachine::COMMIT_STATE_IDLE); + EXPECT_FALSE(state.needs_redraw()); + EXPECT_ACTION_UPDATE_STATE( + SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME); +} + +TEST(SchedulerStateMachineTest, + TestFullCycleWithMainThreadLowLatencyMode_ImplSidePaint) { + SchedulerSettings scheduler_settings; + scheduler_settings.main_thread_should_always_be_low_latency = true; + scheduler_settings.impl_side_painting = true; + StateMachine state(scheduler_settings); + SET_UP_STATE(state) + + // Start clean and set commit. + state.SetNeedsCommit(); + + // Begin the frame. + state.OnBeginImplFrame(); + EXPECT_ACTION_UPDATE_STATE( + SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME); + EXPECT_COMMIT_STATE( + SchedulerStateMachine::COMMIT_STATE_BEGIN_MAIN_FRAME_SENT); + EXPECT_FALSE(state.NeedsCommit()); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); + + // Tell the scheduler the frame finished. + state.NotifyBeginMainFrameStarted(); + state.NotifyReadyToCommit(); + EXPECT_COMMIT_STATE(SchedulerStateMachine::COMMIT_STATE_READY_TO_COMMIT); + + // Commit. + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_COMMIT); + + // Now commit should wait for activation. + EXPECT_COMMIT_STATE( + SchedulerStateMachine::COMMIT_STATE_WAITING_FOR_ACTIVATION); + + // No activation yet, so this commit is not drawn yet. Force to draw this + // frame, and still block BeginMainFrame. + state.SetNeedsRedraw(true); + state.SetNeedsCommit(); + 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); + + // Cannot BeginMainFrame yet since last commit is not yet activated and drawn. + state.OnBeginImplFrame(); + EXPECT_COMMIT_STATE( + SchedulerStateMachine::COMMIT_STATE_WAITING_FOR_ACTIVATION); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); + + // Now activate sync tree. + state.NotifyReadyToActivate(); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_ACTIVATE_SYNC_TREE); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); + EXPECT_TRUE(state.active_tree_needs_first_draw()); + EXPECT_TRUE(state.needs_redraw()); + EXPECT_COMMIT_STATE(SchedulerStateMachine::COMMIT_STATE_WAITING_FOR_DRAW); + + // Swap throttled. Do not draw. + state.DidSwapBuffers(); + state.OnBeginImplFrameDeadline(); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_ANIMATE); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); + state.DidSwapBuffersComplete(); + + // Haven't draw since last commit, do not begin new main frame. + state.OnBeginImplFrame(); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_ANIMATE); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); + + // At BeginImplFrame deadline, draw. This draws unblocks BeginMainFrame. + state.OnBeginImplFrameDeadline(); + EXPECT_ACTION_UPDATE_STATE( + SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE); + state.DidSwapBuffers(); + state.DidDrawIfPossibleCompleted(DRAW_SUCCESS); + state.DidSwapBuffersComplete(); + + // Now will be able to start main frame. + EXPECT_COMMIT_STATE(SchedulerStateMachine::COMMIT_STATE_IDLE); + EXPECT_FALSE(state.needs_redraw()); + EXPECT_ACTION_UPDATE_STATE( + SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME); +} + TEST(SchedulerStateMachineTest, TestFullCycleWithCommitRequestInbetween) { SchedulerSettings default_scheduler_settings; StateMachine state(default_scheduler_settings); - state.SetCanStart(); - state.UpdateState(state.NextAction()); - state.CreateAndInitializeOutputSurfaceWithActivatedCommit(); - state.SetVisible(true); - state.SetCanDraw(true); + SET_UP_STATE(state) // Start clean and set commit. state.SetNeedsCommit(); // Begin the frame. - state.OnBeginImplFrame(CreateBeginFrameArgsForTesting()); + state.OnBeginImplFrame(); EXPECT_ACTION_UPDATE_STATE( SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME); - EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_BEGIN_MAIN_FRAME_SENT, - state.CommitState()); + EXPECT_COMMIT_STATE( + SchedulerStateMachine::COMMIT_STATE_BEGIN_MAIN_FRAME_SENT); EXPECT_FALSE(state.NeedsCommit()); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); @@ -866,8 +1072,7 @@ TEST(SchedulerStateMachineTest, TestFullCycleWithCommitRequestInbetween) { // Tell the scheduler the frame finished. state.NotifyBeginMainFrameStarted(); state.NotifyReadyToCommit(); - EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_READY_TO_COMMIT, - state.CommitState()); + EXPECT_COMMIT_STATE(SchedulerStateMachine::COMMIT_STATE_READY_TO_COMMIT); // First commit. EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_COMMIT); @@ -888,11 +1093,11 @@ TEST(SchedulerStateMachineTest, TestFullCycleWithCommitRequestInbetween) { // Should be synchronized, no draw needed, no action needed. EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); - EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_IDLE, state.CommitState()); + EXPECT_COMMIT_STATE(SchedulerStateMachine::COMMIT_STATE_IDLE); EXPECT_FALSE(state.needs_redraw()); // Next BeginImplFrame should initiate second commit. - state.OnBeginImplFrame(CreateBeginFrameArgsForTesting()); + state.OnBeginImplFrame(); EXPECT_ACTION_UPDATE_STATE( SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME); } @@ -907,33 +1112,35 @@ TEST(SchedulerStateMachineTest, TestRequestCommitInvisible) { EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); } -TEST(SchedulerStateMachineTest, TestGoesInvisibleBeforeFinishCommit) { +// See ThreadProxy::BeginMainFrame "EarlyOut_NotVisible" / +// "EarlyOut_OutputSurfaceLost" cases. +TEST(SchedulerStateMachineTest, TestAbortBeginMainFrameBecauseInvisible) { SchedulerSettings default_scheduler_settings; StateMachine state(default_scheduler_settings); - state.SetCanStart(); - state.UpdateState(state.NextAction()); - state.CreateAndInitializeOutputSurfaceWithActivatedCommit(); - state.SetVisible(true); - state.SetCanDraw(true); + SET_UP_STATE(state) // Start clean and set commit. state.SetNeedsCommit(); // Begin the frame while visible. - state.OnBeginImplFrame(CreateBeginFrameArgsForTesting()); + state.OnBeginImplFrame(); EXPECT_ACTION_UPDATE_STATE( SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME); - EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_BEGIN_MAIN_FRAME_SENT, - state.CommitState()); + EXPECT_COMMIT_STATE( + SchedulerStateMachine::COMMIT_STATE_BEGIN_MAIN_FRAME_SENT); EXPECT_FALSE(state.NeedsCommit()); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); // Become invisible and abort BeginMainFrame. state.SetVisible(false); - state.BeginMainFrameAborted(false); + state.BeginMainFrameAborted(CommitEarlyOutReason::ABORTED_NOT_VISIBLE); + + // NeedsCommit should now be true again because we never actually did a + // commit. + EXPECT_TRUE(state.NeedsCommit()); // We should now be back in the idle state as if we never started the frame. - EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_IDLE, state.CommitState()); + EXPECT_COMMIT_STATE(SchedulerStateMachine::COMMIT_STATE_IDLE); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); // We shouldn't do anything on the BeginImplFrame deadline. @@ -945,22 +1152,23 @@ TEST(SchedulerStateMachineTest, TestGoesInvisibleBeforeFinishCommit) { // Although we have aborted on this frame and haven't cancelled the commit // (i.e. need another), don't send another BeginMainFrame yet. - EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_IDLE, state.CommitState()); - EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction()); + EXPECT_COMMIT_STATE(SchedulerStateMachine::COMMIT_STATE_IDLE); + EXPECT_ACTION(SchedulerStateMachine::ACTION_NONE); EXPECT_TRUE(state.NeedsCommit()); // Start a new frame. - state.OnBeginImplFrame(CreateBeginFrameArgsForTesting()); + state.OnBeginImplFrame(); EXPECT_ACTION_UPDATE_STATE( SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME); // We should be starting the commit now. - EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_BEGIN_MAIN_FRAME_SENT, - state.CommitState()); + EXPECT_COMMIT_STATE( + SchedulerStateMachine::COMMIT_STATE_BEGIN_MAIN_FRAME_SENT); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); } -TEST(SchedulerStateMachineTest, AbortBeginMainFrameAndCancelCommit) { +// See ThreadProxy::BeginMainFrame "EarlyOut_NoUpdates" case. +TEST(SchedulerStateMachineTest, TestAbortBeginMainFrameBecauseCommitNotNeeded) { SchedulerSettings default_scheduler_settings; StateMachine state(default_scheduler_settings); state.SetCanStart(); @@ -971,25 +1179,22 @@ TEST(SchedulerStateMachineTest, AbortBeginMainFrameAndCancelCommit) { // Get into a begin frame / commit state. state.SetNeedsCommit(); - + state.OnBeginImplFrame(); EXPECT_ACTION_UPDATE_STATE( SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME); - EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_BEGIN_MAIN_FRAME_SENT, - state.CommitState()); + EXPECT_COMMIT_STATE( + SchedulerStateMachine::COMMIT_STATE_BEGIN_MAIN_FRAME_SENT); EXPECT_FALSE(state.NeedsCommit()); - EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction()); + EXPECT_ACTION(SchedulerStateMachine::ACTION_NONE); - // Abort the commit, cancelling future commits. - state.BeginMainFrameAborted(true); + // Abort the commit, true means that the BeginMainFrame was sent but there + // was no work to do on the main thread. + state.BeginMainFrameAborted(CommitEarlyOutReason::FINISHED_NO_UPDATES); - // 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()); + // NeedsCommit should now be false because the commit was actually handled. EXPECT_FALSE(state.NeedsCommit()); - // Start a new frame; draw because this is the first frame since output - // surface init'd. - state.OnBeginImplFrame(CreateBeginFrameArgsForTesting()); + // Even though the commit was aborted, we still expect to draw the new frame. EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_ANIMATE); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); state.OnBeginImplFrameDeadline(); @@ -997,163 +1202,22 @@ TEST(SchedulerStateMachineTest, AbortBeginMainFrameAndCancelCommit) { SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE); state.DidSwapBuffers(); state.DidSwapBuffersComplete(); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); // 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, - 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()); + EXPECT_COMMIT_STATE(SchedulerStateMachine::COMMIT_STATE_IDLE); - // Become visible and start a new frame. - state.SetVisible(true); - state.OnBeginImplFrame(CreateBeginFrameArgsForTesting()); - EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_ANIMATE); + state.OnBeginImplFrame(); 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()); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); // 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); + EXPECT_COMMIT_STATE(SchedulerStateMachine::COMMIT_STATE_IDLE); + state.OnBeginImplFrame(); + EXPECT_ACTION(SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME); } TEST(SchedulerStateMachineTest, TestFirstContextCreation) { @@ -1169,14 +1233,14 @@ TEST(SchedulerStateMachineTest, TestFirstContextCreation) { EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); // Check that the first init does not SetNeedsCommit. - state.OnBeginImplFrame(CreateBeginFrameArgsForTesting()); + state.OnBeginImplFrame(); 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(CreateBeginFrameArgsForTesting()); + state.OnBeginImplFrame(); EXPECT_ACTION_UPDATE_STATE( SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME); } @@ -1184,19 +1248,13 @@ TEST(SchedulerStateMachineTest, TestFirstContextCreation) { TEST(SchedulerStateMachineTest, TestContextLostWhenCompletelyIdle) { SchedulerSettings default_scheduler_settings; StateMachine state(default_scheduler_settings); - state.SetCanStart(); - state.UpdateState(state.NextAction()); - state.CreateAndInitializeOutputSurfaceWithActivatedCommit(); - - state.SetVisible(true); - state.SetCanDraw(true); + SET_UP_STATE(state) EXPECT_NE(SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION, state.NextAction()); state.DidLoseOutputSurface(); - EXPECT_EQ(SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION, - state.NextAction()); + EXPECT_ACTION(SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION); state.UpdateState(state.NextAction()); // Once context recreation begins, nothing should happen. @@ -1206,7 +1264,7 @@ TEST(SchedulerStateMachineTest, TestContextLostWhenCompletelyIdle) { state.CreateAndInitializeOutputSurfaceWithActivatedCommit(); // When the context is recreated, we should begin a commit. - state.OnBeginImplFrame(CreateBeginFrameArgsForTesting()); + state.OnBeginImplFrame(); EXPECT_ACTION_UPDATE_STATE( SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME); } @@ -1214,84 +1272,140 @@ TEST(SchedulerStateMachineTest, TestContextLostWhenCompletelyIdle) { TEST(SchedulerStateMachineTest, TestContextLostWhenIdleAndCommitRequestedWhileRecreating) { SchedulerSettings default_scheduler_settings; + // We use impl side painting because it's the more complicated version. + default_scheduler_settings.impl_side_painting = true; StateMachine state(default_scheduler_settings); - state.SetCanStart(); - state.UpdateState(state.NextAction()); - state.CreateAndInitializeOutputSurfaceWithActivatedCommit(); - state.SetVisible(true); - state.SetCanDraw(true); + SET_UP_STATE(state) EXPECT_NE(SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION, state.NextAction()); state.DidLoseOutputSurface(); + EXPECT_EQ(state.output_surface_state(), + SchedulerStateMachine::OUTPUT_SURFACE_LOST); EXPECT_ACTION_UPDATE_STATE( SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); // Once context recreation begins, nothing should happen. - state.OnBeginImplFrame(CreateBeginFrameArgsForTesting()); + state.OnBeginImplFrame(); 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(CreateBeginFrameArgsForTesting()); + state.OnBeginImplFrame(); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); state.OnBeginImplFrameDeadline(); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); // Recreate the context state.DidCreateAndInitializeOutputSurface(); + EXPECT_EQ(state.output_surface_state(), + SchedulerStateMachine::OUTPUT_SURFACE_WAITING_FOR_FIRST_COMMIT); EXPECT_FALSE(state.RedrawPending()); - // When the context is recreated, we should begin a commit + // When the context is recreated, we wait until the next BeginImplFrame + // before starting. + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); + + // When the BeginFrame comes in we should begin a commit + state.OnBeginImplFrame(); EXPECT_ACTION_UPDATE_STATE( SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); - EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_BEGIN_MAIN_FRAME_SENT, - state.CommitState()); + EXPECT_COMMIT_STATE( + SchedulerStateMachine::COMMIT_STATE_BEGIN_MAIN_FRAME_SENT); + + // Until that commit finishes, we shouldn't be drawing or animate. + state.OnBeginImplFrameDeadline(); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); + // Finish the commit, which should make the surface active. state.NotifyBeginMainFrameStarted(); state.NotifyReadyToCommit(); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_COMMIT); + EXPECT_EQ(state.output_surface_state(), + SchedulerStateMachine::OUTPUT_SURFACE_WAITING_FOR_FIRST_ACTIVATION); + state.NotifyReadyToActivate(); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_ACTIVATE_SYNC_TREE); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); + EXPECT_EQ(state.output_surface_state(), + SchedulerStateMachine::OUTPUT_SURFACE_ACTIVE); + // Finishing the first commit after initializing an output surface should // automatically cause a redraw. EXPECT_TRUE(state.RedrawPending()); + state.OnBeginImplFrame(); + 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); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); + EXPECT_FALSE(state.RedrawPending()); + + // Next frame as no work to do. + state.OnBeginImplFrame(); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); + state.OnBeginImplFrameDeadline(); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); // Once the context is recreated, whether we draw should be based on - // SetCanDraw. - state.OnBeginImplFrame(CreateBeginFrameArgsForTesting()); + // SetCanDraw if waiting on first draw after activate. + state.SetNeedsRedraw(true); + state.OnBeginImplFrame(); 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, - state.NextAction()); + EXPECT_ACTION(SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE); state.SetCanDraw(false); - EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_AND_SWAP_ABORT, - state.NextAction()); + EXPECT_ACTION(SchedulerStateMachine::ACTION_NONE); state.SetCanDraw(true); - EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE, - state.NextAction()); + EXPECT_ACTION_UPDATE_STATE( + SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); + + // Once the context is recreated, whether we draw should be based on + // SetCanDraw if waiting on first draw after activate. + state.SetNeedsRedraw(true); + state.SetNeedsCommit(); + state.OnBeginImplFrame(); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_ANIMATE); + EXPECT_ACTION_UPDATE_STATE( + SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); + // Activate so we need the first draw + state.NotifyBeginMainFrameStarted(); + state.NotifyReadyToCommit(); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_COMMIT); + state.NotifyReadyToActivate(); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_ACTIVATE_SYNC_TREE); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); + EXPECT_TRUE(state.active_tree_needs_first_draw()); + EXPECT_TRUE(state.needs_redraw()); + + state.OnBeginImplFrameDeadline(); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_ANIMATE); + EXPECT_ACTION(SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE); + state.SetCanDraw(false); + EXPECT_ACTION(SchedulerStateMachine::ACTION_DRAW_AND_SWAP_ABORT); + state.SetCanDraw(true); + EXPECT_ACTION(SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE); } TEST(SchedulerStateMachineTest, TestContextLostWhileCommitInProgress) { SchedulerSettings scheduler_settings; StateMachine state(scheduler_settings); - state.SetCanStart(); - state.UpdateState(state.NextAction()); - state.CreateAndInitializeOutputSurfaceWithActivatedCommit(); - state.SetVisible(true); - state.SetCanDraw(true); + SET_UP_STATE(state) // Get a commit in flight. state.SetNeedsCommit(); // Set damage and expect a draw. state.SetNeedsRedraw(true); - state.OnBeginImplFrame(CreateBeginFrameArgsForTesting()); + state.OnBeginImplFrame(); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_ANIMATE); EXPECT_ACTION_UPDATE_STATE( SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME); @@ -1308,7 +1422,7 @@ TEST(SchedulerStateMachineTest, TestContextLostWhileCommitInProgress) { // Ask for another draw. Expect nothing happens. state.SetNeedsRedraw(true); - EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction()); + EXPECT_ACTION(SchedulerStateMachine::ACTION_NONE); // Finish the frame, and commit. state.NotifyBeginMainFrameStarted(); @@ -1321,36 +1435,30 @@ TEST(SchedulerStateMachineTest, TestContextLostWhileCommitInProgress) { EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_DRAW_AND_SWAP_ABORT); // 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()); + EXPECT_IMPL_FRAME_STATE(SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_IDLE); + EXPECT_ACTION(SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION); - state.OnBeginImplFrame(CreateBeginFrameArgsForTesting()); - EXPECT_EQ(SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_BEGIN_FRAME_STARTING, - state.begin_impl_frame_state()); - EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction()); + state.OnBeginImplFrame(); + EXPECT_IMPL_FRAME_STATE( + SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_BEGIN_FRAME_STARTING); + EXPECT_ACTION(SchedulerStateMachine::ACTION_NONE); state.OnBeginImplFrameDeadlinePending(); - EXPECT_EQ(SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_INSIDE_BEGIN_FRAME, - state.begin_impl_frame_state()); - EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction()); + EXPECT_IMPL_FRAME_STATE( + SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_INSIDE_BEGIN_FRAME); + EXPECT_ACTION(SchedulerStateMachine::ACTION_NONE); state.OnBeginImplFrameDeadline(); - EXPECT_EQ(SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_INSIDE_DEADLINE, - state.begin_impl_frame_state()); - EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction()); + EXPECT_IMPL_FRAME_STATE( + SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_INSIDE_DEADLINE); + EXPECT_ACTION(SchedulerStateMachine::ACTION_NONE); } TEST(SchedulerStateMachineTest, TestContextLostWhileCommitInProgressAndAnotherCommitRequested) { SchedulerSettings scheduler_settings; StateMachine state(scheduler_settings); - state.SetCanStart(); - state.UpdateState(state.NextAction()); - state.CreateAndInitializeOutputSurfaceWithActivatedCommit(); - state.SetVisible(true); - state.SetCanDraw(true); + SET_UP_STATE(state) // Get a commit in flight. state.SetNeedsCommit(); @@ -1358,7 +1466,7 @@ TEST(SchedulerStateMachineTest, // Set damage and expect a draw. state.SetNeedsRedraw(true); - state.OnBeginImplFrame(CreateBeginFrameArgsForTesting()); + state.OnBeginImplFrame(); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_ANIMATE); EXPECT_ACTION_UPDATE_STATE( SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME); @@ -1388,25 +1496,23 @@ TEST(SchedulerStateMachineTest, EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_DRAW_AND_SWAP_ABORT); // 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()); + EXPECT_IMPL_FRAME_STATE(SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_IDLE); + EXPECT_ACTION(SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION); - state.OnBeginImplFrame(CreateBeginFrameArgsForTesting()); - EXPECT_EQ(SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_BEGIN_FRAME_STARTING, - state.begin_impl_frame_state()); - EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction()); + state.OnBeginImplFrame(); + EXPECT_IMPL_FRAME_STATE( + SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_BEGIN_FRAME_STARTING); + EXPECT_ACTION(SchedulerStateMachine::ACTION_NONE); state.OnBeginImplFrameDeadlinePending(); - EXPECT_EQ(SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_INSIDE_BEGIN_FRAME, - state.begin_impl_frame_state()); - EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction()); + EXPECT_IMPL_FRAME_STATE( + SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_INSIDE_BEGIN_FRAME); + EXPECT_ACTION(SchedulerStateMachine::ACTION_NONE); state.OnBeginImplFrameDeadline(); - EXPECT_EQ(SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_INSIDE_DEADLINE, - state.begin_impl_frame_state()); - EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction()); + EXPECT_IMPL_FRAME_STATE( + SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_INSIDE_DEADLINE); + EXPECT_ACTION(SchedulerStateMachine::ACTION_NONE); state.OnBeginImplFrameIdle(); EXPECT_ACTION_UPDATE_STATE( @@ -1414,7 +1520,7 @@ TEST(SchedulerStateMachineTest, // After we get a new output surface, the commit flow should start. state.CreateAndInitializeOutputSurfaceWithActivatedCommit(); - state.OnBeginImplFrame(CreateBeginFrameArgsForTesting()); + state.OnBeginImplFrame(); EXPECT_ACTION_UPDATE_STATE( SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); @@ -1434,25 +1540,19 @@ TEST(SchedulerStateMachineTest, TEST(SchedulerStateMachineTest, DontDrawBeforeCommitAfterLostOutputSurface) { SchedulerSettings default_scheduler_settings; StateMachine state(default_scheduler_settings); - state.SetCanStart(); - state.UpdateState(state.NextAction()); - state.CreateAndInitializeOutputSurfaceWithActivatedCommit(); - state.SetVisible(true); - state.SetCanDraw(true); + SET_UP_STATE(state) state.SetNeedsRedraw(true); // Cause a lost output surface, and restore it. state.DidLoseOutputSurface(); - EXPECT_EQ(SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION, - state.NextAction()); + EXPECT_ACTION(SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION); state.UpdateState(state.NextAction()); state.DidCreateAndInitializeOutputSurface(); EXPECT_FALSE(state.RedrawPending()); - state.OnBeginImplFrame(CreateBeginFrameArgsForTesting()); - EXPECT_EQ(SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME, - state.NextAction()); + state.OnBeginImplFrame(); + EXPECT_ACTION(SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME); } TEST(SchedulerStateMachineTest, @@ -1460,11 +1560,7 @@ TEST(SchedulerStateMachineTest, SchedulerSettings settings; settings.impl_side_painting = true; StateMachine state(settings); - state.SetCanStart(); - state.UpdateState(state.NextAction()); - state.CreateAndInitializeOutputSurfaceWithActivatedCommit(); - state.SetVisible(true); - state.SetCanDraw(true); + SET_UP_STATE(state) state.SetCommitState( SchedulerStateMachine::COMMIT_STATE_BEGIN_MAIN_FRAME_SENT); @@ -1483,6 +1579,25 @@ TEST(SchedulerStateMachineTest, EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_DRAW_AND_SWAP_ABORT); } +TEST(SchedulerStateMachineTest, TestNoBeginFrameNeededWhenInvisible) { + SchedulerSettings default_scheduler_settings; + StateMachine state(default_scheduler_settings); + state.SetCanStart(); + state.UpdateState(state.NextAction()); + state.CreateAndInitializeOutputSurfaceWithActivatedCommit(); + state.SetVisible(true); + + EXPECT_FALSE(state.BeginFrameNeeded()); + state.SetNeedsRedraw(true); + EXPECT_TRUE(state.BeginFrameNeeded()); + + state.SetVisible(false); + EXPECT_FALSE(state.BeginFrameNeeded()); + + state.SetVisible(true); + EXPECT_TRUE(state.BeginFrameNeeded()); +} + TEST(SchedulerStateMachineTest, TestNoBeginMainFrameWhenInvisible) { SchedulerSettings default_scheduler_settings; StateMachine state(default_scheduler_settings); @@ -1491,7 +1606,15 @@ TEST(SchedulerStateMachineTest, TestNoBeginMainFrameWhenInvisible) { state.CreateAndInitializeOutputSurfaceWithActivatedCommit(); state.SetVisible(false); state.SetNeedsCommit(); - EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction()); + EXPECT_ACTION(SchedulerStateMachine::ACTION_NONE); + EXPECT_FALSE(state.BeginFrameNeeded()); + + // When become visible again, the needs commit should still be pending. + state.SetVisible(true); + EXPECT_TRUE(state.BeginFrameNeeded()); + state.OnBeginImplFrame(); + EXPECT_ACTION_UPDATE_STATE( + SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME); } TEST(SchedulerStateMachineTest, TestFinishCommitWhenCommitInProgress) { @@ -1507,7 +1630,7 @@ TEST(SchedulerStateMachineTest, TestFinishCommitWhenCommitInProgress) { state.NotifyBeginMainFrameStarted(); state.NotifyReadyToCommit(); - EXPECT_EQ(SchedulerStateMachine::ACTION_COMMIT, state.NextAction()); + EXPECT_ACTION(SchedulerStateMachine::ACTION_COMMIT); state.UpdateState(state.NextAction()); EXPECT_TRUE(state.active_tree_needs_first_draw()); @@ -1517,11 +1640,7 @@ TEST(SchedulerStateMachineTest, TestFinishCommitWhenCommitInProgress) { TEST(SchedulerStateMachineTest, TestInitialActionsWhenContextLost) { SchedulerSettings default_scheduler_settings; StateMachine state(default_scheduler_settings); - state.SetCanStart(); - state.UpdateState(state.NextAction()); - state.CreateAndInitializeOutputSurfaceWithActivatedCommit(); - state.SetVisible(true); - state.SetCanDraw(true); + SET_UP_STATE(state) state.SetNeedsCommit(); state.DidLoseOutputSurface(); @@ -1538,19 +1657,13 @@ TEST(SchedulerStateMachineTest, TestInitialActionsWhenContextLost) { // lost the output surface and are trying to get the first commit, since the // main thread will just abort anyway. state.SetVisible(false); - EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction()) - << state.AsValue()->ToString(); + EXPECT_ACTION(SchedulerStateMachine::ACTION_NONE); } TEST(SchedulerStateMachineTest, ReportIfNotDrawing) { SchedulerSettings default_scheduler_settings; StateMachine state(default_scheduler_settings); - state.SetCanStart(); - state.UpdateState(state.NextAction()); - state.CreateAndInitializeOutputSurfaceWithActivatedCommit(); - - state.SetCanDraw(true); - state.SetVisible(true); + SET_UP_STATE(state) EXPECT_FALSE(state.PendingDrawsShouldBeAborted()); state.SetCanDraw(false); @@ -1570,19 +1683,16 @@ TEST(SchedulerStateMachineTest, ReportIfNotDrawing) { EXPECT_FALSE(state.PendingDrawsShouldBeAborted()); } -TEST(SchedulerStateMachineTest, TestTriggerDeadlineEarlyAfterAbortedCommit) { +TEST(SchedulerStateMachineTest, + TestTriggerDeadlineImmediatelyAfterAbortedCommit) { SchedulerSettings settings; settings.impl_side_painting = true; StateMachine state(settings); - state.SetCanStart(); - state.UpdateState(state.NextAction()); - state.CreateAndInitializeOutputSurfaceWithActivatedCommit(); - state.SetVisible(true); - state.SetCanDraw(true); + SET_UP_STATE(state) // 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()); + state.OnBeginImplFrame(); // As a response the compositor requests a redraw and a commit to tell the // main thread about the new scroll offset. @@ -1597,11 +1707,11 @@ TEST(SchedulerStateMachineTest, TestTriggerDeadlineEarlyAfterAbortedCommit) { // Since only the scroll offset changed, the main thread will abort the // commit. - state.BeginMainFrameAborted(true); + state.BeginMainFrameAborted(CommitEarlyOutReason::FINISHED_NO_UPDATES); // Since the commit was aborted, we should draw right away instead of waiting // for the deadline. - EXPECT_TRUE(state.ShouldTriggerBeginImplFrameDeadlineEarly()); + EXPECT_TRUE(state.ShouldTriggerBeginImplFrameDeadlineImmediately()); } void FinishPreviousCommitAndDrawWithoutExitingDeadline( @@ -1617,11 +1727,11 @@ void FinishPreviousCommitAndDrawWithoutExitingDeadline( EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_ACTIVATE_SYNC_TREE); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); - state.OnBeginImplFrame(CreateBeginFrameArgsForTesting()); + state.OnBeginImplFrame(); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_ANIMATE); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); - EXPECT_TRUE(state.ShouldTriggerBeginImplFrameDeadlineEarly()); + EXPECT_TRUE(state.ShouldTriggerBeginImplFrameDeadlineImmediately()); state.OnBeginImplFrameDeadline(); EXPECT_ACTION_UPDATE_STATE( SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE); @@ -1632,17 +1742,13 @@ TEST(SchedulerStateMachineTest, TestImplLatencyTakesPriority) { SchedulerSettings settings; settings.impl_side_painting = true; StateMachine state(settings); - state.SetCanStart(); - state.UpdateState(state.NextAction()); - state.CreateAndInitializeOutputSurfaceWithActivatedCommit(); - state.SetVisible(true); - state.SetCanDraw(true); + SET_UP_STATE(state) // This test ensures that impl-draws are prioritized over main thread updates // in prefer impl latency mode. state.SetNeedsRedraw(true); state.SetNeedsCommit(); - state.OnBeginImplFrame(CreateBeginFrameArgsForTesting()); + state.OnBeginImplFrame(); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_ANIMATE); EXPECT_ACTION_UPDATE_STATE( SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME); @@ -1650,9 +1756,9 @@ TEST(SchedulerStateMachineTest, TestImplLatencyTakesPriority) { // Verify the deadline is not triggered early until we enter // prefer impl latency mode. - EXPECT_FALSE(state.ShouldTriggerBeginImplFrameDeadlineEarly()); + EXPECT_FALSE(state.ShouldTriggerBeginImplFrameDeadlineImmediately()); state.SetImplLatencyTakesPriority(true); - EXPECT_TRUE(state.ShouldTriggerBeginImplFrameDeadlineEarly()); + EXPECT_TRUE(state.ShouldTriggerBeginImplFrameDeadlineImmediately()); // Trigger the deadline. state.OnBeginImplFrameDeadline(); @@ -1679,52 +1785,64 @@ TEST(SchedulerStateMachineTest, TestImplLatencyTakesPriority) { // and did not just swap. state.SetNeedsCommit(); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); - state.OnBeginImplFrame(CreateBeginFrameArgsForTesting()); + state.OnBeginImplFrame(); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); - EXPECT_FALSE(state.ShouldTriggerBeginImplFrameDeadlineEarly()); + EXPECT_FALSE(state.ShouldTriggerBeginImplFrameDeadlineImmediately()); state.OnBeginImplFrameDeadline(); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); } -TEST(SchedulerStateMachineTest, TestTriggerDeadlineEarlyOnLostOutputSurface) { +TEST(SchedulerStateMachineTest, + TestTriggerDeadlineImmediatelyOnLostOutputSurface) { SchedulerSettings default_scheduler_settings; StateMachine state(default_scheduler_settings); - state.SetCanStart(); - state.UpdateState(state.NextAction()); - state.CreateAndInitializeOutputSurfaceWithActivatedCommit(); - state.SetVisible(true); - state.SetCanDraw(true); + SET_UP_STATE(state) state.SetNeedsCommit(); - state.OnBeginImplFrame(CreateBeginFrameArgsForTesting()); + state.OnBeginImplFrame(); EXPECT_ACTION_UPDATE_STATE( SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); - EXPECT_FALSE(state.ShouldTriggerBeginImplFrameDeadlineEarly()); + EXPECT_FALSE(state.ShouldTriggerBeginImplFrameDeadlineImmediately()); state.DidLoseOutputSurface(); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); // The deadline should be triggered immediately when output surface is lost. - EXPECT_TRUE(state.ShouldTriggerBeginImplFrameDeadlineEarly()); + EXPECT_TRUE(state.ShouldTriggerBeginImplFrameDeadlineImmediately()); +} + +TEST(SchedulerStateMachineTest, TestTriggerDeadlineImmediatelyWhenInvisible) { + SchedulerSettings settings; + settings.impl_side_painting = true; + StateMachine state(settings); + SET_UP_STATE(state) + + state.SetNeedsCommit(); + + state.OnBeginImplFrame(); + EXPECT_ACTION_UPDATE_STATE( + SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); + EXPECT_FALSE(state.ShouldTriggerBeginImplFrameDeadlineImmediately()); + + state.SetVisible(false); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); + EXPECT_TRUE(state.ShouldTriggerBeginImplFrameDeadlineImmediately()); } TEST(SchedulerStateMachineTest, TestSetNeedsAnimate) { SchedulerSettings settings; settings.impl_side_painting = true; StateMachine state(settings); - state.SetCanStart(); - state.UpdateState(state.NextAction()); - state.CreateAndInitializeOutputSurfaceWithActivatedCommit(); - state.SetVisible(true); - state.SetCanDraw(true); + SET_UP_STATE(state) // 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.OnBeginImplFrame(CreateBeginFrameArgsForTesting()); + state.OnBeginImplFrame(); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_ANIMATE); state.OnBeginImplFrameDeadlinePending(); @@ -1737,11 +1855,7 @@ TEST(SchedulerStateMachineTest, TestAnimateBeforeCommit) { SchedulerSettings settings; settings.impl_side_painting = true; StateMachine state(settings); - state.SetCanStart(); - state.UpdateState(state.NextAction()); - state.CreateAndInitializeOutputSurfaceWithActivatedCommit(); - state.SetVisible(true); - state.SetCanDraw(true); + SET_UP_STATE(state) // Check that animations are updated before we start a commit. state.SetNeedsAnimate(); @@ -1750,7 +1864,7 @@ TEST(SchedulerStateMachineTest, TestAnimateBeforeCommit) { EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); EXPECT_TRUE(state.BeginFrameNeeded()); - state.OnBeginImplFrame(CreateBeginFrameArgsForTesting()); + state.OnBeginImplFrame(); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_ANIMATE); EXPECT_ACTION_UPDATE_STATE( SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME); @@ -1765,11 +1879,7 @@ TEST(SchedulerStateMachineTest, TestAnimateAfterCommitBeforeDraw) { SchedulerSettings settings; settings.impl_side_painting = true; StateMachine state(settings); - state.SetCanStart(); - state.UpdateState(state.NextAction()); - state.CreateAndInitializeOutputSurfaceWithActivatedCommit(); - state.SetVisible(true); - state.SetCanDraw(true); + SET_UP_STATE(state) // Check that animations are updated before we start a commit. state.SetNeedsAnimate(); @@ -1778,7 +1888,7 @@ TEST(SchedulerStateMachineTest, TestAnimateAfterCommitBeforeDraw) { EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); EXPECT_TRUE(state.BeginFrameNeeded()); - state.OnBeginImplFrame(CreateBeginFrameArgsForTesting()); + state.OnBeginImplFrame(); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_ANIMATE); EXPECT_ACTION_UPDATE_STATE( SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME); @@ -1798,11 +1908,7 @@ TEST(SchedulerStateMachineTest, TestSetNeedsAnimateAfterAnimate) { SchedulerSettings settings; settings.impl_side_painting = true; StateMachine state(settings); - state.SetCanStart(); - state.UpdateState(state.NextAction()); - state.CreateAndInitializeOutputSurfaceWithActivatedCommit(); - state.SetVisible(true); - state.SetCanDraw(true); + SET_UP_STATE(state) // Test requesting an animation after we have already animated during this // frame. @@ -1810,7 +1916,7 @@ TEST(SchedulerStateMachineTest, TestSetNeedsAnimateAfterAnimate) { EXPECT_TRUE(state.BeginFrameNeeded()); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); - state.OnBeginImplFrame(CreateBeginFrameArgsForTesting()); + state.OnBeginImplFrame(); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_ANIMATE); state.SetNeedsAnimate(); @@ -1821,5 +1927,51 @@ TEST(SchedulerStateMachineTest, TestSetNeedsAnimateAfterAnimate) { SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE); } +TEST(SchedulerStateMachineTest, TestForwardBeginFramesToChildren) { + SchedulerSettings settings; + StateMachine state(settings); + SET_UP_STATE(state) + + EXPECT_FALSE(state.BeginFrameNeeded()); + state.SetChildrenNeedBeginFrames(true); + EXPECT_TRUE(state.BeginFrameNeeded()); +} + +TEST(SchedulerStateMachineTest, TestDeferCommit) { + SchedulerSettings settings; + StateMachine state(settings); + SET_UP_STATE(state) + + state.SetDeferCommits(true); + + state.SetNeedsCommit(); + EXPECT_FALSE(state.BeginFrameNeeded()); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); + + state.OnBeginImplFrame(); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); + + state.OnBeginImplFrameDeadline(); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); + + state.SetDeferCommits(false); + state.OnBeginImplFrame(); + EXPECT_ACTION_UPDATE_STATE( + SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME); +} + +TEST(SchedulerStateMachineTest, EarlyOutCommitWantsProactiveBeginFrame) { + SchedulerSettings settings; + StateMachine state(settings); + SET_UP_STATE(state); + + EXPECT_FALSE(state.ProactiveBeginFrameWanted()); + bool commit_has_no_updates = true; + state.UpdateStateOnCommit(commit_has_no_updates); + EXPECT_TRUE(state.ProactiveBeginFrameWanted()); + state.OnBeginImplFrame(); + EXPECT_FALSE(state.ProactiveBeginFrameWanted()); +} + } // namespace } // namespace cc diff --git a/chromium/cc/scheduler/scheduler_unittest.cc b/chromium/cc/scheduler/scheduler_unittest.cc index 7da62676c6c..d2b12d768a5 100644 --- a/chromium/cc/scheduler/scheduler_unittest.cc +++ b/chromium/cc/scheduler/scheduler_unittest.cc @@ -1,35 +1,34 @@ // Copyright 2011 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. + #include "cc/scheduler/scheduler.h" #include <string> #include <vector> -#include "base/debug/trace_event.h" #include "base/logging.h" #include "base/memory/scoped_vector.h" #include "base/message_loop/message_loop.h" -#include "base/power_monitor/power_monitor.h" -#include "base/power_monitor/power_monitor_source.h" #include "base/run_loop.h" #include "base/time/time.h" +#include "base/trace_event/trace_event.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) \ - do { \ - 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(i); \ +#define EXPECT_ACTION(action, client, action_index, expected_num_actions) \ + do { \ + EXPECT_EQ(expected_num_actions, client->num_actions_()); \ + if (action_index >= 0) { \ + ASSERT_LT(action_index, client->num_actions_()) << scheduler_.get(); \ + 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(i); \ } while (false) #define EXPECT_NO_ACTION(client) EXPECT_ACTION("", client, -1, 0) @@ -37,64 +36,20 @@ #define EXPECT_SINGLE_ACTION(action, client) \ EXPECT_ACTION(action, client, 0, 1) +#define EXPECT_SCOPED(statements) \ + { \ + SCOPED_TRACE(""); \ + statements; \ + } + namespace cc { namespace { -class FakeSchedulerClient; - -void InitializeOutputSurfaceAndFirstCommit(Scheduler* scheduler, - FakeSchedulerClient* client); - class FakeSchedulerClient : public SchedulerClient { public: - struct FakeBeginFrameSourceForFakeSchedulerClient - : public FakeBeginFrameSource { - FakeSchedulerClient* client_; - - explicit FakeBeginFrameSourceForFakeSchedulerClient( - FakeSchedulerClient* client) - : client_(client) {} - - void OnNeedsBeginFramesChange(bool needs_begin_frames) override { - if (needs_begin_frames) { - client_->actions_.push_back("SetNeedsBeginFrames(true)"); - } else { - client_->actions_.push_back("SetNeedsBeginFrames(false)"); - } - client_->states_.push_back(client_->scheduler_->AsValue()); - } - }; - - class FakePowerMonitorSource : public base::PowerMonitorSource { - public: - FakePowerMonitorSource() {} - ~FakePowerMonitorSource() override {} - void GeneratePowerStateEvent(bool on_battery_power) { - on_battery_power_impl_ = on_battery_power; - ProcessPowerEvent(POWER_STATE_EVENT); - base::MessageLoop::current()->RunUntilIdle(); - } - bool IsOnBatteryPowerImpl() override { return on_battery_power_impl_; } - - private: - bool on_battery_power_impl_; - }; - FakeSchedulerClient() : automatic_swap_ack_(true), - swap_contains_incomplete_tile_(false), - redraw_will_happen_if_update_visible_tiles_happens_(false), - now_src_(TestNowSource::Create()), - task_runner_(new OrderedSimpleTaskRunner(now_src_, true)), - fake_frame_source_(this), - fake_power_monitor_source_(new FakePowerMonitorSource), - power_monitor_(make_scoped_ptr<base::PowerMonitorSource>( - fake_power_monitor_source_)), scheduler_(nullptr) { - // A bunch of tests require Now() to be > BeginFrameArgs::DefaultInterval() - now_src_->AdvanceNow(base::TimeDelta::FromMilliseconds(100)); - // Fail if we need to run 100 tasks in a row. - task_runner_->SetRunTaskLimit(100); Reset(); } @@ -105,21 +60,19 @@ class FakeSchedulerClient : public SchedulerClient { swap_will_happen_if_draw_happens_ = true; num_draws_ = 0; log_anticipated_draw_time_change_ = false; + begin_frame_args_sent_to_children_ = BeginFrameArgs(); } - TestScheduler* CreateScheduler(const SchedulerSettings& settings) { - scheduler_ = TestScheduler::Create( - now_src_, this, settings, 0, task_runner_, &power_monitor_); - DCHECK(scheduler_); - return scheduler_.get(); - } + void set_scheduler(TestScheduler* scheduler) { scheduler_ = scheduler; } // Most tests don't care about DidAnticipatedDrawTimeChange, so only record it // for tests that do. void set_log_anticipated_draw_time_change(bool log) { log_anticipated_draw_time_change_ = log; } - bool needs_begin_frames() { return fake_frame_source_.NeedsBeginFrames(); } + bool needs_begin_frames() { + return scheduler_->frame_source().NeedsBeginFrames(); + } 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]; } @@ -128,40 +81,6 @@ class FakeSchedulerClient : public SchedulerClient { return posted_begin_impl_frame_deadline_; } - bool ExternalBeginFrame() { - return scheduler_->settings().begin_frame_scheduling_enabled && - scheduler_->settings().throttle_frame_production; - } - FakeBeginFrameSource* ExternalBeginFrameSource() override { - return &fake_frame_source_; - } - - base::PowerMonitor* PowerMonitor() { return &power_monitor_; } - - FakePowerMonitorSource* PowerMonitorSource() { - return fake_power_monitor_source_; - } - - void AdvanceFrame() { - TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug.scheduler.frames"), - "FakeSchedulerClient::AdvanceFrame"); - // EXPECT_TRUE(needs_begin_frames()); - if (ExternalBeginFrame()) { - // Creep the time forward so that any BeginFrameArgs is not equal to the - // last one otherwise we violate the BeginFrameSource contract. - now_src_->AdvanceNowMicroseconds(1); - fake_frame_source_.TestOnBeginFrame( - CreateBeginFrameArgsForTesting(now_src_)); - EXPECT_TRUE(scheduler_->BeginImplFrameDeadlinePending()); - } - - EXPECT_TRUE(task_runner().RunTasksWhile(ImplFrameDeadlinePending(false))); - EXPECT_TRUE(scheduler_->BeginImplFrameDeadlinePending()); - } - - OrderedSimpleTaskRunner& task_runner() { return *task_runner_; } - TestNowSource* now_src() { return now_src_.get(); } - int ActionIndex(const char* action) const { for (size_t i = 0; i < actions_.size(); i++) if (!strcmp(actions_[i], action)) @@ -169,10 +88,6 @@ 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; } @@ -186,25 +101,20 @@ class FakeSchedulerClient : public SchedulerClient { 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. void WillBeginImplFrame(const BeginFrameArgs& args) override { - actions_.push_back("WillBeginImplFrame"); - states_.push_back(scheduler_->AsValue()); + PushAction("WillBeginImplFrame"); } + void DidFinishImplFrame() override {} + void ScheduledActionSendBeginMainFrame() override { - actions_.push_back("ScheduledActionSendBeginMainFrame"); - states_.push_back(scheduler_->AsValue()); + PushAction("ScheduledActionSendBeginMainFrame"); } void ScheduledActionAnimate() override { - actions_.push_back("ScheduledActionAnimate"); - states_.push_back(scheduler_->AsValue()); + PushAction("ScheduledActionAnimate"); } DrawResult ScheduledActionDrawAndSwapIfPossible() override { - actions_.push_back("ScheduledActionDrawAndSwapIfPossible"); - states_.push_back(scheduler_->AsValue()); + PushAction("ScheduledActionDrawAndSwapIfPossible"); num_draws_++; DrawResult result = draw_will_happen_ ? DRAW_SUCCESS : DRAW_ABORTED_CHECKERBOARD_ANIMATIONS; @@ -212,12 +122,6 @@ class FakeSchedulerClient : public SchedulerClient { 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(); @@ -225,35 +129,26 @@ class FakeSchedulerClient : public SchedulerClient { return result; } DrawResult ScheduledActionDrawAndSwapForced() override { - actions_.push_back("ScheduledActionDrawAndSwapForced"); - states_.push_back(scheduler_->AsValue()); + PushAction("ScheduledActionDrawAndSwapForced"); return DRAW_SUCCESS; } - void ScheduledActionCommit() override { - actions_.push_back("ScheduledActionCommit"); - states_.push_back(scheduler_->AsValue()); - } - void ScheduledActionUpdateVisibleTiles() override { - actions_.push_back("ScheduledActionUpdateVisibleTiles"); - states_.push_back(scheduler_->AsValue()); - if (redraw_will_happen_if_update_visible_tiles_happens_) - scheduler_->SetNeedsRedraw(); - } + void ScheduledActionCommit() override { PushAction("ScheduledActionCommit"); } void ScheduledActionActivateSyncTree() override { - actions_.push_back("ScheduledActionActivateSyncTree"); - states_.push_back(scheduler_->AsValue()); + PushAction("ScheduledActionActivateSyncTree"); } void ScheduledActionBeginOutputSurfaceCreation() override { - actions_.push_back("ScheduledActionBeginOutputSurfaceCreation"); - states_.push_back(scheduler_->AsValue()); + PushAction("ScheduledActionBeginOutputSurfaceCreation"); + } + void ScheduledActionPrepareTiles() override { + PushAction("ScheduledActionPrepareTiles"); } - void ScheduledActionManageTiles() override { - actions_.push_back("ScheduledActionManageTiles"); + void ScheduledActionInvalidateOutputSurface() override { + actions_.push_back("ScheduledActionInvalidateOutputSurface"); states_.push_back(scheduler_->AsValue()); } void DidAnticipatedDrawTimeChange(base::TimeTicks) override { if (log_anticipated_draw_time_change_) - actions_.push_back("DidAnticipatedDrawTimeChange"); + PushAction("DidAnticipatedDrawTimeChange"); } base::TimeDelta DrawDurationEstimate() override { return base::TimeDelta(); } base::TimeDelta BeginMainFrameToCommitDurationEstimate() override { @@ -263,7 +158,13 @@ class FakeSchedulerClient : public SchedulerClient { return base::TimeDelta(); } - void DidBeginImplFrameDeadline() override {} + void SendBeginFramesToChildren(const BeginFrameArgs& args) override { + begin_frame_args_sent_to_children_ = args; + } + + void SendBeginMainFrameNotExpectedSoon() override { + PushAction("SendBeginMainFrameNotExpectedSoon"); + } base::Callback<bool(void)> ImplFrameDeadlinePending(bool state) { return base::Bind(&FakeSchedulerClient::ImplFrameDeadlinePendingCallback, @@ -271,6 +172,19 @@ class FakeSchedulerClient : public SchedulerClient { state); } + bool begin_frame_is_sent_to_children() const { + return begin_frame_args_sent_to_children_.IsValid(); + } + + const BeginFrameArgs& begin_frame_args_sent_to_children() const { + return begin_frame_args_sent_to_children_; + } + + void PushAction(const char* description) { + actions_.push_back(description); + states_.push_back(scheduler_->AsValue()); + } + protected: bool ImplFrameDeadlinePendingCallback(bool state) { return scheduler_->BeginImplFrameDeadlinePending() == state; @@ -281,217 +195,548 @@ class FakeSchedulerClient : public SchedulerClient { 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_; + BeginFrameArgs begin_frame_args_sent_to_children_; base::TimeTicks posted_begin_impl_frame_deadline_; std::vector<const char*> actions_; - std::vector<scoped_refptr<base::debug::ConvertableToTraceFormat>> states_; + std::vector<scoped_refptr<base::trace_event::ConvertableToTraceFormat>> + states_; + TestScheduler* scheduler_; +}; + +class SchedulerClientWithFixedEstimates : public FakeSchedulerClient { + public: + SchedulerClientWithFixedEstimates( + base::TimeDelta draw_duration, + base::TimeDelta begin_main_frame_to_commit_duration, + base::TimeDelta commit_to_activate_duration) + : draw_duration_(draw_duration), + begin_main_frame_to_commit_duration_( + begin_main_frame_to_commit_duration), + commit_to_activate_duration_(commit_to_activate_duration) {} + + base::TimeDelta DrawDurationEstimate() override { return draw_duration_; } + base::TimeDelta BeginMainFrameToCommitDurationEstimate() override { + return begin_main_frame_to_commit_duration_; + } + base::TimeDelta CommitToActivateDurationEstimate() override { + return commit_to_activate_duration_; + } + + private: + base::TimeDelta draw_duration_; + base::TimeDelta begin_main_frame_to_commit_duration_; + base::TimeDelta commit_to_activate_duration_; +}; + +class FakeExternalBeginFrameSource : public BeginFrameSourceMixIn { + public: + explicit FakeExternalBeginFrameSource(FakeSchedulerClient* client) + : client_(client) {} + ~FakeExternalBeginFrameSource() override {} + + void OnNeedsBeginFramesChange(bool needs_begin_frames) override { + if (needs_begin_frames) { + client_->PushAction("SetNeedsBeginFrames(true)"); + } else { + client_->PushAction("SetNeedsBeginFrames(false)"); + } + } + + void TestOnBeginFrame(const BeginFrameArgs& args) { + return CallOnBeginFrame(args); + } + + private: + FakeSchedulerClient* client_; +}; + +class SchedulerTest : public testing::Test { + public: + SchedulerTest() + : now_src_(TestNowSource::Create()), + task_runner_(new OrderedSimpleTaskRunner(now_src_, true)), + fake_external_begin_frame_source_(nullptr) { + // A bunch of tests require Now() to be > BeginFrameArgs::DefaultInterval() + now_src_->AdvanceNow(base::TimeDelta::FromMilliseconds(100)); + // Fail if we need to run 100 tasks in a row. + task_runner_->SetRunTaskLimit(100); + } + + ~SchedulerTest() override {} + + protected: + TestScheduler* CreateScheduler() { + scoped_ptr<FakeExternalBeginFrameSource> fake_external_begin_frame_source; + if (scheduler_settings_.use_external_begin_frame_source) { + fake_external_begin_frame_source.reset( + new FakeExternalBeginFrameSource(client_.get())); + fake_external_begin_frame_source_ = + fake_external_begin_frame_source.get(); + } + scheduler_ = TestScheduler::Create(now_src_, client_.get(), + scheduler_settings_, 0, task_runner_, + fake_external_begin_frame_source.Pass()); + DCHECK(scheduler_); + client_->set_scheduler(scheduler_.get()); + return scheduler_.get(); + } + + void CreateSchedulerAndInitSurface() { + CreateScheduler(); + EXPECT_SCOPED(InitializeOutputSurfaceAndFirstCommit()); + } + + void SetUpScheduler(bool initSurface) { + SetUpScheduler(make_scoped_ptr(new FakeSchedulerClient), initSurface); + } + + void SetUpScheduler(scoped_ptr<FakeSchedulerClient> client, + bool initSurface) { + client_ = client.Pass(); + if (initSurface) + CreateSchedulerAndInitSurface(); + else + CreateScheduler(); + } + + OrderedSimpleTaskRunner& task_runner() { return *task_runner_; } + TestNowSource* now_src() { return now_src_.get(); } + + // As this function contains EXPECT macros, to allow debugging it should be + // called inside EXPECT_SCOPED like so; + // EXPECT_SCOPED(client.InitializeOutputSurfaceAndFirstCommit(scheduler)); + void InitializeOutputSurfaceAndFirstCommit() { + TRACE_EVENT0("cc", + "SchedulerUnitTest::InitializeOutputSurfaceAndFirstCommit"); + DCHECK(scheduler_); + + // Check the client doesn't have any actions queued when calling this + // function. + EXPECT_NO_ACTION(client_); + EXPECT_FALSE(client_->needs_begin_frames()); + + // Start the initial output surface creation. + EXPECT_FALSE(scheduler_->CanStart()); + scheduler_->SetCanStart(); + scheduler_->SetVisible(true); + scheduler_->SetCanDraw(true); + EXPECT_SINGLE_ACTION("ScheduledActionBeginOutputSurfaceCreation", client_); + + client_->Reset(); + + // We don't see anything happening until the first impl frame. + scheduler_->DidCreateAndInitializeOutputSurface(); + scheduler_->SetNeedsCommit(); + EXPECT_TRUE(client_->needs_begin_frames()); + EXPECT_FALSE(scheduler_->BeginImplFrameDeadlinePending()); + client_->Reset(); + + { + SCOPED_TRACE("Do first frame to commit after initialize."); + AdvanceFrame(); + + scheduler_->NotifyBeginMainFrameStarted(); + scheduler_->NotifyReadyToCommitThenActivateIfNeeded(); + + EXPECT_FALSE(scheduler_->CommitPending()); + + if (scheduler_settings_.using_synchronous_renderer_compositor) { + scheduler_->SetNeedsRedraw(); + scheduler_->OnDrawForOutputSurface(); + } else { + // Run the posted deadline task. + EXPECT_TRUE(scheduler_->BeginImplFrameDeadlinePending()); + task_runner_->RunTasksWhile(client_->ImplFrameDeadlinePending(true)); + } + + EXPECT_FALSE(scheduler_->BeginImplFrameDeadlinePending()); + } + + client_->Reset(); + + { + SCOPED_TRACE( + "Run second frame so Scheduler calls SetNeedsBeginFrame(false)."); + AdvanceFrame(); + + if (!scheduler_settings_.using_synchronous_renderer_compositor) { + // Run the posted deadline task. + EXPECT_TRUE(scheduler_->BeginImplFrameDeadlinePending()); + task_runner_->RunTasksWhile(client_->ImplFrameDeadlinePending(true)); + } + + EXPECT_FALSE(scheduler_->BeginImplFrameDeadlinePending()); + } + + EXPECT_FALSE(client_->needs_begin_frames()); + client_->Reset(); + } + + // As this function contains EXPECT macros, to allow debugging it should be + // called inside EXPECT_SCOPED like so; + // EXPECT_SCOPED(client.AdvanceFrame()); + void AdvanceFrame() { + TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug.scheduler.frames"), + "FakeSchedulerClient::AdvanceFrame"); + // Consume any previous deadline first, if no deadline is currently + // pending, ImplFrameDeadlinePending will return false straight away and we + // will run no tasks. + task_runner_->RunTasksWhile(client_->ImplFrameDeadlinePending(true)); + EXPECT_FALSE(scheduler_->BeginImplFrameDeadlinePending()); + + // Send the next BeginFrame message if using an external source, otherwise + // it will be already in the task queue. + if (scheduler_->settings().use_external_begin_frame_source && + scheduler_->FrameProductionThrottled()) { + EXPECT_TRUE(client_->needs_begin_frames()); + SendNextBeginFrame(); + } + + if (!scheduler_->settings().using_synchronous_renderer_compositor) { + // Then run tasks until new deadline is scheduled. + EXPECT_TRUE(task_runner_->RunTasksWhile( + client_->ImplFrameDeadlinePending(false))); + EXPECT_TRUE(scheduler_->BeginImplFrameDeadlinePending()); + } + } + + void SendNextBeginFrame() { + DCHECK(scheduler_->settings().use_external_begin_frame_source); + // Creep the time forward so that any BeginFrameArgs is not equal to the + // last one otherwise we violate the BeginFrameSource contract. + now_src_->AdvanceNow(BeginFrameArgs::DefaultInterval()); + fake_external_begin_frame_source_->TestOnBeginFrame( + CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, now_src())); + } + + FakeExternalBeginFrameSource* fake_external_begin_frame_source() const { + return fake_external_begin_frame_source_; + } + + void MainFrameInHighLatencyMode( + int64 begin_main_frame_to_commit_estimate_in_ms, + int64 commit_to_activate_estimate_in_ms, + bool impl_latency_takes_priority, + bool should_send_begin_main_frame); + void BeginFramesNotFromClient(bool use_external_begin_frame_source, + bool throttle_frame_production); + void BeginFramesNotFromClient_SwapThrottled( + bool use_external_begin_frame_source, + bool throttle_frame_production); + void DidLoseOutputSurfaceAfterBeginFrameStartedWithHighLatency( + bool impl_side_painting); + void DidLoseOutputSurfaceAfterReadyToCommit(bool impl_side_painting); + scoped_refptr<TestNowSource> now_src_; scoped_refptr<OrderedSimpleTaskRunner> task_runner_; - FakeBeginFrameSourceForFakeSchedulerClient fake_frame_source_; - FakePowerMonitorSource* fake_power_monitor_source_; - base::PowerMonitor power_monitor_; + FakeExternalBeginFrameSource* fake_external_begin_frame_source_; + SchedulerSettings scheduler_settings_; + scoped_ptr<FakeSchedulerClient> client_; scoped_ptr<TestScheduler> scheduler_; }; -void InitializeOutputSurfaceAndFirstCommit(Scheduler* scheduler, - FakeSchedulerClient* client) { - TRACE_EVENT0("cc", - "SchedulerUnitTest::InitializeOutputSurfaceAndFirstCommit"); - - scheduler->DidCreateAndInitializeOutputSurface(); - scheduler->SetNeedsCommit(); - scheduler->NotifyBeginMainFrameStarted(); - scheduler->NotifyReadyToCommit(); - if (scheduler->settings().impl_side_painting) - scheduler->NotifyReadyToActivate(); - - { - SCOPED_TRACE("Go through the motions to draw the commit"); - client->AdvanceFrame(); - } +TEST_F(SchedulerTest, InitializeOutputSurfaceDoesNotBeginImplFrame) { + scheduler_settings_.use_external_begin_frame_source = true; + SetUpScheduler(false); + scheduler_->SetCanStart(); + scheduler_->SetVisible(true); + scheduler_->SetCanDraw(true); + + EXPECT_SINGLE_ACTION("ScheduledActionBeginOutputSurfaceCreation", client_); + client_->Reset(); + scheduler_->DidCreateAndInitializeOutputSurface(); + EXPECT_NO_ACTION(client_); +} - // Run the posted deadline task. - EXPECT_TRUE(scheduler->BeginImplFrameDeadlinePending()); - client->task_runner().RunTasksWhile(client->ImplFrameDeadlinePending(true)); - EXPECT_FALSE(scheduler->BeginImplFrameDeadlinePending()); +TEST_F(SchedulerTest, SendBeginFramesToChildren) { + scheduler_settings_.use_external_begin_frame_source = true; + SetUpScheduler(true); - { - SCOPED_TRACE( - "We need another BeginImplFrame so Scheduler calls " - "SetNeedsBeginFrame(false)."); - client->AdvanceFrame(); - } + EXPECT_FALSE(client_->begin_frame_is_sent_to_children()); + scheduler_->SetNeedsCommit(); + EXPECT_SINGLE_ACTION("SetNeedsBeginFrames(true)", client_); + EXPECT_TRUE(client_->needs_begin_frames()); + + scheduler_->SetChildrenNeedBeginFrames(true); + + client_->Reset(); + EXPECT_SCOPED(AdvanceFrame()); + EXPECT_TRUE(client_->begin_frame_is_sent_to_children()); + EXPECT_TRUE(scheduler_->BeginImplFrameDeadlinePending()); + EXPECT_ACTION("WillBeginImplFrame", client_, 0, 2); + EXPECT_ACTION("ScheduledActionSendBeginMainFrame", client_, 1, 2); + EXPECT_TRUE(client_->needs_begin_frames()); +} - // Run the posted deadline task. - EXPECT_TRUE(scheduler->BeginImplFrameDeadlinePending()); - client->task_runner().RunTasksWhile(client->ImplFrameDeadlinePending(true)); - EXPECT_FALSE(scheduler->BeginImplFrameDeadlinePending()); +TEST_F(SchedulerTest, SendBeginFramesToChildrenWithoutCommit) { + scheduler_settings_.use_external_begin_frame_source = true; + SetUpScheduler(true); - // EXPECT_FALSE(client->needs_begin_frames()); + EXPECT_FALSE(client_->needs_begin_frames()); + scheduler_->SetChildrenNeedBeginFrames(true); + EXPECT_SINGLE_ACTION("SetNeedsBeginFrames(true)", client_); + EXPECT_TRUE(client_->needs_begin_frames()); + + client_->Reset(); + EXPECT_SCOPED(AdvanceFrame()); + EXPECT_TRUE(client_->begin_frame_is_sent_to_children()); } -TEST(SchedulerTest, InitializeOutputSurfaceDoesNotBeginImplFrame) { - FakeSchedulerClient client; - SchedulerSettings default_scheduler_settings; - TestScheduler* scheduler = client.CreateScheduler(default_scheduler_settings); - scheduler->SetCanStart(); - scheduler->SetVisible(true); - scheduler->SetCanDraw(true); - - EXPECT_SINGLE_ACTION("ScheduledActionBeginOutputSurfaceCreation", client); - client.Reset(); - scheduler->DidCreateAndInitializeOutputSurface(); - EXPECT_NO_ACTION(client); +TEST_F(SchedulerTest, SendBeginFramesToChildrenDeadlineNotAdjusted) { + // Set up client with specified estimates. + SchedulerClientWithFixedEstimates* client = + new SchedulerClientWithFixedEstimates( + base::TimeDelta::FromMilliseconds(1), + base::TimeDelta::FromMilliseconds(2), + base::TimeDelta::FromMilliseconds(4)); + scheduler_settings_.use_external_begin_frame_source = true; + SetUpScheduler(make_scoped_ptr(client).Pass(), true); + + EXPECT_FALSE(client_->needs_begin_frames()); + scheduler_->SetChildrenNeedBeginFrames(true); + EXPECT_SINGLE_ACTION("SetNeedsBeginFrames(true)", client_); + EXPECT_TRUE(client_->needs_begin_frames()); + + client_->Reset(); + + BeginFrameArgs frame_args = + CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, now_src()); + fake_external_begin_frame_source()->TestOnBeginFrame(frame_args); + + EXPECT_TRUE(client_->begin_frame_is_sent_to_children()); + EXPECT_EQ(client_->begin_frame_args_sent_to_children().deadline, + frame_args.deadline); } -TEST(SchedulerTest, RequestCommit) { - FakeSchedulerClient client; - SchedulerSettings scheduler_settings; - TestScheduler* scheduler = client.CreateScheduler(scheduler_settings); - scheduler->SetCanStart(); - scheduler->SetVisible(true); - scheduler->SetCanDraw(true); +TEST_F(SchedulerTest, VideoNeedsBeginFrames) { + scheduler_settings_.use_external_begin_frame_source = true; + SetUpScheduler(true); + + scheduler_->SetVideoNeedsBeginFrames(true); + EXPECT_SINGLE_ACTION("SetNeedsBeginFrames(true)", client_); + EXPECT_TRUE(client_->needs_begin_frames()); + + client_->Reset(); + EXPECT_SCOPED(AdvanceFrame()); + EXPECT_TRUE(scheduler_->BeginImplFrameDeadlinePending()); + // WillBeginImplFrame is responsible for sending BeginFrames to video. + EXPECT_SINGLE_ACTION("WillBeginImplFrame", client_); + + client_->Reset(); + EXPECT_SCOPED(AdvanceFrame()); + EXPECT_TRUE(scheduler_->BeginImplFrameDeadlinePending()); + EXPECT_SINGLE_ACTION("WillBeginImplFrame", client_); + + client_->Reset(); + scheduler_->SetVideoNeedsBeginFrames(false); + EXPECT_NO_ACTION(client_); + + client_->Reset(); + task_runner_->RunTasksWhile(client_->ImplFrameDeadlinePending(true)); + EXPECT_FALSE(scheduler_->BeginImplFrameDeadlinePending()); + EXPECT_ACTION("SetNeedsBeginFrames(false)", client_, 0, 2); + EXPECT_ACTION("SendBeginMainFrameNotExpectedSoon", client_, 1, 2); + EXPECT_FALSE(client_->needs_begin_frames()); +} - EXPECT_SINGLE_ACTION("ScheduledActionBeginOutputSurfaceCreation", client); - InitializeOutputSurfaceAndFirstCommit(scheduler, &client); +TEST_F(SchedulerTest, RequestCommit) { + scheduler_settings_.use_external_begin_frame_source = true; + SetUpScheduler(true); // SetNeedsCommit should begin the frame on the next BeginImplFrame. - client.Reset(); - scheduler->SetNeedsCommit(); - EXPECT_SINGLE_ACTION("SetNeedsBeginFrames(true)", client); - client.Reset(); + scheduler_->SetNeedsCommit(); + EXPECT_SINGLE_ACTION("SetNeedsBeginFrames(true)", client_); + client_->Reset(); - client.AdvanceFrame(); - EXPECT_ACTION("WillBeginImplFrame", client, 0, 2); - EXPECT_ACTION("ScheduledActionSendBeginMainFrame", client, 1, 2); - EXPECT_TRUE(scheduler->BeginImplFrameDeadlinePending()); - EXPECT_TRUE(client.needs_begin_frames()); - client.Reset(); + EXPECT_SCOPED(AdvanceFrame()); + EXPECT_ACTION("WillBeginImplFrame", client_, 0, 2); + EXPECT_ACTION("ScheduledActionSendBeginMainFrame", client_, 1, 2); + EXPECT_TRUE(scheduler_->BeginImplFrameDeadlinePending()); + EXPECT_TRUE(client_->needs_begin_frames()); + 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_TRUE(client.needs_begin_frames()); - client.Reset(); + task_runner().RunPendingTasks(); // Run posted deadline. + EXPECT_NO_ACTION(client_); + EXPECT_FALSE(scheduler_->BeginImplFrameDeadlinePending()); + EXPECT_TRUE(client_->needs_begin_frames()); + client_->Reset(); // NotifyReadyToCommit should trigger the commit. - scheduler->NotifyBeginMainFrameStarted(); - scheduler->NotifyReadyToCommit(); - EXPECT_SINGLE_ACTION("ScheduledActionCommit", client); - EXPECT_TRUE(client.needs_begin_frames()); - client.Reset(); + scheduler_->NotifyBeginMainFrameStarted(); + scheduler_->NotifyReadyToCommit(); + EXPECT_SINGLE_ACTION("ScheduledActionCommit", client_); + EXPECT_TRUE(client_->needs_begin_frames()); + client_->Reset(); // BeginImplFrame should prepare the draw. - client.AdvanceFrame(); - EXPECT_ACTION("WillBeginImplFrame", client, 0, 2); - EXPECT_ACTION("ScheduledActionAnimate", client, 1, 2); - EXPECT_TRUE(scheduler->BeginImplFrameDeadlinePending()); - EXPECT_TRUE(client.needs_begin_frames()); - client.Reset(); + EXPECT_SCOPED(AdvanceFrame()); + EXPECT_ACTION("WillBeginImplFrame", client_, 0, 2); + EXPECT_ACTION("ScheduledActionAnimate", client_, 1, 2); + EXPECT_TRUE(scheduler_->BeginImplFrameDeadlinePending()); + EXPECT_TRUE(client_->needs_begin_frames()); + 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_frames()); - client.Reset(); + task_runner().RunPendingTasks(); // Run posted deadline. + EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client_, 0, 1); + EXPECT_FALSE(scheduler_->BeginImplFrameDeadlinePending()); + EXPECT_TRUE(client_->needs_begin_frames()); + client_->Reset(); // The following BeginImplFrame deadline should SetNeedsBeginFrame(false) // to avoid excessive toggles. - client.AdvanceFrame(); - EXPECT_SINGLE_ACTION("WillBeginImplFrame", client); - EXPECT_TRUE(scheduler->BeginImplFrameDeadlinePending()); - client.Reset(); + EXPECT_SCOPED(AdvanceFrame()); + EXPECT_SINGLE_ACTION("WillBeginImplFrame", client_); + EXPECT_TRUE(scheduler_->BeginImplFrameDeadlinePending()); + client_->Reset(); + + task_runner().RunPendingTasks(); // Run posted deadline. + EXPECT_ACTION("SetNeedsBeginFrames(false)", client_, 0, 2); + EXPECT_ACTION("SendBeginMainFrameNotExpectedSoon", client_, 1, 2); + client_->Reset(); +} + +TEST_F(SchedulerTest, RequestCommitAfterSetDeferCommit) { + scheduler_settings_.use_external_begin_frame_source = true; + SetUpScheduler(true); + + scheduler_->SetDeferCommits(true); + + scheduler_->SetNeedsCommit(); + EXPECT_NO_ACTION(client_); + + client_->Reset(); + task_runner().RunPendingTasks(); + // There are no pending tasks or actions. + EXPECT_NO_ACTION(client_); + EXPECT_FALSE(client_->needs_begin_frames()); - client.task_runner().RunPendingTasks(); // Run posted deadline. - EXPECT_SINGLE_ACTION("SetNeedsBeginFrames(false)", client); - client.Reset(); + client_->Reset(); + scheduler_->SetDeferCommits(false); + EXPECT_SINGLE_ACTION("SetNeedsBeginFrames(true)", client_); + + // Start new BeginMainFrame after defer commit is off. + client_->Reset(); + EXPECT_SCOPED(AdvanceFrame()); + EXPECT_ACTION("WillBeginImplFrame", client_, 0, 2); + EXPECT_ACTION("ScheduledActionSendBeginMainFrame", client_, 1, 2); + EXPECT_TRUE(scheduler_->BeginImplFrameDeadlinePending()); } -TEST(SchedulerTest, RequestCommitAfterBeginMainFrameSent) { - FakeSchedulerClient client; - SchedulerSettings scheduler_settings; - TestScheduler* scheduler = client.CreateScheduler(scheduler_settings); - scheduler->SetCanStart(); - scheduler->SetVisible(true); - scheduler->SetCanDraw(true); +TEST_F(SchedulerTest, DeferCommitWithRedraw) { + scheduler_settings_.use_external_begin_frame_source = true; + SetUpScheduler(true); + + scheduler_->SetDeferCommits(true); + + scheduler_->SetNeedsCommit(); + EXPECT_NO_ACTION(client_); + + // The SetNeedsRedraw will override the SetDeferCommits(true), to allow a + // begin frame to be needed. + client_->Reset(); + scheduler_->SetNeedsRedraw(); + EXPECT_SINGLE_ACTION("SetNeedsBeginFrames(true)", client_); + + client_->Reset(); + AdvanceFrame(); + // BeginMainFrame is not sent during the defer commit is on. + EXPECT_ACTION("WillBeginImplFrame", client_, 0, 2); + EXPECT_ACTION("ScheduledActionAnimate", client_, 1, 2); - EXPECT_SINGLE_ACTION("ScheduledActionBeginOutputSurfaceCreation", client); - InitializeOutputSurfaceAndFirstCommit(scheduler, &client); - client.Reset(); + client_->Reset(); + task_runner().RunPendingTasks(); // Run posted deadline. + EXPECT_SINGLE_ACTION("ScheduledActionDrawAndSwapIfPossible", client_); + EXPECT_FALSE(scheduler_->BeginImplFrameDeadlinePending()); + EXPECT_TRUE(client_->needs_begin_frames()); + + client_->Reset(); + AdvanceFrame(); + EXPECT_SINGLE_ACTION("WillBeginImplFrame", client_); +} + +TEST_F(SchedulerTest, RequestCommitAfterBeginMainFrameSent) { + scheduler_settings_.use_external_begin_frame_source = true; + SetUpScheduler(true); // SetNeedsCommit should begin the frame. - scheduler->SetNeedsCommit(); - EXPECT_SINGLE_ACTION("SetNeedsBeginFrames(true)", client); + scheduler_->SetNeedsCommit(); + EXPECT_SINGLE_ACTION("SetNeedsBeginFrames(true)", client_); - client.Reset(); - client.AdvanceFrame(); - EXPECT_ACTION("WillBeginImplFrame", client, 0, 2); - EXPECT_ACTION("ScheduledActionSendBeginMainFrame", client, 1, 2); - EXPECT_TRUE(scheduler->BeginImplFrameDeadlinePending()); + client_->Reset(); + EXPECT_SCOPED(AdvanceFrame()); + EXPECT_ACTION("WillBeginImplFrame", client_, 0, 2); + EXPECT_ACTION("ScheduledActionSendBeginMainFrame", client_, 1, 2); + EXPECT_TRUE(scheduler_->BeginImplFrameDeadlinePending()); - EXPECT_TRUE(client.needs_begin_frames()); - client.Reset(); + EXPECT_TRUE(client_->needs_begin_frames()); + client_->Reset(); // Now SetNeedsCommit again. Calling here means we need a second commit. - scheduler->SetNeedsCommit(); - EXPECT_EQ(client.num_actions_(), 0); - client.Reset(); + scheduler_->SetNeedsCommit(); + EXPECT_EQ(client_->num_actions_(), 0); + client_->Reset(); // Finish the first commit. - 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()); + scheduler_->NotifyBeginMainFrameStarted(); + scheduler_->NotifyReadyToCommit(); + EXPECT_SINGLE_ACTION("ScheduledActionCommit", client_); + EXPECT_TRUE(scheduler_->BeginImplFrameDeadlinePending()); + client_->Reset(); + 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_frames()); - client.Reset(); + EXPECT_TRUE(client_->needs_begin_frames()); + client_->Reset(); // Since another commit is needed, the next BeginImplFrame should initiate // the second commit. - client.AdvanceFrame(); - EXPECT_ACTION("WillBeginImplFrame", client, 0, 2); - EXPECT_ACTION("ScheduledActionSendBeginMainFrame", client, 1, 2); - EXPECT_TRUE(scheduler->BeginImplFrameDeadlinePending()); - client.Reset(); + EXPECT_SCOPED(AdvanceFrame()); + 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->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()); - EXPECT_TRUE(client.needs_begin_frames()); - client.Reset(); + scheduler_->NotifyBeginMainFrameStarted(); + scheduler_->NotifyReadyToCommit(); + EXPECT_SINGLE_ACTION("ScheduledActionCommit", client_); + EXPECT_TRUE(scheduler_->BeginImplFrameDeadlinePending()); + client_->Reset(); + 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_frames()); + client_->Reset(); // On the next BeginImplFrame, verify we go back to a quiescent state and // no longer request BeginImplFrames. - client.AdvanceFrame(); - client.task_runner().RunPendingTasks(); // Run posted deadline. - EXPECT_FALSE(client.needs_begin_frames()); - client.Reset(); + EXPECT_SCOPED(AdvanceFrame()); + task_runner().RunPendingTasks(); // Run posted deadline. + EXPECT_FALSE(client_->needs_begin_frames()); + client_->Reset(); } class SchedulerClientThatsetNeedsDrawInsideDraw : public FakeSchedulerClient { public: + SchedulerClientThatsetNeedsDrawInsideDraw() + : FakeSchedulerClient(), request_redraws_(false) {} + void ScheduledActionSendBeginMainFrame() override {} + + void SetRequestRedrawsInsideDraw(bool enable) { request_redraws_ = enable; } + DrawResult ScheduledActionDrawAndSwapIfPossible() override { // Only SetNeedsRedraw the first time this is called - if (!num_draws_) + if (request_redraws_) { scheduler_->SetNeedsRedraw(); + } return FakeSchedulerClient::ScheduledActionDrawAndSwapIfPossible(); } @@ -501,95 +746,95 @@ class SchedulerClientThatsetNeedsDrawInsideDraw : public FakeSchedulerClient { } void ScheduledActionCommit() override {} - void ScheduledActionBeginOutputSurfaceCreation() override {} void DidAnticipatedDrawTimeChange(base::TimeTicks) override {} + + private: + bool request_redraws_; }; // Tests for two different situations: // 1. the scheduler dropping SetNeedsRedraw requests that happen inside // a ScheduledActionDrawAndSwap // 2. the scheduler drawing twice inside a single tick -TEST(SchedulerTest, RequestRedrawInsideDraw) { - SchedulerClientThatsetNeedsDrawInsideDraw client; - SchedulerSettings default_scheduler_settings; - TestScheduler* scheduler = client.CreateScheduler(default_scheduler_settings); - scheduler->SetCanStart(); - scheduler->SetVisible(true); - scheduler->SetCanDraw(true); - InitializeOutputSurfaceAndFirstCommit(scheduler, &client); - client.Reset(); - - scheduler->SetNeedsRedraw(); - EXPECT_TRUE(scheduler->RedrawPending()); - EXPECT_TRUE(client.needs_begin_frames()); - EXPECT_EQ(0, client.num_draws()); - - client.AdvanceFrame(); - client.task_runner().RunPendingTasks(); // Run posted deadline. - EXPECT_EQ(1, client.num_draws()); - EXPECT_TRUE(scheduler->RedrawPending()); - EXPECT_TRUE(client.needs_begin_frames()); - - client.AdvanceFrame(); - client.task_runner().RunPendingTasks(); // Run posted deadline. - EXPECT_EQ(2, client.num_draws()); - EXPECT_FALSE(scheduler->RedrawPending()); - EXPECT_TRUE(client.needs_begin_frames()); +TEST_F(SchedulerTest, RequestRedrawInsideDraw) { + SchedulerClientThatsetNeedsDrawInsideDraw* client = + new SchedulerClientThatsetNeedsDrawInsideDraw; + scheduler_settings_.use_external_begin_frame_source = true; + SetUpScheduler(make_scoped_ptr(client).Pass(), true); + client->SetRequestRedrawsInsideDraw(true); + + scheduler_->SetNeedsRedraw(); + EXPECT_TRUE(scheduler_->RedrawPending()); + EXPECT_TRUE(client->needs_begin_frames()); + EXPECT_EQ(0, client->num_draws()); + + EXPECT_SCOPED(AdvanceFrame()); + task_runner().RunPendingTasks(); // Run posted deadline. + EXPECT_EQ(1, client->num_draws()); + EXPECT_TRUE(scheduler_->RedrawPending()); + EXPECT_TRUE(client->needs_begin_frames()); + + client->SetRequestRedrawsInsideDraw(false); + + EXPECT_SCOPED(AdvanceFrame()); + task_runner().RunPendingTasks(); // Run posted deadline. + EXPECT_EQ(2, client_->num_draws()); + EXPECT_FALSE(scheduler_->RedrawPending()); + EXPECT_TRUE(client->needs_begin_frames()); // We stop requesting BeginImplFrames after a BeginImplFrame where we don't // swap. - client.AdvanceFrame(); - client.task_runner().RunPendingTasks(); // Run posted deadline. - EXPECT_EQ(2, client.num_draws()); - EXPECT_FALSE(scheduler->RedrawPending()); - EXPECT_FALSE(client.needs_begin_frames()); + EXPECT_SCOPED(AdvanceFrame()); + task_runner().RunPendingTasks(); // Run posted deadline. + EXPECT_EQ(2, client->num_draws()); + EXPECT_FALSE(scheduler_->RedrawPending()); + EXPECT_FALSE(client->needs_begin_frames()); } // Test that requesting redraw inside a failed draw doesn't lose the request. -TEST(SchedulerTest, RequestRedrawInsideFailedDraw) { - SchedulerClientThatsetNeedsDrawInsideDraw 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(); - - client.SetDrawWillHappen(false); - - scheduler->SetNeedsRedraw(); - EXPECT_TRUE(scheduler->RedrawPending()); - EXPECT_TRUE(client.needs_begin_frames()); - EXPECT_EQ(0, client.num_draws()); +TEST_F(SchedulerTest, RequestRedrawInsideFailedDraw) { + SchedulerClientThatsetNeedsDrawInsideDraw* client = + new SchedulerClientThatsetNeedsDrawInsideDraw; + scheduler_settings_.use_external_begin_frame_source = true; + SetUpScheduler(make_scoped_ptr(client).Pass(), true); + + client->SetRequestRedrawsInsideDraw(true); + client->SetDrawWillHappen(false); + + scheduler_->SetNeedsRedraw(); + EXPECT_TRUE(scheduler_->RedrawPending()); + EXPECT_TRUE(client->needs_begin_frames()); + EXPECT_EQ(0, client->num_draws()); // Fail the draw. - client.AdvanceFrame(); - client.task_runner().RunPendingTasks(); // Run posted deadline. - EXPECT_EQ(1, client.num_draws()); + EXPECT_SCOPED(AdvanceFrame()); + 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_frames()); + EXPECT_TRUE(scheduler_->CommitPending()); + EXPECT_TRUE(scheduler_->RedrawPending()); + EXPECT_TRUE(client->needs_begin_frames()); + + client->SetRequestRedrawsInsideDraw(false); // Fail the draw again. - client.AdvanceFrame(); - 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_frames()); + EXPECT_SCOPED(AdvanceFrame()); + 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_frames()); // Draw successfully. - client.SetDrawWillHappen(true); - client.AdvanceFrame(); - 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_frames()); + client->SetDrawWillHappen(true); + EXPECT_SCOPED(AdvanceFrame()); + 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_frames()); } class SchedulerClientThatSetNeedsCommitInsideDraw : public FakeSchedulerClient { @@ -613,7 +858,6 @@ class SchedulerClientThatSetNeedsCommitInsideDraw : public FakeSchedulerClient { } void ScheduledActionCommit() override {} - void ScheduledActionBeginOutputSurfaceCreation() override {} void DidAnticipatedDrawTimeChange(base::TimeTicks) override {} void SetNeedsCommitOnNextDraw() { set_needs_commit_on_next_draw_ = true; } @@ -624,1521 +868,1844 @@ class SchedulerClientThatSetNeedsCommitInsideDraw : public FakeSchedulerClient { // Tests for the scheduler infinite-looping on SetNeedsCommit requests that // happen inside a ScheduledActionDrawAndSwap -TEST(SchedulerTest, RequestCommitInsideDraw) { - SchedulerClientThatSetNeedsCommitInsideDraw 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(); - - EXPECT_FALSE(client.needs_begin_frames()); - scheduler->SetNeedsRedraw(); - EXPECT_TRUE(scheduler->RedrawPending()); - EXPECT_EQ(0, client.num_draws()); - EXPECT_TRUE(client.needs_begin_frames()); - - client.SetNeedsCommitOnNextDraw(); - client.AdvanceFrame(); - client.SetNeedsCommitOnNextDraw(); - client.task_runner().RunPendingTasks(); // Run posted deadline. - EXPECT_EQ(1, client.num_draws()); - EXPECT_TRUE(scheduler->CommitPending()); - EXPECT_TRUE(client.needs_begin_frames()); - scheduler->NotifyBeginMainFrameStarted(); - scheduler->NotifyReadyToCommit(); - - client.AdvanceFrame(); - 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_frames()); +TEST_F(SchedulerTest, RequestCommitInsideDraw) { + SchedulerClientThatSetNeedsCommitInsideDraw* client = + new SchedulerClientThatSetNeedsCommitInsideDraw; + + scheduler_settings_.use_external_begin_frame_source = true; + SetUpScheduler(make_scoped_ptr(client).Pass(), true); + + EXPECT_FALSE(client->needs_begin_frames()); + scheduler_->SetNeedsRedraw(); + EXPECT_TRUE(scheduler_->RedrawPending()); + EXPECT_EQ(0, client->num_draws()); + EXPECT_TRUE(client->needs_begin_frames()); + + client->SetNeedsCommitOnNextDraw(); + EXPECT_SCOPED(AdvanceFrame()); + client->SetNeedsCommitOnNextDraw(); + task_runner().RunPendingTasks(); // Run posted deadline. + EXPECT_EQ(1, client->num_draws()); + EXPECT_TRUE(scheduler_->CommitPending()); + EXPECT_TRUE(client->needs_begin_frames()); + scheduler_->NotifyBeginMainFrameStarted(); + scheduler_->NotifyReadyToCommit(); + + EXPECT_SCOPED(AdvanceFrame()); + 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_frames()); // We stop requesting BeginImplFrames after a BeginImplFrame where we don't // swap. - client.AdvanceFrame(); - 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_frames()); + EXPECT_SCOPED(AdvanceFrame()); + 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_frames()); } // Tests that when a draw fails then the pending commit should not be dropped. -TEST(SchedulerTest, RequestCommitInsideFailedDraw) { - SchedulerClientThatsetNeedsDrawInsideDraw 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(); - - client.SetDrawWillHappen(false); - - scheduler->SetNeedsRedraw(); - EXPECT_TRUE(scheduler->RedrawPending()); - EXPECT_TRUE(client.needs_begin_frames()); - EXPECT_EQ(0, client.num_draws()); +TEST_F(SchedulerTest, RequestCommitInsideFailedDraw) { + SchedulerClientThatsetNeedsDrawInsideDraw* client = + new SchedulerClientThatsetNeedsDrawInsideDraw; + scheduler_settings_.use_external_begin_frame_source = true; + SetUpScheduler(make_scoped_ptr(client).Pass(), true); + + client->SetDrawWillHappen(false); + + scheduler_->SetNeedsRedraw(); + EXPECT_TRUE(scheduler_->RedrawPending()); + EXPECT_TRUE(client->needs_begin_frames()); + EXPECT_EQ(0, client->num_draws()); // Fail the draw. - client.AdvanceFrame(); - client.task_runner().RunPendingTasks(); // Run posted deadline. - EXPECT_EQ(1, client.num_draws()); + EXPECT_SCOPED(AdvanceFrame()); + 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_frames()); + EXPECT_TRUE(scheduler_->CommitPending()); + EXPECT_TRUE(scheduler_->RedrawPending()); + EXPECT_TRUE(client->needs_begin_frames()); // Fail the draw again. - client.AdvanceFrame(); + EXPECT_SCOPED(AdvanceFrame()); - 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_frames()); + 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_frames()); // Draw successfully. - client.SetDrawWillHappen(true); - client.AdvanceFrame(); - 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_frames()); + client->SetDrawWillHappen(true); + EXPECT_SCOPED(AdvanceFrame()); + 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_frames()); } -TEST(SchedulerTest, NoSwapWhenDrawFails) { - SchedulerClientThatSetNeedsCommitInsideDraw 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(); - EXPECT_TRUE(scheduler->RedrawPending()); - EXPECT_TRUE(client.needs_begin_frames()); - EXPECT_EQ(0, client.num_draws()); +TEST_F(SchedulerTest, NoSwapWhenDrawFails) { + SchedulerClientThatSetNeedsCommitInsideDraw* client = + new SchedulerClientThatSetNeedsCommitInsideDraw; + scheduler_settings_.use_external_begin_frame_source = true; + SetUpScheduler(make_scoped_ptr(client).Pass(), true); + + scheduler_->SetNeedsRedraw(); + EXPECT_TRUE(scheduler_->RedrawPending()); + EXPECT_TRUE(client->needs_begin_frames()); + EXPECT_EQ(0, client->num_draws()); // Draw successfully, this starts a new frame. - client.SetNeedsCommitOnNextDraw(); - client.AdvanceFrame(); - client.task_runner().RunPendingTasks(); // Run posted deadline. - EXPECT_EQ(1, client.num_draws()); + client->SetNeedsCommitOnNextDraw(); + EXPECT_SCOPED(AdvanceFrame()); + task_runner().RunPendingTasks(); // Run posted deadline. + EXPECT_EQ(1, client->num_draws()); - scheduler->SetNeedsRedraw(); - EXPECT_TRUE(scheduler->RedrawPending()); - EXPECT_TRUE(client.needs_begin_frames()); + scheduler_->SetNeedsRedraw(); + EXPECT_TRUE(scheduler_->RedrawPending()); + EXPECT_TRUE(client->needs_begin_frames()); // Fail to draw, this should not start a frame. - client.SetDrawWillHappen(false); - client.SetNeedsCommitOnNextDraw(); - client.AdvanceFrame(); - client.task_runner().RunPendingTasks(); // Run posted deadline. - EXPECT_EQ(2, client.num_draws()); + client->SetDrawWillHappen(false); + client->SetNeedsCommitOnNextDraw(); + EXPECT_SCOPED(AdvanceFrame()); + task_runner().RunPendingTasks(); // Run posted deadline. + EXPECT_EQ(2, client->num_draws()); } -class SchedulerClientNeedsManageTilesInDraw : public FakeSchedulerClient { +class SchedulerClientNeedsPrepareTilesInDraw : public FakeSchedulerClient { public: DrawResult ScheduledActionDrawAndSwapIfPossible() override { - scheduler_->SetNeedsManageTiles(); + scheduler_->SetNeedsPrepareTiles(); return FakeSchedulerClient::ScheduledActionDrawAndSwapIfPossible(); } }; -// Test manage tiles is independant of draws. -TEST(SchedulerTest, ManageTiles) { - SchedulerClientNeedsManageTilesInDraw client; - SchedulerSettings default_scheduler_settings; - TestScheduler* scheduler = client.CreateScheduler(default_scheduler_settings); - scheduler->SetCanStart(); - scheduler->SetVisible(true); - scheduler->SetCanDraw(true); - InitializeOutputSurfaceAndFirstCommit(scheduler, &client); - - // Request both draw and manage tiles. ManageTiles shouldn't +// Test prepare tiles is independant of draws. +TEST_F(SchedulerTest, PrepareTiles) { + SchedulerClientNeedsPrepareTilesInDraw* client = + new SchedulerClientNeedsPrepareTilesInDraw; + scheduler_settings_.use_external_begin_frame_source = true; + SetUpScheduler(make_scoped_ptr(client).Pass(), true); + + // Request both draw and prepare tiles. PrepareTiles shouldn't // be trigged until BeginImplFrame. - client.Reset(); - scheduler->SetNeedsManageTiles(); - scheduler->SetNeedsRedraw(); - EXPECT_TRUE(scheduler->RedrawPending()); - EXPECT_TRUE(scheduler->ManageTilesPending()); - EXPECT_TRUE(client.needs_begin_frames()); - EXPECT_EQ(0, client.num_draws()); - EXPECT_FALSE(client.HasAction("ScheduledActionManageTiles")); - EXPECT_FALSE(client.HasAction("ScheduledActionDrawAndSwapIfPossible")); + client->Reset(); + scheduler_->SetNeedsPrepareTiles(); + scheduler_->SetNeedsRedraw(); + EXPECT_TRUE(scheduler_->RedrawPending()); + EXPECT_TRUE(scheduler_->PrepareTilesPending()); + EXPECT_TRUE(client->needs_begin_frames()); + EXPECT_EQ(0, client->num_draws()); + EXPECT_FALSE(client->HasAction("ScheduledActionPrepareTiles")); + EXPECT_FALSE(client->HasAction("ScheduledActionDrawAndSwapIfPossible")); // We have no immediate actions to perform, so the BeginImplFrame should post // the deadline task. - client.Reset(); - client.AdvanceFrame(); + client->Reset(); + EXPECT_SCOPED(AdvanceFrame()); EXPECT_ACTION("WillBeginImplFrame", client, 0, 2); EXPECT_ACTION("ScheduledActionAnimate", client, 1, 2); - EXPECT_TRUE(scheduler->BeginImplFrameDeadlinePending()); + EXPECT_TRUE(scheduler_->BeginImplFrameDeadlinePending()); // On the deadline, he actions should have occured in the right order. - 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()); - - // 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_frames()); - EXPECT_EQ(0, client.num_draws()); + client->Reset(); + task_runner().RunPendingTasks(); // Run posted deadline. + EXPECT_EQ(1, client->num_draws()); + EXPECT_TRUE(client->HasAction("ScheduledActionDrawAndSwapIfPossible")); + EXPECT_TRUE(client->HasAction("ScheduledActionPrepareTiles")); + EXPECT_LT(client->ActionIndex("ScheduledActionDrawAndSwapIfPossible"), + client->ActionIndex("ScheduledActionPrepareTiles")); + EXPECT_FALSE(scheduler_->RedrawPending()); + EXPECT_FALSE(scheduler_->PrepareTilesPending()); + EXPECT_FALSE(scheduler_->BeginImplFrameDeadlinePending()); + + // Request a draw. We don't need a PrepareTiles yet. + client->Reset(); + scheduler_->SetNeedsRedraw(); + EXPECT_TRUE(scheduler_->RedrawPending()); + EXPECT_FALSE(scheduler_->PrepareTilesPending()); + EXPECT_TRUE(client->needs_begin_frames()); + EXPECT_EQ(0, client->num_draws()); // We have no immediate actions to perform, so the BeginImplFrame should post // the deadline task. - client.Reset(); - client.AdvanceFrame(); + client->Reset(); + EXPECT_SCOPED(AdvanceFrame()); 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(); - 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()); + EXPECT_TRUE(scheduler_->BeginImplFrameDeadlinePending()); + + // Draw. The draw will trigger SetNeedsPrepareTiles, and + // then the PrepareTiles action will be triggered after the Draw. + // Afterwards, neither a draw nor PrepareTiles are pending. + client->Reset(); + task_runner().RunPendingTasks(); // Run posted deadline. + EXPECT_EQ(1, client->num_draws()); + EXPECT_TRUE(client->HasAction("ScheduledActionDrawAndSwapIfPossible")); + EXPECT_TRUE(client->HasAction("ScheduledActionPrepareTiles")); + EXPECT_LT(client->ActionIndex("ScheduledActionDrawAndSwapIfPossible"), + client->ActionIndex("ScheduledActionPrepareTiles")); + EXPECT_FALSE(scheduler_->RedrawPending()); + EXPECT_FALSE(scheduler_->PrepareTilesPending()); + EXPECT_FALSE(scheduler_->BeginImplFrameDeadlinePending()); // We need a BeginImplFrame where we don't swap to go idle. - client.Reset(); - client.AdvanceFrame(); + client->Reset(); + EXPECT_SCOPED(AdvanceFrame()); EXPECT_SINGLE_ACTION("WillBeginImplFrame", client); - EXPECT_TRUE(scheduler->BeginImplFrameDeadlinePending()); - client.Reset(); - client.task_runner().RunPendingTasks(); // Run posted deadline. - EXPECT_SINGLE_ACTION("SetNeedsBeginFrames(false)", client); - 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_frames()); - scheduler->SetNeedsManageTiles(); - EXPECT_TRUE(client.needs_begin_frames()); - EXPECT_TRUE(scheduler->ManageTilesPending()); - EXPECT_FALSE(scheduler->RedrawPending()); - - // BeginImplFrame. There will be no draw, only ManageTiles. - client.Reset(); - client.AdvanceFrame(); + EXPECT_TRUE(scheduler_->BeginImplFrameDeadlinePending()); + client->Reset(); + task_runner().RunPendingTasks(); // Run posted deadline. + EXPECT_ACTION("SetNeedsBeginFrames(false)", client_, 0, 2); + EXPECT_ACTION("SendBeginMainFrameNotExpectedSoon", client_, 1, 2); + EXPECT_FALSE(scheduler_->BeginImplFrameDeadlinePending()); + EXPECT_EQ(0, client->num_draws()); + + // Now trigger a PrepareTiles outside of a draw. We will then need + // a begin-frame for the PrepareTiles, but we don't need a draw. + client->Reset(); + EXPECT_FALSE(client->needs_begin_frames()); + scheduler_->SetNeedsPrepareTiles(); + EXPECT_TRUE(client->needs_begin_frames()); + EXPECT_TRUE(scheduler_->PrepareTilesPending()); + EXPECT_FALSE(scheduler_->RedrawPending()); + + // BeginImplFrame. There will be no draw, only PrepareTiles. + client->Reset(); + EXPECT_SCOPED(AdvanceFrame()); EXPECT_SINGLE_ACTION("WillBeginImplFrame", client); - EXPECT_TRUE(scheduler->BeginImplFrameDeadlinePending()); - client.Reset(); - 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()); + EXPECT_TRUE(scheduler_->BeginImplFrameDeadlinePending()); + client->Reset(); + task_runner().RunPendingTasks(); // Run posted deadline. + EXPECT_EQ(0, client->num_draws()); + EXPECT_FALSE(client->HasAction("ScheduledActionDrawAndSwapIfPossible")); + EXPECT_TRUE(client->HasAction("ScheduledActionPrepareTiles")); + EXPECT_FALSE(scheduler_->BeginImplFrameDeadlinePending()); } -// Test that ManageTiles only happens once per frame. If an external caller -// initiates it, then the state machine should not ManageTiles on that frame. -TEST(SchedulerTest, ManageTilesOncePerFrame) { - FakeSchedulerClient client; - SchedulerSettings default_scheduler_settings; - TestScheduler* scheduler = client.CreateScheduler(default_scheduler_settings); - scheduler->SetCanStart(); - scheduler->SetVisible(true); - scheduler->SetCanDraw(true); - InitializeOutputSurfaceAndFirstCommit(scheduler, &client); - - // If DidManageTiles during a frame, then ManageTiles should not occur again. - scheduler->SetNeedsManageTiles(); - scheduler->SetNeedsRedraw(); - client.Reset(); - client.AdvanceFrame(); - EXPECT_ACTION("WillBeginImplFrame", client, 0, 2); - EXPECT_ACTION("ScheduledActionAnimate", client, 1, 2); - EXPECT_TRUE(scheduler->BeginImplFrameDeadlinePending()); - - EXPECT_TRUE(scheduler->ManageTilesPending()); - scheduler->DidManageTiles(); // An explicit ManageTiles. - EXPECT_FALSE(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->ManageTilesPending()); - EXPECT_FALSE(scheduler->BeginImplFrameDeadlinePending()); - - // Next frame without DidManageTiles should ManageTiles with draw. - scheduler->SetNeedsManageTiles(); - scheduler->SetNeedsRedraw(); - client.Reset(); - client.AdvanceFrame(); - 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 - - // 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(); - client.AdvanceFrame(); - 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(); - client.AdvanceFrame(); - 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(); - client.AdvanceFrame(); - 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 that PrepareTiles only happens once per frame. If an external caller +// initiates it, then the state machine should not PrepareTiles on that frame. +TEST_F(SchedulerTest, PrepareTilesOncePerFrame) { + scheduler_settings_.use_external_begin_frame_source = true; + SetUpScheduler(true); + + // If DidPrepareTiles during a frame, then PrepareTiles should not occur + // again. + scheduler_->SetNeedsPrepareTiles(); + scheduler_->SetNeedsRedraw(); + client_->Reset(); + EXPECT_SCOPED(AdvanceFrame()); + EXPECT_ACTION("WillBeginImplFrame", client_, 0, 2); + EXPECT_ACTION("ScheduledActionAnimate", client_, 1, 2); + EXPECT_TRUE(scheduler_->BeginImplFrameDeadlinePending()); + + EXPECT_TRUE(scheduler_->PrepareTilesPending()); + scheduler_->DidPrepareTiles(); // An explicit PrepareTiles. + EXPECT_FALSE(scheduler_->PrepareTilesPending()); + + client_->Reset(); + task_runner().RunPendingTasks(); // Run posted deadline. + EXPECT_EQ(1, client_->num_draws()); + EXPECT_TRUE(client_->HasAction("ScheduledActionDrawAndSwapIfPossible")); + EXPECT_FALSE(client_->HasAction("ScheduledActionPrepareTiles")); + EXPECT_FALSE(scheduler_->RedrawPending()); + EXPECT_FALSE(scheduler_->PrepareTilesPending()); + EXPECT_FALSE(scheduler_->BeginImplFrameDeadlinePending()); + + // Next frame without DidPrepareTiles should PrepareTiles with draw. + scheduler_->SetNeedsPrepareTiles(); + scheduler_->SetNeedsRedraw(); + client_->Reset(); + EXPECT_SCOPED(AdvanceFrame()); + EXPECT_ACTION("WillBeginImplFrame", client_, 0, 2); + EXPECT_ACTION("ScheduledActionAnimate", client_, 1, 2); + EXPECT_TRUE(scheduler_->BeginImplFrameDeadlinePending()); + + client_->Reset(); + task_runner().RunPendingTasks(); // Run posted deadline. + EXPECT_EQ(1, client_->num_draws()); + EXPECT_TRUE(client_->HasAction("ScheduledActionDrawAndSwapIfPossible")); + EXPECT_TRUE(client_->HasAction("ScheduledActionPrepareTiles")); + EXPECT_LT(client_->ActionIndex("ScheduledActionDrawAndSwapIfPossible"), + client_->ActionIndex("ScheduledActionPrepareTiles")); + EXPECT_FALSE(scheduler_->RedrawPending()); + EXPECT_FALSE(scheduler_->PrepareTilesPending()); + EXPECT_FALSE(scheduler_->BeginImplFrameDeadlinePending()); + scheduler_->DidPrepareTiles(); // Corresponds to ScheduledActionPrepareTiles + + // If we get another DidPrepareTiles within the same frame, we should + // not PrepareTiles on the next frame. + scheduler_->DidPrepareTiles(); // An explicit PrepareTiles. + scheduler_->SetNeedsPrepareTiles(); + scheduler_->SetNeedsRedraw(); + client_->Reset(); + EXPECT_SCOPED(AdvanceFrame()); + EXPECT_ACTION("WillBeginImplFrame", client_, 0, 2); + EXPECT_ACTION("ScheduledActionAnimate", client_, 1, 2); + EXPECT_TRUE(scheduler_->BeginImplFrameDeadlinePending()); + + EXPECT_TRUE(scheduler_->PrepareTilesPending()); + + client_->Reset(); + task_runner().RunPendingTasks(); // Run posted deadline. + EXPECT_EQ(1, client_->num_draws()); + EXPECT_TRUE(client_->HasAction("ScheduledActionDrawAndSwapIfPossible")); + EXPECT_FALSE(client_->HasAction("ScheduledActionPrepareTiles")); + EXPECT_FALSE(scheduler_->RedrawPending()); + EXPECT_FALSE(scheduler_->BeginImplFrameDeadlinePending()); + + // If we get another DidPrepareTiles, we should not PrepareTiles on the next + // frame. This verifies we don't alternate calling PrepareTiles once and + // twice. + EXPECT_TRUE(scheduler_->PrepareTilesPending()); + scheduler_->DidPrepareTiles(); // An explicit PrepareTiles. + EXPECT_FALSE(scheduler_->PrepareTilesPending()); + scheduler_->SetNeedsPrepareTiles(); + scheduler_->SetNeedsRedraw(); + client_->Reset(); + EXPECT_SCOPED(AdvanceFrame()); + EXPECT_ACTION("WillBeginImplFrame", client_, 0, 2); + EXPECT_ACTION("ScheduledActionAnimate", client_, 1, 2); + EXPECT_TRUE(scheduler_->BeginImplFrameDeadlinePending()); + + EXPECT_TRUE(scheduler_->PrepareTilesPending()); + + client_->Reset(); + task_runner().RunPendingTasks(); // Run posted deadline. + EXPECT_EQ(1, client_->num_draws()); + EXPECT_TRUE(client_->HasAction("ScheduledActionDrawAndSwapIfPossible")); + EXPECT_FALSE(client_->HasAction("ScheduledActionPrepareTiles")); + EXPECT_FALSE(scheduler_->RedrawPending()); + EXPECT_FALSE(scheduler_->BeginImplFrameDeadlinePending()); + + // Next frame without DidPrepareTiles should PrepareTiles with draw. + scheduler_->SetNeedsPrepareTiles(); + scheduler_->SetNeedsRedraw(); + client_->Reset(); + EXPECT_SCOPED(AdvanceFrame()); + EXPECT_ACTION("WillBeginImplFrame", client_, 0, 2); + EXPECT_ACTION("ScheduledActionAnimate", client_, 1, 2); + EXPECT_TRUE(scheduler_->BeginImplFrameDeadlinePending()); + + client_->Reset(); + task_runner().RunPendingTasks(); // Run posted deadline. + EXPECT_EQ(1, client_->num_draws()); + EXPECT_TRUE(client_->HasAction("ScheduledActionDrawAndSwapIfPossible")); + EXPECT_TRUE(client_->HasAction("ScheduledActionPrepareTiles")); + EXPECT_LT(client_->ActionIndex("ScheduledActionDrawAndSwapIfPossible"), + client_->ActionIndex("ScheduledActionPrepareTiles")); + EXPECT_FALSE(scheduler_->RedrawPending()); + EXPECT_FALSE(scheduler_->PrepareTilesPending()); + EXPECT_FALSE(scheduler_->BeginImplFrameDeadlinePending()); + scheduler_->DidPrepareTiles(); // Corresponds to ScheduledActionPrepareTiles } -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); +TEST_F(SchedulerTest, TriggerBeginFrameDeadlineEarly) { + SchedulerClientNeedsPrepareTilesInDraw* client = + new SchedulerClientNeedsPrepareTilesInDraw; + scheduler_settings_.use_external_begin_frame_source = true; + SetUpScheduler(make_scoped_ptr(client).Pass(), true); - // SetNeedsCommit should begin the frame. - client.Reset(); - scheduler->SetNeedsCommit(); - EXPECT_SINGLE_ACTION("SetNeedsBeginFrames(true)", client); + scheduler_->SetNeedsRedraw(); + EXPECT_SCOPED(AdvanceFrame()); - client.Reset(); - client.AdvanceFrame(); - 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("ScheduledActionActivateSyncTree", 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(); - client.AdvanceFrame(); - 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); + // The deadline should be zero since there is no work other than drawing + // pending. + EXPECT_EQ(base::TimeTicks(), client->posted_begin_impl_frame_deadline()); +} - client.Reset(); - client.AdvanceFrame(); - EXPECT_SINGLE_ACTION("WillBeginImplFrame", client); - EXPECT_TRUE(scheduler->BeginImplFrameDeadlinePending()); +TEST_F(SchedulerTest, WaitForReadyToDrawDoNotPostDeadline) { + SchedulerClientNeedsPrepareTilesInDraw* client = + new SchedulerClientNeedsPrepareTilesInDraw; + scheduler_settings_.use_external_begin_frame_source = true; + scheduler_settings_.impl_side_painting = true; + SetUpScheduler(make_scoped_ptr(client).Pass(), true); - // No more UpdateVisibleTiles(). - client.Reset(); - client.task_runner().RunPendingTasks(); // Run posted deadline. - EXPECT_SINGLE_ACTION("SetNeedsBeginFrames(false)", client); + // SetNeedsCommit should begin the frame on the next BeginImplFrame. + scheduler_->SetNeedsCommit(); + EXPECT_SINGLE_ACTION("SetNeedsBeginFrames(true)", client_); + client_->Reset(); + + // Begin new frame. + EXPECT_SCOPED(AdvanceFrame()); + scheduler_->NotifyBeginMainFrameStarted(); + EXPECT_ACTION("WillBeginImplFrame", client_, 0, 2); + EXPECT_ACTION("ScheduledActionSendBeginMainFrame", client_, 1, 2); + + client_->Reset(); + scheduler_->NotifyReadyToCommit(); + EXPECT_SINGLE_ACTION("ScheduledActionCommit", client_); + + client_->Reset(); + scheduler_->NotifyReadyToActivate(); + EXPECT_SINGLE_ACTION("ScheduledActionActivateSyncTree", client_); + + // Set scheduler to wait for ready to draw. Schedule won't post deadline in + // the mode. + scheduler_->SetWaitForReadyToDraw(); + client_->Reset(); + task_runner().RunPendingTasks(); // Try to run posted deadline. + // There is no posted deadline. + EXPECT_NO_ACTION(client_); + + // Scheduler received ready to draw signal, and posted deadline. + scheduler_->NotifyReadyToDraw(); + client_->Reset(); + task_runner().RunPendingTasks(); // Run posted deadline. + EXPECT_EQ(1, client_->num_draws()); + EXPECT_TRUE(client_->HasAction("ScheduledActionDrawAndSwapIfPossible")); } -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(); - client.AdvanceFrame(); +TEST_F(SchedulerTest, WaitForReadyToDrawCancelledWhenLostOutputSurface) { + SchedulerClientNeedsPrepareTilesInDraw* client = + new SchedulerClientNeedsPrepareTilesInDraw; + scheduler_settings_.use_external_begin_frame_source = true; + scheduler_settings_.impl_side_painting = true; + SetUpScheduler(make_scoped_ptr(client).Pass(), true); - // The deadline should be zero since there is no work other than drawing - // pending. - EXPECT_EQ(base::TimeTicks(), client.posted_begin_impl_frame_deadline()); + // SetNeedsCommit should begin the frame on the next BeginImplFrame. + scheduler_->SetNeedsCommit(); + EXPECT_SINGLE_ACTION("SetNeedsBeginFrames(true)", client_); + client_->Reset(); + + // Begin new frame. + EXPECT_SCOPED(AdvanceFrame()); + scheduler_->NotifyBeginMainFrameStarted(); + EXPECT_ACTION("WillBeginImplFrame", client_, 0, 2); + EXPECT_ACTION("ScheduledActionSendBeginMainFrame", client_, 1, 2); + + client_->Reset(); + scheduler_->NotifyReadyToCommit(); + EXPECT_SINGLE_ACTION("ScheduledActionCommit", client_); + + client_->Reset(); + scheduler_->NotifyReadyToActivate(); + EXPECT_SINGLE_ACTION("ScheduledActionActivateSyncTree", client_); + + // Set scheduler to wait for ready to draw. Schedule won't post deadline in + // the mode. + scheduler_->SetWaitForReadyToDraw(); + client_->Reset(); + task_runner().RunPendingTasks(); // Try to run posted deadline. + // There is no posted deadline. + EXPECT_NO_ACTION(client_); + + // Scheduler loses output surface, and stops waiting for ready to draw signal. + client_->Reset(); + scheduler_->DidLoseOutputSurface(); + EXPECT_TRUE(scheduler_->BeginImplFrameDeadlinePending()); + task_runner().RunPendingTasks(); // Run posted deadline. + EXPECT_ACTION("ScheduledActionBeginOutputSurfaceCreation", client_, 0, 3); + EXPECT_ACTION("SetNeedsBeginFrames(false)", client_, 1, 3); + EXPECT_ACTION("SendBeginMainFrameNotExpectedSoon", client_, 2, 3); } -class SchedulerClientWithFixedEstimates : public FakeSchedulerClient { - public: - SchedulerClientWithFixedEstimates( - base::TimeDelta draw_duration, - base::TimeDelta begin_main_frame_to_commit_duration, - base::TimeDelta commit_to_activate_duration) - : draw_duration_(draw_duration), - begin_main_frame_to_commit_duration_( - begin_main_frame_to_commit_duration), - commit_to_activate_duration_(commit_to_activate_duration) {} - - base::TimeDelta DrawDurationEstimate() override { return draw_duration_; } - base::TimeDelta BeginMainFrameToCommitDurationEstimate() override { - return begin_main_frame_to_commit_duration_; - } - base::TimeDelta CommitToActivateDurationEstimate() override { - return commit_to_activate_duration_; - } +void SchedulerTest::MainFrameInHighLatencyMode( + int64 begin_main_frame_to_commit_estimate_in_ms, + int64 commit_to_activate_estimate_in_ms, + bool impl_latency_takes_priority, + bool should_send_begin_main_frame) { + // Set up client with specified estimates (draw duration is set to 1). + SchedulerClientWithFixedEstimates* client = + new SchedulerClientWithFixedEstimates( + base::TimeDelta::FromMilliseconds(1), + base::TimeDelta::FromMilliseconds( + begin_main_frame_to_commit_estimate_in_ms), + base::TimeDelta::FromMilliseconds(commit_to_activate_estimate_in_ms)); - private: - base::TimeDelta draw_duration_; - base::TimeDelta begin_main_frame_to_commit_duration_; - base::TimeDelta commit_to_activate_duration_; -}; + scheduler_settings_.use_external_begin_frame_source = true; + SetUpScheduler(make_scoped_ptr(client).Pass(), true); -void MainFrameInHighLatencyMode(int64 begin_main_frame_to_commit_estimate_in_ms, - int64 commit_to_activate_estimate_in_ms, - bool impl_latency_takes_priority, - bool should_send_begin_main_frame) { - // Set up client with specified estimates (draw duration is set to 1). - SchedulerClientWithFixedEstimates client( - base::TimeDelta::FromMilliseconds(1), - base::TimeDelta::FromMilliseconds( - begin_main_frame_to_commit_estimate_in_ms), - base::TimeDelta::FromMilliseconds(commit_to_activate_estimate_in_ms)); - SchedulerSettings default_scheduler_settings; - TestScheduler* scheduler = client.CreateScheduler(default_scheduler_settings); - scheduler->SetCanStart(); - scheduler->SetVisible(true); - scheduler->SetCanDraw(true); - scheduler->SetImplLatencyTakesPriority(impl_latency_takes_priority); - InitializeOutputSurfaceAndFirstCommit(scheduler, &client); + scheduler_->SetImplLatencyTakesPriority(impl_latency_takes_priority); // Impl thread hits deadline before commit finishes. - client.Reset(); - scheduler->SetNeedsCommit(); - EXPECT_FALSE(scheduler->MainThreadIsInHighLatencyMode()); - client.AdvanceFrame(); - EXPECT_FALSE(scheduler->MainThreadIsInHighLatencyMode()); - client.task_runner().RunPendingTasks(); // Run posted deadline. - EXPECT_TRUE(scheduler->MainThreadIsInHighLatencyMode()); - scheduler->NotifyBeginMainFrameStarted(); - scheduler->NotifyReadyToCommit(); - EXPECT_TRUE(scheduler->MainThreadIsInHighLatencyMode()); - EXPECT_TRUE(client.HasAction("ScheduledActionSendBeginMainFrame")); - - client.Reset(); - scheduler->SetNeedsCommit(); - EXPECT_TRUE(scheduler->MainThreadIsInHighLatencyMode()); - client.AdvanceFrame(); - EXPECT_TRUE(scheduler->MainThreadIsInHighLatencyMode()); - client.task_runner().RunPendingTasks(); // Run posted deadline. - EXPECT_EQ(scheduler->MainThreadIsInHighLatencyMode(), + scheduler_->SetNeedsCommit(); + EXPECT_FALSE(scheduler_->MainThreadIsInHighLatencyMode()); + EXPECT_SCOPED(AdvanceFrame()); + EXPECT_FALSE(scheduler_->MainThreadIsInHighLatencyMode()); + task_runner().RunPendingTasks(); // Run posted deadline. + EXPECT_TRUE(scheduler_->MainThreadIsInHighLatencyMode()); + scheduler_->NotifyBeginMainFrameStarted(); + scheduler_->NotifyReadyToCommit(); + EXPECT_TRUE(scheduler_->MainThreadIsInHighLatencyMode()); + EXPECT_TRUE(client->HasAction("ScheduledActionSendBeginMainFrame")); + + client->Reset(); + scheduler_->SetNeedsCommit(); + EXPECT_TRUE(scheduler_->MainThreadIsInHighLatencyMode()); + EXPECT_SCOPED(AdvanceFrame()); + EXPECT_TRUE(scheduler_->MainThreadIsInHighLatencyMode()); + task_runner().RunPendingTasks(); // Run posted deadline. + EXPECT_EQ(scheduler_->MainThreadIsInHighLatencyMode(), should_send_begin_main_frame); - EXPECT_EQ(client.HasAction("ScheduledActionSendBeginMainFrame"), + EXPECT_EQ(client->HasAction("ScheduledActionSendBeginMainFrame"), should_send_begin_main_frame); } -TEST(SchedulerTest, - SkipMainFrameIfHighLatencyAndCanCommitAndActivateBeforeDeadline) { +TEST_F(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, false); + EXPECT_SCOPED(MainFrameInHighLatencyMode(1, 1, false, false)); } -TEST(SchedulerTest, NotSkipMainFrameIfHighLatencyAndCanCommitTooLong) { +TEST_F(SchedulerTest, NotSkipMainFrameIfHighLatencyAndCanCommitTooLong) { // Set up client so that estimates indicate that the commit cannot finish // before the deadline (~8ms by default). - MainFrameInHighLatencyMode(10, 1, false, true); + EXPECT_SCOPED(MainFrameInHighLatencyMode(10, 1, false, true)); } -TEST(SchedulerTest, NotSkipMainFrameIfHighLatencyAndCanActivateTooLong) { +TEST_F(SchedulerTest, NotSkipMainFrameIfHighLatencyAndCanActivateTooLong) { // Set up client so that estimates indicate that the activate cannot finish // before the deadline (~8ms by default). - MainFrameInHighLatencyMode(1, 10, false, true); + EXPECT_SCOPED(MainFrameInHighLatencyMode(1, 10, false, true)); } -TEST(SchedulerTest, NotSkipMainFrameInPreferImplLatencyMode) { +TEST_F(SchedulerTest, NotSkipMainFrameInPreferImplLatencyMode) { // Set up client so that estimates indicate that we can commit and activate // before the deadline (~8ms by default), but also enable impl latency takes // priority mode. - MainFrameInHighLatencyMode(1, 1, true, true); + EXPECT_SCOPED(MainFrameInHighLatencyMode(1, 1, true, true)); } -TEST(SchedulerTest, PollForCommitCompletion) { +TEST_F(SchedulerTest, PollForCommitCompletion) { // 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 default_scheduler_settings; - TestScheduler* scheduler = client.CreateScheduler(default_scheduler_settings); - - scheduler->SetCanDraw(true); - scheduler->SetCanStart(); - scheduler->SetVisible(true); - scheduler->DidCreateAndInitializeOutputSurface(); - - scheduler->SetNeedsCommit(); - EXPECT_TRUE(scheduler->CommitPending()); - scheduler->NotifyBeginMainFrameStarted(); - scheduler->NotifyReadyToCommit(); - scheduler->SetNeedsRedraw(); - - BeginFrameArgs frame_args = CreateBeginFrameArgsForTesting(client.now_src()); + SchedulerClientWithFixedEstimates* client = + new SchedulerClientWithFixedEstimates( + base::TimeDelta::FromMilliseconds(1), + base::TimeDelta::FromMilliseconds(32), + base::TimeDelta::FromMilliseconds(32)); + scheduler_settings_.use_external_begin_frame_source = true; + SetUpScheduler(make_scoped_ptr(client).Pass(), true); + + client->set_log_anticipated_draw_time_change(true); + + BeginFrameArgs frame_args = + CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, now_src()); frame_args.interval = base::TimeDelta::FromMilliseconds(1000); - client.ExternalBeginFrameSource()->TestOnBeginFrame(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(); - client.ExternalBeginFrameSource()->TestOnBeginFrame(frame_args); - EXPECT_TRUE(scheduler->CommitPending()); + EXPECT_FALSE(scheduler_->CommitPending()); + scheduler_->SetNeedsCommit(); + fake_external_begin_frame_source()->TestOnBeginFrame(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(); + EXPECT_TRUE(scheduler_->BeginImplFrameDeadlinePending()); + 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_(); + 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) { EXPECT_EQ((frame_args.interval * 2).InMicroseconds(), - client.task_runner().DelayToNextTaskTime().InMicroseconds()) - << scheduler->AsValue()->ToString(); - client.task_runner().RunPendingTasks(); - EXPECT_GT(client.num_actions_(), actions_so_far); - EXPECT_STREQ(client.Action(client.num_actions_() - 1), + task_runner().DelayToNextTaskTime().InMicroseconds()) + << scheduler_->AsValue()->ToString(); + 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_(); + actions_so_far = client->num_actions_(); } // Do the same thing after BeginMainFrame starts but still before activation. - scheduler->NotifyBeginMainFrameStarted(); + scheduler_->NotifyBeginMainFrameStarted(); for (int i = 0; i < 3; ++i) { EXPECT_EQ((frame_args.interval * 2).InMicroseconds(), - client.task_runner().DelayToNextTaskTime().InMicroseconds()) - << scheduler->AsValue()->ToString(); - client.task_runner().RunPendingTasks(); - EXPECT_GT(client.num_actions_(), actions_so_far); - EXPECT_STREQ(client.Action(client.num_actions_() - 1), + task_runner().DelayToNextTaskTime().InMicroseconds()) + << scheduler_->AsValue()->ToString(); + 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_(); + 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); +TEST_F(SchedulerTest, BeginRetroFrame) { + scheduler_settings_.use_external_begin_frame_source = true; + SetUpScheduler(true); // SetNeedsCommit should begin the frame on the next BeginImplFrame. - client.Reset(); - scheduler->SetNeedsCommit(); - EXPECT_SINGLE_ACTION("SetNeedsBeginFrames(true)", client); - client.Reset(); + scheduler_->SetNeedsCommit(); + EXPECT_SINGLE_ACTION("SetNeedsBeginFrames(true)", 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(client.now_src()); + BeginFrameArgs args = + CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, now_src()); args.deadline += base::TimeDelta::FromHours(1); - client.ExternalBeginFrameSource()->TestOnBeginFrame(args); - EXPECT_ACTION("WillBeginImplFrame", client, 0, 2); - EXPECT_ACTION("ScheduledActionSendBeginMainFrame", client, 1, 2); - EXPECT_TRUE(scheduler->BeginImplFrameDeadlinePending()); - EXPECT_TRUE(client.needs_begin_frames()); - client.Reset(); + fake_external_begin_frame_source()->TestOnBeginFrame(args); + EXPECT_ACTION("WillBeginImplFrame", client_, 0, 2); + EXPECT_ACTION("ScheduledActionSendBeginMainFrame", client_, 1, 2); + EXPECT_TRUE(scheduler_->BeginImplFrameDeadlinePending()); + EXPECT_TRUE(client_->needs_begin_frames()); + client_->Reset(); // Queue BeginFrames while we are still handling the previous BeginFrame. args.frame_time += base::TimeDelta::FromSeconds(1); - client.ExternalBeginFrameSource()->TestOnBeginFrame(args); + fake_external_begin_frame_source()->TestOnBeginFrame(args); args.frame_time += base::TimeDelta::FromSeconds(1); - client.ExternalBeginFrameSource()->TestOnBeginFrame(args); + fake_external_begin_frame_source()->TestOnBeginFrame(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_frames()); - client.Reset(); + task_runner().RunPendingTasks(); // Run posted deadline. + EXPECT_NO_ACTION(client_); + EXPECT_FALSE(scheduler_->BeginImplFrameDeadlinePending()); + EXPECT_TRUE(client_->needs_begin_frames()); + client_->Reset(); // NotifyReadyToCommit should trigger the commit. - scheduler->NotifyBeginMainFrameStarted(); - scheduler->NotifyReadyToCommit(); - EXPECT_SINGLE_ACTION("ScheduledActionCommit", client); - EXPECT_TRUE(client.needs_begin_frames()); - client.Reset(); + scheduler_->NotifyBeginMainFrameStarted(); + scheduler_->NotifyReadyToCommit(); + EXPECT_SINGLE_ACTION("ScheduledActionCommit", client_); + EXPECT_TRUE(client_->needs_begin_frames()); + 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_frames()); - client.Reset(); + 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_frames()); + 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_frames()); - client.Reset(); + task_runner().RunPendingTasks(); // Run posted deadline. + EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client_, 0, 1); + EXPECT_FALSE(scheduler_->BeginImplFrameDeadlinePending()); + EXPECT_TRUE(client_->needs_begin_frames()); + 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("SetNeedsBeginFrames(false)", client); - client.Reset(); + task_runner().RunPendingTasks(); // Run posted BeginRetroFrame. + EXPECT_SINGLE_ACTION("WillBeginImplFrame", client_); + EXPECT_TRUE(scheduler_->BeginImplFrameDeadlinePending()); + client_->Reset(); + + task_runner().RunPendingTasks(); // Run posted deadline. + EXPECT_ACTION("SetNeedsBeginFrames(false)", client_, 0, 2); + EXPECT_ACTION("SendBeginMainFrameNotExpectedSoon", client_, 1, 2); + 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); +TEST_F(SchedulerTest, BeginRetroFrame_SwapThrottled) { + scheduler_settings_.use_external_begin_frame_source = true; + SetUpScheduler(true); + + scheduler_->SetEstimatedParentDrawTime(base::TimeDelta::FromMicroseconds(1)); // To test swap ack throttling, this test disables automatic swap acks. - scheduler->SetMaxSwapsPending(1); - client.SetAutomaticSwapAck(false); + scheduler_->SetMaxSwapsPending(1); + client_->SetAutomaticSwapAck(false); // SetNeedsCommit should begin the frame on the next BeginImplFrame. - client.Reset(); - scheduler->SetNeedsCommit(); - EXPECT_SINGLE_ACTION("SetNeedsBeginFrames(true)", 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(client.now_src()); - args.deadline += base::TimeDelta::FromHours(1); - client.ExternalBeginFrameSource()->TestOnBeginFrame(args); - EXPECT_ACTION("WillBeginImplFrame", client, 0, 2); - EXPECT_ACTION("ScheduledActionSendBeginMainFrame", client, 1, 2); - EXPECT_TRUE(scheduler->BeginImplFrameDeadlinePending()); - EXPECT_TRUE(client.needs_begin_frames()); - client.Reset(); + client_->Reset(); + scheduler_->SetNeedsCommit(); + EXPECT_SINGLE_ACTION("SetNeedsBeginFrames(true)", client_); + client_->Reset(); + + EXPECT_SCOPED(AdvanceFrame()); + EXPECT_ACTION("WillBeginImplFrame", client_, 0, 2); + EXPECT_ACTION("ScheduledActionSendBeginMainFrame", client_, 1, 2); + EXPECT_TRUE(scheduler_->BeginImplFrameDeadlinePending()); + EXPECT_TRUE(client_->needs_begin_frames()); + client_->Reset(); // Queue BeginFrame while we are still handling the previous BeginFrame. - EXPECT_TRUE(scheduler->BeginImplFrameDeadlinePending()); - args.frame_time += base::TimeDelta::FromSeconds(1); - client.ExternalBeginFrameSource()->TestOnBeginFrame(args); - EXPECT_NO_ACTION(client); - EXPECT_TRUE(scheduler->BeginImplFrameDeadlinePending()); - client.Reset(); + SendNextBeginFrame(); + EXPECT_NO_ACTION(client_); + EXPECT_TRUE(scheduler_->BeginImplFrameDeadlinePending()); + EXPECT_TRUE(client_->needs_begin_frames()); + client_->Reset(); // NotifyReadyToCommit should trigger the pending commit and draw. - scheduler->NotifyBeginMainFrameStarted(); - scheduler->NotifyReadyToCommit(); - EXPECT_SINGLE_ACTION("ScheduledActionCommit", client); - EXPECT_TRUE(client.needs_begin_frames()); - client.Reset(); + scheduler_->NotifyBeginMainFrameStarted(); + scheduler_->NotifyReadyToCommit(); + EXPECT_SINGLE_ACTION("ScheduledActionCommit", client_); + EXPECT_TRUE(client_->needs_begin_frames()); + 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_frames()); - client.Reset(); + // Run posted deadline. + task_runner().RunTasksWhile(client_->ImplFrameDeadlinePending(true)); + EXPECT_ACTION("ScheduledActionAnimate", client_, 0, 2); + EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client_, 1, 2); + EXPECT_FALSE(scheduler_->BeginImplFrameDeadlinePending()); + EXPECT_TRUE(client_->needs_begin_frames()); + 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_frames()); - client.Reset(); - - // Queue BeginFrame while we are still handling the previous BeginFrame. - args.frame_time += base::TimeDelta::FromSeconds(1); - client.ExternalBeginFrameSource()->TestOnBeginFrame(args); - EXPECT_NO_ACTION(client); - EXPECT_TRUE(scheduler->BeginImplFrameDeadlinePending()); - EXPECT_TRUE(client.needs_begin_frames()); - client.Reset(); + scheduler_->SetNeedsCommit(); + scheduler_->SetNeedsRedraw(); + // Run posted BeginRetroFrame. + task_runner().RunTasksWhile(client_->ImplFrameDeadlinePending(false)); + EXPECT_ACTION("WillBeginImplFrame", client_, 0, 2); + EXPECT_ACTION("ScheduledActionAnimate", client_, 1, 2); + EXPECT_TRUE(scheduler_->BeginImplFrameDeadlinePending()); + EXPECT_TRUE(client_->needs_begin_frames()); + client_->Reset(); + + // Let time pass sufficiently beyond the regular deadline but not beyond the + // late deadline. + now_src()->AdvanceNow(BeginFrameArgs::DefaultInterval() - + base::TimeDelta::FromMicroseconds(1)); + task_runner().RunUntilTime(now_src()->Now()); + EXPECT_TRUE(scheduler_->BeginImplFrameDeadlinePending()); // 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_frames()); - client.Reset(); - - // BeginImplFrame deadline should draw. - scheduler->SetNeedsRedraw(); + scheduler_->DidSwapBuffersComplete(); + EXPECT_SINGLE_ACTION("ScheduledActionSendBeginMainFrame", client_); + EXPECT_TRUE(scheduler_->BeginImplFrameDeadlinePending()); + EXPECT_TRUE(client_->needs_begin_frames()); + client_->Reset(); + + // Verify that the deadline was rescheduled. + task_runner().RunUntilTime(now_src()->Now()); + EXPECT_SINGLE_ACTION("ScheduledActionDrawAndSwapIfPossible", client_); + EXPECT_FALSE(scheduler_->BeginImplFrameDeadlinePending()); + EXPECT_TRUE(client_->needs_begin_frames()); + client_->Reset(); +} - EXPECT_TRUE(client.task_runner().RunTasksWhile( - client.ImplFrameDeadlinePending(true))); +TEST_F(SchedulerTest, RetroFrameDoesNotExpireTooEarly) { + scheduler_settings_.use_external_begin_frame_source = true; + SetUpScheduler(true); + + scheduler_->SetNeedsCommit(); + EXPECT_TRUE(client_->needs_begin_frames()); + EXPECT_SINGLE_ACTION("SetNeedsBeginFrames(true)", client_); + + client_->Reset(); + EXPECT_SCOPED(AdvanceFrame()); + EXPECT_ACTION("WillBeginImplFrame", client_, 0, 2); + EXPECT_ACTION("ScheduledActionSendBeginMainFrame", client_, 1, 2); + EXPECT_TRUE(scheduler_->BeginImplFrameDeadlinePending()); + + client_->Reset(); + scheduler_->NotifyBeginMainFrameStarted(); + + client_->Reset(); + SendNextBeginFrame(); + // This BeginFrame is queued up as a retro frame. + EXPECT_NO_ACTION(client_); + // The previous deadline is still pending. + EXPECT_TRUE(scheduler_->BeginImplFrameDeadlinePending()); + + client_->Reset(); + // This commit should schedule the (previous) deadline to trigger immediately. + scheduler_->NotifyReadyToCommit(); + EXPECT_SINGLE_ACTION("ScheduledActionCommit", client_); + + client_->Reset(); + // The deadline task should trigger causing a draw. + EXPECT_TRUE(scheduler_->BeginImplFrameDeadlinePending()); + task_runner().RunTasksWhile(client_->ImplFrameDeadlinePending(true)); + EXPECT_ACTION("ScheduledActionAnimate", client_, 0, 2); + EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client_, 1, 2); + + // Keep animating. + client_->Reset(); + scheduler_->SetNeedsAnimate(); + scheduler_->SetNeedsRedraw(); + EXPECT_NO_ACTION(client_); + + // Let's advance sufficiently past the next frame's deadline. + now_src()->AdvanceNow(BeginFrameArgs::DefaultInterval() - + BeginFrameArgs::DefaultEstimatedParentDrawTime() + + base::TimeDelta::FromMicroseconds(1)); + + // The retro frame hasn't expired yet. + task_runner().RunTasksWhile(client_->ImplFrameDeadlinePending(false)); + EXPECT_ACTION("WillBeginImplFrame", client_, 0, 2); + EXPECT_ACTION("ScheduledActionAnimate", client_, 1, 2); + EXPECT_TRUE(scheduler_->BeginImplFrameDeadlinePending()); + + // This is an immediate deadline case. + client_->Reset(); + task_runner().RunPendingTasks(); + EXPECT_FALSE(scheduler_->BeginImplFrameDeadlinePending()); + EXPECT_SINGLE_ACTION("ScheduledActionDrawAndSwapIfPossible", client_); +} - EXPECT_ACTION("ScheduledActionAnimate", client, 0, 2); - EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client, 1, 2); - EXPECT_FALSE(scheduler->BeginImplFrameDeadlinePending()); - EXPECT_TRUE(client.needs_begin_frames()); - client.Reset(); +TEST_F(SchedulerTest, RetroFrameDoesNotExpireTooLate) { + scheduler_settings_.use_external_begin_frame_source = true; + SetUpScheduler(true); + + scheduler_->SetNeedsCommit(); + EXPECT_TRUE(client_->needs_begin_frames()); + EXPECT_SINGLE_ACTION("SetNeedsBeginFrames(true)", client_); + + client_->Reset(); + EXPECT_SCOPED(AdvanceFrame()); + EXPECT_ACTION("WillBeginImplFrame", client_, 0, 2); + EXPECT_ACTION("ScheduledActionSendBeginMainFrame", client_, 1, 2); + EXPECT_TRUE(scheduler_->BeginImplFrameDeadlinePending()); + + client_->Reset(); + scheduler_->NotifyBeginMainFrameStarted(); + + client_->Reset(); + SendNextBeginFrame(); + // This BeginFrame is queued up as a retro frame. + EXPECT_NO_ACTION(client_); + // The previous deadline is still pending. + EXPECT_TRUE(scheduler_->BeginImplFrameDeadlinePending()); + + client_->Reset(); + // This commit should schedule the (previous) deadline to trigger immediately. + scheduler_->NotifyReadyToCommit(); + EXPECT_SINGLE_ACTION("ScheduledActionCommit", client_); + + client_->Reset(); + // The deadline task should trigger causing a draw. + EXPECT_TRUE(scheduler_->BeginImplFrameDeadlinePending()); + task_runner().RunTasksWhile(client_->ImplFrameDeadlinePending(true)); + EXPECT_ACTION("ScheduledActionAnimate", client_, 0, 2); + EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client_, 1, 2); + + // Keep animating. + client_->Reset(); + scheduler_->SetNeedsAnimate(); + scheduler_->SetNeedsRedraw(); + EXPECT_NO_ACTION(client_); + + // Let's advance sufficiently past the next frame's deadline. + now_src()->AdvanceNow(BeginFrameArgs::DefaultInterval() + + base::TimeDelta::FromMicroseconds(1)); + + // The retro frame should've expired. + EXPECT_NO_ACTION(client_); } -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); +void SchedulerTest::BeginFramesNotFromClient( + bool use_external_begin_frame_source, + bool throttle_frame_production) { + scheduler_settings_.use_external_begin_frame_source = + use_external_begin_frame_source; + scheduler_settings_.throttle_frame_production = throttle_frame_production; + SetUpScheduler(true); // SetNeedsCommit should begin the frame on the next BeginImplFrame // without calling SetNeedsBeginFrame. - client.Reset(); - scheduler->SetNeedsCommit(); - EXPECT_FALSE(client.needs_begin_frames()); - EXPECT_NO_ACTION(client); - client.Reset(); + scheduler_->SetNeedsCommit(); + 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_frames()); - client.Reset(); + task_runner().RunPendingTasks(); // Run posted BeginFrame. + EXPECT_ACTION("WillBeginImplFrame", client_, 0, 2); + EXPECT_ACTION("ScheduledActionSendBeginMainFrame", client_, 1, 2); + EXPECT_TRUE(scheduler_->BeginImplFrameDeadlinePending()); + 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_frames()); - client.Reset(); + task_runner().RunPendingTasks(); // Run posted deadline. + EXPECT_NO_ACTION(client_); + EXPECT_FALSE(scheduler_->BeginImplFrameDeadlinePending()); + client_->Reset(); // NotifyReadyToCommit should trigger the commit. - scheduler->NotifyBeginMainFrameStarted(); - scheduler->NotifyReadyToCommit(); - EXPECT_SINGLE_ACTION("ScheduledActionCommit", client); - EXPECT_FALSE(client.needs_begin_frames()); - client.Reset(); + scheduler_->NotifyBeginMainFrameStarted(); + scheduler_->NotifyReadyToCommit(); + EXPECT_SINGLE_ACTION("ScheduledActionCommit", client_); + 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_frames()); - client.Reset(); + task_runner().RunPendingTasks(); // Run posted BeginFrame. + EXPECT_ACTION("WillBeginImplFrame", client_, 0, 2); + EXPECT_ACTION("ScheduledActionAnimate", client_, 1, 2); + EXPECT_TRUE(scheduler_->BeginImplFrameDeadlinePending()); + client_->Reset(); // BeginImplFrame deadline should draw. - client.task_runner().RunTasksWhile(client.ImplFrameDeadlinePending(true)); - EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client, 0, 1); - EXPECT_FALSE(scheduler->BeginImplFrameDeadlinePending()); - EXPECT_FALSE(client.needs_begin_frames()); - client.Reset(); + task_runner().RunTasksWhile(client_->ImplFrameDeadlinePending(true)); + EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client_, 0, 1); + EXPECT_FALSE(scheduler_->BeginImplFrameDeadlinePending()); + 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(); + 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_frames()); - client.Reset(); + task_runner().RunPendingTasks(); // Run posted deadline. + EXPECT_SINGLE_ACTION("SendBeginMainFrameNotExpectedSoon", client_); + client_->Reset(); } -TEST(SchedulerTest, SyntheticBeginFrames) { - bool begin_frame_scheduling_enabled = false; +TEST_F(SchedulerTest, SyntheticBeginFrames) { + bool use_external_begin_frame_source = false; bool throttle_frame_production = true; - BeginFramesNotFromClient(begin_frame_scheduling_enabled, + BeginFramesNotFromClient(use_external_begin_frame_source, throttle_frame_production); } -TEST(SchedulerTest, VSyncThrottlingDisabled) { - bool begin_frame_scheduling_enabled = true; +TEST_F(SchedulerTest, VSyncThrottlingDisabled) { + bool use_external_begin_frame_source = true; bool throttle_frame_production = false; - BeginFramesNotFromClient(begin_frame_scheduling_enabled, + BeginFramesNotFromClient(use_external_begin_frame_source, throttle_frame_production); } -TEST(SchedulerTest, SyntheticBeginFrames_And_VSyncThrottlingDisabled) { - bool begin_frame_scheduling_enabled = false; +TEST_F(SchedulerTest, SyntheticBeginFrames_And_VSyncThrottlingDisabled) { + bool use_external_begin_frame_source = false; bool throttle_frame_production = false; - BeginFramesNotFromClient(begin_frame_scheduling_enabled, + BeginFramesNotFromClient(use_external_begin_frame_source, 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); +void SchedulerTest::BeginFramesNotFromClient_SwapThrottled( + bool use_external_begin_frame_source, + bool throttle_frame_production) { + scheduler_settings_.use_external_begin_frame_source = + use_external_begin_frame_source; + scheduler_settings_.throttle_frame_production = throttle_frame_production; + SetUpScheduler(true); + + scheduler_->SetEstimatedParentDrawTime(base::TimeDelta::FromMicroseconds(1)); // To test swap ack throttling, this test disables automatic swap acks. - scheduler->SetMaxSwapsPending(1); - client.SetAutomaticSwapAck(false); + scheduler_->SetMaxSwapsPending(1); + client_->SetAutomaticSwapAck(false); // SetNeedsCommit should begin the frame on the next BeginImplFrame. - client.Reset(); - scheduler->SetNeedsCommit(); - EXPECT_FALSE(client.needs_begin_frames()); - EXPECT_NO_ACTION(client); - client.Reset(); + client_->Reset(); + scheduler_->SetNeedsCommit(); + 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_frames()); - client.Reset(); + EXPECT_SCOPED(AdvanceFrame()); + EXPECT_ACTION("WillBeginImplFrame", client_, 0, 2); + EXPECT_ACTION("ScheduledActionSendBeginMainFrame", client_, 1, 2); + EXPECT_TRUE(scheduler_->BeginImplFrameDeadlinePending()); + client_->Reset(); // NotifyReadyToCommit should trigger the pending commit and draw. - scheduler->NotifyBeginMainFrameStarted(); - scheduler->NotifyReadyToCommit(); - EXPECT_SINGLE_ACTION("ScheduledActionCommit", client); - EXPECT_FALSE(client.needs_begin_frames()); - client.Reset(); + scheduler_->NotifyBeginMainFrameStarted(); + scheduler_->NotifyReadyToCommit(); + EXPECT_SINGLE_ACTION("ScheduledActionCommit", client_); + 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_frames()); - client.Reset(); + // Run posted deadline. + task_runner().RunTasksWhile(client_->ImplFrameDeadlinePending(true)); + EXPECT_ACTION("ScheduledActionAnimate", client_, 0, 2); + EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client_, 1, 2); + EXPECT_FALSE(scheduler_->BeginImplFrameDeadlinePending()); + 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_frames()); - client.Reset(); + scheduler_->SetNeedsCommit(); + scheduler_->SetNeedsRedraw(); + EXPECT_SCOPED(AdvanceFrame()); // Run posted BeginFrame. + EXPECT_ACTION("WillBeginImplFrame", client_, 0, 2); + EXPECT_ACTION("ScheduledActionAnimate", client_, 1, 2); + EXPECT_TRUE(scheduler_->BeginImplFrameDeadlinePending()); + client_->Reset(); + + // Let time pass sufficiently beyond the regular deadline but not beyond the + // late deadline. + now_src()->AdvanceNow(BeginFrameArgs::DefaultInterval() - + base::TimeDelta::FromMicroseconds(1)); + task_runner().RunUntilTime(now_src()->Now()); + EXPECT_TRUE(scheduler_->BeginImplFrameDeadlinePending()); // 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_frames()); - 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_frames()); - client.Reset(); + scheduler_->DidSwapBuffersComplete(); + EXPECT_SINGLE_ACTION("ScheduledActionSendBeginMainFrame", client_); + EXPECT_TRUE(scheduler_->BeginImplFrameDeadlinePending()); + client_->Reset(); + + // Verify that the deadline was rescheduled. + // We can't use RunUntilTime(now) here because the next frame is also + // scheduled if throttle_frame_production = false. + base::TimeTicks before_deadline = now_src()->Now(); + task_runner().RunTasksWhile(client_->ImplFrameDeadlinePending(true)); + base::TimeTicks after_deadline = now_src()->Now(); + EXPECT_EQ(after_deadline, before_deadline); + EXPECT_FALSE(scheduler_->BeginImplFrameDeadlinePending()); + client_->Reset(); } -TEST(SchedulerTest, SyntheticBeginFrames_SwapThrottled) { - bool begin_frame_scheduling_enabled = false; +TEST_F(SchedulerTest, SyntheticBeginFrames_SwapThrottled) { + bool use_external_begin_frame_source = false; bool throttle_frame_production = true; - BeginFramesNotFromClient_SwapThrottled(begin_frame_scheduling_enabled, + BeginFramesNotFromClient_SwapThrottled(use_external_begin_frame_source, throttle_frame_production); } -TEST(SchedulerTest, VSyncThrottlingDisabled_SwapThrottled) { - bool begin_frame_scheduling_enabled = true; +TEST_F(SchedulerTest, VSyncThrottlingDisabled_SwapThrottled) { + bool use_external_begin_frame_source = true; bool throttle_frame_production = false; - BeginFramesNotFromClient_SwapThrottled(begin_frame_scheduling_enabled, + BeginFramesNotFromClient_SwapThrottled(use_external_begin_frame_source, throttle_frame_production); } -TEST(SchedulerTest, - SyntheticBeginFrames_And_VSyncThrottlingDisabled_SwapThrottled) { - bool begin_frame_scheduling_enabled = false; +TEST_F(SchedulerTest, + SyntheticBeginFrames_And_VSyncThrottlingDisabled_SwapThrottled) { + bool use_external_begin_frame_source = false; bool throttle_frame_production = false; - BeginFramesNotFromClient_SwapThrottled(begin_frame_scheduling_enabled, + BeginFramesNotFromClient_SwapThrottled(use_external_begin_frame_source, 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_F(SchedulerTest, DidLoseOutputSurfaceAfterOutputSurfaceIsInitialized) { + scheduler_settings_.use_external_begin_frame_source = true; + SetUpScheduler(false); -TEST(SchedulerTest, DidLoseOutputSurfaceAfterBeginFrameStarted) { - FakeSchedulerClient client; - SchedulerSettings scheduler_settings; - TestScheduler* scheduler = client.CreateScheduler(scheduler_settings); - scheduler->SetCanStart(); - scheduler->SetVisible(true); - scheduler->SetCanDraw(true); + 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("SetNeedsBeginFrames(true)", client); + EXPECT_SINGLE_ACTION("ScheduledActionBeginOutputSurfaceCreation", client_); + client_->Reset(); + scheduler_->DidCreateAndInitializeOutputSurface(); + EXPECT_NO_ACTION(client_); - client.Reset(); - client.AdvanceFrame(); - 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_SINGLE_ACTION("SetNeedsBeginFrames(false)", client); + scheduler_->DidLoseOutputSurface(); + EXPECT_SINGLE_ACTION("ScheduledActionBeginOutputSurfaceCreation", client_); +} - client.Reset(); - scheduler->NotifyBeginMainFrameStarted(); - scheduler->NotifyReadyToCommit(); - EXPECT_ACTION("ScheduledActionCommit", client, 0, 1); +TEST_F(SchedulerTest, DidLoseOutputSurfaceAfterBeginFrameStarted) { + scheduler_settings_.use_external_begin_frame_source = true; + SetUpScheduler(true); - client.Reset(); - client.task_runner().RunPendingTasks(); // Run posted deadline. - EXPECT_SINGLE_ACTION("ScheduledActionBeginOutputSurfaceCreation", client); + // SetNeedsCommit should begin the frame. + scheduler_->SetNeedsCommit(); + EXPECT_SINGLE_ACTION("SetNeedsBeginFrames(true)", client_); + + client_->Reset(); + EXPECT_SCOPED(AdvanceFrame()); + EXPECT_ACTION("WillBeginImplFrame", client_, 0, 2); + EXPECT_ACTION("ScheduledActionSendBeginMainFrame", client_, 1, 2); + EXPECT_TRUE(scheduler_->BeginImplFrameDeadlinePending()); + + client_->Reset(); + scheduler_->DidLoseOutputSurface(); + // SetNeedsBeginFrames(false) is not called until the end of the frame. + EXPECT_NO_ACTION(client_); + + client_->Reset(); + scheduler_->NotifyBeginMainFrameStarted(); + scheduler_->NotifyReadyToCommit(); + EXPECT_ACTION("ScheduledActionCommit", client_, 0, 1); + + client_->Reset(); + task_runner().RunTasksWhile(client_->ImplFrameDeadlinePending(true)); + EXPECT_ACTION("ScheduledActionBeginOutputSurfaceCreation", client_, 0, 3); + EXPECT_ACTION("SetNeedsBeginFrames(false)", client_, 1, 3); + EXPECT_ACTION("SendBeginMainFrameNotExpectedSoon", client_, 2, 3); } -void DidLoseOutputSurfaceAfterBeginFrameStartedWithHighLatency( +void SchedulerTest::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); + scheduler_settings_.impl_side_painting = impl_side_painting; + scheduler_settings_.use_external_begin_frame_source = true; + SetUpScheduler(true); // SetNeedsCommit should begin the frame. - client.Reset(); - scheduler->SetNeedsCommit(); - EXPECT_SINGLE_ACTION("SetNeedsBeginFrames(true)", client); + scheduler_->SetNeedsCommit(); + EXPECT_SINGLE_ACTION("SetNeedsBeginFrames(true)", client_); - client.Reset(); - client.AdvanceFrame(); - EXPECT_ACTION("WillBeginImplFrame", client, 0, 2); - EXPECT_ACTION("ScheduledActionSendBeginMainFrame", client, 1, 2); - EXPECT_TRUE(scheduler->BeginImplFrameDeadlinePending()); + client_->Reset(); + EXPECT_SCOPED(AdvanceFrame()); + EXPECT_ACTION("WillBeginImplFrame", client_, 0, 2); + EXPECT_ACTION("ScheduledActionSendBeginMainFrame", client_, 1, 2); + EXPECT_TRUE(scheduler_->BeginImplFrameDeadlinePending()); - client.Reset(); - scheduler->DidLoseOutputSurface(); + client_->Reset(); + scheduler_->DidLoseOutputSurface(); // Do nothing when impl frame is in deadine pending state. - EXPECT_SINGLE_ACTION("SetNeedsBeginFrames(false)", client); + EXPECT_NO_ACTION(client_); - client.Reset(); + client_->Reset(); // Run posted deadline. - EXPECT_TRUE(scheduler->BeginImplFrameDeadlinePending()); - client.task_runner().RunTasksWhile(client.ImplFrameDeadlinePending(true)); - // OnBeginImplFrameDeadline didn't schedule any actions because main frame is - // not yet completed. - EXPECT_NO_ACTION(client); - EXPECT_FALSE(scheduler->BeginImplFrameDeadlinePending()); + EXPECT_TRUE(scheduler_->BeginImplFrameDeadlinePending()); + task_runner().RunTasksWhile(client_->ImplFrameDeadlinePending(true)); + // OnBeginImplFrameDeadline didn't schedule output surface creation because + // main frame is not yet completed. + EXPECT_ACTION("SetNeedsBeginFrames(false)", client_, 0, 2); + EXPECT_ACTION("SendBeginMainFrameNotExpectedSoon", client_, 1, 2); + EXPECT_FALSE(scheduler_->BeginImplFrameDeadlinePending()); // BeginImplFrame is not started. - client.task_runner().RunUntilTime(client.now_src()->Now() + - base::TimeDelta::FromMilliseconds(10)); - EXPECT_NO_ACTION(client); - EXPECT_FALSE(scheduler->BeginImplFrameDeadlinePending()); - - client.Reset(); - scheduler->NotifyBeginMainFrameStarted(); - scheduler->NotifyReadyToCommit(); + client_->Reset(); + task_runner().RunUntilTime(now_src()->Now() + + base::TimeDelta::FromMilliseconds(10)); + 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("ScheduledActionActivateSyncTree", client, 1, 3); - EXPECT_ACTION("ScheduledActionBeginOutputSurfaceCreation", client, 2, 3); + EXPECT_ACTION("ScheduledActionCommit", client_, 0, 3); + EXPECT_ACTION("ScheduledActionActivateSyncTree", client_, 1, 3); + EXPECT_ACTION("ScheduledActionBeginOutputSurfaceCreation", client_, 2, 3); } else { - EXPECT_ACTION("ScheduledActionCommit", client, 0, 2); - EXPECT_ACTION("ScheduledActionBeginOutputSurfaceCreation", client, 1, 2); + EXPECT_ACTION("ScheduledActionCommit", client_, 0, 2); + EXPECT_ACTION("ScheduledActionBeginOutputSurfaceCreation", client_, 1, 2); } } -TEST(SchedulerTest, DidLoseOutputSurfaceAfterBeginFrameStartedWithHighLatency) { +TEST_F(SchedulerTest, + DidLoseOutputSurfaceAfterBeginFrameStartedWithHighLatency) { bool impl_side_painting = false; DidLoseOutputSurfaceAfterBeginFrameStartedWithHighLatency(impl_side_painting); } -TEST(SchedulerTest, - DidLoseOutputSurfaceAfterBeginFrameStartedWithHighLatencyWithImplPaint) { +TEST_F(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); +void SchedulerTest::DidLoseOutputSurfaceAfterReadyToCommit( + bool impl_side_painting) { + scheduler_settings_.impl_side_painting = impl_side_painting; + scheduler_settings_.use_external_begin_frame_source = true; + SetUpScheduler(true); // SetNeedsCommit should begin the frame. - client.Reset(); - scheduler->SetNeedsCommit(); - EXPECT_SINGLE_ACTION("SetNeedsBeginFrames(true)", client); - - client.Reset(); - client.AdvanceFrame(); - 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(); + scheduler_->SetNeedsCommit(); + EXPECT_SINGLE_ACTION("SetNeedsBeginFrames(true)", client_); + + client_->Reset(); + EXPECT_SCOPED(AdvanceFrame()); + 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(); + // SetNeedsBeginFrames(false) is not called until the end of the frame. if (impl_side_painting) { // Sync tree should be forced to activate. - EXPECT_ACTION("SetNeedsBeginFrames(false)", client, 0, 2); - EXPECT_ACTION("ScheduledActionActivateSyncTree", client, 1, 2); + EXPECT_SINGLE_ACTION("ScheduledActionActivateSyncTree", client_); } else { - EXPECT_SINGLE_ACTION("SetNeedsBeginFrames(false)", client); + EXPECT_NO_ACTION(client_); } - client.Reset(); - client.task_runner().RunPendingTasks(); // Run posted deadline. - EXPECT_SINGLE_ACTION("ScheduledActionBeginOutputSurfaceCreation", client); + client_->Reset(); + task_runner().RunTasksWhile(client_->ImplFrameDeadlinePending(true)); + EXPECT_ACTION("ScheduledActionBeginOutputSurfaceCreation", client_, 0, 3); + EXPECT_ACTION("SetNeedsBeginFrames(false)", client_, 1, 3); + EXPECT_ACTION("SendBeginMainFrameNotExpectedSoon", client_, 2, 3); } -TEST(SchedulerTest, DidLoseOutputSurfaceAfterReadyToCommit) { +TEST_F(SchedulerTest, DidLoseOutputSurfaceAfterReadyToCommit) { DidLoseOutputSurfaceAfterReadyToCommit(false); } -TEST(SchedulerTest, DidLoseOutputSurfaceAfterReadyToCommitWithImplPainting) { +TEST_F(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("SetNeedsBeginFrames(true)", client); - - client.Reset(); - client.AdvanceFrame(); - EXPECT_ACTION("WillBeginImplFrame", client, 0, 2); - EXPECT_ACTION("ScheduledActionAnimate", client, 1, 2); - EXPECT_TRUE(scheduler->BeginImplFrameDeadlinePending()); - - client.Reset(); - scheduler->DidLoseOutputSurface(); - EXPECT_SINGLE_ACTION("SetNeedsBeginFrames(false)", client); - - client.Reset(); - client.task_runner().RunPendingTasks(); // Run posted deadline. - EXPECT_ACTION("ScheduledActionManageTiles", client, 0, 2); - EXPECT_ACTION("ScheduledActionBeginOutputSurfaceCreation", client, 1, 2); +TEST_F(SchedulerTest, DidLoseOutputSurfaceAfterSetNeedsPrepareTiles) { + scheduler_settings_.use_external_begin_frame_source = true; + SetUpScheduler(true); + + scheduler_->SetNeedsPrepareTiles(); + scheduler_->SetNeedsRedraw(); + EXPECT_SINGLE_ACTION("SetNeedsBeginFrames(true)", client_); + + client_->Reset(); + EXPECT_SCOPED(AdvanceFrame()); + EXPECT_ACTION("WillBeginImplFrame", client_, 0, 2); + EXPECT_ACTION("ScheduledActionAnimate", client_, 1, 2); + EXPECT_TRUE(scheduler_->BeginImplFrameDeadlinePending()); + + client_->Reset(); + scheduler_->DidLoseOutputSurface(); + // SetNeedsBeginFrames(false) is not called until the end of the frame. + EXPECT_NO_ACTION(client_); + + client_->Reset(); + task_runner().RunTasksWhile(client_->ImplFrameDeadlinePending(true)); + EXPECT_ACTION("ScheduledActionPrepareTiles", client_, 0, 4); + EXPECT_ACTION("ScheduledActionBeginOutputSurfaceCreation", client_, 1, 4); + EXPECT_ACTION("SetNeedsBeginFrames(false)", client_, 2, 4); + EXPECT_ACTION("SendBeginMainFrameNotExpectedSoon", client_, 3, 4); } -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); +TEST_F(SchedulerTest, DidLoseOutputSurfaceAfterBeginRetroFramePosted) { + scheduler_settings_.use_external_begin_frame_source = true; + SetUpScheduler(true); // SetNeedsCommit should begin the frame on the next BeginImplFrame. - client.Reset(); - scheduler->SetNeedsCommit(); - EXPECT_SINGLE_ACTION("SetNeedsBeginFrames(true)", client); + scheduler_->SetNeedsCommit(); + EXPECT_SINGLE_ACTION("SetNeedsBeginFrames(true)", 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(client.now_src()); + client_->Reset(); + BeginFrameArgs args = + CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, now_src()); args.deadline += base::TimeDelta::FromHours(1); - client.ExternalBeginFrameSource()->TestOnBeginFrame(args); - EXPECT_ACTION("WillBeginImplFrame", client, 0, 2); - EXPECT_ACTION("ScheduledActionSendBeginMainFrame", client, 1, 2); - EXPECT_TRUE(scheduler->BeginImplFrameDeadlinePending()); - EXPECT_TRUE(client.needs_begin_frames()); + fake_external_begin_frame_source()->TestOnBeginFrame(args); + EXPECT_ACTION("WillBeginImplFrame", client_, 0, 2); + EXPECT_ACTION("ScheduledActionSendBeginMainFrame", client_, 1, 2); + EXPECT_TRUE(scheduler_->BeginImplFrameDeadlinePending()); + EXPECT_TRUE(client_->needs_begin_frames()); // Queue BeginFrames while we are still handling the previous BeginFrame. args.frame_time += base::TimeDelta::FromSeconds(1); - client.ExternalBeginFrameSource()->TestOnBeginFrame(args); + fake_external_begin_frame_source()->TestOnBeginFrame(args); args.frame_time += base::TimeDelta::FromSeconds(1); - client.ExternalBeginFrameSource()->TestOnBeginFrame(args); + fake_external_begin_frame_source()->TestOnBeginFrame(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_frames()); + client_->Reset(); + task_runner().RunPendingTasks(); // Run posted deadline. + EXPECT_NO_ACTION(client_); + EXPECT_FALSE(scheduler_->BeginImplFrameDeadlinePending()); + EXPECT_TRUE(client_->needs_begin_frames()); // NotifyReadyToCommit should trigger the commit. - client.Reset(); - scheduler->NotifyBeginMainFrameStarted(); - scheduler->NotifyReadyToCommit(); - EXPECT_SINGLE_ACTION("ScheduledActionCommit", client); - EXPECT_TRUE(client.needs_begin_frames()); - - client.Reset(); - EXPECT_FALSE(scheduler->IsBeginRetroFrameArgsEmpty()); - scheduler->DidLoseOutputSurface(); - EXPECT_ACTION("SetNeedsBeginFrames(false)", client, 0, 2); - EXPECT_ACTION("ScheduledActionBeginOutputSurfaceCreation", client, 1, 2); - EXPECT_TRUE(scheduler->IsBeginRetroFrameArgsEmpty()); + client_->Reset(); + scheduler_->NotifyBeginMainFrameStarted(); + scheduler_->NotifyReadyToCommit(); + EXPECT_SINGLE_ACTION("ScheduledActionCommit", client_); + EXPECT_TRUE(client_->needs_begin_frames()); + + client_->Reset(); + EXPECT_FALSE(scheduler_->IsBeginRetroFrameArgsEmpty()); + scheduler_->DidLoseOutputSurface(); + EXPECT_ACTION("ScheduledActionBeginOutputSurfaceCreation", client_, 0, 3); + EXPECT_ACTION("SetNeedsBeginFrames(false)", client_, 1, 3); + EXPECT_ACTION("SendBeginMainFrameNotExpectedSoon", client_, 2, 3); + EXPECT_TRUE(scheduler_->IsBeginRetroFrameArgsEmpty()); // Posted BeginRetroFrame is aborted. - client.Reset(); - client.task_runner().RunPendingTasks(); - EXPECT_NO_ACTION(client); + client_->Reset(); + 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); +TEST_F(SchedulerTest, DidLoseOutputSurfaceDuringBeginRetroFrameRunning) { + scheduler_settings_.use_external_begin_frame_source = true; + SetUpScheduler(true); // SetNeedsCommit should begin the frame on the next BeginImplFrame. - client.Reset(); - scheduler->SetNeedsCommit(); - EXPECT_SINGLE_ACTION("SetNeedsBeginFrames(true)", client); + scheduler_->SetNeedsCommit(); + EXPECT_SINGLE_ACTION("SetNeedsBeginFrames(true)", 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(client.now_src()); + client_->Reset(); + BeginFrameArgs args = + CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, now_src()); args.deadline += base::TimeDelta::FromHours(1); - client.ExternalBeginFrameSource()->TestOnBeginFrame(args); - EXPECT_ACTION("WillBeginImplFrame", client, 0, 2); - EXPECT_ACTION("ScheduledActionSendBeginMainFrame", client, 1, 2); - EXPECT_TRUE(scheduler->BeginImplFrameDeadlinePending()); - EXPECT_TRUE(client.needs_begin_frames()); + fake_external_begin_frame_source()->TestOnBeginFrame(args); + EXPECT_ACTION("WillBeginImplFrame", client_, 0, 2); + EXPECT_ACTION("ScheduledActionSendBeginMainFrame", client_, 1, 2); + EXPECT_TRUE(scheduler_->BeginImplFrameDeadlinePending()); + EXPECT_TRUE(client_->needs_begin_frames()); // Queue BeginFrames while we are still handling the previous BeginFrame. args.frame_time += base::TimeDelta::FromSeconds(1); - client.ExternalBeginFrameSource()->TestOnBeginFrame(args); + fake_external_begin_frame_source()->TestOnBeginFrame(args); args.frame_time += base::TimeDelta::FromSeconds(1); - client.ExternalBeginFrameSource()->TestOnBeginFrame(args); + fake_external_begin_frame_source()->TestOnBeginFrame(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_frames()); + client_->Reset(); + task_runner().RunPendingTasks(); // Run posted deadline. + EXPECT_NO_ACTION(client_); + EXPECT_FALSE(scheduler_->BeginImplFrameDeadlinePending()); + EXPECT_TRUE(client_->needs_begin_frames()); // NotifyReadyToCommit should trigger the commit. - client.Reset(); - scheduler->NotifyBeginMainFrameStarted(); - scheduler->NotifyReadyToCommit(); - EXPECT_SINGLE_ACTION("ScheduledActionCommit", client); - EXPECT_TRUE(client.needs_begin_frames()); + client_->Reset(); + scheduler_->NotifyBeginMainFrameStarted(); + scheduler_->NotifyReadyToCommit(); + EXPECT_SINGLE_ACTION("ScheduledActionCommit", client_); + EXPECT_TRUE(client_->needs_begin_frames()); // 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_frames()); - - client.Reset(); - EXPECT_FALSE(scheduler->IsBeginRetroFrameArgsEmpty()); - scheduler->DidLoseOutputSurface(); - EXPECT_SINGLE_ACTION("SetNeedsBeginFrames(false)", client); - EXPECT_TRUE(scheduler->IsBeginRetroFrameArgsEmpty()); + client_->Reset(); + 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_frames()); + + 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_FALSE(client.needs_begin_frames()); + client_->Reset(); + task_runner().RunTasksWhile(client_->ImplFrameDeadlinePending(true)); + EXPECT_ACTION("ScheduledActionBeginOutputSurfaceCreation", client_, 0, 3); + EXPECT_ACTION("SetNeedsBeginFrames(false)", client_, 1, 3); + EXPECT_ACTION("SendBeginMainFrameNotExpectedSoon", client_, 2, 3); + EXPECT_FALSE(scheduler_->BeginImplFrameDeadlinePending()); + EXPECT_FALSE(client_->needs_begin_frames()); // No more BeginRetroFrame because BeginRetroFrame queue is cleared. - client.Reset(); - client.task_runner().RunPendingTasks(); - EXPECT_NO_ACTION(client); + client_->Reset(); + 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); +TEST_F(SchedulerTest, DidLoseOutputSurfaceWithSyntheticBeginFrameSource) { + SetUpScheduler(true); // SetNeedsCommit should begin the frame on the next BeginImplFrame. - client.Reset(); - EXPECT_FALSE(scheduler->frame_source().NeedsBeginFrames()); - scheduler->SetNeedsCommit(); - EXPECT_TRUE(scheduler->frame_source().NeedsBeginFrames()); + EXPECT_FALSE(scheduler_->frame_source().NeedsBeginFrames()); + scheduler_->SetNeedsCommit(); + EXPECT_TRUE(scheduler_->frame_source().NeedsBeginFrames()); - 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->frame_source().NeedsBeginFrames()); + client_->Reset(); + AdvanceFrame(); + EXPECT_ACTION("WillBeginImplFrame", client_, 0, 2); + EXPECT_ACTION("ScheduledActionSendBeginMainFrame", client_, 1, 2); + EXPECT_TRUE(scheduler_->BeginImplFrameDeadlinePending()); + EXPECT_TRUE(scheduler_->frame_source().NeedsBeginFrames()); // NotifyReadyToCommit should trigger the commit. - client.Reset(); - scheduler->NotifyBeginMainFrameStarted(); - scheduler->NotifyReadyToCommit(); - EXPECT_SINGLE_ACTION("ScheduledActionCommit", client); - EXPECT_TRUE(scheduler->frame_source().NeedsBeginFrames()); - - client.Reset(); - scheduler->DidLoseOutputSurface(); - EXPECT_NO_ACTION(client); - EXPECT_FALSE(scheduler->frame_source().NeedsBeginFrames()); - - client.Reset(); - client.task_runner().RunPendingTasks(); // Run posted deadline. - EXPECT_SINGLE_ACTION("ScheduledActionBeginOutputSurfaceCreation", client); - EXPECT_FALSE(scheduler->frame_source().NeedsBeginFrames()); + client_->Reset(); + scheduler_->NotifyBeginMainFrameStarted(); + scheduler_->NotifyReadyToCommit(); + EXPECT_SINGLE_ACTION("ScheduledActionCommit", client_); + EXPECT_TRUE(scheduler_->frame_source().NeedsBeginFrames()); + + client_->Reset(); + scheduler_->DidLoseOutputSurface(); + // SetNeedsBeginFrames(false) is not called until the end of the frame. + EXPECT_NO_ACTION(client_); + EXPECT_TRUE(scheduler_->frame_source().NeedsBeginFrames()); + + client_->Reset(); + task_runner().RunTasksWhile(client_->ImplFrameDeadlinePending(true)); + EXPECT_ACTION("ScheduledActionBeginOutputSurfaceCreation", client_, 0, 2); + EXPECT_ACTION("SendBeginMainFrameNotExpectedSoon", client_, 1, 2); + EXPECT_FALSE(scheduler_->frame_source().NeedsBeginFrames()); } -TEST(SchedulerTest, ScheduledActionActivateAfterBecomingInvisible) { - 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); +TEST_F(SchedulerTest, DidLoseOutputSurfaceWhenIdle) { + scheduler_settings_.use_external_begin_frame_source = true; + SetUpScheduler(true); - EXPECT_SINGLE_ACTION("ScheduledActionBeginOutputSurfaceCreation", client); - InitializeOutputSurfaceAndFirstCommit(scheduler, &client); + // SetNeedsCommit should begin the frame. + scheduler_->SetNeedsCommit(); + EXPECT_SINGLE_ACTION("SetNeedsBeginFrames(true)", client_); + + client_->Reset(); + EXPECT_SCOPED(AdvanceFrame()); + 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(); + task_runner().RunTasksWhile(client_->ImplFrameDeadlinePending(true)); + EXPECT_ACTION("ScheduledActionAnimate", client_, 0, 2); + EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client_, 1, 2); + + // Idle time between BeginFrames. + client_->Reset(); + scheduler_->DidLoseOutputSurface(); + EXPECT_ACTION("ScheduledActionBeginOutputSurfaceCreation", client_, 0, 3); + EXPECT_ACTION("SetNeedsBeginFrames(false)", client_, 1, 3); + EXPECT_ACTION("SendBeginMainFrameNotExpectedSoon", client_, 2, 3); +} + +TEST_F(SchedulerTest, ScheduledActionActivateAfterBecomingInvisible) { + scheduler_settings_.impl_side_painting = true; + scheduler_settings_.use_external_begin_frame_source = true; + SetUpScheduler(true); // SetNeedsCommit should begin the frame. - client.Reset(); - scheduler->SetNeedsCommit(); - EXPECT_SINGLE_ACTION("SetNeedsBeginFrames(true)", client); + scheduler_->SetNeedsCommit(); + EXPECT_SINGLE_ACTION("SetNeedsBeginFrames(true)", client_); - client.Reset(); - client.AdvanceFrame(); - EXPECT_ACTION("WillBeginImplFrame", client, 0, 2); - EXPECT_ACTION("ScheduledActionSendBeginMainFrame", client, 1, 2); - EXPECT_TRUE(scheduler->BeginImplFrameDeadlinePending()); + client_->Reset(); + EXPECT_SCOPED(AdvanceFrame()); + 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_->NotifyBeginMainFrameStarted(); + scheduler_->NotifyReadyToCommit(); + EXPECT_SINGLE_ACTION("ScheduledActionCommit", client_); + EXPECT_TRUE(scheduler_->BeginImplFrameDeadlinePending()); + + client_->Reset(); + scheduler_->SetVisible(false); + task_runner().RunPendingTasks(); // Run posted deadline. - client.Reset(); - scheduler->SetVisible(false); // Sync tree should be forced to activate. - EXPECT_ACTION("SetNeedsBeginFrames(false)", client, 0, 2); - EXPECT_ACTION("ScheduledActionActivateSyncTree", client, 1, 2); + EXPECT_ACTION("ScheduledActionActivateSyncTree", client_, 0, 3); + EXPECT_ACTION("SetNeedsBeginFrames(false)", client_, 1, 3); + EXPECT_ACTION("SendBeginMainFrameNotExpectedSoon", client_, 2, 3); } -TEST(SchedulerTest, SchedulerPowerMonitoring) { - FakeSchedulerClient client; - SchedulerSettings settings; - settings.disable_hi_res_timer_tasks_on_battery = true; - TestScheduler* scheduler = client.CreateScheduler(settings); - - base::TimeTicks before_deadline, after_deadline; +// Tests to ensure frame sources can be successfully changed while drawing. +TEST_F(SchedulerTest, SwitchFrameSourceToUnthrottled) { + scheduler_settings_.use_external_begin_frame_source = true; + SetUpScheduler(true); + + // SetNeedsRedraw should begin the frame on the next BeginImplFrame. + scheduler_->SetNeedsRedraw(); + EXPECT_SINGLE_ACTION("SetNeedsBeginFrames(true)", client_); + client_->Reset(); + + EXPECT_SCOPED(AdvanceFrame()); + EXPECT_ACTION("WillBeginImplFrame", client_, 0, 2); + EXPECT_ACTION("ScheduledActionAnimate", client_, 1, 2); + EXPECT_TRUE(scheduler_->BeginImplFrameDeadlinePending()); + EXPECT_TRUE(client_->needs_begin_frames()); + client_->Reset(); + task_runner().RunPendingTasks(); // Run posted deadline. + EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client_, 0, 1); + scheduler_->SetNeedsRedraw(); + + // Switch to an unthrottled frame source. + scheduler_->SetThrottleFrameProduction(false); + client_->Reset(); + + // Unthrottled frame source will immediately begin a new frame. + task_runner().RunPendingTasks(); // Run posted BeginFrame. + EXPECT_ACTION("WillBeginImplFrame", client_, 0, 2); + EXPECT_ACTION("ScheduledActionAnimate", client_, 1, 2); + EXPECT_TRUE(scheduler_->BeginImplFrameDeadlinePending()); + client_->Reset(); - scheduler->SetCanStart(); - scheduler->SetVisible(true); - scheduler->SetCanDraw(true); + // If we don't swap on the deadline, we wait for the next BeginFrame. + task_runner().RunPendingTasks(); // Run posted deadline. + EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client_, 0, 1); + EXPECT_FALSE(scheduler_->BeginImplFrameDeadlinePending()); + client_->Reset(); +} - InitializeOutputSurfaceAndFirstCommit(scheduler, &client); +// Tests to ensure frame sources can be successfully changed while a frame +// deadline is pending. +TEST_F(SchedulerTest, SwitchFrameSourceToUnthrottledBeforeDeadline) { + scheduler_settings_.use_external_begin_frame_source = true; + SetUpScheduler(true); + + // SetNeedsRedraw should begin the frame on the next BeginImplFrame. + scheduler_->SetNeedsRedraw(); + EXPECT_SINGLE_ACTION("SetNeedsBeginFrames(true)", client_); + client_->Reset(); + + EXPECT_SCOPED(AdvanceFrame()); + EXPECT_ACTION("WillBeginImplFrame", client_, 0, 2); + EXPECT_ACTION("ScheduledActionAnimate", client_, 1, 2); + + // Switch to an unthrottled frame source before the frame deadline is hit. + scheduler_->SetThrottleFrameProduction(false); + client_->Reset(); + + EXPECT_TRUE(scheduler_->BeginImplFrameDeadlinePending()); + EXPECT_TRUE(client_->needs_begin_frames()); + client_->Reset(); + + task_runner().RunPendingTasks(); // Run posted deadline and BeginFrame. + EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client_, 0, 2); + // Unthrottled frame source will immediately begin a new frame. + EXPECT_ACTION("WillBeginImplFrame", client_, 1, 2); + scheduler_->SetNeedsRedraw(); + client_->Reset(); + + task_runner().RunPendingTasks(); // Run posted deadline. + EXPECT_ACTION("ScheduledActionAnimate", client_, 0, 2); + EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client_, 1, 2); + EXPECT_FALSE(scheduler_->BeginImplFrameDeadlinePending()); + client_->Reset(); +} - scheduler->SetNeedsCommit(); - scheduler->SetNeedsRedraw(); - client.Reset(); +// Tests to ensure that the active frame source can successfully be changed from +// unthrottled to throttled. +TEST_F(SchedulerTest, SwitchFrameSourceToThrottled) { + scheduler_settings_.throttle_frame_production = false; + scheduler_settings_.use_external_begin_frame_source = true; + SetUpScheduler(true); + + scheduler_->SetNeedsRedraw(); + EXPECT_NO_ACTION(client_); + client_->Reset(); + + task_runner().RunPendingTasks(); // Run posted BeginFrame. + EXPECT_ACTION("WillBeginImplFrame", client_, 0, 2); + EXPECT_ACTION("ScheduledActionAnimate", client_, 1, 2); + EXPECT_TRUE(scheduler_->BeginImplFrameDeadlinePending()); + client_->Reset(); + + task_runner().RunPendingTasks(); // Run posted deadline. + EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client_, 0, 1); + EXPECT_FALSE(scheduler_->BeginImplFrameDeadlinePending()); + client_->Reset(); + + // Switch to a throttled frame source. + scheduler_->SetThrottleFrameProduction(true); + client_->Reset(); + + // SetNeedsRedraw should begin the frame on the next BeginImplFrame. + scheduler_->SetNeedsRedraw(); + task_runner().RunPendingTasks(); + EXPECT_NO_ACTION(client_); + client_->Reset(); + + EXPECT_SCOPED(AdvanceFrame()); + EXPECT_ACTION("WillBeginImplFrame", client_, 0, 2); + EXPECT_ACTION("ScheduledActionAnimate", client_, 1, 2); + EXPECT_TRUE(scheduler_->BeginImplFrameDeadlinePending()); + EXPECT_TRUE(client_->needs_begin_frames()); + client_->Reset(); + task_runner().RunPendingTasks(); // Run posted deadline. + EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client_, 0, 1); +} - // On non-battery power - EXPECT_FALSE(client.PowerMonitor()->IsOnBatteryPower()); +// Tests to ensure that we send a BeginMainFrameNotExpectedSoon when expected. +TEST_F(SchedulerTest, SendBeginMainFrameNotExpectedSoon) { + scheduler_settings_.use_external_begin_frame_source = true; + SetUpScheduler(true); - client.AdvanceFrame(); - client.Reset(); + // SetNeedsCommit should begin the frame on the next BeginImplFrame. + scheduler_->SetNeedsCommit(); + EXPECT_SINGLE_ACTION("SetNeedsBeginFrames(true)", client_); + client_->Reset(); + + // Trigger a frame draw. + EXPECT_SCOPED(AdvanceFrame()); + scheduler_->NotifyBeginMainFrameStarted(); + scheduler_->NotifyReadyToCommit(); + task_runner().RunPendingTasks(); + EXPECT_ACTION("WillBeginImplFrame", client_, 0, 5); + EXPECT_ACTION("ScheduledActionSendBeginMainFrame", client_, 1, 5); + EXPECT_ACTION("ScheduledActionCommit", client_, 2, 5); + EXPECT_ACTION("ScheduledActionAnimate", client_, 3, 5); + EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client_, 4, 5); + client_->Reset(); - before_deadline = client.now_src()->Now(); - EXPECT_TRUE(client.task_runner().RunTasksWhile( - client.ImplFrameDeadlinePending(true))); - after_deadline = client.now_src()->Now(); + // The following BeginImplFrame deadline should SetNeedsBeginFrame(false) + // and send a SendBeginMainFrameNotExpectedSoon. + EXPECT_SCOPED(AdvanceFrame()); + EXPECT_SINGLE_ACTION("WillBeginImplFrame", client_); + EXPECT_TRUE(scheduler_->BeginImplFrameDeadlinePending()); + client_->Reset(); + + task_runner().RunPendingTasks(); // Run posted deadline. + EXPECT_ACTION("SetNeedsBeginFrames(false)", client_, 0, 2); + EXPECT_ACTION("SendBeginMainFrameNotExpectedSoon", client_, 1, 2); + client_->Reset(); +} - // We post a non-zero deadline task when not on battery - EXPECT_LT(before_deadline, after_deadline); +TEST_F(SchedulerTest, SynchronousCompositorAnimation) { + scheduler_settings_.using_synchronous_renderer_compositor = true; + scheduler_settings_.use_external_begin_frame_source = true; + scheduler_settings_.impl_side_painting = true; + SetUpScheduler(true); + + scheduler_->SetNeedsAnimate(); + EXPECT_SINGLE_ACTION("SetNeedsBeginFrames(true)", client_); + client_->Reset(); + + // Next vsync. + AdvanceFrame(); + EXPECT_ACTION("WillBeginImplFrame", client_, 0, 3); + EXPECT_ACTION("ScheduledActionAnimate", client_, 1, 3); + EXPECT_ACTION("ScheduledActionInvalidateOutputSurface", client_, 2, 3); + EXPECT_FALSE(scheduler_->BeginImplFrameDeadlinePending()); + client_->Reset(); + + // Continue with animation. + scheduler_->SetNeedsAnimate(); + EXPECT_NO_ACTION(client_); + + // Android onDraw. + scheduler_->SetNeedsRedraw(); + scheduler_->OnDrawForOutputSurface(); + EXPECT_SINGLE_ACTION("ScheduledActionDrawAndSwapIfPossible", client_); + EXPECT_FALSE(scheduler_->BeginImplFrameDeadlinePending()); + client_->Reset(); + + // Next vsync. + AdvanceFrame(); + EXPECT_ACTION("WillBeginImplFrame", client_, 0, 3); + EXPECT_ACTION("ScheduledActionAnimate", client_, 1, 3); + EXPECT_ACTION("ScheduledActionInvalidateOutputSurface", client_, 2, 3); + EXPECT_FALSE(scheduler_->BeginImplFrameDeadlinePending()); + client_->Reset(); + + // Android onDraw. + scheduler_->SetNeedsRedraw(); + scheduler_->OnDrawForOutputSurface(); + EXPECT_SINGLE_ACTION("ScheduledActionDrawAndSwapIfPossible", client_); + EXPECT_FALSE(scheduler_->BeginImplFrameDeadlinePending()); + client_->Reset(); + + // Idle on next vsync. + AdvanceFrame(); + EXPECT_ACTION("WillBeginImplFrame", client_, 0, 3); + EXPECT_ACTION("SetNeedsBeginFrames(false)", client_, 1, 3); + EXPECT_ACTION("SendBeginMainFrameNotExpectedSoon", client_, 2, 3); + EXPECT_FALSE(scheduler_->BeginImplFrameDeadlinePending()); + client_->Reset(); +} - // Switch to battery power - client.PowerMonitorSource()->GeneratePowerStateEvent(true); - EXPECT_TRUE(client.PowerMonitor()->IsOnBatteryPower()); +TEST_F(SchedulerTest, SynchronousCompositorOnDrawDuringIdle) { + scheduler_settings_.using_synchronous_renderer_compositor = true; + scheduler_settings_.use_external_begin_frame_source = true; + scheduler_settings_.impl_side_painting = true; + SetUpScheduler(true); + + scheduler_->SetNeedsRedraw(); + scheduler_->OnDrawForOutputSurface(); + EXPECT_ACTION("SetNeedsBeginFrames(true)", client_, 0, 3); + EXPECT_ACTION("ScheduledActionAnimate", client_, 1, 3); + EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client_, 2, 3); + EXPECT_FALSE(scheduler_->BeginImplFrameDeadlinePending()); + client_->Reset(); + + // Idle on next vsync. + AdvanceFrame(); + EXPECT_ACTION("WillBeginImplFrame", client_, 0, 3); + EXPECT_ACTION("SetNeedsBeginFrames(false)", client_, 1, 3); + EXPECT_ACTION("SendBeginMainFrameNotExpectedSoon", client_, 2, 3); + EXPECT_FALSE(scheduler_->BeginImplFrameDeadlinePending()); + client_->Reset(); +} - client.AdvanceFrame(); - scheduler->SetNeedsCommit(); - scheduler->SetNeedsRedraw(); - client.Reset(); +TEST_F(SchedulerTest, SynchronousCompositorCommit) { + scheduler_settings_.using_synchronous_renderer_compositor = true; + scheduler_settings_.use_external_begin_frame_source = true; + scheduler_settings_.impl_side_painting = true; + SetUpScheduler(true); + + scheduler_->SetNeedsCommit(); + EXPECT_SINGLE_ACTION("SetNeedsBeginFrames(true)", client_); + client_->Reset(); + + // Next vsync. + AdvanceFrame(); + EXPECT_ACTION("WillBeginImplFrame", client_, 0, 2); + EXPECT_ACTION("ScheduledActionSendBeginMainFrame", client_, 1, 2); + EXPECT_FALSE(scheduler_->BeginImplFrameDeadlinePending()); + client_->Reset(); + + scheduler_->NotifyBeginMainFrameStarted(); + EXPECT_NO_ACTION(client_); + + // Next vsync. + AdvanceFrame(); + EXPECT_SINGLE_ACTION("WillBeginImplFrame", client_); + EXPECT_FALSE(scheduler_->BeginImplFrameDeadlinePending()); + client_->Reset(); + + scheduler_->NotifyReadyToCommit(); + EXPECT_SINGLE_ACTION("ScheduledActionCommit", client_); + client_->Reset(); + + scheduler_->NotifyReadyToActivate(); + EXPECT_SINGLE_ACTION("ScheduledActionActivateSyncTree", client_); + client_->Reset(); + + // Next vsync. + AdvanceFrame(); + EXPECT_ACTION("WillBeginImplFrame", client_, 0, 3); + EXPECT_ACTION("ScheduledActionAnimate", client_, 1, 3); + EXPECT_ACTION("ScheduledActionInvalidateOutputSurface", client_, 2, 3); + EXPECT_FALSE(scheduler_->BeginImplFrameDeadlinePending()); + client_->Reset(); + + // Android onDraw. + scheduler_->SetNeedsRedraw(); + scheduler_->OnDrawForOutputSurface(); + EXPECT_SINGLE_ACTION("ScheduledActionDrawAndSwapIfPossible", client_); + EXPECT_FALSE(scheduler_->BeginImplFrameDeadlinePending()); + client_->Reset(); + + // Idle on next vsync. + AdvanceFrame(); + EXPECT_ACTION("WillBeginImplFrame", client_, 0, 3); + EXPECT_ACTION("SetNeedsBeginFrames(false)", client_, 1, 3); + EXPECT_ACTION("SendBeginMainFrameNotExpectedSoon", client_, 2, 3); + EXPECT_FALSE(scheduler_->BeginImplFrameDeadlinePending()); + client_->Reset(); +} - before_deadline = client.now_src()->Now(); - EXPECT_TRUE(client.task_runner().RunTasksWhile( - client.ImplFrameDeadlinePending(true))); - after_deadline = client.now_src()->Now(); +TEST_F(SchedulerTest, SynchronousCompositorDoubleCommitWithoutDraw) { + scheduler_settings_.using_synchronous_renderer_compositor = true; + scheduler_settings_.use_external_begin_frame_source = true; + scheduler_settings_.impl_side_painting = true; + SetUpScheduler(true); + + scheduler_->SetNeedsCommit(); + EXPECT_SINGLE_ACTION("SetNeedsBeginFrames(true)", client_); + client_->Reset(); + + // Next vsync. + AdvanceFrame(); + EXPECT_ACTION("WillBeginImplFrame", client_, 0, 2); + EXPECT_ACTION("ScheduledActionSendBeginMainFrame", client_, 1, 2); + EXPECT_FALSE(scheduler_->BeginImplFrameDeadlinePending()); + client_->Reset(); + + scheduler_->NotifyBeginMainFrameStarted(); + EXPECT_NO_ACTION(client_); + + scheduler_->NotifyReadyToCommit(); + EXPECT_SINGLE_ACTION("ScheduledActionCommit", client_); + client_->Reset(); + + scheduler_->NotifyReadyToActivate(); + EXPECT_SINGLE_ACTION("ScheduledActionActivateSyncTree", client_); + client_->Reset(); + + // Ask for another commit. + scheduler_->SetNeedsCommit(); + + AdvanceFrame(); + EXPECT_ACTION("WillBeginImplFrame", client_, 0, 4); + EXPECT_ACTION("ScheduledActionAnimate", client_, 1, 4); + EXPECT_ACTION("ScheduledActionSendBeginMainFrame", client_, 2, 4); + EXPECT_ACTION("ScheduledActionInvalidateOutputSurface", client_, 3, 4); + EXPECT_FALSE(scheduler_->BeginImplFrameDeadlinePending()); + client_->Reset(); + + scheduler_->NotifyBeginMainFrameStarted(); + EXPECT_NO_ACTION(client_); + + // Allow new commit even though previous commit hasn't been drawn. + scheduler_->NotifyReadyToCommit(); + EXPECT_SINGLE_ACTION("ScheduledActionCommit", client_); + client_->Reset(); +} - // We post a zero deadline task when on battery - EXPECT_EQ(before_deadline, after_deadline); +class SchedulerClientSetNeedsPrepareTilesOnDraw : public FakeSchedulerClient { + public: + SchedulerClientSetNeedsPrepareTilesOnDraw() : FakeSchedulerClient() {} - // Switch to non-battery power - client.PowerMonitorSource()->GeneratePowerStateEvent(false); - EXPECT_FALSE(client.PowerMonitor()->IsOnBatteryPower()); + protected: + DrawResult ScheduledActionDrawAndSwapIfPossible() override { + scheduler_->SetNeedsPrepareTiles(); + return FakeSchedulerClient::ScheduledActionDrawAndSwapIfPossible(); + } - client.AdvanceFrame(); - scheduler->SetNeedsCommit(); - scheduler->SetNeedsRedraw(); - client.Reset(); + void ScheduledActionPrepareTiles() override { + FakeSchedulerClient::ScheduledActionPrepareTiles(); + scheduler_->DidPrepareTiles(); + } +}; - // Same as before - before_deadline = client.now_src()->Now(); - EXPECT_TRUE(client.task_runner().RunTasksWhile( - client.ImplFrameDeadlinePending(true))); - after_deadline = client.now_src()->Now(); +TEST_F(SchedulerTest, SynchronousCompositorPrepareTilesOnDraw) { + scheduler_settings_.using_synchronous_renderer_compositor = true; + scheduler_settings_.use_external_begin_frame_source = true; + scheduler_settings_.impl_side_painting = true; + + scoped_ptr<FakeSchedulerClient> client = + make_scoped_ptr(new SchedulerClientSetNeedsPrepareTilesOnDraw); + SetUpScheduler(client.Pass(), true); + + scheduler_->SetNeedsRedraw(); + EXPECT_SINGLE_ACTION("SetNeedsBeginFrames(true)", client_); + client_->Reset(); + + // Next vsync. + EXPECT_SCOPED(AdvanceFrame()); + EXPECT_ACTION("WillBeginImplFrame", client_, 0, 3); + EXPECT_ACTION("ScheduledActionAnimate", client_, 1, 3); + EXPECT_ACTION("ScheduledActionInvalidateOutputSurface", client_, 2, 3); + client_->Reset(); + + // Android onDraw. + scheduler_->SetNeedsRedraw(); + scheduler_->OnDrawForOutputSurface(); + EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client_, 0, 2); + EXPECT_ACTION("ScheduledActionPrepareTiles", client_, 1, 2); + EXPECT_FALSE(scheduler_->BeginImplFrameDeadlinePending()); + EXPECT_FALSE(scheduler_->PrepareTilesPending()); + client_->Reset(); + + // Android onDraw. + scheduler_->SetNeedsRedraw(); + scheduler_->OnDrawForOutputSurface(); + EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client_, 0, 2); + EXPECT_ACTION("ScheduledActionPrepareTiles", client_, 1, 2); + EXPECT_FALSE(scheduler_->BeginImplFrameDeadlinePending()); + EXPECT_FALSE(scheduler_->PrepareTilesPending()); + client_->Reset(); + + // Next vsync. + EXPECT_SCOPED(AdvanceFrame()); + EXPECT_FALSE(scheduler_->PrepareTilesPending()); + EXPECT_ACTION("WillBeginImplFrame", client_, 0, 3); + EXPECT_ACTION("SetNeedsBeginFrames(false)", client_, 1, 3); + EXPECT_ACTION("SendBeginMainFrameNotExpectedSoon", client_, 2, 3); + EXPECT_FALSE(client_->needs_begin_frames()); + client_->Reset(); } -TEST(SchedulerTest, - SimulateWindowsLowResolutionTimerOnBattery_PrioritizeImplLatencyOff) { - FakeSchedulerClient client; - SchedulerSettings settings; - TestScheduler* scheduler = client.CreateScheduler(settings); - - scheduler->SetCanStart(); - scheduler->SetVisible(true); - scheduler->SetCanDraw(true); - - InitializeOutputSurfaceAndFirstCommit(scheduler, &client); - - // Set needs commit so that the scheduler tries to wait for the main thread - scheduler->SetNeedsCommit(); - // Set needs redraw so that the scheduler doesn't wait too long - scheduler->SetNeedsRedraw(); - client.Reset(); - - // Switch to battery power - client.PowerMonitorSource()->GeneratePowerStateEvent(true); - EXPECT_TRUE(client.PowerMonitor()->IsOnBatteryPower()); - - client.AdvanceFrame(); - scheduler->SetNeedsCommit(); - scheduler->SetNeedsRedraw(); - client.Reset(); - - // Disable auto-advancing of now_src - client.task_runner().SetAutoAdvanceNowToPendingTasks(false); - - // Deadline task is pending - EXPECT_TRUE(scheduler->BeginImplFrameDeadlinePending()); - client.task_runner().RunPendingTasks(); - // Deadline task is still pending - EXPECT_TRUE(scheduler->BeginImplFrameDeadlinePending()); - - // Advance now by 15 ms - same as windows low res timer - client.now_src()->AdvanceNowMicroseconds(15000); - EXPECT_TRUE(scheduler->BeginImplFrameDeadlinePending()); - client.task_runner().RunPendingTasks(); - // Deadline task finally completes - EXPECT_FALSE(scheduler->BeginImplFrameDeadlinePending()); -} +TEST_F(SchedulerTest, AuthoritativeVSyncInterval) { + SetUpScheduler(true); + + base::TimeDelta initial_interval = + scheduler_->begin_impl_frame_args().interval; + base::TimeDelta authoritative_interval = + base::TimeDelta::FromMilliseconds(33); + + scheduler_->SetNeedsCommit(); + EXPECT_SCOPED(AdvanceFrame()); + + EXPECT_EQ(initial_interval, scheduler_->begin_impl_frame_args().interval); + + scheduler_->NotifyBeginMainFrameStarted(); + scheduler_->NotifyReadyToCommit(); + task_runner().RunTasksWhile(client_->ImplFrameDeadlinePending(true)); + + scheduler_->SetAuthoritativeVSyncInterval(authoritative_interval); + + EXPECT_SCOPED(AdvanceFrame()); -TEST(SchedulerTest, - SimulateWindowsLowResolutionTimerOnBattery_PrioritizeImplLatencyOn) { - FakeSchedulerClient client; - SchedulerSettings settings; - settings.disable_hi_res_timer_tasks_on_battery = true; - TestScheduler* scheduler = client.CreateScheduler(settings); - - scheduler->SetCanStart(); - scheduler->SetVisible(true); - scheduler->SetCanDraw(true); - - InitializeOutputSurfaceAndFirstCommit(scheduler, &client); - - // Set needs commit so that the scheduler tries to wait for the main thread - scheduler->SetNeedsCommit(); - // Set needs redraw so that the scheduler doesn't wait too long - scheduler->SetNeedsRedraw(); - client.Reset(); - - // Switch to battery power - client.PowerMonitorSource()->GeneratePowerStateEvent(true); - EXPECT_TRUE(client.PowerMonitor()->IsOnBatteryPower()); - - client.AdvanceFrame(); - scheduler->SetNeedsCommit(); - scheduler->SetNeedsRedraw(); - client.Reset(); - - // Disable auto-advancing of now_src - client.task_runner().SetAutoAdvanceNowToPendingTasks(false); - - // Deadline task is pending - EXPECT_TRUE(scheduler->BeginImplFrameDeadlinePending()); - client.task_runner().RunPendingTasks(); - // Deadline task runs immediately - EXPECT_FALSE(scheduler->BeginImplFrameDeadlinePending()); + // At the next BeginFrame, authoritative interval is used instead of previous + // interval. + EXPECT_NE(initial_interval, scheduler_->begin_impl_frame_args().interval); + EXPECT_EQ(authoritative_interval, + scheduler_->begin_impl_frame_args().interval); } } // namespace diff --git a/chromium/cc/scheduler/video_frame_controller.h b/chromium/cc/scheduler/video_frame_controller.h new file mode 100644 index 00000000000..0307cd5e402 --- /dev/null +++ b/chromium/cc/scheduler/video_frame_controller.h @@ -0,0 +1,40 @@ +// 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_VIDEO_FRAME_CONTROLLER_H_ +#define CC_SCHEDULER_VIDEO_FRAME_CONTROLLER_H_ + +#include "cc/base/cc_export.h" +#include "cc/output/begin_frame_args.h" + +namespace cc { + +class VideoFrameController; + +class CC_EXPORT VideoFrameControllerClient { + public: + virtual void AddVideoFrameController(VideoFrameController* controller) = 0; + virtual void RemoveVideoFrameController(VideoFrameController* controller) = 0; + + protected: + virtual ~VideoFrameControllerClient() {} +}; + +// TODO(sunnyps): Consider making this a BeginFrameObserver some day. +class CC_EXPORT VideoFrameController { + public: + virtual void OnBeginFrame(const BeginFrameArgs& args) = 0; + + // Called upon completion of LayerTreeHostImpl::DidDrawAllLayers(), regardless + // of whether the controller issued a SetNeedsRedraw(). May be used to + // determine when SetNeedsRedraw() is called but the draw is aborted. + virtual void DidDrawFrame() = 0; + + protected: + virtual ~VideoFrameController() {} +}; + +} // namespace cc + +#endif // CC_SCHEDULER_VIDEO_FRAME_CONTROLLER_H_ |