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}
This commit is contained in:

committed by
Commit Bot

parent
079e92c3bd
commit
6beef1e62e
@ -36,15 +36,11 @@ Scheduler::Scheduler(
|
||||
task_runner_(task_runner),
|
||||
compositor_timing_history_(std::move(compositor_timing_history)),
|
||||
begin_impl_frame_tracker_(BEGINFRAMETRACKER_FROM_HERE),
|
||||
state_machine_(settings),
|
||||
weak_factory_(this) {
|
||||
state_machine_(settings) {
|
||||
TRACE_EVENT1("cc", "Scheduler::Scheduler", "settings", settings_.AsValue());
|
||||
DCHECK(client_);
|
||||
DCHECK(!state_machine_.BeginFrameNeeded());
|
||||
|
||||
begin_impl_frame_deadline_closure_ = base::Bind(
|
||||
&Scheduler::OnBeginImplFrameDeadline, weak_factory_.GetWeakPtr());
|
||||
|
||||
// We want to handle animate_only BeginFrames.
|
||||
wants_animate_only_begin_frames_ = true;
|
||||
|
||||
@ -238,30 +234,62 @@ void Scheduler::BeginImplFrameNotExpectedSoon() {
|
||||
}
|
||||
}
|
||||
|
||||
void Scheduler::SetupNextBeginFrameIfNeeded() {
|
||||
void Scheduler::StartOrStopBeginFrames() {
|
||||
if (state_machine_.begin_impl_frame_state() !=
|
||||
SchedulerStateMachine::BeginImplFrameState::IDLE) {
|
||||
return;
|
||||
}
|
||||
|
||||
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;
|
||||
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_) {
|
||||
} else {
|
||||
observing_begin_frame_source_ = false;
|
||||
if (begin_frame_source_)
|
||||
begin_frame_source_->RemoveObserver(this);
|
||||
missed_begin_frame_task_.Cancel();
|
||||
// We're going idle so drop pending begin frame.
|
||||
CancelPendingBeginFrameTask();
|
||||
BeginImplFrameNotExpectedSoon();
|
||||
devtools_instrumentation::NeedsBeginFrameChanged(layer_tree_host_id_,
|
||||
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) {
|
||||
if (state_machine_.begin_frame_source_paused() == paused)
|
||||
return;
|
||||
@ -279,6 +307,8 @@ bool Scheduler::OnBeginFrameDerivedImpl(const viz::BeginFrameArgs& args) {
|
||||
TRACE_EVENT1("cc,benchmark", "Scheduler::BeginFrame", "args", args.AsValue());
|
||||
|
||||
if (ShouldDropBeginFrame(args)) {
|
||||
DCHECK(!settings_.using_synchronous_renderer_compositor);
|
||||
DCHECK(!pending_begin_frame_args_.IsValid());
|
||||
TRACE_EVENT_INSTANT0("cc", "Scheduler::BeginFrameDropped",
|
||||
TRACE_EVENT_SCOPE_THREAD);
|
||||
// Since we don't use the BeginFrame, we may later receive the same
|
||||
@ -298,18 +328,36 @@ bool Scheduler::OnBeginFrameDerivedImpl(const viz::BeginFrameArgs& args) {
|
||||
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, viz::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());
|
||||
return true;
|
||||
}
|
||||
bool inside_previous_begin_frame =
|
||||
state_machine_.begin_impl_frame_state() ==
|
||||
SchedulerStateMachine::BeginImplFrameState::INSIDE_BEGIN_FRAME;
|
||||
|
||||
BeginImplFrameWithDeadline(args);
|
||||
if (inside_process_scheduled_actions_ || inside_previous_begin_frame ||
|
||||
pending_begin_frame_args_.IsValid()) {
|
||||
// The BFS can send a begin frame while scheduler is processing previous
|
||||
// frame, or a MISSED begin frame inside the ProcessScheduledActions loop
|
||||
// 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);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -335,53 +383,46 @@ void Scheduler::OnDrawForLayerTreeFrameSink(bool resourceless_software_draw,
|
||||
state_machine_.SetResourcelessSoftwareDraw(false);
|
||||
}
|
||||
|
||||
// This is separate from BeginImplFrameWithDeadline() because we only want at
|
||||
// most one outstanding task even if |pending_begin_frame_args_| changes.
|
||||
void Scheduler::HandlePendingBeginFrame() {
|
||||
DCHECK(pending_begin_frame_args_.IsValid());
|
||||
viz::BeginFrameArgs args = pending_begin_frame_args_;
|
||||
pending_begin_frame_args_ = viz::BeginFrameArgs();
|
||||
pending_begin_frame_task_.Cancel();
|
||||
|
||||
BeginImplFrameWithDeadline(args);
|
||||
}
|
||||
|
||||
void Scheduler::BeginImplFrameWithDeadline(const viz::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.
|
||||
viz::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();
|
||||
DCHECK(pending_begin_frame_task_.IsCancelled());
|
||||
DCHECK(!pending_begin_frame_args_.IsValid());
|
||||
|
||||
base::TimeTicks now = Now();
|
||||
|
||||
// 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;
|
||||
|
||||
// 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(),
|
||||
SchedulerStateMachine::BeginImplFrameState::IDLE);
|
||||
|
||||
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::BeginImplFrameState::IDLE);
|
||||
base::TimeTicks now = Now();
|
||||
// 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 -= kDeadlineFudgeFactor;
|
||||
|
||||
@ -502,15 +543,19 @@ void Scheduler::BeginImplFrameSynchronous(const viz::BeginFrameArgs& args) {
|
||||
|
||||
void Scheduler::FinishImplFrame() {
|
||||
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_);
|
||||
{
|
||||
base::AutoReset<bool> mark_inside(&inside_scheduled_action_, true);
|
||||
client_->DidFinishImplFrame();
|
||||
}
|
||||
SendBeginFrameAck(begin_main_frame_args_, kBeginFrameFinished);
|
||||
begin_impl_frame_tracker_.Finish();
|
||||
}
|
||||
|
||||
void Scheduler::SendBeginFrameAck(const viz::BeginFrameArgs& args,
|
||||
@ -562,69 +607,74 @@ void Scheduler::BeginImplFrame(const viz::BeginFrameArgs& args,
|
||||
}
|
||||
|
||||
void Scheduler::ScheduleBeginImplFrameDeadline() {
|
||||
// The synchronous compositor does not post a deadline task.
|
||||
DCHECK(!settings_.using_synchronous_renderer_compositor);
|
||||
|
||||
begin_impl_frame_deadline_task_.Cancel();
|
||||
begin_impl_frame_deadline_task_.Reset(begin_impl_frame_deadline_closure_);
|
||||
base::TimeTicks new_deadline;
|
||||
|
||||
begin_impl_frame_deadline_mode_ =
|
||||
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_) {
|
||||
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;
|
||||
case SchedulerStateMachine::BeginImplFrameDeadlineMode::IMMEDIATE:
|
||||
// We are ready to draw a new active tree immediately.
|
||||
// We don't use Now() here because it's somewhat expensive to call.
|
||||
deadline_ = base::TimeTicks();
|
||||
case SchedulerStateMachine::BeginImplFrameDeadlineMode::BLOCKED: {
|
||||
// TODO(sunnyps): Posting the deadline for pending begin frame is required
|
||||
// for browser compositor (commit_to_active_tree) to make progress in some
|
||||
// cases. Change browser compositor deadline to LATE in state machine to
|
||||
// fix this.
|
||||
//
|
||||
// TODO(sunnyps): Full pipeline mode should always go from blocking
|
||||
// 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;
|
||||
}
|
||||
case SchedulerStateMachine::BeginImplFrameDeadlineMode::LATE:
|
||||
// We are waiting for a commit without needing active tree draw or we have
|
||||
// nothing to do.
|
||||
new_deadline = begin_impl_frame_tracker_.Current().frame_time +
|
||||
begin_impl_frame_tracker_.Current().interval;
|
||||
break;
|
||||
case SchedulerStateMachine::BeginImplFrameDeadlineMode::REGULAR:
|
||||
// We are animating on the impl thread but we can wait for some time.
|
||||
deadline_ = begin_impl_frame_tracker_.Current().deadline;
|
||||
// We are animating the active tree but we're also waiting for commit.
|
||||
new_deadline = begin_impl_frame_tracker_.Current().deadline;
|
||||
break;
|
||||
case SchedulerStateMachine::BeginImplFrameDeadlineMode::LATE:
|
||||
// We are blocked for one reason or another and we should wait.
|
||||
// TODO(brianderson): Handle long deadlines (that are past the next
|
||||
// frame's frame time) properly instead of using this hack.
|
||||
deadline_ = begin_impl_frame_tracker_.Current().frame_time +
|
||||
begin_impl_frame_tracker_.Current().interval;
|
||||
case SchedulerStateMachine::BeginImplFrameDeadlineMode::IMMEDIATE:
|
||||
new_deadline = base::TimeTicks();
|
||||
break;
|
||||
case SchedulerStateMachine::BeginImplFrameDeadlineMode::BLOCKED:
|
||||
// We are blocked because we are waiting for ReadyToDraw signal. We would
|
||||
// post deadline after we received ReadyToDraw singal.
|
||||
TRACE_EVENT1("cc", "Scheduler::ScheduleBeginImplFrameDeadline",
|
||||
"deadline_mode", "blocked");
|
||||
return;
|
||||
}
|
||||
|
||||
TRACE_EVENT2("cc", "Scheduler::ScheduleBeginImplFrameDeadline", "mode",
|
||||
SchedulerStateMachine::BeginImplFrameDeadlineModeToString(
|
||||
begin_impl_frame_deadline_mode_),
|
||||
"deadline", deadline_);
|
||||
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(
|
||||
begin_impl_frame_deadline_mode_));
|
||||
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);
|
||||
}
|
||||
begin_impl_frame_deadline_task_.Reset(base::BindOnce(
|
||||
&Scheduler::OnBeginImplFrameDeadline, base::Unretained(this)));
|
||||
|
||||
void Scheduler::ScheduleBeginImplFrameDeadlineIfNeeded() {
|
||||
if (settings_.using_synchronous_renderer_compositor)
|
||||
return;
|
||||
|
||||
if (state_machine_.begin_impl_frame_state() !=
|
||||
SchedulerStateMachine::BeginImplFrameState::INSIDE_BEGIN_FRAME)
|
||||
return;
|
||||
|
||||
if (begin_impl_frame_deadline_mode_ ==
|
||||
state_machine_.CurrentBeginImplFrameDeadlineMode() &&
|
||||
!begin_impl_frame_deadline_task_.IsCancelled()) {
|
||||
return;
|
||||
base::TimeDelta delay =
|
||||
std::max(deadline_ - deadline_scheduled_at_, base::TimeDelta());
|
||||
task_runner_->PostDelayedTask(
|
||||
FROM_HERE, begin_impl_frame_deadline_task_.callback(), delay);
|
||||
}
|
||||
|
||||
ScheduleBeginImplFrameDeadline();
|
||||
}
|
||||
|
||||
void Scheduler::OnBeginImplFrameDeadline() {
|
||||
@ -782,8 +832,10 @@ void Scheduler::ProcessScheduledActions() {
|
||||
}
|
||||
} while (action != SchedulerStateMachine::Action::NONE);
|
||||
|
||||
ScheduleBeginImplFrameDeadlineIfNeeded();
|
||||
SetupNextBeginFrameIfNeeded();
|
||||
ScheduleBeginImplFrameDeadline();
|
||||
|
||||
PostPendingBeginFrameTask();
|
||||
StartOrStopBeginFrames();
|
||||
}
|
||||
|
||||
std::unique_ptr<base::trace_event::ConvertableToTraceFormat>
|
||||
@ -804,8 +856,8 @@ void Scheduler::AsValueInto(base::trace_event::TracedValue* state) const {
|
||||
observing_begin_frame_source_);
|
||||
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->SetBoolean("pending_begin_frame_task",
|
||||
!pending_begin_frame_task_.IsCancelled());
|
||||
state->SetBoolean("skipped_last_frame_missed_exceeded_deadline",
|
||||
skipped_last_frame_missed_exceeded_deadline_);
|
||||
state->SetBoolean("skipped_last_frame_to_reduce_latency",
|
||||
|
@ -193,6 +193,7 @@ class CC_EXPORT Scheduler : public viz::BeginFrameObserverBase {
|
||||
|
||||
std::unique_ptr<CompositorTimingHistory> compositor_timing_history_;
|
||||
|
||||
// What the latest deadline was, and when it was scheduled.
|
||||
SchedulerStateMachine::BeginImplFrameDeadlineMode
|
||||
begin_impl_frame_deadline_mode_ =
|
||||
SchedulerStateMachine::BeginImplFrameDeadlineMode::NONE;
|
||||
@ -202,9 +203,18 @@ class CC_EXPORT Scheduler : public viz::BeginFrameObserverBase {
|
||||
BeginFrameTracker begin_impl_frame_tracker_;
|
||||
viz::BeginFrameArgs begin_main_frame_args_;
|
||||
|
||||
base::Closure begin_impl_frame_deadline_closure_;
|
||||
base::CancelableClosure begin_impl_frame_deadline_task_;
|
||||
base::CancelableClosure missed_begin_frame_task_;
|
||||
// Task posted for the deadline or drawing phase of the scheduler. This task
|
||||
// can be rescheduled e.g. when the condition for the deadline is met, it is
|
||||
// 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_;
|
||||
bool inside_process_scheduled_actions_ = false;
|
||||
@ -215,11 +225,32 @@ class CC_EXPORT Scheduler : public viz::BeginFrameObserverBase {
|
||||
bool stopped_ = false;
|
||||
|
||||
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 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 BeginMainFrameNotExpectedUntil(base::TimeTicks time);
|
||||
void SetupNextBeginFrameIfNeeded();
|
||||
void DrawIfPossible();
|
||||
void DrawForced();
|
||||
void ProcessScheduledActions();
|
||||
@ -235,6 +266,7 @@ class CC_EXPORT Scheduler : public viz::BeginFrameObserverBase {
|
||||
base::TimeTicks now) const;
|
||||
void AdvanceCommitStateIfPossible();
|
||||
bool IsBeginMainFrameSentOrStarted() const;
|
||||
|
||||
void BeginImplFrameWithDeadline(const viz::BeginFrameArgs& args);
|
||||
void BeginImplFrameSynchronous(const viz::BeginFrameArgs& args);
|
||||
void BeginImplFrame(const viz::BeginFrameArgs& args, base::TimeTicks now);
|
||||
@ -250,8 +282,6 @@ class CC_EXPORT Scheduler : public viz::BeginFrameObserverBase {
|
||||
return inside_action_ == action;
|
||||
}
|
||||
|
||||
base::WeakPtrFactory<Scheduler> weak_factory_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(Scheduler);
|
||||
};
|
||||
|
||||
|
@ -1076,12 +1076,18 @@ void SchedulerStateMachine::OnBeginImplFrameIdle() {
|
||||
|
||||
SchedulerStateMachine::BeginImplFrameDeadlineMode
|
||||
SchedulerStateMachine::CurrentBeginImplFrameDeadlineMode() const {
|
||||
if (settings_.using_synchronous_renderer_compositor) {
|
||||
// No deadline for synchronous compositor.
|
||||
const bool outside_begin_frame =
|
||||
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;
|
||||
} 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;
|
||||
} 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;
|
||||
} else if (needs_redraw_) {
|
||||
// We have an animation or fast input path on the impl thread that wants
|
||||
@ -1089,7 +1095,7 @@ SchedulerStateMachine::CurrentBeginImplFrameDeadlineMode() const {
|
||||
return BeginImplFrameDeadlineMode::REGULAR;
|
||||
} else {
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
@ -69,12 +69,18 @@ class CC_EXPORT SchedulerStateMachine {
|
||||
};
|
||||
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 {
|
||||
NONE,
|
||||
IMMEDIATE,
|
||||
REGULAR,
|
||||
LATE,
|
||||
BLOCKED,
|
||||
NONE, // No deadline should be scheduled e.g. for synchronous compositor.
|
||||
IMMEDIATE, // Deadline should be scheduled to run immediately.
|
||||
REGULAR, // Deadline should be scheduled to run at the deadline provided by
|
||||
// in the BeginFrameArgs.
|
||||
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(
|
||||
BeginImplFrameDeadlineMode mode);
|
||||
@ -168,6 +174,8 @@ class CC_EXPORT SchedulerStateMachine {
|
||||
BeginImplFrameState begin_impl_frame_state() const {
|
||||
return begin_impl_frame_state_;
|
||||
}
|
||||
|
||||
// Returns BeginImplFrameDeadlineMode computed based on current state.
|
||||
BeginImplFrameDeadlineMode CurrentBeginImplFrameDeadlineMode() const;
|
||||
|
||||
// If the main thread didn't manage to produce a new frame in time for the
|
||||
@ -320,13 +328,20 @@ class CC_EXPORT SchedulerStateMachine {
|
||||
bool BeginFrameNeededForVideo() 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 CouldCreatePendingTree() const;
|
||||
bool ShouldDeferInvalidatingForMainFrame() const;
|
||||
|
||||
bool ShouldTriggerBeginImplFrameDeadlineImmediately() const;
|
||||
bool ShouldBlockDeadlineIndefinitely() const;
|
||||
|
||||
bool ShouldAbortCurrentFrame() const;
|
||||
|
||||
bool ShouldBeginLayerTreeFrameSinkCreation() const;
|
||||
|
@ -511,6 +511,9 @@ class SchedulerTest : public testing::Test {
|
||||
if (scheduler_->begin_frame_source() ==
|
||||
fake_external_begin_frame_source_.get()) {
|
||||
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);
|
||||
} else {
|
||||
task_runner_->RunTasksWhile(client_->FrameHasNotAdvancedCallback());
|
||||
@ -3678,10 +3681,11 @@ TEST_F(SchedulerTest, BeginFrameAckForBeginFrameBeforeLastDeadline) {
|
||||
client_->Reset();
|
||||
|
||||
// Send the next BeginFrame before the previous one's deadline was executed.
|
||||
// This should trigger the previous BeginFrame's deadline synchronously,
|
||||
// during which tiles will be prepared. As a result of that, no further
|
||||
// BeginFrames will be needed, and the new BeginFrame should be dropped.
|
||||
// This should post the previous BeginFrame's deadline, during which tiles
|
||||
// will be prepared. As a result of that, no further BeginFrames will be
|
||||
// needed, and the new BeginFrame should be dropped.
|
||||
viz::BeginFrameArgs args = SendNextBeginFrame();
|
||||
task_runner_->RunPendingTasks(); // Run posted deadline.
|
||||
EXPECT_ACTIONS("ScheduledActionPrepareTiles", "RemoveObserver(this)");
|
||||
EXPECT_FALSE(client_->IsInsideBeginImplFrame());
|
||||
EXPECT_FALSE(scheduler_->begin_frames_expected());
|
||||
@ -3748,6 +3752,7 @@ TEST_F(SchedulerTest, BeginFrameAckForLateMissedBeginFrame) {
|
||||
task_runner_->AdvanceMockTickClock(viz::BeginFrameArgs::DefaultInterval());
|
||||
EXPECT_GT(task_runner_->NowTicks(), args.deadline);
|
||||
fake_external_begin_frame_source_->TestOnBeginFrame(args);
|
||||
task_runner_->RunPendingTasks();
|
||||
|
||||
EXPECT_NO_ACTION();
|
||||
EXPECT_FALSE(client_->IsInsideBeginImplFrame());
|
||||
|
Reference in New Issue
Block a user