| Index: cc/scheduler/scheduler.cc
|
| diff --git a/cc/scheduler/scheduler.cc b/cc/scheduler/scheduler.cc
|
| index 44720bc91b9eb0572787e8478f564ad04f9d716a..4213754f3b19114376773c4c3a0aa33b3ce3bd5f 100644
|
| --- a/cc/scheduler/scheduler.cc
|
| +++ b/cc/scheduler/scheduler.cc
|
| @@ -64,6 +64,8 @@
|
| DCHECK(client_);
|
| DCHECK(!state_machine_.BeginFrameNeeded());
|
|
|
| + begin_retro_frame_closure_ =
|
| + base::Bind(&Scheduler::BeginRetroFrame, weak_factory_.GetWeakPtr());
|
| begin_impl_frame_deadline_closure_ = base::Bind(
|
| &Scheduler::OnBeginImplFrameDeadline, weak_factory_.GetWeakPtr());
|
|
|
| @@ -193,6 +195,8 @@
|
|
|
| void Scheduler::DidLoseCompositorFrameSink() {
|
| TRACE_EVENT0("cc", "Scheduler::DidLoseCompositorFrameSink");
|
| + begin_retro_frame_args_.clear();
|
| + begin_retro_frame_task_.Cancel();
|
| state_machine_.DidLoseCompositorFrameSink();
|
| UpdateCompositorTimingHistoryRecordingEnabled();
|
| ProcessScheduledActions();
|
| @@ -229,26 +233,29 @@
|
| }
|
|
|
| void Scheduler::SetupNextBeginFrameIfNeeded() {
|
| - if (state_machine_.begin_impl_frame_state() !=
|
| - SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_IDLE) {
|
| - return;
|
| - }
|
| -
|
| - bool needs_begin_frames = state_machine_.BeginFrameNeeded();
|
| - if (needs_begin_frames && !observing_begin_frame_source_) {
|
| - observing_begin_frame_source_ = true;
|
| - if (begin_frame_source_)
|
| - begin_frame_source_->AddObserver(this);
|
| - devtools_instrumentation::NeedsBeginFrameChanged(layer_tree_host_id_, true);
|
| - } else if (!needs_begin_frames && observing_begin_frame_source_) {
|
| - observing_begin_frame_source_ = false;
|
| - if (begin_frame_source_)
|
| - begin_frame_source_->RemoveObserver(this);
|
| - missed_begin_frame_task_.Cancel();
|
| - BeginImplFrameNotExpectedSoon();
|
| - devtools_instrumentation::NeedsBeginFrameChanged(layer_tree_host_id_,
|
| - false);
|
| - }
|
| + // Never call SetNeedsBeginFrames if the frame source already has the right
|
| + // value.
|
| + if (observing_begin_frame_source_ != state_machine_.BeginFrameNeeded()) {
|
| + if (state_machine_.BeginFrameNeeded()) {
|
| + // Call AddObserver as soon as possible.
|
| + observing_begin_frame_source_ = true;
|
| + if (begin_frame_source_)
|
| + begin_frame_source_->AddObserver(this);
|
| + devtools_instrumentation::NeedsBeginFrameChanged(layer_tree_host_id_,
|
| + true);
|
| + } else if (state_machine_.begin_impl_frame_state() ==
|
| + SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_IDLE) {
|
| + // Call RemoveObserver in between frames only.
|
| + observing_begin_frame_source_ = false;
|
| + if (begin_frame_source_)
|
| + begin_frame_source_->RemoveObserver(this);
|
| + BeginImplFrameNotExpectedSoon();
|
| + devtools_instrumentation::NeedsBeginFrameChanged(layer_tree_host_id_,
|
| + false);
|
| + }
|
| + }
|
| +
|
| + PostBeginRetroFrameIfNeeded();
|
| }
|
|
|
| void Scheduler::OnBeginFrameSourcePausedChanged(bool paused) {
|
| @@ -267,12 +274,6 @@
|
| bool Scheduler::OnBeginFrameDerivedImpl(const BeginFrameArgs& args) {
|
| TRACE_EVENT1("cc,benchmark", "Scheduler::BeginFrame", "args", args.AsValue());
|
|
|
| - if (!state_machine_.BeginFrameNeeded()) {
|
| - TRACE_EVENT_INSTANT0("cc", "Scheduler::BeginFrameDropped",
|
| - TRACE_EVENT_SCOPE_THREAD);
|
| - return false;
|
| - }
|
| -
|
| // Trace this begin frame time through the Chrome stack
|
| TRACE_EVENT_FLOW_BEGIN0(
|
| TRACE_DISABLED_BY_DEFAULT("cc.debug.scheduler.frames"), "BeginFrameArgs",
|
| @@ -283,18 +284,31 @@
|
| return true;
|
| }
|
|
|
| - if (inside_process_scheduled_actions_) {
|
| - // The BFS can send a missed begin frame inside AddObserver. We can't handle
|
| - // a begin frame inside ProcessScheduledActions so post a task.
|
| - DCHECK_EQ(args.type, BeginFrameArgs::MISSED);
|
| - DCHECK(missed_begin_frame_task_.IsCancelled());
|
| - missed_begin_frame_task_.Reset(base::Bind(
|
| - &Scheduler::BeginImplFrameWithDeadline, base::Unretained(this), args));
|
| - task_runner_->PostTask(FROM_HERE, missed_begin_frame_task_.callback());
|
| + // 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);
|
| + PostBeginRetroFrameIfNeeded();
|
| return true;
|
| }
|
|
|
| - BeginImplFrameWithDeadline(args);
|
| + bool should_defer_begin_frame =
|
| + !begin_retro_frame_args_.empty() ||
|
| + !begin_retro_frame_task_.IsCancelled() ||
|
| + !observing_begin_frame_source_ ||
|
| + (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);
|
| + // Queuing the frame counts as "using it", so we need to return true.
|
| + } else {
|
| + BeginImplFrameWithDeadline(args);
|
| + }
|
| return true;
|
| }
|
|
|
| @@ -318,45 +332,87 @@
|
| state_machine_.SetResourcelessSoftwareDraw(false);
|
| }
|
|
|
| -void Scheduler::BeginImplFrameWithDeadline(const BeginFrameArgs& args) {
|
| - // The storage for |args| is owned by the missed begin frame task. Therefore
|
| - // save |args| before cancelling the task either here or in the deadline.
|
| - BeginFrameArgs adjusted_args = args;
|
| - // Cancel the missed begin frame task in case the BFS sends a begin frame
|
| - // before the missed frame task runs.
|
| - missed_begin_frame_task_.Cancel();
|
| -
|
| - // Discard missed begin frames if they are too late.
|
| - if (adjusted_args.type == BeginFrameArgs::MISSED &&
|
| - Now() > adjusted_args.deadline) {
|
| - begin_frame_source_->DidFinishFrame(this, 0);
|
| - return;
|
| - }
|
| -
|
| - // Run the previous deadline if any.
|
| - if (state_machine_.begin_impl_frame_state() ==
|
| - SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_INSIDE_BEGIN_FRAME) {
|
| - OnBeginImplFrameDeadline();
|
| - // We may not need begin frames any longer.
|
| - if (!observing_begin_frame_source_) {
|
| - begin_frame_source_->DidFinishFrame(this, 0);
|
| - return;
|
| - }
|
| - }
|
| +// 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,benchmark", "Scheduler::BeginRetroFrame");
|
| + DCHECK(!settings_.using_synchronous_renderer_compositor);
|
| + 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);
|
|
|
| + begin_retro_frame_task_.Cancel();
|
| +
|
| + // 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 = Now();
|
| +
|
| + while (!begin_retro_frame_args_.empty()) {
|
| + const BeginFrameArgs& args = begin_retro_frame_args_.front();
|
| + base::TimeTicks expiration_time = args.deadline;
|
| + if (now <= expiration_time)
|
| + break;
|
| + 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();
|
| + if (begin_frame_source_)
|
| + begin_frame_source_->DidFinishFrame(this, begin_retro_frame_args_.size());
|
| + }
|
| +
|
| + if (begin_retro_frame_args_.empty()) {
|
| + TRACE_EVENT_INSTANT0("cc", "Scheduler::BeginRetroFrames all expired",
|
| + TRACE_EVENT_SCOPE_THREAD);
|
| + } else {
|
| + BeginFrameArgs front = begin_retro_frame_args_.front();
|
| + begin_retro_frame_args_.pop_front();
|
| + BeginImplFrameWithDeadline(front);
|
| + }
|
| +}
|
| +
|
| +// 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() {
|
| + TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("cc.debug.scheduler"),
|
| + "Scheduler::PostBeginRetroFrameIfNeeded", "state", AsValue());
|
| + if (!observing_begin_frame_source_)
|
| + return;
|
| +
|
| + if (begin_retro_frame_args_.empty() || !begin_retro_frame_task_.IsCancelled())
|
| + 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_task_.Reset(begin_retro_frame_closure_);
|
| +
|
| + task_runner_->PostTask(FROM_HERE, begin_retro_frame_task_.callback());
|
| +}
|
| +
|
| +void Scheduler::BeginImplFrameWithDeadline(const BeginFrameArgs& args) {
|
| bool main_thread_is_in_high_latency_mode =
|
| state_machine_.main_thread_missed_last_deadline();
|
| TRACE_EVENT2("cc,benchmark", "Scheduler::BeginImplFrame", "args",
|
| - adjusted_args.AsValue(), "main_thread_missed_last_deadline",
|
| + args.AsValue(), "main_thread_missed_last_deadline",
|
| 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);
|
| -
|
| + BeginFrameArgs adjusted_args = args;
|
| adjusted_args.deadline -= compositor_timing_history_->DrawDurationEstimate();
|
| adjusted_args.deadline -= kDeadlineFudgeFactor;
|
|
|
| @@ -371,7 +427,7 @@
|
| compositor_timing_history_->BeginMainFrameQueueDurationCriticalEstimate();
|
|
|
| state_machine_.SetCriticalBeginMainFrameToActivateIsFast(
|
| - bmf_to_activate_estimate_critical < adjusted_args.interval);
|
| + bmf_to_activate_estimate_critical < args.interval);
|
|
|
| // Update the BeginMainFrame args now that we know whether the main
|
| // thread will be on the critical path or not.
|
| @@ -399,7 +455,7 @@
|
| TRACE_EVENT_INSTANT0("cc", "SkipBeginImplFrameToReduceLatency",
|
| TRACE_EVENT_SCOPE_THREAD);
|
| if (begin_frame_source_)
|
| - begin_frame_source_->DidFinishFrame(this, 0);
|
| + begin_frame_source_->DidFinishFrame(this, begin_retro_frame_args_.size());
|
| return;
|
| }
|
|
|
| @@ -428,7 +484,7 @@
|
|
|
| client_->DidFinishImplFrame();
|
| if (begin_frame_source_)
|
| - begin_frame_source_->DidFinishFrame(this, 0);
|
| + begin_frame_source_->DidFinishFrame(this, begin_retro_frame_args_.size());
|
| begin_impl_frame_tracker_.Finish();
|
| }
|
|
|
| @@ -653,10 +709,12 @@
|
| state->BeginDictionary("scheduler_state");
|
| state->SetBoolean("observing_begin_frame_source",
|
| observing_begin_frame_source_);
|
| + state->SetInteger("begin_retro_frame_args",
|
| + static_cast<int>(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("missed_begin_frame_task",
|
| - !missed_begin_frame_task_.IsCancelled());
|
| state->SetString("inside_action",
|
| SchedulerStateMachine::ActionToString(inside_action_));
|
|
|
|
|