diff options
Diffstat (limited to 'chromium/cc/scheduler/scheduler.cc')
-rw-r--r-- | chromium/cc/scheduler/scheduler.cc | 599 |
1 files changed, 325 insertions, 274 deletions
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"); |