1 // Copyright 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "cc/scheduler/scheduler.h"
8 #include "base/auto_reset.h"
9 #include "base/debug/trace_event.h"
10 #include "base/logging.h"
11 #include "cc/debug/devtools_instrumentation.h"
12 #include "cc/debug/traced_value.h"
13 #include "ui/gfx/frame_time.h"
17 Scheduler::Scheduler(SchedulerClient
* client
,
18 const SchedulerSettings
& scheduler_settings
,
19 int layer_tree_host_id
)
20 : settings_(scheduler_settings
),
22 layer_tree_host_id_(layer_tree_host_id
),
23 last_set_needs_begin_impl_frame_(false),
24 state_machine_(scheduler_settings
),
25 inside_process_scheduled_actions_(false),
26 inside_action_(SchedulerStateMachine::ACTION_NONE
),
29 DCHECK(!state_machine_
.BeginImplFrameNeeded());
32 Scheduler::~Scheduler() {}
34 void Scheduler::SetCanStart() {
35 state_machine_
.SetCanStart();
36 ProcessScheduledActions();
39 void Scheduler::SetVisible(bool visible
) {
40 state_machine_
.SetVisible(visible
);
41 ProcessScheduledActions();
44 void Scheduler::SetCanDraw(bool can_draw
) {
45 state_machine_
.SetCanDraw(can_draw
);
46 ProcessScheduledActions();
49 void Scheduler::NotifyReadyToActivate() {
50 state_machine_
.NotifyReadyToActivate();
51 ProcessScheduledActions();
54 void Scheduler::ActivatePendingTree() {
55 client_
->ScheduledActionActivatePendingTree();
58 void Scheduler::SetNeedsCommit() {
59 state_machine_
.SetNeedsCommit();
60 ProcessScheduledActions();
63 void Scheduler::SetNeedsForcedCommitForReadback() {
64 state_machine_
.SetNeedsCommit();
65 state_machine_
.SetNeedsForcedCommitForReadback();
66 ProcessScheduledActions();
69 void Scheduler::SetNeedsRedraw() {
70 state_machine_
.SetNeedsRedraw();
71 ProcessScheduledActions();
74 void Scheduler::SetNeedsManageTiles() {
75 DCHECK(!IsInsideAction(SchedulerStateMachine::ACTION_MANAGE_TILES
));
76 state_machine_
.SetNeedsManageTiles();
77 ProcessScheduledActions();
80 void Scheduler::SetSwapUsedIncompleteTile(bool used_incomplete_tile
) {
81 state_machine_
.SetSwapUsedIncompleteTile(used_incomplete_tile
);
82 ProcessScheduledActions();
85 void Scheduler::SetSmoothnessTakesPriority(bool smoothness_takes_priority
) {
86 state_machine_
.SetSmoothnessTakesPriority(smoothness_takes_priority
);
87 ProcessScheduledActions();
90 void Scheduler::SetMainThreadNeedsLayerTextures() {
91 state_machine_
.SetMainThreadNeedsLayerTextures();
92 ProcessScheduledActions();
95 void Scheduler::FinishCommit() {
96 TRACE_EVENT0("cc", "Scheduler::FinishCommit");
97 state_machine_
.FinishCommit();
98 ProcessScheduledActions();
101 void Scheduler::BeginMainFrameAborted(bool did_handle
) {
102 TRACE_EVENT0("cc", "Scheduler::BeginMainFrameAborted");
103 state_machine_
.BeginMainFrameAborted(did_handle
);
104 ProcessScheduledActions();
107 void Scheduler::DidManageTiles() {
108 state_machine_
.DidManageTiles();
111 void Scheduler::DidLoseOutputSurface() {
112 TRACE_EVENT0("cc", "Scheduler::DidLoseOutputSurface");
113 last_set_needs_begin_impl_frame_
= false;
114 begin_impl_frame_deadline_closure_
.Cancel();
115 state_machine_
.DidLoseOutputSurface();
116 ProcessScheduledActions();
119 void Scheduler::DidCreateAndInitializeOutputSurface() {
120 TRACE_EVENT0("cc", "Scheduler::DidCreateAndInitializeOutputSurface");
121 DCHECK(!last_set_needs_begin_impl_frame_
);
122 DCHECK(begin_impl_frame_deadline_closure_
.IsCancelled());
123 state_machine_
.DidCreateAndInitializeOutputSurface();
124 ProcessScheduledActions();
127 base::TimeTicks
Scheduler::AnticipatedDrawTime() {
128 TRACE_EVENT0("cc", "Scheduler::AnticipatedDrawTime");
130 if (!last_set_needs_begin_impl_frame_
||
131 last_begin_impl_frame_args_
.interval
<= base::TimeDelta())
132 return base::TimeTicks();
134 base::TimeTicks now
= gfx::FrameTime::Now();
135 base::TimeTicks timebase
= std::max(last_begin_impl_frame_args_
.frame_time
,
136 last_begin_impl_frame_args_
.deadline
);
138 1 + ((now
- timebase
) / last_begin_impl_frame_args_
.interval
);
139 return timebase
+ (last_begin_impl_frame_args_
.interval
* intervals
);
142 base::TimeTicks
Scheduler::LastBeginImplFrameTime() {
143 return last_begin_impl_frame_args_
.frame_time
;
146 void Scheduler::SetupNextBeginImplFrameIfNeeded() {
147 bool needs_begin_impl_frame
=
148 state_machine_
.BeginImplFrameNeeded();
150 bool at_end_of_deadline
=
151 state_machine_
.begin_impl_frame_state() ==
152 SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_INSIDE_DEADLINE
;
154 bool should_call_set_needs_begin_impl_frame
=
155 // Always request the BeginImplFrame immediately if it wasn't needed
157 (needs_begin_impl_frame
&& !last_set_needs_begin_impl_frame_
) ||
158 // We always need to explicitly request our next BeginImplFrame.
161 if (should_call_set_needs_begin_impl_frame
) {
162 client_
->SetNeedsBeginImplFrame(needs_begin_impl_frame
);
163 last_set_needs_begin_impl_frame_
= needs_begin_impl_frame
;
166 bool needs_advance_commit_state_timer
= false;
167 // Setup PollForAnticipatedDrawTriggers if we need to monitor state but
168 // aren't expecting any more BeginImplFrames. This should only be needed by
169 // the synchronous compositor when BeginImplFrameNeeded is false.
170 if (state_machine_
.ShouldPollForAnticipatedDrawTriggers()) {
171 DCHECK(!state_machine_
.SupportsProactiveBeginImplFrame());
172 DCHECK(!needs_begin_impl_frame
);
173 if (poll_for_draw_triggers_closure_
.IsCancelled()) {
174 poll_for_draw_triggers_closure_
.Reset(
175 base::Bind(&Scheduler::PollForAnticipatedDrawTriggers
,
176 weak_factory_
.GetWeakPtr()));
177 base::MessageLoop::current()->PostDelayedTask(
179 poll_for_draw_triggers_closure_
.callback(),
180 last_begin_impl_frame_args_
.interval
);
183 poll_for_draw_triggers_closure_
.Cancel();
185 // At this point we'd prefer to advance through the commit flow by
186 // drawing a frame, however it's possible that the frame rate controller
187 // will not give us a BeginImplFrame until the commit completes. See
188 // crbug.com/317430 for an example of a swap ack being held on commit. Thus
189 // we set a repeating timer to poll on ProcessScheduledActions until we
190 // successfully reach BeginImplFrame.
191 if (state_machine_
.IsCommitStateWaiting())
192 needs_advance_commit_state_timer
= true;
194 if (needs_advance_commit_state_timer
!=
195 advance_commit_state_timer_
.IsRunning()) {
196 if (needs_advance_commit_state_timer
&&
197 last_begin_impl_frame_args_
.IsValid()) {
198 // Since we'd rather get a BeginImplFrame by the normally mechanism, we set
199 // the interval to twice the interval from the previous frame.
200 advance_commit_state_timer_
.Start(
202 last_begin_impl_frame_args_
.interval
* 2,
203 base::Bind(&Scheduler::ProcessScheduledActions
,
204 base::Unretained(this)));
206 advance_commit_state_timer_
.Stop();
211 void Scheduler::BeginImplFrame(const BeginFrameArgs
& args
) {
212 TRACE_EVENT0("cc", "Scheduler::BeginImplFrame");
213 DCHECK(state_machine_
.begin_impl_frame_state() ==
214 SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_IDLE
);
215 DCHECK(state_machine_
.HasInitializedOutputSurface());
216 last_begin_impl_frame_args_
= args
;
217 last_begin_impl_frame_args_
.deadline
-= client_
->DrawDurationEstimate();
218 state_machine_
.OnBeginImplFrame(last_begin_impl_frame_args_
);
220 if (settings_
.switch_to_low_latency_if_possible
) {
221 state_machine_
.SetSkipBeginMainFrameToReduceLatency(
222 state_machine_
.MainThreadIsInHighLatencyMode() &&
223 CanCommitAndActivateBeforeDeadline());
226 ProcessScheduledActions();
228 if (!state_machine_
.HasInitializedOutputSurface())
231 state_machine_
.OnBeginImplFrameDeadlinePending();
232 devtools_instrumentation::didBeginFrame(layer_tree_host_id_
);
233 if (settings_
.using_synchronous_renderer_compositor
) {
234 // The synchronous renderer compositor has to make its GL calls
235 // within this call to BeginImplFrame.
236 // TODO(brianderson): Have the OutputSurface initiate the deadline tasks
237 // so the sychronous renderer compositor can take advantage of splitting
238 // up the BeginImplFrame and deadline as well.
239 OnBeginImplFrameDeadline();
240 } else if (!settings_
.deadline_scheduling_enabled
) {
241 // We emulate the old non-deadline scheduler here by posting the
242 // deadline task without any delay.
243 PostBeginImplFrameDeadline(base::TimeTicks());
244 } else if (state_machine_
.ShouldTriggerBeginImplFrameDeadlineEarly()) {
245 // We are ready to draw a new active tree immediately.
246 PostBeginImplFrameDeadline(base::TimeTicks());
247 } else if (state_machine_
.needs_redraw()) {
248 // We have an animation or fast input path on the impl thread that wants
249 // to draw, so don't wait too long for a new active tree.
250 PostBeginImplFrameDeadline(last_begin_impl_frame_args_
.deadline
);
252 // The impl thread doesn't have anything it wants to draw and we are just
253 // waiting for a new active tree, so post the deadline for the next
254 // expected BeginImplFrame start. This allows us to draw immediately when
255 // there is a new active tree, instead of waiting for the next
257 // TODO(brianderson): Handle long deadlines (that are past the next frame's
258 // frame time) properly instead of using this hack.
259 PostBeginImplFrameDeadline(last_begin_impl_frame_args_
.frame_time
+
260 last_begin_impl_frame_args_
.interval
);
264 void Scheduler::PostBeginImplFrameDeadline(base::TimeTicks deadline
) {
265 begin_impl_frame_deadline_closure_
.Cancel();
266 begin_impl_frame_deadline_closure_
.Reset(
267 base::Bind(&Scheduler::OnBeginImplFrameDeadline
,
268 weak_factory_
.GetWeakPtr()));
269 client_
->PostBeginImplFrameDeadline(
270 begin_impl_frame_deadline_closure_
.callback(), deadline
);
273 void Scheduler::OnBeginImplFrameDeadline() {
274 TRACE_EVENT0("cc", "Scheduler::OnBeginImplFrameDeadline");
275 DCHECK(state_machine_
.HasInitializedOutputSurface());
276 begin_impl_frame_deadline_closure_
.Cancel();
277 state_machine_
.OnBeginImplFrameDeadline();
278 ProcessScheduledActions();
280 if (state_machine_
.HasInitializedOutputSurface()) {
281 // We only transition out of BEGIN_IMPL_FRAME_STATE_INSIDE_DEADLINE when all
282 // actions that occur back-to-back in response to entering
283 // BEGIN_IMPL_FRAME_STATE_INSIDE_DEADLINE have completed. This is important
284 // because sending the BeginMainFrame will not occur if we transition to
285 // BEGIN_IMPL_FRAME_STATE_IDLE too early.
286 state_machine_
.OnBeginImplFrameIdle();
289 client_
->DidBeginImplFrameDeadline();
292 void Scheduler::PollForAnticipatedDrawTriggers() {
293 TRACE_EVENT0("cc", "Scheduler::PollForAnticipatedDrawTriggers");
294 poll_for_draw_triggers_closure_
.Cancel();
295 state_machine_
.DidEnterPollForAnticipatedDrawTriggers();
296 ProcessScheduledActions();
297 state_machine_
.DidLeavePollForAnticipatedDrawTriggers();
300 void Scheduler::DrawAndSwapIfPossible() {
301 DrawSwapReadbackResult result
=
302 client_
->ScheduledActionDrawAndSwapIfPossible();
303 state_machine_
.DidDrawIfPossibleCompleted(result
.did_draw
);
306 void Scheduler::DrawAndSwapForced() {
307 client_
->ScheduledActionDrawAndSwapForced();
310 void Scheduler::DrawAndReadback() {
311 DrawSwapReadbackResult result
= client_
->ScheduledActionDrawAndReadback();
312 DCHECK(!result
.did_swap
);
315 void Scheduler::ProcessScheduledActions() {
316 // We do not allow ProcessScheduledActions to be recursive.
317 // The top-level call will iteratively execute the next action for us anyway.
318 if (inside_process_scheduled_actions_
)
321 base::AutoReset
<bool> mark_inside(&inside_process_scheduled_actions_
, true);
323 SchedulerStateMachine::Action action
;
325 state_machine_
.CheckInvariants();
326 action
= state_machine_
.NextAction();
327 TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("cc.debug.scheduler"),
328 "SchedulerStateMachine",
330 TracedValue::FromValue(state_machine_
.AsValue().release()));
331 state_machine_
.UpdateState(action
);
332 base::AutoReset
<SchedulerStateMachine::Action
>
333 mark_inside_action(&inside_action_
, action
);
335 case SchedulerStateMachine::ACTION_NONE
:
337 case SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME
:
338 client_
->ScheduledActionSendBeginMainFrame();
340 case SchedulerStateMachine::ACTION_COMMIT
:
341 client_
->ScheduledActionCommit();
343 case SchedulerStateMachine::ACTION_UPDATE_VISIBLE_TILES
:
344 client_
->ScheduledActionUpdateVisibleTiles();
346 case SchedulerStateMachine::ACTION_ACTIVATE_PENDING_TREE
:
347 ActivatePendingTree();
349 case SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE
:
350 DrawAndSwapIfPossible();
352 case SchedulerStateMachine::ACTION_DRAW_AND_SWAP_FORCED
:
355 case SchedulerStateMachine::ACTION_DRAW_AND_SWAP_ABORT
:
356 // No action is actually performed, but this allows the state machine to
357 // advance out of its waiting to draw state without actually drawing.
359 case SchedulerStateMachine::ACTION_DRAW_AND_READBACK
:
362 case SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION
:
363 client_
->ScheduledActionBeginOutputSurfaceCreation();
365 case SchedulerStateMachine::ACTION_ACQUIRE_LAYER_TEXTURES_FOR_MAIN_THREAD
:
366 client_
->ScheduledActionAcquireLayerTexturesForMainThread();
368 case SchedulerStateMachine::ACTION_MANAGE_TILES
:
369 client_
->ScheduledActionManageTiles();
372 } while (action
!= SchedulerStateMachine::ACTION_NONE
);
374 SetupNextBeginImplFrameIfNeeded();
375 client_
->DidAnticipatedDrawTimeChange(AnticipatedDrawTime());
377 if (state_machine_
.ShouldTriggerBeginImplFrameDeadlineEarly())
378 PostBeginImplFrameDeadline(base::TimeTicks());
381 bool Scheduler::WillDrawIfNeeded() const {
382 return !state_machine_
.PendingDrawsShouldBeAborted();
385 bool Scheduler::CanCommitAndActivateBeforeDeadline() const {
386 // Check if the main thread computation and commit can be finished before the
387 // impl thread's deadline.
388 base::TimeTicks estimated_draw_time
=
389 last_begin_impl_frame_args_
.frame_time
+
390 client_
->BeginMainFrameToCommitDurationEstimate() +
391 client_
->CommitToActivateDurationEstimate();
393 return estimated_draw_time
< last_begin_impl_frame_args_
.deadline
;