Commit 1607d912 authored by Sunny Sachanandani's avatar Sunny Sachanandani Committed by Commit Bot

Reland "cc: Throttle incoming begin frames in scheduler"

This is a reland of 6beef1e6

Removed incorrect DCHECKs that were added in the original change.

Original change's description:
> cc: Throttle incoming begin frames in scheduler
>
> Unlike PostTask from IO thread to compositor thread in Chrome IPC, mojo
> polls for messages on the compositor thread which means it can dequeue a
> large number of begin frame messages after the compositor thread has
> been busy for some time. All but the last begin frame cancels the
> previous begin frame, and is essentially a nop, but it still ticks
> animations. When a page has a large number of animations each begin
> frame can take a long time and push out other tasks such as tile manager
> callbacks stalling the pipeline.
>
> Throttling the begin frames in viz doesn't fully solve the problem
> because we have to allow at least two begin frames in flight for
> pipelining, and so the client can still process two begin frames back to
> back.
>
> Throttling in AsyncLTFS causes issues with LTFS lifetime and ordering
> with respect to other messages.
>
> Saving incoming begin frame in scheduler and posting a task works and
> ensures that only one begin frame is outstanding at any time.
>
> R=danakj
> BUG=782002
>
> Cq-Include-Trybots: luci.chromium.try:android_optional_gpu_tests_rel;master.tryserver.blink:linux_trusty_blink_rel
> Change-Id: I247a87ad7475d33f878a215ce87056d20482f88c
> Reviewed-on: https://chromium-review.googlesource.com/1130082
> Commit-Queue: Sunny Sachanandani <sunnyps@chromium.org>
> Reviewed-by: danakj <danakj@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#577046}

TBR=danakj
BUG=782002

Change-Id: Iec7bd9e421bdb372f101ecebc6cb71835dcb27bf
Cq-Include-Trybots: luci.chromium.try:android_optional_gpu_tests_rel;master.tryserver.blink:linux_trusty_blink_rel
Reviewed-on: https://chromium-review.googlesource.com/1147082
Commit-Queue: Sunny Sachanandani <sunnyps@chromium.org>
Reviewed-by: default avatarSunny Sachanandani <sunnyps@chromium.org>
Cr-Commit-Position: refs/heads/master@{#577332}
parent 2a37c011
...@@ -36,15 +36,11 @@ Scheduler::Scheduler( ...@@ -36,15 +36,11 @@ Scheduler::Scheduler(
task_runner_(task_runner), task_runner_(task_runner),
compositor_timing_history_(std::move(compositor_timing_history)), compositor_timing_history_(std::move(compositor_timing_history)),
begin_impl_frame_tracker_(BEGINFRAMETRACKER_FROM_HERE), begin_impl_frame_tracker_(BEGINFRAMETRACKER_FROM_HERE),
state_machine_(settings), state_machine_(settings) {
weak_factory_(this) {
TRACE_EVENT1("cc", "Scheduler::Scheduler", "settings", settings_.AsValue()); TRACE_EVENT1("cc", "Scheduler::Scheduler", "settings", settings_.AsValue());
DCHECK(client_); DCHECK(client_);
DCHECK(!state_machine_.BeginFrameNeeded()); DCHECK(!state_machine_.BeginFrameNeeded());
begin_impl_frame_deadline_closure_ = base::Bind(
&Scheduler::OnBeginImplFrameDeadline, weak_factory_.GetWeakPtr());
// We want to handle animate_only BeginFrames. // We want to handle animate_only BeginFrames.
wants_animate_only_begin_frames_ = true; wants_animate_only_begin_frames_ = true;
...@@ -238,30 +234,62 @@ void Scheduler::BeginImplFrameNotExpectedSoon() { ...@@ -238,30 +234,62 @@ void Scheduler::BeginImplFrameNotExpectedSoon() {
} }
} }
void Scheduler::SetupNextBeginFrameIfNeeded() { void Scheduler::StartOrStopBeginFrames() {
if (state_machine_.begin_impl_frame_state() != if (state_machine_.begin_impl_frame_state() !=
SchedulerStateMachine::BeginImplFrameState::IDLE) { SchedulerStateMachine::BeginImplFrameState::IDLE) {
return; return;
} }
bool needs_begin_frames = state_machine_.BeginFrameNeeded(); bool needs_begin_frames = state_machine_.BeginFrameNeeded();
if (needs_begin_frames == observing_begin_frame_source_)
return;
if (needs_begin_frames && !observing_begin_frame_source_) { if (needs_begin_frames) {
observing_begin_frame_source_ = true; observing_begin_frame_source_ = true;
if (begin_frame_source_) if (begin_frame_source_)
begin_frame_source_->AddObserver(this); begin_frame_source_->AddObserver(this);
devtools_instrumentation::NeedsBeginFrameChanged(layer_tree_host_id_, true); devtools_instrumentation::NeedsBeginFrameChanged(layer_tree_host_id_, true);
} else if (!needs_begin_frames && observing_begin_frame_source_) { } else {
observing_begin_frame_source_ = false; observing_begin_frame_source_ = false;
if (begin_frame_source_) if (begin_frame_source_)
begin_frame_source_->RemoveObserver(this); begin_frame_source_->RemoveObserver(this);
missed_begin_frame_task_.Cancel(); // We're going idle so drop pending begin frame.
CancelPendingBeginFrameTask();
BeginImplFrameNotExpectedSoon(); BeginImplFrameNotExpectedSoon();
devtools_instrumentation::NeedsBeginFrameChanged(layer_tree_host_id_, devtools_instrumentation::NeedsBeginFrameChanged(layer_tree_host_id_,
false); false);
} }
} }
void Scheduler::CancelPendingBeginFrameTask() {
if (pending_begin_frame_args_.IsValid()) {
TRACE_EVENT_INSTANT0("cc", "Scheduler::BeginFrameDropped",
TRACE_EVENT_SCOPE_THREAD);
SendBeginFrameAck(pending_begin_frame_args_, kBeginFrameSkipped);
// Make pending begin frame invalid so that we don't accidentally use it.
pending_begin_frame_args_ = viz::BeginFrameArgs();
}
pending_begin_frame_task_.Cancel();
}
void Scheduler::PostPendingBeginFrameTask() {
bool is_idle = state_machine_.begin_impl_frame_state() ==
SchedulerStateMachine::BeginImplFrameState::IDLE;
bool needs_begin_frames = state_machine_.BeginFrameNeeded();
// We only post one pending begin frame task at a time, but we update the args
// whenever we get a new begin frame.
bool has_pending_begin_frame_args = pending_begin_frame_args_.IsValid();
bool has_no_pending_begin_frame_task =
pending_begin_frame_task_.IsCancelled();
if (is_idle && needs_begin_frames && has_pending_begin_frame_args &&
has_no_pending_begin_frame_task) {
pending_begin_frame_task_.Reset(base::BindOnce(
&Scheduler::HandlePendingBeginFrame, base::Unretained(this)));
task_runner_->PostTask(FROM_HERE, pending_begin_frame_task_.callback());
}
}
void Scheduler::OnBeginFrameSourcePausedChanged(bool paused) { void Scheduler::OnBeginFrameSourcePausedChanged(bool paused) {
if (state_machine_.begin_frame_source_paused() == paused) if (state_machine_.begin_frame_source_paused() == paused)
return; return;
...@@ -298,18 +326,36 @@ bool Scheduler::OnBeginFrameDerivedImpl(const viz::BeginFrameArgs& args) { ...@@ -298,18 +326,36 @@ bool Scheduler::OnBeginFrameDerivedImpl(const viz::BeginFrameArgs& args) {
return true; return true;
} }
if (inside_process_scheduled_actions_) { bool inside_previous_begin_frame =
// The BFS can send a missed begin frame inside AddObserver. We can't handle state_machine_.begin_impl_frame_state() ==
// a begin frame inside ProcessScheduledActions so post a task. SchedulerStateMachine::BeginImplFrameState::INSIDE_BEGIN_FRAME;
DCHECK_EQ(args.type, viz::BeginFrameArgs::MISSED);
DCHECK(missed_begin_frame_task_.IsCancelled()); if (inside_process_scheduled_actions_ || inside_previous_begin_frame ||
missed_begin_frame_task_.Reset(base::Bind( pending_begin_frame_args_.IsValid()) {
&Scheduler::BeginImplFrameWithDeadline, base::Unretained(this), args)); // The BFS can send a begin frame while scheduler is processing previous
task_runner_->PostTask(FROM_HERE, missed_begin_frame_task_.callback()); // frame, or a MISSED begin frame inside the ProcessScheduledActions loop
return true; // when AddObserver is called. The BFS (e.g. mojo) may queue up many begin
// frame calls, but we only want to process the last one. Saving the args,
// and posting a task achieves that.
if (pending_begin_frame_args_.IsValid()) {
TRACE_EVENT_INSTANT0("cc", "Scheduler::BeginFrameDropped",
TRACE_EVENT_SCOPE_THREAD);
SendBeginFrameAck(pending_begin_frame_args_, kBeginFrameSkipped);
} }
pending_begin_frame_args_ = args;
// ProcessScheduledActions() will post the previous frame's deadline if it
// hasn't run yet, or post the begin frame task if the previous frame's
// deadline has already run. If we're already inside
// ProcessScheduledActions() this call will be a nop and the above will
// happen at end of the top most call to ProcessScheduledActions().
ProcessScheduledActions();
} else {
// This starts the begin frame immediately, and puts us in the
// INSIDE_BEGIN_FRAME state, so if the message loop calls a bunch of
// BeginFrames immediately after this call, they will be posted as a single
// task, and all but the last BeginFrame will be dropped.
BeginImplFrameWithDeadline(args); BeginImplFrameWithDeadline(args);
}
return true; return true;
} }
...@@ -335,53 +381,46 @@ void Scheduler::OnDrawForLayerTreeFrameSink(bool resourceless_software_draw, ...@@ -335,53 +381,46 @@ void Scheduler::OnDrawForLayerTreeFrameSink(bool resourceless_software_draw,
state_machine_.SetResourcelessSoftwareDraw(false); state_machine_.SetResourcelessSoftwareDraw(false);
} }
void Scheduler::BeginImplFrameWithDeadline(const viz::BeginFrameArgs& args) { // This is separate from BeginImplFrameWithDeadline() because we only want at
// The storage for |args| is owned by the missed begin frame task. Therefore // most one outstanding task even if |pending_begin_frame_args_| changes.
// save |args| before cancelling the task either here or in the deadline. void Scheduler::HandlePendingBeginFrame() {
viz::BeginFrameArgs adjusted_args = args; DCHECK(pending_begin_frame_args_.IsValid());
// Cancel the missed begin frame task in case the BFS sends a begin frame viz::BeginFrameArgs args = pending_begin_frame_args_;
// before the missed frame task runs. pending_begin_frame_args_ = viz::BeginFrameArgs();
missed_begin_frame_task_.Cancel(); pending_begin_frame_task_.Cancel();
base::TimeTicks now = Now();
// Discard missed begin frames if they are too late. In full-pipe mode, we BeginImplFrameWithDeadline(args);
// ignore BeginFrame deadlines. }
if (adjusted_args.type == viz::BeginFrameArgs::MISSED &&
now > adjusted_args.deadline &&
!settings_.wait_for_all_pipeline_stages_before_draw) {
skipped_last_frame_missed_exceeded_deadline_ = true;
SendBeginFrameAck(adjusted_args, kBeginFrameSkipped);
return;
}
skipped_last_frame_missed_exceeded_deadline_ = false; void Scheduler::BeginImplFrameWithDeadline(const viz::BeginFrameArgs& args) {
DCHECK(pending_begin_frame_task_.IsCancelled());
DCHECK(!pending_begin_frame_args_.IsValid());
// Run the previous deadline if any.
if (state_machine_.begin_impl_frame_state() ==
SchedulerStateMachine::BeginImplFrameState::INSIDE_BEGIN_FRAME) {
OnBeginImplFrameDeadline();
// We may not need begin frames any longer.
if (!observing_begin_frame_source_) {
// We need to confirm the ignored BeginFrame, since we don't have updates.
SendBeginFrameAck(adjusted_args, kBeginFrameSkipped);
return;
}
}
DCHECK_EQ(state_machine_.begin_impl_frame_state(), DCHECK_EQ(state_machine_.begin_impl_frame_state(),
SchedulerStateMachine::BeginImplFrameState::IDLE); SchedulerStateMachine::BeginImplFrameState::IDLE);
bool main_thread_is_in_high_latency_mode = bool main_thread_is_in_high_latency_mode =
state_machine_.main_thread_missed_last_deadline(); state_machine_.main_thread_missed_last_deadline();
TRACE_EVENT2("cc,benchmark", "Scheduler::BeginImplFrame", "args", 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); main_thread_is_in_high_latency_mode);
TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("cc.debug.scheduler"), TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("cc.debug.scheduler"),
"MainThreadLatency", main_thread_is_in_high_latency_mode); "MainThreadLatency", main_thread_is_in_high_latency_mode);
DCHECK_EQ(state_machine_.begin_impl_frame_state(), base::TimeTicks now = Now();
SchedulerStateMachine::BeginImplFrameState::IDLE); // Discard missed begin frames if they are too late. In full-pipe mode, we
// ignore BeginFrame deadlines.
if (!settings_.wait_for_all_pipeline_stages_before_draw &&
args.type == viz::BeginFrameArgs::MISSED && args.deadline < now) {
TRACE_EVENT_INSTANT0("cc", "Scheduler::MissedBeginFrameDropped",
TRACE_EVENT_SCOPE_THREAD);
skipped_last_frame_missed_exceeded_deadline_ = true;
SendBeginFrameAck(args, kBeginFrameSkipped);
return;
}
skipped_last_frame_missed_exceeded_deadline_ = false;
viz::BeginFrameArgs adjusted_args = args;
adjusted_args.deadline -= compositor_timing_history_->DrawDurationEstimate(); adjusted_args.deadline -= compositor_timing_history_->DrawDurationEstimate();
adjusted_args.deadline -= kDeadlineFudgeFactor; adjusted_args.deadline -= kDeadlineFudgeFactor;
...@@ -502,15 +541,19 @@ void Scheduler::BeginImplFrameSynchronous(const viz::BeginFrameArgs& args) { ...@@ -502,15 +541,19 @@ void Scheduler::BeginImplFrameSynchronous(const viz::BeginFrameArgs& args) {
void Scheduler::FinishImplFrame() { void Scheduler::FinishImplFrame() {
state_machine_.OnBeginImplFrameIdle(); state_machine_.OnBeginImplFrameIdle();
ProcessScheduledActions();
// Send ack before calling ProcessScheduledActions() because it might send an
// ack for any pending begin frame if we are going idle after this. This
// ensures that the acks are sent in order.
SendBeginFrameAck(begin_main_frame_args_, kBeginFrameFinished);
begin_impl_frame_tracker_.Finish();
ProcessScheduledActions();
DCHECK(!inside_scheduled_action_); DCHECK(!inside_scheduled_action_);
{ {
base::AutoReset<bool> mark_inside(&inside_scheduled_action_, true); base::AutoReset<bool> mark_inside(&inside_scheduled_action_, true);
client_->DidFinishImplFrame(); client_->DidFinishImplFrame();
} }
SendBeginFrameAck(begin_main_frame_args_, kBeginFrameFinished);
begin_impl_frame_tracker_.Finish();
} }
void Scheduler::SendBeginFrameAck(const viz::BeginFrameArgs& args, void Scheduler::SendBeginFrameAck(const viz::BeginFrameArgs& args,
...@@ -562,69 +605,74 @@ void Scheduler::BeginImplFrame(const viz::BeginFrameArgs& args, ...@@ -562,69 +605,74 @@ void Scheduler::BeginImplFrame(const viz::BeginFrameArgs& args,
} }
void Scheduler::ScheduleBeginImplFrameDeadline() { void Scheduler::ScheduleBeginImplFrameDeadline() {
// The synchronous compositor does not post a deadline task. base::TimeTicks new_deadline;
DCHECK(!settings_.using_synchronous_renderer_compositor);
begin_impl_frame_deadline_task_.Cancel();
begin_impl_frame_deadline_task_.Reset(begin_impl_frame_deadline_closure_);
begin_impl_frame_deadline_mode_ = begin_impl_frame_deadline_mode_ =
state_machine_.CurrentBeginImplFrameDeadlineMode(); state_machine_.CurrentBeginImplFrameDeadlineMode();
// Avoid using Now() for immediate deadlines because it's expensive, and this
// method is called in every ProcessScheduledActions() call. Using
// base::TimeTicks() achieves the same result.
switch (begin_impl_frame_deadline_mode_) { switch (begin_impl_frame_deadline_mode_) {
case SchedulerStateMachine::BeginImplFrameDeadlineMode::NONE: case SchedulerStateMachine::BeginImplFrameDeadlineMode::NONE:
// No deadline. // NONE is returned when deadlines aren't used (synchronous compositor),
// or when outside a begin frame. In either case deadline task shouldn't
// be posted or should be cancelled already.
DCHECK(begin_impl_frame_deadline_task_.IsCancelled());
return; return;
case SchedulerStateMachine::BeginImplFrameDeadlineMode::IMMEDIATE: case SchedulerStateMachine::BeginImplFrameDeadlineMode::BLOCKED: {
// We are ready to draw a new active tree immediately. // TODO(sunnyps): Posting the deadline for pending begin frame is required
// We don't use Now() here because it's somewhat expensive to call. // for browser compositor (commit_to_active_tree) to make progress in some
deadline_ = base::TimeTicks(); // cases. Change browser compositor deadline to LATE in state machine to
break; // fix this.
case SchedulerStateMachine::BeginImplFrameDeadlineMode::REGULAR: //
// We are animating on the impl thread but we can wait for some time. // TODO(sunnyps): Full pipeline mode should always go from blocking
deadline_ = begin_impl_frame_tracker_.Current().deadline; // deadline to triggering deadline immediately, but DCHECKing for this
// causes layout test failures.
bool has_pending_begin_frame = pending_begin_frame_args_.IsValid();
if (has_pending_begin_frame) {
new_deadline = base::TimeTicks();
} else {
begin_impl_frame_deadline_task_.Cancel();
return;
}
break; break;
}
case SchedulerStateMachine::BeginImplFrameDeadlineMode::LATE: case SchedulerStateMachine::BeginImplFrameDeadlineMode::LATE:
// We are blocked for one reason or another and we should wait. // We are waiting for a commit without needing active tree draw or we have
// TODO(brianderson): Handle long deadlines (that are past the next // nothing to do.
// frame's frame time) properly instead of using this hack. new_deadline = begin_impl_frame_tracker_.Current().frame_time +
deadline_ = begin_impl_frame_tracker_.Current().frame_time +
begin_impl_frame_tracker_.Current().interval; begin_impl_frame_tracker_.Current().interval;
break; break;
case SchedulerStateMachine::BeginImplFrameDeadlineMode::BLOCKED: case SchedulerStateMachine::BeginImplFrameDeadlineMode::REGULAR:
// We are blocked because we are waiting for ReadyToDraw signal. We would // We are animating the active tree but we're also waiting for commit.
// post deadline after we received ReadyToDraw singal. new_deadline = begin_impl_frame_tracker_.Current().deadline;
TRACE_EVENT1("cc", "Scheduler::ScheduleBeginImplFrameDeadline", break;
"deadline_mode", "blocked"); case SchedulerStateMachine::BeginImplFrameDeadlineMode::IMMEDIATE:
return; new_deadline = base::TimeTicks();
break;
} }
TRACE_EVENT2("cc", "Scheduler::ScheduleBeginImplFrameDeadline", "mode", bool has_no_deadline_task = begin_impl_frame_deadline_task_.IsCancelled();
// Post deadline task only if we didn't have one already or something caused
// us to change the deadline. Comparing deadline mode is not sufficient
// because the calculated deadline also depends on whether we have a pending
// begin frame which the state machine doesn't know about.
if (has_no_deadline_task || new_deadline != deadline_) {
TRACE_EVENT2("cc", "Scheduler::ScheduleBeginImplFrameDeadline",
"new deadline", new_deadline, "deadline mode",
SchedulerStateMachine::BeginImplFrameDeadlineModeToString( SchedulerStateMachine::BeginImplFrameDeadlineModeToString(
begin_impl_frame_deadline_mode_), begin_impl_frame_deadline_mode_));
"deadline", deadline_); deadline_ = new_deadline;
deadline_scheduled_at_ = Now(); deadline_scheduled_at_ = Now();
base::TimeDelta delta =
std::max(deadline_ - deadline_scheduled_at_, base::TimeDelta());
task_runner_->PostDelayedTask(
FROM_HERE, begin_impl_frame_deadline_task_.callback(), delta);
}
void Scheduler::ScheduleBeginImplFrameDeadlineIfNeeded() { begin_impl_frame_deadline_task_.Reset(base::BindOnce(
if (settings_.using_synchronous_renderer_compositor) &Scheduler::OnBeginImplFrameDeadline, base::Unretained(this)));
return;
if (state_machine_.begin_impl_frame_state() !=
SchedulerStateMachine::BeginImplFrameState::INSIDE_BEGIN_FRAME)
return;
if (begin_impl_frame_deadline_mode_ == base::TimeDelta delay =
state_machine_.CurrentBeginImplFrameDeadlineMode() && std::max(deadline_ - deadline_scheduled_at_, base::TimeDelta());
!begin_impl_frame_deadline_task_.IsCancelled()) { task_runner_->PostDelayedTask(
return; FROM_HERE, begin_impl_frame_deadline_task_.callback(), delay);
} }
ScheduleBeginImplFrameDeadline();
} }
void Scheduler::OnBeginImplFrameDeadline() { void Scheduler::OnBeginImplFrameDeadline() {
...@@ -782,8 +830,10 @@ void Scheduler::ProcessScheduledActions() { ...@@ -782,8 +830,10 @@ void Scheduler::ProcessScheduledActions() {
} }
} while (action != SchedulerStateMachine::Action::NONE); } while (action != SchedulerStateMachine::Action::NONE);
ScheduleBeginImplFrameDeadlineIfNeeded(); ScheduleBeginImplFrameDeadline();
SetupNextBeginFrameIfNeeded();
PostPendingBeginFrameTask();
StartOrStopBeginFrames();
} }
std::unique_ptr<base::trace_event::ConvertableToTraceFormat> std::unique_ptr<base::trace_event::ConvertableToTraceFormat>
...@@ -804,8 +854,8 @@ void Scheduler::AsValueInto(base::trace_event::TracedValue* state) const { ...@@ -804,8 +854,8 @@ void Scheduler::AsValueInto(base::trace_event::TracedValue* state) const {
observing_begin_frame_source_); observing_begin_frame_source_);
state->SetBoolean("begin_impl_frame_deadline_task", state->SetBoolean("begin_impl_frame_deadline_task",
!begin_impl_frame_deadline_task_.IsCancelled()); !begin_impl_frame_deadline_task_.IsCancelled());
state->SetBoolean("missed_begin_frame_task", state->SetBoolean("pending_begin_frame_task",
!missed_begin_frame_task_.IsCancelled()); !pending_begin_frame_task_.IsCancelled());
state->SetBoolean("skipped_last_frame_missed_exceeded_deadline", state->SetBoolean("skipped_last_frame_missed_exceeded_deadline",
skipped_last_frame_missed_exceeded_deadline_); skipped_last_frame_missed_exceeded_deadline_);
state->SetBoolean("skipped_last_frame_to_reduce_latency", state->SetBoolean("skipped_last_frame_to_reduce_latency",
......
...@@ -193,6 +193,7 @@ class CC_EXPORT Scheduler : public viz::BeginFrameObserverBase { ...@@ -193,6 +193,7 @@ class CC_EXPORT Scheduler : public viz::BeginFrameObserverBase {
std::unique_ptr<CompositorTimingHistory> compositor_timing_history_; std::unique_ptr<CompositorTimingHistory> compositor_timing_history_;
// What the latest deadline was, and when it was scheduled.
SchedulerStateMachine::BeginImplFrameDeadlineMode SchedulerStateMachine::BeginImplFrameDeadlineMode
begin_impl_frame_deadline_mode_ = begin_impl_frame_deadline_mode_ =
SchedulerStateMachine::BeginImplFrameDeadlineMode::NONE; SchedulerStateMachine::BeginImplFrameDeadlineMode::NONE;
...@@ -202,9 +203,18 @@ class CC_EXPORT Scheduler : public viz::BeginFrameObserverBase { ...@@ -202,9 +203,18 @@ class CC_EXPORT Scheduler : public viz::BeginFrameObserverBase {
BeginFrameTracker begin_impl_frame_tracker_; BeginFrameTracker begin_impl_frame_tracker_;
viz::BeginFrameArgs begin_main_frame_args_; viz::BeginFrameArgs begin_main_frame_args_;
base::Closure begin_impl_frame_deadline_closure_; // Task posted for the deadline or drawing phase of the scheduler. This task
base::CancelableClosure begin_impl_frame_deadline_task_; // can be rescheduled e.g. when the condition for the deadline is met, it is
base::CancelableClosure missed_begin_frame_task_; // scheduled to run immediately.
// NOTE: Scheduler weak ptrs are not necessary if CancelableCallback is used.
base::CancelableOnceClosure begin_impl_frame_deadline_task_;
// This is used for queueing begin frames while scheduler is waiting for
// previous frame's deadline, or if it's inside ProcessScheduledActions().
// Only one such task is posted at any time, but the args are updated as we
// get new begin frames.
viz::BeginFrameArgs pending_begin_frame_args_;
base::CancelableOnceClosure pending_begin_frame_task_;
SchedulerStateMachine state_machine_; SchedulerStateMachine state_machine_;
bool inside_process_scheduled_actions_ = false; bool inside_process_scheduled_actions_ = false;
...@@ -215,11 +225,32 @@ class CC_EXPORT Scheduler : public viz::BeginFrameObserverBase { ...@@ -215,11 +225,32 @@ class CC_EXPORT Scheduler : public viz::BeginFrameObserverBase {
bool stopped_ = false; bool stopped_ = false;
private: private:
// Posts the deadline task if needed by checking
// SchedulerStateMachine::CurrentBeginImplFrameDeadlineMode(). This only
// happens when the scheduler is processing a begin frame
// (BeginImplFrameState::INSIDE_BEGIN_FRAME).
void ScheduleBeginImplFrameDeadline(); void ScheduleBeginImplFrameDeadline();
void ScheduleBeginImplFrameDeadlineIfNeeded();
// Starts or stops begin frames as needed by checking
// SchedulerStateMachine::BeginFrameNeeded(). This only happens when the
// scheduler is not processing a begin frame (BeginImplFrameState::IDLE).
void StartOrStopBeginFrames();
// This will only post a task if the args are valid and there's no existing
// task. That implies that we're still expecting begin frames. If begin frames
// aren't needed this will be a nop. This only happens when the scheduler is
// not processing a begin frame (BeginImplFrameState::IDLE).
void PostPendingBeginFrameTask();
// Use |pending_begin_frame_args_| to begin a new frame like it was received
// in OnBeginFrameDerivedImpl().
void HandlePendingBeginFrame();
// Used to drop the pending begin frame before we go idle.
void CancelPendingBeginFrameTask();
void BeginImplFrameNotExpectedSoon(); void BeginImplFrameNotExpectedSoon();
void BeginMainFrameNotExpectedUntil(base::TimeTicks time); void BeginMainFrameNotExpectedUntil(base::TimeTicks time);
void SetupNextBeginFrameIfNeeded();
void DrawIfPossible(); void DrawIfPossible();
void DrawForced(); void DrawForced();
void ProcessScheduledActions(); void ProcessScheduledActions();
...@@ -235,6 +266,7 @@ class CC_EXPORT Scheduler : public viz::BeginFrameObserverBase { ...@@ -235,6 +266,7 @@ class CC_EXPORT Scheduler : public viz::BeginFrameObserverBase {
base::TimeTicks now) const; base::TimeTicks now) const;
void AdvanceCommitStateIfPossible(); void AdvanceCommitStateIfPossible();
bool IsBeginMainFrameSentOrStarted() const; bool IsBeginMainFrameSentOrStarted() const;
void BeginImplFrameWithDeadline(const viz::BeginFrameArgs& args); void BeginImplFrameWithDeadline(const viz::BeginFrameArgs& args);
void BeginImplFrameSynchronous(const viz::BeginFrameArgs& args); void BeginImplFrameSynchronous(const viz::BeginFrameArgs& args);
void BeginImplFrame(const viz::BeginFrameArgs& args, base::TimeTicks now); void BeginImplFrame(const viz::BeginFrameArgs& args, base::TimeTicks now);
...@@ -250,8 +282,6 @@ class CC_EXPORT Scheduler : public viz::BeginFrameObserverBase { ...@@ -250,8 +282,6 @@ class CC_EXPORT Scheduler : public viz::BeginFrameObserverBase {
return inside_action_ == action; return inside_action_ == action;
} }
base::WeakPtrFactory<Scheduler> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(Scheduler); DISALLOW_COPY_AND_ASSIGN(Scheduler);
}; };
......
...@@ -1076,12 +1076,18 @@ void SchedulerStateMachine::OnBeginImplFrameIdle() { ...@@ -1076,12 +1076,18 @@ void SchedulerStateMachine::OnBeginImplFrameIdle() {
SchedulerStateMachine::BeginImplFrameDeadlineMode SchedulerStateMachine::BeginImplFrameDeadlineMode
SchedulerStateMachine::CurrentBeginImplFrameDeadlineMode() const { SchedulerStateMachine::CurrentBeginImplFrameDeadlineMode() const {
if (settings_.using_synchronous_renderer_compositor) { const bool outside_begin_frame =
// No deadline for synchronous compositor. begin_impl_frame_state_ != BeginImplFrameState::INSIDE_BEGIN_FRAME;
if (settings_.using_synchronous_renderer_compositor || outside_begin_frame) {
// No deadline for synchronous compositor, or when outside the begin frame.
return BeginImplFrameDeadlineMode::NONE; return BeginImplFrameDeadlineMode::NONE;
} else if (ShouldBlockDeadlineIndefinitely()) { } else if (ShouldBlockDeadlineIndefinitely()) {
// We do not want to wait for a deadline because we're waiting for full
// pipeline to be flushed for headless.
return BeginImplFrameDeadlineMode::BLOCKED; return BeginImplFrameDeadlineMode::BLOCKED;
} else if (ShouldTriggerBeginImplFrameDeadlineImmediately()) { } else if (ShouldTriggerBeginImplFrameDeadlineImmediately()) {
// We are ready to draw a new active tree immediately because there's no
// commit expected or we're prioritizing active tree latency.
return BeginImplFrameDeadlineMode::IMMEDIATE; return BeginImplFrameDeadlineMode::IMMEDIATE;
} else if (needs_redraw_) { } else if (needs_redraw_) {
// We have an animation or fast input path on the impl thread that wants // We have an animation or fast input path on the impl thread that wants
...@@ -1089,7 +1095,7 @@ SchedulerStateMachine::CurrentBeginImplFrameDeadlineMode() const { ...@@ -1089,7 +1095,7 @@ SchedulerStateMachine::CurrentBeginImplFrameDeadlineMode() const {
return BeginImplFrameDeadlineMode::REGULAR; return BeginImplFrameDeadlineMode::REGULAR;
} else { } else {
// The impl thread doesn't have anything it wants to draw and we are just // The impl thread doesn't have anything it wants to draw and we are just
// waiting for a new active tree. In short we are blocked. // waiting for a new active tree.
return BeginImplFrameDeadlineMode::LATE; return BeginImplFrameDeadlineMode::LATE;
} }
} }
......
...@@ -69,12 +69,18 @@ class CC_EXPORT SchedulerStateMachine { ...@@ -69,12 +69,18 @@ class CC_EXPORT SchedulerStateMachine {
}; };
static const char* BeginImplFrameStateToString(BeginImplFrameState state); static const char* BeginImplFrameStateToString(BeginImplFrameState state);
// The scheduler uses a deadline to wait for main thread updates before
// submitting a compositor frame. BeginImplFrameDeadlineMode specifies when
// the deadline should run.
enum class BeginImplFrameDeadlineMode { enum class BeginImplFrameDeadlineMode {
NONE, NONE, // No deadline should be scheduled e.g. for synchronous compositor.
IMMEDIATE, IMMEDIATE, // Deadline should be scheduled to run immediately.
REGULAR, REGULAR, // Deadline should be scheduled to run at the deadline provided by
LATE, // in the BeginFrameArgs.
BLOCKED, LATE, // Deadline should be scheduled run when the next frame is expected
// to arrive.
BLOCKED, // Deadline should be blocked indefinitely until the next frame
// arrives.
}; };
static const char* BeginImplFrameDeadlineModeToString( static const char* BeginImplFrameDeadlineModeToString(
BeginImplFrameDeadlineMode mode); BeginImplFrameDeadlineMode mode);
...@@ -168,6 +174,8 @@ class CC_EXPORT SchedulerStateMachine { ...@@ -168,6 +174,8 @@ class CC_EXPORT SchedulerStateMachine {
BeginImplFrameState begin_impl_frame_state() const { BeginImplFrameState begin_impl_frame_state() const {
return begin_impl_frame_state_; return begin_impl_frame_state_;
} }
// Returns BeginImplFrameDeadlineMode computed based on current state.
BeginImplFrameDeadlineMode CurrentBeginImplFrameDeadlineMode() const; BeginImplFrameDeadlineMode CurrentBeginImplFrameDeadlineMode() const;
// If the main thread didn't manage to produce a new frame in time for the // If the main thread didn't manage to produce a new frame in time for the
...@@ -320,13 +328,20 @@ class CC_EXPORT SchedulerStateMachine { ...@@ -320,13 +328,20 @@ class CC_EXPORT SchedulerStateMachine {
bool BeginFrameNeededForVideo() const; bool BeginFrameNeededForVideo() const;
bool ProactiveBeginFrameWanted() const; bool ProactiveBeginFrameWanted() const;
// Indicates if we should post the deadline to draw immediately. This is true
// when we aren't expecting a commit or activation, or we're prioritizing
// active tree draw (see ImplLatencyTakesPriority()).
bool ShouldTriggerBeginImplFrameDeadlineImmediately() const;
// Indicates if we shouldn't schedule a deadline. Used to defer drawing until
// the entire pipeline is flushed and active tree is ready to draw for
// headless.
bool ShouldBlockDeadlineIndefinitely() const;
bool ShouldPerformImplSideInvalidation() const; bool ShouldPerformImplSideInvalidation() const;
bool CouldCreatePendingTree() const; bool CouldCreatePendingTree() const;
bool ShouldDeferInvalidatingForMainFrame() const; bool ShouldDeferInvalidatingForMainFrame() const;
bool ShouldTriggerBeginImplFrameDeadlineImmediately() const;
bool ShouldBlockDeadlineIndefinitely() const;
bool ShouldAbortCurrentFrame() const; bool ShouldAbortCurrentFrame() const;
bool ShouldBeginLayerTreeFrameSinkCreation() const; bool ShouldBeginLayerTreeFrameSinkCreation() const;
......
...@@ -511,6 +511,9 @@ class SchedulerTest : public testing::Test { ...@@ -511,6 +511,9 @@ class SchedulerTest : public testing::Test {
if (scheduler_->begin_frame_source() == if (scheduler_->begin_frame_source() ==
fake_external_begin_frame_source_.get()) { fake_external_begin_frame_source_.get()) {
EXPECT_TRUE(scheduler_->begin_frames_expected()); EXPECT_TRUE(scheduler_->begin_frames_expected());
// Run the deadline first if we're inside the previous frame.
if (client_->IsInsideBeginImplFrame())
task_runner_->RunPendingTasks();
SendNextBeginFrame(animate_only); SendNextBeginFrame(animate_only);
} else { } else {
task_runner_->RunTasksWhile(client_->FrameHasNotAdvancedCallback()); task_runner_->RunTasksWhile(client_->FrameHasNotAdvancedCallback());
...@@ -3678,10 +3681,11 @@ TEST_F(SchedulerTest, BeginFrameAckForBeginFrameBeforeLastDeadline) { ...@@ -3678,10 +3681,11 @@ TEST_F(SchedulerTest, BeginFrameAckForBeginFrameBeforeLastDeadline) {
client_->Reset(); client_->Reset();
// Send the next BeginFrame before the previous one's deadline was executed. // Send the next BeginFrame before the previous one's deadline was executed.
// This should trigger the previous BeginFrame's deadline synchronously, // This should post the previous BeginFrame's deadline, during which tiles
// during which tiles will be prepared. As a result of that, no further // will be prepared. As a result of that, no further BeginFrames will be
// BeginFrames will be needed, and the new BeginFrame should be dropped. // needed, and the new BeginFrame should be dropped.
viz::BeginFrameArgs args = SendNextBeginFrame(); viz::BeginFrameArgs args = SendNextBeginFrame();
task_runner_->RunPendingTasks(); // Run posted deadline.
EXPECT_ACTIONS("ScheduledActionPrepareTiles", "RemoveObserver(this)"); EXPECT_ACTIONS("ScheduledActionPrepareTiles", "RemoveObserver(this)");
EXPECT_FALSE(client_->IsInsideBeginImplFrame()); EXPECT_FALSE(client_->IsInsideBeginImplFrame());
EXPECT_FALSE(scheduler_->begin_frames_expected()); EXPECT_FALSE(scheduler_->begin_frames_expected());
...@@ -3748,6 +3752,7 @@ TEST_F(SchedulerTest, BeginFrameAckForLateMissedBeginFrame) { ...@@ -3748,6 +3752,7 @@ TEST_F(SchedulerTest, BeginFrameAckForLateMissedBeginFrame) {
task_runner_->AdvanceMockTickClock(viz::BeginFrameArgs::DefaultInterval()); task_runner_->AdvanceMockTickClock(viz::BeginFrameArgs::DefaultInterval());
EXPECT_GT(task_runner_->NowTicks(), args.deadline); EXPECT_GT(task_runner_->NowTicks(), args.deadline);
fake_external_begin_frame_source_->TestOnBeginFrame(args); fake_external_begin_frame_source_->TestOnBeginFrame(args);
task_runner_->RunPendingTasks();
EXPECT_NO_ACTION(); EXPECT_NO_ACTION();
EXPECT_FALSE(client_->IsInsideBeginImplFrame()); EXPECT_FALSE(client_->IsInsideBeginImplFrame());
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment