Commit 6beef1e6 authored by Sunny Sachanandani's avatar Sunny Sachanandani Committed by Commit Bot

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: default avatardanakj <danakj@chromium.org>
Cr-Commit-Position: refs/heads/master@{#577046}
parent 079e92c3
...@@ -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;
...@@ -279,6 +307,8 @@ bool Scheduler::OnBeginFrameDerivedImpl(const viz::BeginFrameArgs& args) { ...@@ -279,6 +307,8 @@ bool Scheduler::OnBeginFrameDerivedImpl(const viz::BeginFrameArgs& args) {
TRACE_EVENT1("cc,benchmark", "Scheduler::BeginFrame", "args", args.AsValue()); TRACE_EVENT1("cc,benchmark", "Scheduler::BeginFrame", "args", args.AsValue());
if (ShouldDropBeginFrame(args)) { if (ShouldDropBeginFrame(args)) {
DCHECK(!settings_.using_synchronous_renderer_compositor);
DCHECK(!pending_begin_frame_args_.IsValid());
TRACE_EVENT_INSTANT0("cc", "Scheduler::BeginFrameDropped", TRACE_EVENT_INSTANT0("cc", "Scheduler::BeginFrameDropped",
TRACE_EVENT_SCOPE_THREAD); TRACE_EVENT_SCOPE_THREAD);
// Since we don't use the BeginFrame, we may later receive the same // Since we don't use the BeginFrame, we may later receive the same
...@@ -298,18 +328,36 @@ bool Scheduler::OnBeginFrameDerivedImpl(const viz::BeginFrameArgs& args) { ...@@ -298,18 +328,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 +383,46 @@ void Scheduler::OnDrawForLayerTreeFrameSink(bool resourceless_software_draw, ...@@ -335,53 +383,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(); BeginImplFrameWithDeadline(args);
}
// Discard missed begin frames if they are too late. In full-pipe mode, we
// 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 +543,19 @@ void Scheduler::BeginImplFrameSynchronous(const viz::BeginFrameArgs& args) { ...@@ -502,15 +543,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 +607,74 @@ void Scheduler::BeginImplFrame(const viz::BeginFrameArgs& args, ...@@ -562,69 +607,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 +832,10 @@ void Scheduler::ProcessScheduledActions() { ...@@ -782,8 +832,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 +856,8 @@ void Scheduler::AsValueInto(base::trace_event::TracedValue* state) const { ...@@ -804,8 +856,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