Index: cc/scheduler/scheduler.cc |
diff --git a/cc/scheduler/scheduler.cc b/cc/scheduler/scheduler.cc |
index 20f1edebbee80ddf4684dbbbebc87f3d251f8e55..5d7001065f01dcdf1dbe0f1f9884e0544bfe8a22 100644 |
--- a/cc/scheduler/scheduler.cc |
+++ b/cc/scheduler/scheduler.cc |
@@ -23,17 +23,20 @@ Scheduler::Scheduler( |
client_(client), |
layer_tree_host_id_(layer_tree_host_id), |
impl_task_runner_(impl_task_runner), |
- last_set_needs_begin_impl_frame_(false), |
+ last_set_needs_begin_frame_(false), |
+ begin_retro_frame_posted_(false), |
state_machine_(scheduler_settings), |
inside_process_scheduled_actions_(false), |
inside_action_(SchedulerStateMachine::ACTION_NONE), |
weak_factory_(this) { |
DCHECK(client_); |
- DCHECK(!state_machine_.BeginImplFrameNeeded()); |
+ DCHECK(!state_machine_.BeginFrameNeeded()); |
if (settings_.main_frame_before_activation_enabled) { |
DCHECK(settings_.main_frame_before_draw_enabled); |
} |
+ begin_retro_frame_closure_ = |
+ base::Bind(&Scheduler::BeginRetroFrame, weak_factory_.GetWeakPtr()); |
begin_impl_frame_deadline_closure_ = base::Bind( |
&Scheduler::OnBeginImplFrameDeadline, weak_factory_.GetWeakPtr()); |
poll_for_draw_triggers_closure_ = base::Bind( |
@@ -118,13 +121,14 @@ void Scheduler::DidManageTiles() { |
void Scheduler::DidLoseOutputSurface() { |
TRACE_EVENT0("cc", "Scheduler::DidLoseOutputSurface"); |
state_machine_.DidLoseOutputSurface(); |
- last_set_needs_begin_impl_frame_ = false; |
+ last_set_needs_begin_frame_ = false; |
+ begin_retro_frame_args_.clear(); |
ProcessScheduledActions(); |
} |
void Scheduler::DidCreateAndInitializeOutputSurface() { |
TRACE_EVENT0("cc", "Scheduler::DidCreateAndInitializeOutputSurface"); |
- DCHECK(!last_set_needs_begin_impl_frame_); |
+ DCHECK(!last_set_needs_begin_frame_); |
DCHECK(begin_impl_frame_deadline_task_.IsCancelled()); |
state_machine_.DidCreateAndInitializeOutputSurface(); |
ProcessScheduledActions(); |
@@ -136,53 +140,54 @@ void Scheduler::NotifyBeginMainFrameStarted() { |
} |
base::TimeTicks Scheduler::AnticipatedDrawTime() const { |
- if (!last_set_needs_begin_impl_frame_ || |
- last_begin_impl_frame_args_.interval <= base::TimeDelta()) |
+ if (!last_set_needs_begin_frame_ || |
+ begin_impl_frame_args_.interval <= base::TimeDelta()) |
return base::TimeTicks(); |
base::TimeTicks now = gfx::FrameTime::Now(); |
- base::TimeTicks timebase = std::max(last_begin_impl_frame_args_.frame_time, |
- last_begin_impl_frame_args_.deadline); |
- int64 intervals = |
- 1 + ((now - timebase) / last_begin_impl_frame_args_.interval); |
- return timebase + (last_begin_impl_frame_args_.interval * intervals); |
+ base::TimeTicks timebase = std::max(begin_impl_frame_args_.frame_time, |
+ begin_impl_frame_args_.deadline); |
+ int64 intervals = 1 + ((now - timebase) / begin_impl_frame_args_.interval); |
+ return timebase + (begin_impl_frame_args_.interval * intervals); |
} |
base::TimeTicks Scheduler::LastBeginImplFrameTime() { |
- return last_begin_impl_frame_args_.frame_time; |
+ return begin_impl_frame_args_.frame_time; |
} |
-void Scheduler::SetupNextBeginImplFrameIfNeeded() { |
- bool needs_begin_impl_frame = |
- state_machine_.BeginImplFrameNeeded(); |
+void Scheduler::SetupNextBeginFrameIfNeeded() { |
+ bool needs_begin_frame = state_machine_.BeginFrameNeeded(); |
bool at_end_of_deadline = |
state_machine_.begin_impl_frame_state() == |
SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_INSIDE_DEADLINE; |
- bool should_call_set_needs_begin_impl_frame = |
- // Always request the BeginImplFrame immediately if it wasn't needed |
- // before. |
- (needs_begin_impl_frame && !last_set_needs_begin_impl_frame_) || |
- // We always need to explicitly request our next BeginImplFrame. |
+ bool should_call_set_needs_begin_frame = |
+ // Always request the BeginFrame immediately if it wasn't needed before. |
+ (needs_begin_frame && !last_set_needs_begin_frame_) || |
+ // We always need to explicitly request our next BeginFrame. |
at_end_of_deadline; |
- if (should_call_set_needs_begin_impl_frame) { |
- client_->SetNeedsBeginImplFrame(needs_begin_impl_frame); |
- last_set_needs_begin_impl_frame_ = needs_begin_impl_frame; |
+ if (should_call_set_needs_begin_frame) { |
+ client_->SetNeedsBeginFrame(needs_begin_frame); |
+ last_set_needs_begin_frame_ = needs_begin_frame; |
} |
+ // Handle retroactive BeginFrames. |
+ if (last_set_needs_begin_frame_) |
+ PostBeginRetroFrameIfNeeded(); |
+ |
bool needs_advance_commit_state_timer = false; |
// Setup PollForAnticipatedDrawTriggers if we need to monitor state but |
- // aren't expecting any more BeginImplFrames. This should only be needed by |
- // the synchronous compositor when BeginImplFrameNeeded is false. |
+ // aren't expecting any more BeginFrames. This should only be needed by |
+ // the synchronous compositor when BeginFrameNeeded is false. |
if (state_machine_.ShouldPollForAnticipatedDrawTriggers()) { |
- DCHECK(!state_machine_.SupportsProactiveBeginImplFrame()); |
- DCHECK(!needs_begin_impl_frame); |
+ 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 = last_begin_impl_frame_args_.IsValid() |
- ? last_begin_impl_frame_args_.interval |
+ base::TimeDelta delay = begin_impl_frame_args_.IsValid() |
+ ? begin_impl_frame_args_.interval |
: BeginFrameArgs::DefaultInterval(); |
impl_task_runner_->PostDelayedTask( |
FROM_HERE, poll_for_draw_triggers_task_.callback(), delay); |
@@ -192,10 +197,10 @@ void Scheduler::SetupNextBeginImplFrameIfNeeded() { |
// At this point we'd prefer to advance through the commit flow by |
// drawing a frame, however it's possible that the frame rate controller |
- // will not give us a BeginImplFrame until the commit completes. See |
+ // will not give us a BeginFrame until the commit completes. See |
// crbug.com/317430 for an example of a swap ack being held on commit. Thus |
// we set a repeating timer to poll on ProcessScheduledActions until we |
- // successfully reach BeginImplFrame. Synchronous compositor does not use |
+ // 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) { |
@@ -205,20 +210,102 @@ void Scheduler::SetupNextBeginImplFrameIfNeeded() { |
if (needs_advance_commit_state_timer) { |
if (advance_commit_state_task_.IsCancelled() && |
- last_begin_impl_frame_args_.IsValid()) { |
+ begin_impl_frame_args_.IsValid()) { |
// Since we'd rather get a BeginImplFrame by the normal mechanism, we |
// set the interval to twice the interval from the previous frame. |
advance_commit_state_task_.Reset(advance_commit_state_closure_); |
- impl_task_runner_->PostDelayedTask( |
- FROM_HERE, |
- advance_commit_state_task_.callback(), |
- last_begin_impl_frame_args_.interval * 2); |
+ impl_task_runner_->PostDelayedTask(FROM_HERE, |
+ advance_commit_state_task_.callback(), |
+ begin_impl_frame_args_.interval * 2); |
} |
} else { |
advance_commit_state_task_.Cancel(); |
} |
} |
+// BeginFrame is the mechanism that tells us that now is a good time to start |
+// making a frame. Usually this means that user input for the frame is complete. |
+// If the scheduler is busy, we queue the BeginFrame to be handled later as |
+// a BeginRetroFrame. |
+void Scheduler::BeginFrame(const BeginFrameArgs& args) { |
+ bool should_defer_begin_frame; |
+ if (settings_.using_synchronous_renderer_compositor) { |
+ should_defer_begin_frame = false; |
+ } else { |
+ should_defer_begin_frame = |
+ !begin_retro_frame_args_.empty() || begin_retro_frame_posted_ || |
+ !last_set_needs_begin_frame_ || |
+ (state_machine_.begin_impl_frame_state() != |
+ SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_IDLE); |
+ } |
+ |
+ if (should_defer_begin_frame) { |
+ begin_retro_frame_args_.push_back(args); |
+ TRACE_EVENT_INSTANT0( |
+ "cc", "Scheduler::BeginFrame deferred", TRACE_EVENT_SCOPE_THREAD); |
+ return; |
+ } |
+ |
+ BeginImplFrame(args); |
+} |
+ |
+// BeginRetroFrame is called for BeginFrames that we've deferred because |
+// the scheduler was in the middle of processing a previous BeginFrame. |
+void Scheduler::BeginRetroFrame() { |
+ TRACE_EVENT0("cc", "Scheduler::BeginRetroFrame"); |
+ DCHECK(begin_retro_frame_posted_); |
+ DCHECK(!begin_retro_frame_args_.empty()); |
+ DCHECK(!settings_.using_synchronous_renderer_compositor); |
+ |
+ // Discard expired BeginRetroFrames |
+ // Today, we should always end up with at most one un-expired BeginRetroFrame |
+ // because deadlines will not be greater than the next frame time. We don't |
+ // DCHECK though because some systems don't always have monotonic timestamps. |
+ // TODO(brianderson): In the future, long deadlines could result in us not |
+ // draining the queue if we don't catch up. If we consistently can't catch |
+ // up, our fallback should be to lower our frame rate. |
+ base::TimeTicks now = gfx::FrameTime::Now(); |
+ base::TimeDelta draw_duration_estimate = client_->DrawDurationEstimate(); |
+ while (!begin_retro_frame_args_.empty() && |
+ now > AdjustedBeginImplFrameDeadline(begin_retro_frame_args_.front(), |
+ draw_duration_estimate)) { |
+ begin_retro_frame_args_.pop_front(); |
+ } |
+ |
+ if (begin_retro_frame_args_.empty()) { |
+ TRACE_EVENT_INSTANT0( |
+ "cc", "Scheduler::BeginRetroFrames expired", TRACE_EVENT_SCOPE_THREAD); |
+ } else { |
+ BeginImplFrame(begin_retro_frame_args_.front()); |
+ begin_retro_frame_args_.pop_front(); |
+ } |
+ |
+ begin_retro_frame_posted_ = false; |
+} |
+ |
+// There could be a race between the posted BeginRetroFrame and a new |
+// BeginFrame arriving via the normal mechanism. Scheduler::BeginFrame |
+// will check if there is a pending BeginRetroFrame to ensure we handle |
+// BeginFrames in FIFO order. |
+void Scheduler::PostBeginRetroFrameIfNeeded() { |
+ if (begin_retro_frame_args_.empty() || begin_retro_frame_posted_) |
+ return; |
+ |
+ // begin_retro_frame_args_ should always be empty for the |
+ // synchronous compositor. |
+ DCHECK(!settings_.using_synchronous_renderer_compositor); |
+ |
+ if (state_machine_.begin_impl_frame_state() != |
+ SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_IDLE) |
+ return; |
+ |
+ begin_retro_frame_posted_ = true; |
+ impl_task_runner_->PostTask(FROM_HERE, begin_retro_frame_closure_); |
+} |
+ |
+// BeginImplFrame starts a compositor frame that will wait up until a deadline |
+// for a BeginMainFrame+activation to complete before it times out and draws |
+// any asynchronous animation and scroll/pinch updates. |
void Scheduler::BeginImplFrame(const BeginFrameArgs& args) { |
TRACE_EVENT0("cc", "Scheduler::BeginImplFrame"); |
DCHECK(state_machine_.begin_impl_frame_state() == |
@@ -227,8 +314,9 @@ void Scheduler::BeginImplFrame(const BeginFrameArgs& args) { |
advance_commit_state_task_.Cancel(); |
- last_begin_impl_frame_args_ = args; |
- last_begin_impl_frame_args_.deadline -= client_->DrawDurationEstimate(); |
+ base::TimeDelta draw_duration_estimate = client_->DrawDurationEstimate(); |
+ begin_impl_frame_args_ = args; |
+ begin_impl_frame_args_.deadline -= draw_duration_estimate; |
if (!state_machine_.smoothness_takes_priority() && |
state_machine_.MainThreadIsInHighLatencyMode() && |
@@ -236,7 +324,8 @@ void Scheduler::BeginImplFrame(const BeginFrameArgs& args) { |
state_machine_.SetSkipNextBeginMainFrameToReduceLatency(); |
} |
- state_machine_.OnBeginImplFrame(last_begin_impl_frame_args_); |
+ client_->WillBeginImplFrame(begin_impl_frame_args_); |
+ state_machine_.OnBeginImplFrame(begin_impl_frame_args_); |
devtools_instrumentation::DidBeginFrame(layer_tree_host_id_); |
ProcessScheduledActions(); |
@@ -245,11 +334,13 @@ void Scheduler::BeginImplFrame(const BeginFrameArgs& args) { |
return; |
state_machine_.OnBeginImplFrameDeadlinePending(); |
- base::TimeTicks adjusted_deadline = AdjustedBeginImplFrameDeadline(); |
- ScheduleBeginImplFrameDeadline(adjusted_deadline); |
+ ScheduleBeginImplFrameDeadline( |
+ AdjustedBeginImplFrameDeadline(args, draw_duration_estimate)); |
} |
-base::TimeTicks Scheduler::AdjustedBeginImplFrameDeadline() const { |
+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(); |
@@ -259,7 +350,7 @@ base::TimeTicks Scheduler::AdjustedBeginImplFrameDeadline() const { |
} 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 last_begin_impl_frame_args_.deadline; |
+ return args.deadline - draw_duration_estimate; |
} else { |
// The impl thread doesn't have anything it wants to draw and we are just |
// waiting for a new active tree, so post the deadline for the next |
@@ -268,8 +359,7 @@ base::TimeTicks Scheduler::AdjustedBeginImplFrameDeadline() const { |
// BeginImplFrame. |
// TODO(brianderson): Handle long deadlines (that are past the next frame's |
// frame time) properly instead of using this hack. |
- return last_begin_impl_frame_args_.frame_time + |
- last_begin_impl_frame_args_.interval; |
+ return args.frame_time + args.interval; |
} |
} |
@@ -402,7 +492,7 @@ void Scheduler::ProcessScheduledActions() { |
} |
} while (action != SchedulerStateMachine::ACTION_NONE); |
- SetupNextBeginImplFrameIfNeeded(); |
+ SetupNextBeginFrameIfNeeded(); |
client_->DidAnticipatedDrawTimeChange(AnticipatedDrawTime()); |
if (state_machine_.ShouldTriggerBeginImplFrameDeadlineEarly()) { |
@@ -423,8 +513,8 @@ scoped_ptr<base::Value> Scheduler::StateAsValue() const { |
scheduler_state->SetDouble( |
"time_until_anticipated_draw_time_ms", |
(AnticipatedDrawTime() - base::TimeTicks::Now()).InMillisecondsF()); |
- scheduler_state->SetBoolean("last_set_needs_begin_impl_frame_", |
- last_set_needs_begin_impl_frame_); |
+ scheduler_state->SetBoolean("last_set_needs_begin_frame_", |
+ last_set_needs_begin_frame_); |
scheduler_state->SetBoolean("begin_impl_frame_deadline_task_", |
!begin_impl_frame_deadline_task_.IsCancelled()); |
scheduler_state->SetBoolean("poll_for_draw_triggers_task_", |
@@ -450,19 +540,19 @@ bool Scheduler::CanCommitAndActivateBeforeDeadline() const { |
// Check if the main thread computation and commit can be finished before the |
// impl thread's deadline. |
base::TimeTicks estimated_draw_time = |
- last_begin_impl_frame_args_.frame_time + |
+ begin_impl_frame_args_.frame_time + |
client_->BeginMainFrameToCommitDurationEstimate() + |
client_->CommitToActivateDurationEstimate(); |
- TRACE_EVENT2(TRACE_DISABLED_BY_DEFAULT("cc.debug.scheduler"), |
- "CanCommitAndActivateBeforeDeadline", |
- "time_left_after_drawing_ms", |
- (last_begin_impl_frame_args_.deadline - estimated_draw_time) |
- .InMillisecondsF(), |
- "state", |
- TracedValue::FromValue(StateAsValue().release())); |
+ TRACE_EVENT2( |
+ TRACE_DISABLED_BY_DEFAULT("cc.debug.scheduler"), |
+ "CanCommitAndActivateBeforeDeadline", |
+ "time_left_after_drawing_ms", |
+ (begin_impl_frame_args_.deadline - estimated_draw_time).InMillisecondsF(), |
+ "state", |
+ TracedValue::FromValue(StateAsValue().release())); |
- return estimated_draw_time < last_begin_impl_frame_args_.deadline; |
+ return estimated_draw_time < begin_impl_frame_args_.deadline; |
} |
bool Scheduler::IsBeginMainFrameSentOrStarted() const { |