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(
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;
......@@ -298,18 +326,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;
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);
}
BeginImplFrameWithDeadline(args);
return true;
}
......@@ -335,53 +381,46 @@ void Scheduler::OnDrawForLayerTreeFrameSink(bool resourceless_software_draw,
state_machine_.SetResourcelessSoftwareDraw(false);
}
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();
base::TimeTicks now = Now();
// 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();
// 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;
}
BeginImplFrameWithDeadline(args);
}
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(),
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 +541,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 +605,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_);
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() {
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;
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();
begin_impl_frame_deadline_task_.Reset(base::BindOnce(
&Scheduler::OnBeginImplFrameDeadline, base::Unretained(this)));
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 +830,10 @@ void Scheduler::ProcessScheduledActions() {
}
} while (action != SchedulerStateMachine::Action::NONE);
ScheduleBeginImplFrameDeadlineIfNeeded();
SetupNextBeginFrameIfNeeded();
ScheduleBeginImplFrameDeadline();
PostPendingBeginFrameTask();
StartOrStopBeginFrames();
}
std::unique_ptr<base::trace_event::ConvertableToTraceFormat>
......@@ -804,8 +854,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());
......
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