diff options
author | brianderson@chromium.org <brianderson@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-04-18 05:06:11 +0000 |
---|---|---|
committer | brianderson@chromium.org <brianderson@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-04-18 05:06:11 +0000 |
commit | c1490266036958df7d1f2959542b872fe21b8601 (patch) | |
tree | fb84f4e62c1d51ba87a4a7b20f66e02c8475bf2b | |
parent | 5e09494c74fbadcd1bf83aedae31b0db86525e46 (diff) | |
download | chromium_src-c1490266036958df7d1f2959542b872fe21b8601.zip chromium_src-c1490266036958df7d1f2959542b872fe21b8601.tar.gz chromium_src-c1490266036958df7d1f2959542b872fe21b8601.tar.bz2 |
cc: Throttle swaps in Scheduler instead of OutputSurface
This doesn't represent a functional change and only moves
the swap throttling logic from the OutputSurface to the
Scheduler.
BUG=311213
Review URL: https://codereview.chromium.org/199523002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@264732 0039d316-1c4b-4281-b951-d872f2087c98
27 files changed, 312 insertions, 478 deletions
diff --git a/cc/layers/texture_layer_unittest.cc b/cc/layers/texture_layer_unittest.cc index dd67086..97b52f4 100644 --- a/cc/layers/texture_layer_unittest.cc +++ b/cc/layers/texture_layer_unittest.cc @@ -1183,7 +1183,6 @@ class TextureLayerNoExtraCommitForMailboxTest for (size_t i = 0; i < resources_to_return.size(); ++i) output_surface()->ReturnResource(resources_to_return[i].id, &ack); host_impl->ReclaimResources(&ack); - host_impl->OnSwapBuffersComplete(); } virtual void AfterTest() OVERRIDE {} @@ -1323,7 +1322,6 @@ class TextureLayerChangeInvisibleMailboxTest for (size_t i = 0; i < resources_to_return.size(); ++i) output_surface()->ReturnResource(resources_to_return[i].id, &ack); host_impl->ReclaimResources(&ack); - host_impl->OnSwapBuffersComplete(); } virtual void AfterTest() OVERRIDE {} diff --git a/cc/output/output_surface.cc b/cc/output/output_surface.cc index 3aaa1a1..7818849 100644 --- a/cc/output/output_surface.cc +++ b/cc/output/output_surface.cc @@ -46,8 +46,8 @@ namespace cc { OutputSurface::OutputSurface(scoped_refptr<ContextProvider> context_provider) : context_provider_(context_provider), device_scale_factor_(-1), - max_frames_pending_(0), - pending_swap_buffers_(0), + begin_frame_interval_(BeginFrameArgs::DefaultInterval()), + throttle_frame_production_(true), needs_begin_frame_(false), client_ready_for_begin_frame_(true), client_(NULL), @@ -59,8 +59,8 @@ OutputSurface::OutputSurface(scoped_refptr<ContextProvider> context_provider) OutputSurface::OutputSurface(scoped_ptr<SoftwareOutputDevice> software_device) : software_device_(software_device.Pass()), device_scale_factor_(-1), - max_frames_pending_(0), - pending_swap_buffers_(0), + begin_frame_interval_(BeginFrameArgs::DefaultInterval()), + throttle_frame_production_(true), needs_begin_frame_(false), client_ready_for_begin_frame_(true), client_(NULL), @@ -74,8 +74,8 @@ OutputSurface::OutputSurface(scoped_refptr<ContextProvider> context_provider, : context_provider_(context_provider), software_device_(software_device.Pass()), device_scale_factor_(-1), - max_frames_pending_(0), - pending_swap_buffers_(0), + begin_frame_interval_(BeginFrameArgs::DefaultInterval()), + throttle_frame_production_(true), needs_begin_frame_(false), client_ready_for_begin_frame_(true), client_(NULL), @@ -84,37 +84,26 @@ OutputSurface::OutputSurface(scoped_refptr<ContextProvider> context_provider, weak_ptr_factory_(this), gpu_latency_history_(kGpuLatencyHistorySize) {} +void OutputSurface::SetThrottleFrameProduction(bool enable) { + DCHECK(!frame_rate_controller_); + throttle_frame_production_ = enable; +} + void OutputSurface::InitializeBeginFrameEmulation( base::SingleThreadTaskRunner* task_runner, - bool throttle_frame_production, base::TimeDelta interval) { - if (throttle_frame_production) { - scoped_refptr<DelayBasedTimeSource> time_source; - if (gfx::FrameTime::TimestampsAreHighRes()) - time_source = DelayBasedTimeSourceHighRes::Create(interval, task_runner); - else - time_source = DelayBasedTimeSource::Create(interval, task_runner); - frame_rate_controller_.reset(new FrameRateController(time_source)); - } else { - frame_rate_controller_.reset(new FrameRateController(task_runner)); - } + DCHECK(throttle_frame_production_); + scoped_refptr<DelayBasedTimeSource> time_source; + if (gfx::FrameTime::TimestampsAreHighRes()) + time_source = DelayBasedTimeSourceHighRes::Create(interval, task_runner); + else + time_source = DelayBasedTimeSource::Create(interval, task_runner); + frame_rate_controller_.reset(new FrameRateController(time_source)); frame_rate_controller_->SetClient(this); - frame_rate_controller_->SetMaxSwapsPending(max_frames_pending_); frame_rate_controller_->SetDeadlineAdjustment( capabilities_.adjust_deadline_for_parent ? BeginFrameArgs::DefaultDeadlineAdjustment() : base::TimeDelta()); - - // The new frame rate controller will consume the swap acks of the old - // frame rate controller, so we set that expectation up here. - for (int i = 0; i < pending_swap_buffers_; i++) - frame_rate_controller_->DidSwapBuffers(); -} - -void OutputSurface::SetMaxFramesPending(int max_frames_pending) { - if (frame_rate_controller_) - frame_rate_controller_->SetMaxSwapsPending(max_frames_pending); - max_frames_pending_ = max_frames_pending; } void OutputSurface::CommitVSyncParameters(base::TimeTicks timebase, @@ -125,17 +114,14 @@ void OutputSurface::CommitVSyncParameters(base::TimeTicks timebase, (timebase - base::TimeTicks()).InSecondsF(), "interval", interval.InSecondsF()); + begin_frame_interval_ = interval; if (frame_rate_controller_) frame_rate_controller_->SetTimebaseAndInterval(timebase, interval); } -void OutputSurface::FrameRateControllerTick(bool throttled, - const BeginFrameArgs& args) { +void OutputSurface::FrameRateControllerTick(const BeginFrameArgs& args) { DCHECK(frame_rate_controller_); - if (throttled) - skipped_begin_frame_args_ = args; - else - BeginFrame(args); + BeginFrame(args); } // Forwarded to OutputSurfaceClient @@ -148,25 +134,29 @@ void OutputSurface::SetNeedsBeginFrame(bool enable) { TRACE_EVENT1("cc", "OutputSurface::SetNeedsBeginFrame", "enable", enable); needs_begin_frame_ = enable; client_ready_for_begin_frame_ = true; - if (frame_rate_controller_) { + if (!throttle_frame_production_) { + if (enable) { + base::TimeTicks frame_time = gfx::FrameTime::Now(); + base::TimeTicks deadline = frame_time + begin_frame_interval_; + skipped_begin_frame_args_ = + BeginFrameArgs::Create(frame_time, deadline, begin_frame_interval_); + } + } else if (frame_rate_controller_) { BeginFrameArgs skipped = frame_rate_controller_->SetActive(enable); if (skipped.IsValid()) skipped_begin_frame_args_ = skipped; } + if (needs_begin_frame_) PostCheckForRetroactiveBeginFrame(); } void OutputSurface::BeginFrame(const BeginFrameArgs& args) { - TRACE_EVENT2("cc", + TRACE_EVENT1("cc", "OutputSurface::BeginFrame", "client_ready_for_begin_frame_", - client_ready_for_begin_frame_, - "pending_swap_buffers_", - pending_swap_buffers_); - if (!needs_begin_frame_ || !client_ready_for_begin_frame_ || - (pending_swap_buffers_ >= max_frames_pending_ && - max_frames_pending_ > 0)) { + client_ready_for_begin_frame_); + if (!needs_begin_frame_ || !client_ready_for_begin_frame_) { skipped_begin_frame_args_ = args; } else { client_ready_for_begin_frame_ = false; @@ -201,28 +191,19 @@ void OutputSurface::PostCheckForRetroactiveBeginFrame() { void OutputSurface::CheckForRetroactiveBeginFrame() { TRACE_EVENT0("cc", "OutputSurface::CheckForRetroactiveBeginFrame"); check_for_retroactive_begin_frame_pending_ = false; - if (gfx::FrameTime::Now() < RetroactiveBeginFrameDeadline()) + if (!throttle_frame_production_ || + gfx::FrameTime::Now() < RetroactiveBeginFrameDeadline()) BeginFrame(skipped_begin_frame_args_); } void OutputSurface::DidSwapBuffers() { - pending_swap_buffers_++; - TRACE_EVENT1("cc", "OutputSurface::DidSwapBuffers", - "pending_swap_buffers_", pending_swap_buffers_); + TRACE_EVENT0("cc", "OutputSurface::DidSwapBuffers"); client_->DidSwapBuffers(); - if (frame_rate_controller_) - frame_rate_controller_->DidSwapBuffers(); - PostCheckForRetroactiveBeginFrame(); } void OutputSurface::OnSwapBuffersComplete() { - pending_swap_buffers_--; - TRACE_EVENT1("cc", "OutputSurface::OnSwapBuffersComplete", - "pending_swap_buffers_", pending_swap_buffers_); - client_->OnSwapBuffersComplete(); - if (frame_rate_controller_) - frame_rate_controller_->DidSwapBuffersComplete(); - PostCheckForRetroactiveBeginFrame(); + TRACE_EVENT0("cc", "OutputSurface::OnSwapBuffersComplete"); + client_->DidSwapBuffersComplete(); } void OutputSurface::ReclaimResources(const CompositorFrameAck* ack) { @@ -232,7 +213,6 @@ void OutputSurface::ReclaimResources(const CompositorFrameAck* ack) { void OutputSurface::DidLoseOutputSurface() { TRACE_EVENT0("cc", "OutputSurface::DidLoseOutputSurface"); client_ready_for_begin_frame_ = true; - pending_swap_buffers_ = 0; skipped_begin_frame_args_ = BeginFrameArgs(); if (frame_rate_controller_) frame_rate_controller_->SetActive(false); diff --git a/cc/output/output_surface.h b/cc/output/output_surface.h index a5d4d7b..471170a 100644 --- a/cc/output/output_surface.h +++ b/cc/output/output_surface.h @@ -105,12 +105,12 @@ class CC_EXPORT OutputSurface : public FrameRateControllerClient { // thread. virtual bool BindToClient(OutputSurfaceClient* client); + // Enable or disable vsync. + void SetThrottleFrameProduction(bool enable); + void InitializeBeginFrameEmulation(base::SingleThreadTaskRunner* task_runner, - bool throttle_frame_production, base::TimeDelta interval); - void SetMaxFramesPending(int max_frames_pending); - virtual void EnsureBackbuffer(); virtual void DiscardBackbuffer(); @@ -162,16 +162,16 @@ class CC_EXPORT OutputSurface : public FrameRateControllerClient { scoped_ptr<OverlayCandidateValidator> overlay_candidate_validator_; gfx::Size surface_size_; float device_scale_factor_; + base::TimeDelta begin_frame_interval_; // The FrameRateController is deprecated. // Platforms should move to native BeginFrames instead. void CommitVSyncParameters(base::TimeTicks timebase, base::TimeDelta interval); - virtual void FrameRateControllerTick(bool throttled, - const BeginFrameArgs& args) OVERRIDE; + virtual void FrameRateControllerTick(const BeginFrameArgs& args) OVERRIDE; scoped_ptr<FrameRateController> frame_rate_controller_; - int max_frames_pending_; - int pending_swap_buffers_; + + bool throttle_frame_production_; bool needs_begin_frame_; bool client_ready_for_begin_frame_; diff --git a/cc/output/output_surface_client.h b/cc/output/output_surface_client.h index 08d9b62..141e01a 100644 --- a/cc/output/output_surface_client.h +++ b/cc/output/output_surface_client.h @@ -33,7 +33,7 @@ class CC_EXPORT OutputSurfaceClient { virtual void SetNeedsRedrawRect(const gfx::Rect& damage_rect) = 0; virtual void BeginFrame(const BeginFrameArgs& args) = 0; virtual void DidSwapBuffers() = 0; - virtual void OnSwapBuffersComplete() = 0; + virtual void DidSwapBuffersComplete() = 0; virtual void ReclaimResources(const CompositorFrameAck* ack) = 0; virtual void DidLoseOutputSurface() = 0; virtual void SetExternalDrawConstraints(const gfx::Transform& transform, diff --git a/cc/output/output_surface_unittest.cc b/cc/output/output_surface_unittest.cc index d40feb1..637ba05 100644 --- a/cc/output/output_surface_unittest.cc +++ b/cc/output/output_surface_unittest.cc @@ -59,10 +59,6 @@ class TestOutputSurface : public OutputSurface { DidSwapBuffers(); } - int pending_swap_buffers() { - return pending_swap_buffers_; - } - void OnSwapBuffersCompleteForTesting() { OnSwapBuffersComplete(); } @@ -232,20 +228,17 @@ TEST(OutputSurfaceTest, BeginFrameEmulation) { // Initialize BeginFrame emulation scoped_refptr<base::TestSimpleTaskRunner> task_runner = new base::TestSimpleTaskRunner; - bool throttle_frame_production = true; const base::TimeDelta display_refresh_interval = BeginFrameArgs::DefaultInterval(); - output_surface.InitializeBeginFrameEmulation( - task_runner.get(), throttle_frame_production, display_refresh_interval); + output_surface.InitializeBeginFrameEmulation(task_runner.get(), + display_refresh_interval); - output_surface.SetMaxFramesPending(2); output_surface.EnableRetroactiveBeginFrameDeadline( false, false, base::TimeDelta()); // We should start off with 0 BeginFrames EXPECT_EQ(client.begin_frame_count(), 0); - EXPECT_EQ(output_surface.pending_swap_buffers(), 0); // We should not have a pending task until a BeginFrame has been // requested. @@ -256,54 +249,29 @@ TEST(OutputSurfaceTest, BeginFrameEmulation) { // BeginFrame should be called on the first tick. task_runner->RunPendingTasks(); EXPECT_EQ(client.begin_frame_count(), 1); - EXPECT_EQ(output_surface.pending_swap_buffers(), 0); // BeginFrame should not be called when there is a pending BeginFrame. task_runner->RunPendingTasks(); EXPECT_EQ(client.begin_frame_count(), 1); - EXPECT_EQ(output_surface.pending_swap_buffers(), 0); - // SetNeedsBeginFrame should clear the pending BeginFrame after // a SwapBuffers. output_surface.DidSwapBuffersForTesting(); output_surface.SetNeedsBeginFrame(true); EXPECT_EQ(client.begin_frame_count(), 1); - EXPECT_EQ(output_surface.pending_swap_buffers(), 1); task_runner->RunPendingTasks(); EXPECT_EQ(client.begin_frame_count(), 2); - EXPECT_EQ(output_surface.pending_swap_buffers(), 1); - - // BeginFrame should be throttled by pending swap buffers. - output_surface.DidSwapBuffersForTesting(); - output_surface.SetNeedsBeginFrame(true); - EXPECT_EQ(client.begin_frame_count(), 2); - EXPECT_EQ(output_surface.pending_swap_buffers(), 2); - task_runner->RunPendingTasks(); - EXPECT_EQ(client.begin_frame_count(), 2); - EXPECT_EQ(output_surface.pending_swap_buffers(), 2); - - // SwapAck should decrement pending swap buffers and unblock BeginFrame - // again. - output_surface.OnSwapBuffersCompleteForTesting(); - EXPECT_EQ(client.begin_frame_count(), 2); - EXPECT_EQ(output_surface.pending_swap_buffers(), 1); - task_runner->RunPendingTasks(); - EXPECT_EQ(client.begin_frame_count(), 3); - EXPECT_EQ(output_surface.pending_swap_buffers(), 1); // Calling SetNeedsBeginFrame again indicates a swap did not occur but // the client still wants another BeginFrame. output_surface.SetNeedsBeginFrame(true); task_runner->RunPendingTasks(); - EXPECT_EQ(client.begin_frame_count(), 4); - EXPECT_EQ(output_surface.pending_swap_buffers(), 1); + EXPECT_EQ(client.begin_frame_count(), 3); // Disabling SetNeedsBeginFrame should prevent further BeginFrames. output_surface.SetNeedsBeginFrame(false); task_runner->RunPendingTasks(); EXPECT_FALSE(task_runner->HasPendingTask()); - EXPECT_EQ(client.begin_frame_count(), 4); - EXPECT_EQ(output_surface.pending_swap_buffers(), 1); + EXPECT_EQ(client.begin_frame_count(), 3); } TEST(OutputSurfaceTest, OptimisticAndRetroactiveBeginFrames) { @@ -315,7 +283,6 @@ TEST(OutputSurfaceTest, OptimisticAndRetroactiveBeginFrames) { EXPECT_TRUE(output_surface.HasClient()); EXPECT_FALSE(client.deferred_initialize_called()); - output_surface.SetMaxFramesPending(2); output_surface.EnableRetroactiveBeginFrameDeadline( true, false, base::TimeDelta()); @@ -341,19 +308,6 @@ TEST(OutputSurfaceTest, OptimisticAndRetroactiveBeginFrames) { output_surface.DidSwapBuffersForTesting(); output_surface.SetNeedsBeginFrame(true); EXPECT_EQ(client.begin_frame_count(), 3); - EXPECT_EQ(output_surface.pending_swap_buffers(), 1); - - // Optimistically injected BeginFrames should be by throttled by pending - // swap buffers... - output_surface.DidSwapBuffersForTesting(); - output_surface.SetNeedsBeginFrame(true); - EXPECT_EQ(client.begin_frame_count(), 3); - EXPECT_EQ(output_surface.pending_swap_buffers(), 2); - output_surface.BeginFrameForTesting(); - EXPECT_EQ(client.begin_frame_count(), 3); - // ...and retroactively triggered by OnSwapBuffersComplete - output_surface.OnSwapBuffersCompleteForTesting(); - EXPECT_EQ(client.begin_frame_count(), 4); } TEST(OutputSurfaceTest, RetroactiveBeginFrameDoesNotDoubleTickWhenEmulating) { @@ -373,23 +327,20 @@ TEST(OutputSurfaceTest, RetroactiveBeginFrameDoesNotDoubleTickWhenEmulating) { // Initialize BeginFrame emulation scoped_refptr<base::TestSimpleTaskRunner> task_runner = new base::TestSimpleTaskRunner; - bool throttle_frame_production = true; const base::TimeDelta display_refresh_interval = big_interval; - output_surface.InitializeBeginFrameEmulation( - task_runner.get(), throttle_frame_production, display_refresh_interval); + output_surface.InitializeBeginFrameEmulation(task_runner.get(), + display_refresh_interval); // We need to subtract an epsilon from Now() because some platforms have // a slow clock. output_surface.CommitVSyncParametersForTesting( gfx::FrameTime::Now() - base::TimeDelta::FromSeconds(1), big_interval); - output_surface.SetMaxFramesPending(2); output_surface.EnableRetroactiveBeginFrameDeadline(true, true, big_interval); // We should start off with 0 BeginFrames EXPECT_EQ(client.begin_frame_count(), 0); - EXPECT_EQ(output_surface.pending_swap_buffers(), 0); // The first SetNeedsBeginFrame(true) should start a retroactive // BeginFrame. diff --git a/cc/scheduler/draw_swap_readback_result.h b/cc/scheduler/draw_swap_readback_result.h index a87a08b..852b897 100644 --- a/cc/scheduler/draw_swap_readback_result.h +++ b/cc/scheduler/draw_swap_readback_result.h @@ -19,15 +19,17 @@ struct DrawSwapReadbackResult { }; DrawSwapReadbackResult() - : draw_result(INVALID_RESULT), did_swap(false), did_readback(false) {} + : draw_result(INVALID_RESULT), + did_request_swap(false), + did_readback(false) {} DrawSwapReadbackResult(DrawResult draw_result, - bool did_swap, + bool did_request_swap, bool did_readback) : draw_result(draw_result), - did_swap(did_swap), + did_request_swap(did_request_swap), did_readback(did_readback) {} DrawResult draw_result; - bool did_swap; + bool did_request_swap; bool did_readback; }; diff --git a/cc/scheduler/frame_rate_controller.cc b/cc/scheduler/frame_rate_controller.cc index 243ef6b..beaa1e1 100644 --- a/cc/scheduler/frame_rate_controller.cc +++ b/cc/scheduler/frame_rate_controller.cc @@ -15,59 +15,15 @@ namespace cc { -class FrameRateControllerTimeSourceAdapter : public TimeSourceClient { - public: - static scoped_ptr<FrameRateControllerTimeSourceAdapter> Create( - FrameRateController* frame_rate_controller) { - return make_scoped_ptr( - new FrameRateControllerTimeSourceAdapter(frame_rate_controller)); - } - virtual ~FrameRateControllerTimeSourceAdapter() {} - - virtual void OnTimerTick() OVERRIDE { - frame_rate_controller_->OnTimerTick(); - } - - private: - explicit FrameRateControllerTimeSourceAdapter( - FrameRateController* frame_rate_controller) - : frame_rate_controller_(frame_rate_controller) {} - - FrameRateController* frame_rate_controller_; -}; - FrameRateController::FrameRateController(scoped_refptr<TimeSource> timer) : client_(NULL), - num_frames_pending_(0), - max_swaps_pending_(0), interval_(BeginFrameArgs::DefaultInterval()), time_source_(timer), - active_(false), - is_time_source_throttling_(true), - manual_tick_pending_(false), - task_runner_(NULL), - weak_factory_(this) { - time_source_client_adapter_ = - FrameRateControllerTimeSourceAdapter::Create(this); - time_source_->SetClient(time_source_client_adapter_.get()); + active_(false) { + time_source_->SetClient(this); } -FrameRateController::FrameRateController( - base::SingleThreadTaskRunner* task_runner) - : client_(NULL), - num_frames_pending_(0), - max_swaps_pending_(0), - interval_(BeginFrameArgs::DefaultInterval()), - active_(false), - is_time_source_throttling_(false), - manual_tick_pending_(false), - task_runner_(task_runner), - weak_factory_(this) {} - -FrameRateController::~FrameRateController() { - if (is_time_source_throttling_) - time_source_->SetActive(false); -} +FrameRateController::~FrameRateController() { time_source_->SetActive(false); } BeginFrameArgs FrameRateController::SetActive(bool active) { if (active_ == active) @@ -75,35 +31,20 @@ BeginFrameArgs FrameRateController::SetActive(bool active) { TRACE_EVENT1("cc", "FrameRateController::SetActive", "active", active); active_ = active; - if (is_time_source_throttling_) { - base::TimeTicks missed_tick_time = time_source_->SetActive(active); - if (!missed_tick_time.is_null()) { - base::TimeTicks deadline = NextTickTime(); - return BeginFrameArgs::Create( - missed_tick_time, deadline + deadline_adjustment_, interval_); - } - } else { - if (active) { - PostManualTick(); - } else { - weak_factory_.InvalidateWeakPtrs(); - manual_tick_pending_ = false; - } + base::TimeTicks missed_tick_time = time_source_->SetActive(active); + if (!missed_tick_time.is_null()) { + base::TimeTicks deadline = NextTickTime(); + return BeginFrameArgs::Create( + missed_tick_time, deadline + deadline_adjustment_, interval_); } return BeginFrameArgs(); } -void FrameRateController::SetMaxSwapsPending(int max_swaps_pending) { - DCHECK_GE(max_swaps_pending, 0); - max_swaps_pending_ = max_swaps_pending; -} - void FrameRateController::SetTimebaseAndInterval(base::TimeTicks timebase, base::TimeDelta interval) { interval_ = interval; - if (is_time_source_throttling_) - time_source_->SetTimebaseAndInterval(timebase, interval); + time_source_->SetTimebaseAndInterval(timebase, interval); } void FrameRateController::SetDeadlineAdjustment(base::TimeDelta delta) { @@ -114,65 +55,22 @@ void FrameRateController::OnTimerTick() { TRACE_EVENT0("cc", "FrameRateController::OnTimerTick"); DCHECK(active_); - // Check if we have too many frames in flight. - bool throttled = - max_swaps_pending_ && num_frames_pending_ >= max_swaps_pending_; - TRACE_COUNTER_ID1("cc", "ThrottledCompositor", task_runner_, throttled); - if (client_) { // TODO(brianderson): Use an adaptive parent compositor deadline. base::TimeTicks frame_time = LastTickTime(); base::TimeTicks deadline = NextTickTime(); BeginFrameArgs args = BeginFrameArgs::Create( frame_time, deadline + deadline_adjustment_, interval_); - client_->FrameRateControllerTick(throttled, args); + client_->FrameRateControllerTick(args); } - - if (!is_time_source_throttling_ && !throttled) - PostManualTick(); -} - -void FrameRateController::PostManualTick() { - if (active_ && !manual_tick_pending_) { - manual_tick_pending_ = true; - task_runner_->PostTask(FROM_HERE, - base::Bind(&FrameRateController::ManualTick, - weak_factory_.GetWeakPtr())); - } -} - -void FrameRateController::ManualTick() { - manual_tick_pending_ = false; - OnTimerTick(); -} - -void FrameRateController::DidSwapBuffers() { - num_frames_pending_++; -} - -void FrameRateController::DidSwapBuffersComplete() { - DCHECK_GT(num_frames_pending_, 0); - num_frames_pending_--; - if (!is_time_source_throttling_) - PostManualTick(); -} - -void FrameRateController::DidAbortAllPendingFrames() { - num_frames_pending_ = 0; } base::TimeTicks FrameRateController::NextTickTime() { - if (is_time_source_throttling_) - return time_source_->NextTickTime(); - - return base::TimeTicks(); + return time_source_->NextTickTime(); } base::TimeTicks FrameRateController::LastTickTime() { - if (is_time_source_throttling_) - return time_source_->LastTickTime(); - - return gfx::FrameTime::Now(); + return time_source_->LastTickTime(); } } // namespace cc diff --git a/cc/scheduler/frame_rate_controller.h b/cc/scheduler/frame_rate_controller.h index 6d86d97..2ec62ac 100644 --- a/cc/scheduler/frame_rate_controller.h +++ b/cc/scheduler/frame_rate_controller.h @@ -11,6 +11,7 @@ #include "base/time/time.h" #include "cc/base/cc_export.h" #include "cc/output/begin_frame_args.h" +#include "cc/scheduler/time_source.h" namespace base { class SingleThreadTaskRunner; } @@ -24,20 +25,14 @@ class CC_EXPORT FrameRateControllerClient { virtual ~FrameRateControllerClient() {} public: - // Throttled is true when we have a maximum number of frames pending. - virtual void FrameRateControllerTick(bool throttled, - const BeginFrameArgs& args) = 0; + virtual void FrameRateControllerTick(const BeginFrameArgs& args) = 0; }; -class FrameRateControllerTimeSourceAdapter; - // The FrameRateController is used in cases where we self-tick (i.e. BeginFrame // is not sent by a parent compositor. -class CC_EXPORT FrameRateController { +class CC_EXPORT FrameRateController : TimeSourceClient { public: explicit FrameRateController(scoped_refptr<TimeSource> timer); - // Alternate form of FrameRateController with unthrottled frame-rate. - explicit FrameRateController(base::SingleThreadTaskRunner* task_runner); virtual ~FrameRateController(); void SetClient(FrameRateControllerClient* client) { client_ = client; } @@ -48,50 +43,24 @@ class CC_EXPORT FrameRateController { bool IsActive() { return active_; } - // Use the following methods to adjust target frame rate. - // - // Multiple frames can be in-progress, but for every DidSwapBuffers, a - // DidFinishFrame should be posted. - // - // If the rendering pipeline crashes, call DidAbortAllPendingFrames. - void DidSwapBuffers(); - void DidSwapBuffersComplete(); - void DidAbortAllPendingFrames(); - void SetMaxSwapsPending(int max_swaps_pending); // 0 for unlimited. - int MaxSwapsPending() const { return max_swaps_pending_; } - int NumSwapsPendingForTesting() const { return num_frames_pending_; } - void SetTimebaseAndInterval(base::TimeTicks timebase, base::TimeDelta interval); void SetDeadlineAdjustment(base::TimeDelta delta); - protected: - friend class FrameRateControllerTimeSourceAdapter; - void OnTimerTick(); - - void PostManualTick(); - void ManualTick(); + virtual void OnTimerTick() OVERRIDE; + protected: // This returns null for unthrottled frame-rate. base::TimeTicks NextTickTime(); // This returns now for unthrottled frame-rate. base::TimeTicks LastTickTime(); FrameRateControllerClient* client_; - int num_frames_pending_; - int max_swaps_pending_; base::TimeDelta interval_; base::TimeDelta deadline_adjustment_; scoped_refptr<TimeSource> time_source_; - scoped_ptr<FrameRateControllerTimeSourceAdapter> time_source_client_adapter_; bool active_; - // Members for unthrottled frame-rate. - bool is_time_source_throttling_; - bool manual_tick_pending_; - base::SingleThreadTaskRunner* task_runner_; - base::WeakPtrFactory<FrameRateController> weak_factory_; - private: DISALLOW_COPY_AND_ASSIGN(FrameRateController); }; diff --git a/cc/scheduler/frame_rate_controller_unittest.cc b/cc/scheduler/frame_rate_controller_unittest.cc index e7d7580..ed7de67 100644 --- a/cc/scheduler/frame_rate_controller_unittest.cc +++ b/cc/scheduler/frame_rate_controller_unittest.cc @@ -19,9 +19,8 @@ class FakeFrameRateControllerClient : public FrameRateControllerClient { bool BeganFrame() const { return frame_count_ > 0; } int frame_count() const { return frame_count_; } - virtual void FrameRateControllerTick( - bool throttled, const BeginFrameArgs& args) OVERRIDE { - frame_count_ += throttled ? 0 : 1; + virtual void FrameRateControllerTick(const BeginFrameArgs& args) OVERRIDE { + frame_count_ += 1; } protected: @@ -50,14 +49,6 @@ TEST(FrameRateControllerTest, TestFrameThrottling_ImmediateAck) { EXPECT_TRUE(client.BeganFrame()); client.Reset(); - // Tell the controller we drew - controller.DidSwapBuffers(); - - // Tell the controller the frame ended 5ms later - time_source->SetNow(time_source->Now() + - base::TimeDelta::FromMilliseconds(5)); - controller.DidSwapBuffersComplete(); - // Trigger another frame, make sure BeginFrame runs again elapsed += task_runner->NextPendingTaskDelay(); // Sanity check that previous code didn't move time backward. @@ -67,145 +58,5 @@ TEST(FrameRateControllerTest, TestFrameThrottling_ImmediateAck) { EXPECT_TRUE(client.BeganFrame()); } -TEST(FrameRateControllerTest, TestFrameThrottling_TwoFramesInFlight) { - scoped_refptr<base::TestSimpleTaskRunner> task_runner = - new base::TestSimpleTaskRunner; - FakeFrameRateControllerClient client; - base::TimeDelta interval = base::TimeDelta::FromMicroseconds( - base::Time::kMicrosecondsPerSecond / 60); - scoped_refptr<FakeDelayBasedTimeSource> time_source = - FakeDelayBasedTimeSource::Create(interval, task_runner.get()); - FrameRateController controller(time_source); - - controller.SetClient(&client); - controller.SetActive(true); - controller.SetMaxSwapsPending(2); - - base::TimeTicks elapsed; // Muck around with time a bit - - // Trigger one frame, make sure the BeginFrame callback is called - elapsed += task_runner->NextPendingTaskDelay(); - time_source->SetNow(elapsed); - task_runner->RunPendingTasks(); - EXPECT_TRUE(client.BeganFrame()); - client.Reset(); - - // Tell the controller we drew - controller.DidSwapBuffers(); - - // Trigger another frame, make sure BeginFrame callback runs again - elapsed += task_runner->NextPendingTaskDelay(); - // Sanity check that previous code didn't move time backward. - EXPECT_GE(elapsed, time_source->Now()); - time_source->SetNow(elapsed); - task_runner->RunPendingTasks(); - EXPECT_TRUE(client.BeganFrame()); - client.Reset(); - - // Tell the controller we drew, again. - controller.DidSwapBuffers(); - - // Trigger another frame. Since two frames are pending, we should not draw. - elapsed += task_runner->NextPendingTaskDelay(); - // Sanity check that previous code didn't move time backward. - EXPECT_GE(elapsed, time_source->Now()); - time_source->SetNow(elapsed); - task_runner->RunPendingTasks(); - EXPECT_FALSE(client.BeganFrame()); - - // Tell the controller the first frame ended 5ms later - time_source->SetNow(time_source->Now() + - base::TimeDelta::FromMilliseconds(5)); - controller.DidSwapBuffersComplete(); - - // Tick should not have been called - EXPECT_FALSE(client.BeganFrame()); - - // Trigger yet another frame. Since one frames is pending, another - // BeginFrame callback should run. - elapsed += task_runner->NextPendingTaskDelay(); - // Sanity check that previous code didn't move time backward. - EXPECT_GE(elapsed, time_source->Now()); - time_source->SetNow(elapsed); - task_runner->RunPendingTasks(); - EXPECT_TRUE(client.BeganFrame()); -} - -TEST(FrameRateControllerTest, TestFrameThrottling_Unthrottled) { - scoped_refptr<base::TestSimpleTaskRunner> task_runner = - new base::TestSimpleTaskRunner; - FakeFrameRateControllerClient client; - FrameRateController controller(task_runner.get()); - - controller.SetClient(&client); - controller.SetMaxSwapsPending(2); - - // SetActive triggers 1st frame, make sure the BeginFrame callback - // is called - controller.SetActive(true); - task_runner->RunPendingTasks(); - EXPECT_TRUE(client.BeganFrame()); - client.Reset(); - - // Even if we don't call DidSwapBuffers, FrameRateController should - // still attempt to tick multiple times until it does result in - // a DidSwapBuffers. - task_runner->RunPendingTasks(); - EXPECT_TRUE(client.BeganFrame()); - client.Reset(); - - task_runner->RunPendingTasks(); - EXPECT_TRUE(client.BeganFrame()); - client.Reset(); - - // DidSwapBuffers triggers 2nd frame, make sure the BeginFrame callback is - // called - controller.DidSwapBuffers(); - task_runner->RunPendingTasks(); - EXPECT_TRUE(client.BeganFrame()); - client.Reset(); - - // DidSwapBuffers triggers 3rd frame (> max_frames_pending), - // make sure the BeginFrame callback is NOT called - controller.DidSwapBuffers(); - task_runner->RunPendingTasks(); - EXPECT_FALSE(client.BeganFrame()); - client.Reset(); - - // Make sure there is no pending task since we can't do anything until we - // receive a DidSwapBuffersComplete anyway. - EXPECT_FALSE(task_runner->HasPendingTask()); - - // DidSwapBuffersComplete triggers a frame, make sure the BeginFrame - // callback is called - controller.DidSwapBuffersComplete(); - task_runner->RunPendingTasks(); - EXPECT_TRUE(client.BeganFrame()); -} - -TEST(FrameRateControllerTest, TestFrameThrottling_NoDoubleTicking) { - scoped_refptr<base::TestSimpleTaskRunner> task_runner = - new base::TestSimpleTaskRunner; - FakeFrameRateControllerClient client; - FrameRateController controller(task_runner.get()); - controller.SetClient(&client); - - // SetActive triggers 1st frame and queues another tick task since the time - // source isn't throttling. - controller.SetActive(true); - task_runner->RunPendingTasks(); - EXPECT_TRUE(client.BeganFrame()); - client.Reset(); - EXPECT_TRUE(task_runner->HasPendingTask()); - - // Simulate a frame swap. This shouldn't queue a second tick task. - controller.DidSwapBuffers(); - controller.DidSwapBuffersComplete(); - - // The client should only be ticked once. - task_runner->RunPendingTasks(); - EXPECT_EQ(1, client.frame_count()); -} - } // namespace } // namespace cc diff --git a/cc/scheduler/scheduler.cc b/cc/scheduler/scheduler.cc index 5d70010..592cbf1 100644 --- a/cc/scheduler/scheduler.cc +++ b/cc/scheduler/scheduler.cc @@ -92,11 +92,29 @@ void Scheduler::SetNeedsManageTiles() { ProcessScheduledActions(); } +void Scheduler::SetMaxSwapsPending(int max) { + state_machine_.SetMaxSwapsPending(max); +} + +void Scheduler::DidSwapBuffers() { + state_machine_.DidSwapBuffers(); + // There is no need to call ProcessScheduledActions here because + // swapping should not trigger any new actions. + if (!inside_process_scheduled_actions_) { + DCHECK_EQ(state_machine_.NextAction(), SchedulerStateMachine::ACTION_NONE); + } +} + void Scheduler::SetSwapUsedIncompleteTile(bool used_incomplete_tile) { state_machine_.SetSwapUsedIncompleteTile(used_incomplete_tile); ProcessScheduledActions(); } +void Scheduler::DidSwapBuffersComplete() { + state_machine_.DidSwapBuffersComplete(); + ProcessScheduledActions(); +} + void Scheduler::SetSmoothnessTakesPriority(bool smoothness_takes_priority) { state_machine_.SetSmoothnessTakesPriority(smoothness_takes_priority); ProcessScheduledActions(); @@ -433,7 +451,7 @@ void Scheduler::DrawAndSwapForced() { void Scheduler::DrawAndReadback() { DrawSwapReadbackResult result = client_->ScheduledActionDrawAndReadback(); - DCHECK(!result.did_swap); + DCHECK(!result.did_request_swap); } void Scheduler::ProcessScheduledActions() { diff --git a/cc/scheduler/scheduler.h b/cc/scheduler/scheduler.h index 18ecb50..7dd531b 100644 --- a/cc/scheduler/scheduler.h +++ b/cc/scheduler/scheduler.h @@ -75,7 +75,10 @@ class CC_EXPORT Scheduler { void SetNeedsManageTiles(); + void SetMaxSwapsPending(int max); + void DidSwapBuffers(); void SetSwapUsedIncompleteTile(bool used_incomplete_tile); + void DidSwapBuffersComplete(); void SetSmoothnessTakesPriority(bool smoothness_takes_priority); diff --git a/cc/scheduler/scheduler_state_machine.cc b/cc/scheduler/scheduler_state_machine.cc index 9a08819..7b2bb43 100644 --- a/cc/scheduler/scheduler_state_machine.cc +++ b/cc/scheduler/scheduler_state_machine.cc @@ -27,6 +27,8 @@ SchedulerStateMachine::SchedulerStateMachine(const SchedulerSettings& settings) last_frame_number_update_visible_tiles_was_called_(-1), manage_tiles_funnel_(0), consecutive_checkerboard_animations_(0), + max_pending_swaps_(1), + pending_swaps_(0), needs_redraw_(false), needs_manage_tiles_(false), swap_used_incomplete_tile_(false), @@ -226,6 +228,8 @@ scoped_ptr<base::Value> SchedulerStateMachine::AsValue() const { minor_state->SetInteger("manage_tiles_funnel", manage_tiles_funnel_); minor_state->SetInteger("consecutive_checkerboard_animations", consecutive_checkerboard_animations_); + minor_state->SetInteger("max_pending_swaps_", max_pending_swaps_); + minor_state->SetInteger("pending_swaps_", pending_swaps_); minor_state->SetBoolean("needs_redraw", needs_redraw_); minor_state->SetBoolean("needs_manage_tiles", needs_manage_tiles_); minor_state->SetBoolean("swap_used_incomplete_tile", @@ -364,6 +368,10 @@ bool SchedulerStateMachine::ShouldDraw() const { if (HasSwappedThisFrame()) return false; + // Do not queue too many swaps. + if (pending_swaps_ >= max_pending_swaps_) + return false; + // Except for the cases above, do not draw outside of the BeginImplFrame // deadline. if (begin_impl_frame_state_ != BEGIN_IMPL_FRAME_STATE_INSIDE_DEADLINE) @@ -472,6 +480,11 @@ bool SchedulerStateMachine::ShouldSendBeginMainFrame() const { if (!HasInitializedOutputSurface()) return false; + // SwapAck throttle the BeginMainFrames + // TODO(brianderson): Remove this restriction to improve throughput. + if (pending_swaps_ >= max_pending_swaps_) + return false; + if (skip_begin_main_frame_to_reduce_latency_) return false; @@ -581,15 +594,15 @@ void SchedulerStateMachine::UpdateState(Action action) { case ACTION_DRAW_AND_SWAP_FORCED: case ACTION_DRAW_AND_SWAP_IF_POSSIBLE: { - bool did_swap = true; - UpdateStateOnDraw(did_swap); + bool did_request_swap = true; + UpdateStateOnDraw(did_request_swap); return; } case ACTION_DRAW_AND_SWAP_ABORT: case ACTION_DRAW_AND_READBACK: { - bool did_swap = false; - UpdateStateOnDraw(did_swap); + bool did_request_swap = false; + UpdateStateOnDraw(did_request_swap); return; } @@ -726,7 +739,7 @@ void SchedulerStateMachine::UpdateStateOnActivation() { needs_redraw_ = true; } -void SchedulerStateMachine::UpdateStateOnDraw(bool did_swap) { +void SchedulerStateMachine::UpdateStateOnDraw(bool did_request_swap) { DCHECK(readback_state_ != READBACK_STATE_WAITING_FOR_REPLACEMENT_COMMIT && readback_state_ != READBACK_STATE_WAITING_FOR_REPLACEMENT_ACTIVATION) << *AsValue(); @@ -751,7 +764,7 @@ void SchedulerStateMachine::UpdateStateOnDraw(bool did_swap) { draw_if_possible_failed_ = false; active_tree_needs_first_draw_ = false; - if (did_swap) + if (did_request_swap) last_frame_number_swap_performed_ = current_frame_number_; } @@ -792,8 +805,7 @@ bool SchedulerStateMachine::SupportsProactiveBeginFrame() const { // Both the synchronous compositor and disabled vsync settings // make it undesirable to proactively request BeginImplFrames. // If this is true, the scheduler should poll. - return !settings_.using_synchronous_renderer_compositor && - settings_.throttle_frame_production; + return !settings_.using_synchronous_renderer_compositor; } // These are the cases where we definitely (or almost definitely) have a @@ -908,6 +920,10 @@ bool SchedulerStateMachine::ShouldTriggerBeginImplFrameDeadlineEarly() const { if (output_surface_state_ == OUTPUT_SURFACE_LOST) return true; + // SwapAck throttle the deadline since we wont draw and swap anyway. + if (pending_swaps_ >= max_pending_swaps_) + return false; + if (active_tree_needs_first_draw_) return true; @@ -996,11 +1012,25 @@ void SchedulerStateMachine::SetNeedsManageTiles() { } } +void SchedulerStateMachine::SetMaxSwapsPending(int max) { + max_pending_swaps_ = max; +} + +void SchedulerStateMachine::DidSwapBuffers() { + pending_swaps_++; + DCHECK_LE(pending_swaps_, max_pending_swaps_); +} + void SchedulerStateMachine::SetSwapUsedIncompleteTile( bool used_incomplete_tile) { swap_used_incomplete_tile_ = used_incomplete_tile; } +void SchedulerStateMachine::DidSwapBuffersComplete() { + DCHECK_GT(pending_swaps_, 0); + pending_swaps_--; +} + void SchedulerStateMachine::SetSmoothnessTakesPriority( bool smoothness_takes_priority) { smoothness_takes_priority_ = smoothness_takes_priority; @@ -1132,6 +1162,7 @@ void SchedulerStateMachine::DidCreateAndInitializeOutputSurface() { needs_commit_ = true; } did_create_and_initialize_first_output_surface_ = true; + pending_swaps_ = 0; } void SchedulerStateMachine::NotifyBeginMainFrameStarted() { diff --git a/cc/scheduler/scheduler_state_machine.h b/cc/scheduler/scheduler_state_machine.h index c8d68fc..2779264 100644 --- a/cc/scheduler/scheduler_state_machine.h +++ b/cc/scheduler/scheduler_state_machine.h @@ -168,10 +168,21 @@ class CC_EXPORT SchedulerStateMachine { // ManageTiles will occur shortly (even if no redraw is required). void SetNeedsManageTiles(); + // Sets how many swaps can be pending to the OutputSurface. + void SetMaxSwapsPending(int max); + + // If the scheduler attempted to draw and swap, this provides feedback + // regarding whether or not the swap actually occured. We might skip the + // swap when there is not damage, for example. + void DidSwapBuffers(); + // Indicates whether a redraw is required because we are currently rendering // with a low resolution or checkerboarded tile. void SetSwapUsedIncompleteTile(bool used_incomplete_tile); + // Notification from the OutputSurface that a swap has been consumed. + void DidSwapBuffersComplete(); + // Indicates whether to prioritize animation smoothness over new content // activation. void SetSmoothnessTakesPriority(bool smoothness_takes_priority); @@ -260,7 +271,7 @@ class CC_EXPORT SchedulerStateMachine { void UpdateStateOnCommit(bool commit_was_aborted); void UpdateStateOnActivation(); - void UpdateStateOnDraw(bool did_swap); + void UpdateStateOnDraw(bool did_request_swap); void UpdateStateOnManageTiles(); const SchedulerSettings settings_; @@ -285,6 +296,8 @@ class CC_EXPORT SchedulerStateMachine { // ManageTile per BeginImplFrame. int manage_tiles_funnel_; int consecutive_checkerboard_animations_; + int max_pending_swaps_; + int pending_swaps_; bool needs_redraw_; bool needs_manage_tiles_; bool swap_used_incomplete_tile_; diff --git a/cc/scheduler/scheduler_unittest.cc b/cc/scheduler/scheduler_unittest.cc index 68dc0d1..21e20ea 100644 --- a/cc/scheduler/scheduler_unittest.cc +++ b/cc/scheduler/scheduler_unittest.cc @@ -40,7 +40,7 @@ void InitializeOutputSurfaceAndFirstCommit(Scheduler* scheduler, class FakeSchedulerClient : public SchedulerClient { public: FakeSchedulerClient() - : needs_begin_impl_frame_(false) { + : needs_begin_impl_frame_(false), automatic_swap_ack_(true) { Reset(); } @@ -92,6 +92,9 @@ class FakeSchedulerClient : public SchedulerClient { void SetSwapWillHappenIfDrawHappens(bool swap_will_happen_if_draw_happens) { swap_will_happen_if_draw_happens_ = swap_will_happen_if_draw_happens; } + void SetAutomaticSwapAck(bool automatic_swap_ack) { + automatic_swap_ack_ = automatic_swap_ack; + } // SchedulerClient implementation. virtual void SetNeedsBeginFrame(bool enable) OVERRIDE { @@ -117,6 +120,13 @@ class FakeSchedulerClient : public SchedulerClient { draw_will_happen_ ? DrawSwapReadbackResult::DRAW_SUCCESS : DrawSwapReadbackResult::DRAW_ABORTED_CHECKERBOARD_ANIMATIONS; + bool swap_will_happen = + draw_will_happen_ && swap_will_happen_if_draw_happens_; + if (swap_will_happen) { + scheduler_->DidSwapBuffers(); + if (automatic_swap_ack_) + scheduler_->DidSwapBuffersComplete(); + } return DrawSwapReadbackResult( result, draw_will_happen_ && swap_will_happen_if_draw_happens_, @@ -125,18 +135,18 @@ class FakeSchedulerClient : public SchedulerClient { virtual DrawSwapReadbackResult ScheduledActionDrawAndSwapForced() OVERRIDE { actions_.push_back("ScheduledActionDrawAndSwapForced"); states_.push_back(scheduler_->StateAsValue().release()); - bool did_swap = swap_will_happen_if_draw_happens_; + bool did_request_swap = swap_will_happen_if_draw_happens_; bool did_readback = false; return DrawSwapReadbackResult( - DrawSwapReadbackResult::DRAW_SUCCESS, did_swap, did_readback); + DrawSwapReadbackResult::DRAW_SUCCESS, did_request_swap, did_readback); } virtual DrawSwapReadbackResult ScheduledActionDrawAndReadback() OVERRIDE { actions_.push_back("ScheduledActionDrawAndReadback"); states_.push_back(scheduler_->StateAsValue().release()); - bool did_swap = false; + bool did_request_swap = false; bool did_readback = true; return DrawSwapReadbackResult( - DrawSwapReadbackResult::DRAW_SUCCESS, did_swap, did_readback); + DrawSwapReadbackResult::DRAW_SUCCESS, did_request_swap, did_readback); } virtual void ScheduledActionCommit() OVERRIDE { actions_.push_back("ScheduledActionCommit"); @@ -178,6 +188,7 @@ class FakeSchedulerClient : public SchedulerClient { bool needs_begin_impl_frame_; bool draw_will_happen_; bool swap_will_happen_if_draw_happens_; + bool automatic_swap_ack_; int num_draws_; bool log_anticipated_draw_time_change_; base::TimeTicks posted_begin_impl_frame_deadline_; @@ -381,10 +392,10 @@ class SchedulerClientThatsetNeedsDrawInsideDraw : public FakeSchedulerClient { virtual DrawSwapReadbackResult ScheduledActionDrawAndSwapForced() OVERRIDE { NOTREACHED(); - bool did_swap = true; + bool did_request_swap = true; bool did_readback = false; return DrawSwapReadbackResult( - DrawSwapReadbackResult::DRAW_SUCCESS, did_swap, did_readback); + DrawSwapReadbackResult::DRAW_SUCCESS, did_request_swap, did_readback); } virtual void ScheduledActionCommit() OVERRIDE {} @@ -497,10 +508,10 @@ class SchedulerClientThatSetNeedsCommitInsideDraw : public FakeSchedulerClient { virtual DrawSwapReadbackResult ScheduledActionDrawAndSwapForced() OVERRIDE { NOTREACHED(); - bool did_swap = false; + bool did_request_swap = false; bool did_readback = false; return DrawSwapReadbackResult( - DrawSwapReadbackResult::DRAW_SUCCESS, did_swap, did_readback); + DrawSwapReadbackResult::DRAW_SUCCESS, did_request_swap, did_readback); } virtual void ScheduledActionCommit() OVERRIDE {} @@ -1058,6 +1069,9 @@ TEST(SchedulerTest, PollForCommitCompletion) { 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()); @@ -1065,6 +1079,13 @@ TEST(SchedulerTest, PollForCommitCompletion) { scheduler->BeginFrame(frame_args); EXPECT_TRUE(scheduler->CommitPending()); + // Draw and swap the frame, but don't ack the swap to simulate the Browser + // blocking on the renderer. + EXPECT_TRUE(scheduler->BeginImplFrameDeadlinePending()); + client.task_runner().RunPendingTasks(); // Run posted deadline. + EXPECT_FALSE(scheduler->BeginImplFrameDeadlinePending()); + scheduler->DidSwapBuffers(); + // Spin the event loop a few times and make sure we get more // DidAnticipateDrawTimeChange calls every time. int actions_so_far = client.num_actions_(); @@ -1095,7 +1116,7 @@ TEST(SchedulerTest, PollForCommitCompletion) { } } -TEST(SchedulerTest, BeginRetroFrame) { +TEST(SchedulerTest, BeginRetroFrameBasic) { FakeSchedulerClient client; SchedulerSettings scheduler_settings; Scheduler* scheduler = client.CreateScheduler(scheduler_settings); @@ -1116,7 +1137,6 @@ TEST(SchedulerTest, BeginRetroFrame) { BeginFrameArgs args = BeginFrameArgs::CreateForTesting(); args.deadline += base::TimeDelta::FromHours(1); scheduler->BeginFrame(args); - EXPECT_ACTION("WillBeginImplFrame", client, 0, 2); EXPECT_ACTION("ScheduledActionSendBeginMainFrame", client, 1, 2); EXPECT_TRUE(scheduler->BeginImplFrameDeadlinePending()); @@ -1172,5 +1192,93 @@ TEST(SchedulerTest, BeginRetroFrame) { client.Reset(); } +TEST(SchedulerTest, BeginRetroFrame_SwapThrottled) { + FakeSchedulerClient client; + SchedulerSettings scheduler_settings; + Scheduler* scheduler = client.CreateScheduler(scheduler_settings); + scheduler->SetCanStart(); + scheduler->SetVisible(true); + scheduler->SetCanDraw(true); + InitializeOutputSurfaceAndFirstCommit(scheduler, &client); + + // To test swap ack throttling, this test disables automatic swap acks. + scheduler->SetMaxSwapsPending(1); + client.SetAutomaticSwapAck(false); + + // SetNeedsCommit should begin the frame on the next BeginImplFrame. + client.Reset(); + scheduler->SetNeedsCommit(); + EXPECT_TRUE(client.needs_begin_impl_frame()); + EXPECT_SINGLE_ACTION("SetNeedsBeginFrame", client); + client.Reset(); + + // Create a BeginFrame with a long deadline to avoid race conditions. + // This is the first BeginFrame, which will be handled immediately. + BeginFrameArgs args = BeginFrameArgs::CreateForTesting(); + args.deadline += base::TimeDelta::FromHours(1); + scheduler->BeginFrame(args); + EXPECT_ACTION("WillBeginImplFrame", client, 0, 2); + EXPECT_ACTION("ScheduledActionSendBeginMainFrame", client, 1, 2); + EXPECT_TRUE(scheduler->BeginImplFrameDeadlinePending()); + EXPECT_TRUE(client.needs_begin_impl_frame()); + client.Reset(); + + // Queue BeginFrame while we are still handling the previous BeginFrame. + EXPECT_TRUE(scheduler->BeginImplFrameDeadlinePending()); + args.frame_time += base::TimeDelta::FromSeconds(1); + scheduler->BeginFrame(args); + EXPECT_EQ(0, client.num_actions_()); + EXPECT_TRUE(scheduler->BeginImplFrameDeadlinePending()); + client.Reset(); + + // NotifyReadyToCommit should trigger the pending commit and draw. + scheduler->NotifyBeginMainFrameStarted(); + scheduler->NotifyReadyToCommit(); + EXPECT_SINGLE_ACTION("ScheduledActionCommit", client); + EXPECT_TRUE(client.needs_begin_impl_frame()); + client.Reset(); + + // Swapping will put us into a swap throttled state. + client.task_runner().RunPendingTasks(); // Run posted deadline. + EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client, 0, 2); + EXPECT_ACTION("SetNeedsBeginFrame", client, 1, 2); + EXPECT_FALSE(scheduler->BeginImplFrameDeadlinePending()); + EXPECT_TRUE(client.needs_begin_impl_frame()); + client.Reset(); + + // While swap throttled, BeginRetroFrames should trigger BeginImplFrames + // but not a BeginMainFrame or draw. + scheduler->SetNeedsCommit(); + client.task_runner().RunPendingTasks(); // Run posted BeginRetroFrame. + EXPECT_ACTION("WillBeginImplFrame", client, 0, 1); + EXPECT_TRUE(scheduler->BeginImplFrameDeadlinePending()); + EXPECT_TRUE(client.needs_begin_impl_frame()); + client.Reset(); + + // Queue BeginFrame while we are still handling the previous BeginFrame. + args.frame_time += base::TimeDelta::FromSeconds(1); + scheduler->BeginFrame(args); + EXPECT_EQ(0, client.num_actions_()); + EXPECT_TRUE(scheduler->BeginImplFrameDeadlinePending()); + EXPECT_TRUE(client.needs_begin_impl_frame()); + client.Reset(); + + // Take us out of a swap throttled state. + scheduler->DidSwapBuffersComplete(); + EXPECT_ACTION("ScheduledActionSendBeginMainFrame", client, 0, 1); + EXPECT_TRUE(scheduler->BeginImplFrameDeadlinePending()); + EXPECT_TRUE(client.needs_begin_impl_frame()); + client.Reset(); + + // BeginImplFrame deadline should draw. + scheduler->SetNeedsRedraw(); + client.task_runner().RunPendingTasks(); // Run posted deadline. + EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client, 0, 2); + EXPECT_ACTION("SetNeedsBeginFrame", client, 1, 2); + EXPECT_FALSE(scheduler->BeginImplFrameDeadlinePending()); + EXPECT_TRUE(client.needs_begin_impl_frame()); + client.Reset(); +} + } // namespace } // namespace cc diff --git a/cc/scheduler/time_source.h b/cc/scheduler/time_source.h index e45ffbb..3b8d048 100644 --- a/cc/scheduler/time_source.h +++ b/cc/scheduler/time_source.h @@ -11,7 +11,7 @@ namespace cc { -class TimeSourceClient { +class CC_EXPORT TimeSourceClient { public: virtual void OnTimerTick() = 0; diff --git a/cc/test/fake_layer_tree_host_impl_client.h b/cc/test/fake_layer_tree_host_impl_client.h index 975992d..bdbea22 100644 --- a/cc/test/fake_layer_tree_host_impl_client.h +++ b/cc/test/fake_layer_tree_host_impl_client.h @@ -15,8 +15,9 @@ class FakeLayerTreeHostImplClient : public LayerTreeHostImplClient { // LayerTreeHostImplClient implementation. virtual void UpdateRendererCapabilitiesOnImplThread() OVERRIDE {} virtual void DidLoseOutputSurfaceOnImplThread() OVERRIDE {} + virtual void SetMaxSwapsPendingOnImplThread(int max) OVERRIDE {} virtual void DidSwapBuffersOnImplThread() OVERRIDE {} - virtual void OnSwapBuffersCompleteOnImplThread() OVERRIDE {} + virtual void DidSwapBuffersCompleteOnImplThread() OVERRIDE {} virtual void BeginFrame(const BeginFrameArgs& args) OVERRIDE {} virtual void OnCanDrawStateChanged(bool can_draw) OVERRIDE {} virtual void NotifyReadyToActivate() OVERRIDE {} diff --git a/cc/test/fake_output_surface_client.h b/cc/test/fake_output_surface_client.h index d64e063..b572c5e 100644 --- a/cc/test/fake_output_surface_client.h +++ b/cc/test/fake_output_surface_client.h @@ -25,7 +25,7 @@ class FakeOutputSurfaceClient : public OutputSurfaceClient { virtual void SetNeedsRedrawRect(const gfx::Rect& damage_rect) OVERRIDE {} virtual void BeginFrame(const BeginFrameArgs& args) OVERRIDE; virtual void DidSwapBuffers() OVERRIDE {} - virtual void OnSwapBuffersComplete() OVERRIDE {} + virtual void DidSwapBuffersComplete() OVERRIDE {} virtual void ReclaimResources(const CompositorFrameAck* ack) OVERRIDE {} virtual void DidLoseOutputSurface() OVERRIDE; virtual void SetExternalDrawConstraints( diff --git a/cc/test/layer_tree_test.cc b/cc/test/layer_tree_test.cc index ed8de34..5c85d16 100644 --- a/cc/test/layer_tree_test.cc +++ b/cc/test/layer_tree_test.cc @@ -128,8 +128,8 @@ class LayerTreeHostImplForTesting : public LayerTreeHostImpl { return result; } - virtual void OnSwapBuffersComplete() OVERRIDE { - LayerTreeHostImpl::OnSwapBuffersComplete(); + virtual void DidSwapBuffersComplete() OVERRIDE { + LayerTreeHostImpl::DidSwapBuffersComplete(); test_hooks_->SwapBuffersCompleteOnThread(this); } diff --git a/cc/test/scheduler_test_common.h b/cc/test/scheduler_test_common.h index 46e6012..071332d 100644 --- a/cc/test/scheduler_test_common.h +++ b/cc/test/scheduler_test_common.h @@ -51,8 +51,6 @@ class FakeFrameRateController : public FrameRateController { public: explicit FakeFrameRateController(scoped_refptr<TimeSource> timer) : FrameRateController(timer) {} - - int NumFramesPending() const { return num_frames_pending_; } }; } // namespace cc diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc index 84cf31d..9fac004 100644 --- a/cc/trees/layer_tree_host_impl.cc +++ b/cc/trees/layer_tree_host_impl.cc @@ -1300,8 +1300,8 @@ void LayerTreeHostImpl::DidSwapBuffers() { client_->DidSwapBuffersOnImplThread(); } -void LayerTreeHostImpl::OnSwapBuffersComplete() { - client_->OnSwapBuffersCompleteOnImplThread(); +void LayerTreeHostImpl::DidSwapBuffersComplete() { + client_->DidSwapBuffersCompleteOnImplThread(); } void LayerTreeHostImpl::ReclaimResources(const CompositorFrameAck* ack) { @@ -1900,8 +1900,11 @@ bool LayerTreeHostImpl::InitializeRenderer( GetRendererCapabilities().allow_rasterize_on_demand); } - // Setup BeginFrameEmulation if it's not supported natively - if (!settings_.begin_impl_frame_scheduling_enabled) { + if (!settings_.throttle_frame_production) { + // Disable VSync + output_surface->SetThrottleFrameProduction(false); + } else if (!settings_.begin_impl_frame_scheduling_enabled) { + // Setup BeginFrameEmulation if it's not supported natively const base::TimeDelta display_refresh_interval = base::TimeDelta::FromMicroseconds( base::Time::kMicrosecondsPerSecond / @@ -1909,7 +1912,6 @@ bool LayerTreeHostImpl::InitializeRenderer( output_surface->InitializeBeginFrameEmulation( proxy_->ImplThreadTaskRunner(), - settings_.throttle_frame_production, display_refresh_interval); } @@ -1917,7 +1919,7 @@ bool LayerTreeHostImpl::InitializeRenderer( output_surface->capabilities().max_frames_pending; if (max_frames_pending <= 0) max_frames_pending = OutputSurface::DEFAULT_MAX_FRAMES_PENDING; - output_surface->SetMaxFramesPending(max_frames_pending); + client_->SetMaxSwapsPendingOnImplThread(max_frames_pending); resource_provider_ = resource_provider.Pass(); output_surface_ = output_surface.Pass(); diff --git a/cc/trees/layer_tree_host_impl.h b/cc/trees/layer_tree_host_impl.h index 7f0aab6..a1f4fae 100644 --- a/cc/trees/layer_tree_host_impl.h +++ b/cc/trees/layer_tree_host_impl.h @@ -63,8 +63,9 @@ class LayerTreeHostImplClient { public: virtual void UpdateRendererCapabilitiesOnImplThread() = 0; virtual void DidLoseOutputSurfaceOnImplThread() = 0; + virtual void SetMaxSwapsPendingOnImplThread(int max) = 0; virtual void DidSwapBuffersOnImplThread() = 0; - virtual void OnSwapBuffersCompleteOnImplThread() = 0; + virtual void DidSwapBuffersCompleteOnImplThread() = 0; virtual void BeginFrame(const BeginFrameArgs& args) = 0; virtual void OnCanDrawStateChanged(bool can_draw) = 0; virtual void NotifyReadyToActivate() = 0; @@ -233,7 +234,7 @@ class CC_EXPORT LayerTreeHostImpl bool valid_for_tile_management) OVERRIDE; virtual void DidLoseOutputSurface() OVERRIDE; virtual void DidSwapBuffers() OVERRIDE; - virtual void OnSwapBuffersComplete() OVERRIDE; + virtual void DidSwapBuffersComplete() OVERRIDE; virtual void ReclaimResources(const CompositorFrameAck* ack) OVERRIDE; virtual void SetMemoryPolicy(const ManagedMemoryPolicy& policy) OVERRIDE; virtual void SetTreeActivationCallback(const base::Closure& callback) diff --git a/cc/trees/layer_tree_host_impl_unittest.cc b/cc/trees/layer_tree_host_impl_unittest.cc index 04d377e..35cd1fa 100644 --- a/cc/trees/layer_tree_host_impl_unittest.cc +++ b/cc/trees/layer_tree_host_impl_unittest.cc @@ -108,8 +108,9 @@ class LayerTreeHostImplTest : public testing::Test, virtual void UpdateRendererCapabilitiesOnImplThread() OVERRIDE {} virtual void DidLoseOutputSurfaceOnImplThread() OVERRIDE {} + virtual void SetMaxSwapsPendingOnImplThread(int max) OVERRIDE {} virtual void DidSwapBuffersOnImplThread() OVERRIDE {} - virtual void OnSwapBuffersCompleteOnImplThread() OVERRIDE {} + virtual void DidSwapBuffersCompleteOnImplThread() OVERRIDE {} virtual void BeginFrame(const BeginFrameArgs& args) OVERRIDE {} virtual void OnCanDrawStateChanged(bool can_draw) OVERRIDE { on_can_draw_state_changed_called_ = true; @@ -5354,7 +5355,7 @@ class CompositorFrameMetadataTest : public LayerTreeHostImplTest { CompositorFrameMetadataTest() : swap_buffers_complete_(0) {} - virtual void OnSwapBuffersCompleteOnImplThread() OVERRIDE { + virtual void DidSwapBuffersCompleteOnImplThread() OVERRIDE { swap_buffers_complete_++; } @@ -5372,7 +5373,7 @@ TEST_F(CompositorFrameMetadataTest, CompositorFrameAckCountsAsSwapComplete) { } CompositorFrameAck ack; host_impl_->ReclaimResources(&ack); - host_impl_->OnSwapBuffersComplete(); + host_impl_->DidSwapBuffersComplete(); EXPECT_EQ(swap_buffers_complete_, 1); } diff --git a/cc/trees/layer_tree_host_unittest_delegated.cc b/cc/trees/layer_tree_host_unittest_delegated.cc index 7cf8e69..ab5e4c2 100644 --- a/cc/trees/layer_tree_host_unittest_delegated.cc +++ b/cc/trees/layer_tree_host_unittest_delegated.cc @@ -240,7 +240,6 @@ class LayerTreeHostDelegatedTest : public LayerTreeTest { for (size_t i = 0; i < resources_to_return.size(); ++i) output_surface()->ReturnResource(resources_to_return[i], &ack); host_impl->ReclaimResources(&ack); - host_impl->OnSwapBuffersComplete(); } }; @@ -1648,7 +1647,6 @@ class LayerTreeHostDelegatedTestResourceSentToParent CompositorFrameAck ack; output_surface()->ReturnResource(map.find(999)->second, &ack); host_impl->ReclaimResources(&ack); - host_impl->OnSwapBuffersComplete(); } virtual void UnusedResourcesAreAvailable() OVERRIDE { diff --git a/cc/trees/single_thread_proxy.cc b/cc/trees/single_thread_proxy.cc index 5ccef20..72f16dc 100644 --- a/cc/trees/single_thread_proxy.cc +++ b/cc/trees/single_thread_proxy.cc @@ -382,8 +382,8 @@ void SingleThreadProxy::DidSwapBuffersOnImplThread() { client_->DidPostSwapBuffers(); } -void SingleThreadProxy::OnSwapBuffersCompleteOnImplThread() { - TRACE_EVENT0("cc", "SingleThreadProxy::OnSwapBuffersCompleteOnImplThread"); +void SingleThreadProxy::DidSwapBuffersCompleteOnImplThread() { + TRACE_EVENT0("cc", "SingleThreadProxy::DidSwapBuffersCompleteOnImplThread"); client_->DidCompleteSwapBuffers(); } diff --git a/cc/trees/single_thread_proxy.h b/cc/trees/single_thread_proxy.h index a52a6f5..0f86673 100644 --- a/cc/trees/single_thread_proxy.h +++ b/cc/trees/single_thread_proxy.h @@ -55,8 +55,9 @@ class SingleThreadProxy : public Proxy, LayerTreeHostImplClient { // LayerTreeHostImplClient implementation virtual void UpdateRendererCapabilitiesOnImplThread() OVERRIDE; virtual void DidLoseOutputSurfaceOnImplThread() OVERRIDE; + virtual void SetMaxSwapsPendingOnImplThread(int max) OVERRIDE {} virtual void DidSwapBuffersOnImplThread() OVERRIDE; - virtual void OnSwapBuffersCompleteOnImplThread() OVERRIDE; + virtual void DidSwapBuffersCompleteOnImplThread() OVERRIDE; virtual void BeginFrame(const BeginFrameArgs& args) OVERRIDE {} virtual void OnCanDrawStateChanged(bool can_draw) OVERRIDE; virtual void NotifyReadyToActivate() OVERRIDE; diff --git a/cc/trees/thread_proxy.cc b/cc/trees/thread_proxy.cc index e84937b..cac9bc6 100644 --- a/cc/trees/thread_proxy.cc +++ b/cc/trees/thread_proxy.cc @@ -433,9 +433,18 @@ void ThreadProxy::CheckOutputSurfaceStatusOnImplThread() { impl().scheduler->DidLoseOutputSurface(); } -void ThreadProxy::OnSwapBuffersCompleteOnImplThread() { - TRACE_EVENT0("cc", "ThreadProxy::OnSwapBuffersCompleteOnImplThread"); +void ThreadProxy::SetMaxSwapsPendingOnImplThread(int max) { + impl().scheduler->SetMaxSwapsPending(max); +} + +void ThreadProxy::DidSwapBuffersOnImplThread() { + impl().scheduler->DidSwapBuffers(); +} + +void ThreadProxy::DidSwapBuffersCompleteOnImplThread() { + TRACE_EVENT0("cc", "ThreadProxy::DidSwapBuffersCompleteOnImplThread"); DCHECK(IsImplThread()); + impl().scheduler->DidSwapBuffersComplete(); Proxy::MainThreadTaskRunner()->PostTask( FROM_HERE, base::Bind(&ThreadProxy::DidCompleteSwapBuffers, main_thread_weak_ptr_)); @@ -1237,10 +1246,10 @@ DrawSwapReadbackResult ThreadProxy::DrawSwapReadbackInternal( impl().readback_request = NULL; } else if (draw_frame) { DCHECK(swap_requested); - result.did_swap = impl().layer_tree_host_impl->SwapBuffers(frame); + result.did_request_swap = impl().layer_tree_host_impl->SwapBuffers(frame); // We don't know if we have incomplete tiles if we didn't actually swap. - if (result.did_swap) { + if (result.did_request_swap) { DCHECK(!frame.has_no_damage); SetSwapUsedIncompleteTileOnImplThread(frame.contains_incomplete_tile); } diff --git a/cc/trees/thread_proxy.h b/cc/trees/thread_proxy.h index b105c93..e2a8f21 100644 --- a/cc/trees/thread_proxy.h +++ b/cc/trees/thread_proxy.h @@ -73,8 +73,9 @@ class ThreadProxy : public Proxy, // LayerTreeHostImplClient implementation virtual void UpdateRendererCapabilitiesOnImplThread() OVERRIDE; virtual void DidLoseOutputSurfaceOnImplThread() OVERRIDE; - virtual void DidSwapBuffersOnImplThread() OVERRIDE {} - virtual void OnSwapBuffersCompleteOnImplThread() OVERRIDE; + virtual void SetMaxSwapsPendingOnImplThread(int max) OVERRIDE; + virtual void DidSwapBuffersOnImplThread() OVERRIDE; + virtual void DidSwapBuffersCompleteOnImplThread() OVERRIDE; virtual void BeginFrame(const BeginFrameArgs& args) OVERRIDE; virtual void OnCanDrawStateChanged(bool can_draw) OVERRIDE; virtual void NotifyReadyToActivate() OVERRIDE; |