| 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 {
|
|
|