OLD | NEW |
| (Empty) |
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. | |
4 | |
5 #include "cc/scheduler/scheduler.h" | |
6 | |
7 #include <algorithm> | |
8 | |
9 #include "base/auto_reset.h" | |
10 #include "base/logging.h" | |
11 #include "base/profiler/scoped_tracker.h" | |
12 #include "base/single_thread_task_runner.h" | |
13 #include "base/trace_event/trace_event.h" | |
14 #include "base/trace_event/trace_event_argument.h" | |
15 #include "cc/debug/traced_value.h" | |
16 #include "cc/scheduler/delay_based_time_source.h" | |
17 #include "ui/gfx/frame_time.h" | |
18 | |
19 namespace cc { | |
20 | |
21 BeginFrameSource* SchedulerFrameSourcesConstructor::ConstructPrimaryFrameSource( | |
22 Scheduler* scheduler) { | |
23 if (scheduler->settings_.use_external_begin_frame_source) { | |
24 TRACE_EVENT1("cc", | |
25 "Scheduler::Scheduler()", | |
26 "PrimaryFrameSource", | |
27 "ExternalBeginFrameSource"); | |
28 DCHECK(scheduler->primary_frame_source_internal_) | |
29 << "Need external BeginFrameSource"; | |
30 return scheduler->primary_frame_source_internal_.get(); | |
31 } else { | |
32 TRACE_EVENT1("cc", | |
33 "Scheduler::Scheduler()", | |
34 "PrimaryFrameSource", | |
35 "SyntheticBeginFrameSource"); | |
36 scoped_ptr<SyntheticBeginFrameSource> synthetic_source = | |
37 SyntheticBeginFrameSource::Create(scheduler->task_runner_.get(), | |
38 scheduler->Now(), | |
39 BeginFrameArgs::DefaultInterval()); | |
40 | |
41 DCHECK(!scheduler->vsync_observer_); | |
42 scheduler->vsync_observer_ = synthetic_source.get(); | |
43 | |
44 DCHECK(!scheduler->primary_frame_source_internal_); | |
45 scheduler->primary_frame_source_internal_ = synthetic_source.Pass(); | |
46 return scheduler->primary_frame_source_internal_.get(); | |
47 } | |
48 } | |
49 | |
50 BeginFrameSource* | |
51 SchedulerFrameSourcesConstructor::ConstructBackgroundFrameSource( | |
52 Scheduler* scheduler) { | |
53 TRACE_EVENT1("cc", | |
54 "Scheduler::Scheduler()", | |
55 "BackgroundFrameSource", | |
56 "SyntheticBeginFrameSource"); | |
57 DCHECK(!(scheduler->background_frame_source_internal_)); | |
58 scheduler->background_frame_source_internal_ = | |
59 SyntheticBeginFrameSource::Create( | |
60 scheduler->task_runner_.get(), scheduler->Now(), | |
61 scheduler->settings_.background_frame_interval); | |
62 return scheduler->background_frame_source_internal_.get(); | |
63 } | |
64 | |
65 BeginFrameSource* | |
66 SchedulerFrameSourcesConstructor::ConstructUnthrottledFrameSource( | |
67 Scheduler* scheduler) { | |
68 TRACE_EVENT1("cc", "Scheduler::Scheduler()", "UnthrottledFrameSource", | |
69 "BackToBackBeginFrameSource"); | |
70 DCHECK(!scheduler->unthrottled_frame_source_internal_); | |
71 scheduler->unthrottled_frame_source_internal_ = | |
72 BackToBackBeginFrameSource::Create(scheduler->task_runner_.get()); | |
73 return scheduler->unthrottled_frame_source_internal_.get(); | |
74 } | |
75 | |
76 Scheduler::Scheduler( | |
77 SchedulerClient* client, | |
78 const SchedulerSettings& scheduler_settings, | |
79 int layer_tree_host_id, | |
80 const scoped_refptr<base::SingleThreadTaskRunner>& task_runner, | |
81 scoped_ptr<BeginFrameSource> external_begin_frame_source, | |
82 SchedulerFrameSourcesConstructor* frame_sources_constructor) | |
83 : frame_source_(), | |
84 primary_frame_source_(NULL), | |
85 background_frame_source_(NULL), | |
86 primary_frame_source_internal_(external_begin_frame_source.Pass()), | |
87 background_frame_source_internal_(), | |
88 vsync_observer_(NULL), | |
89 throttle_frame_production_(scheduler_settings.throttle_frame_production), | |
90 settings_(scheduler_settings), | |
91 client_(client), | |
92 layer_tree_host_id_(layer_tree_host_id), | |
93 task_runner_(task_runner), | |
94 state_machine_(scheduler_settings), | |
95 inside_process_scheduled_actions_(false), | |
96 inside_action_(SchedulerStateMachine::ACTION_NONE), | |
97 weak_factory_(this) { | |
98 TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("cc.debug.scheduler"), | |
99 "Scheduler::Scheduler", | |
100 "settings", | |
101 settings_.AsValue()); | |
102 DCHECK(client_); | |
103 DCHECK(!state_machine_.BeginFrameNeeded()); | |
104 | |
105 begin_retro_frame_closure_ = | |
106 base::Bind(&Scheduler::BeginRetroFrame, weak_factory_.GetWeakPtr()); | |
107 begin_impl_frame_deadline_closure_ = base::Bind( | |
108 &Scheduler::OnBeginImplFrameDeadline, weak_factory_.GetWeakPtr()); | |
109 poll_for_draw_triggers_closure_ = base::Bind( | |
110 &Scheduler::PollForAnticipatedDrawTriggers, weak_factory_.GetWeakPtr()); | |
111 advance_commit_state_closure_ = base::Bind( | |
112 &Scheduler::PollToAdvanceCommitState, weak_factory_.GetWeakPtr()); | |
113 | |
114 frame_source_ = BeginFrameSourceMultiplexer::Create(); | |
115 frame_source_->AddObserver(this); | |
116 | |
117 // Primary frame source | |
118 primary_frame_source_ = | |
119 frame_sources_constructor->ConstructPrimaryFrameSource(this); | |
120 frame_source_->AddSource(primary_frame_source_); | |
121 primary_frame_source_->SetClientReady(); | |
122 | |
123 // Background ticking frame source | |
124 background_frame_source_ = | |
125 frame_sources_constructor->ConstructBackgroundFrameSource(this); | |
126 frame_source_->AddSource(background_frame_source_); | |
127 | |
128 // Unthrottled frame source | |
129 unthrottled_frame_source_ = | |
130 frame_sources_constructor->ConstructUnthrottledFrameSource(this); | |
131 frame_source_->AddSource(unthrottled_frame_source_); | |
132 } | |
133 | |
134 Scheduler::~Scheduler() { | |
135 if (frame_source_->NeedsBeginFrames()) | |
136 frame_source_->SetNeedsBeginFrames(false); | |
137 } | |
138 | |
139 base::TimeTicks Scheduler::Now() const { | |
140 base::TimeTicks now = gfx::FrameTime::Now(); | |
141 TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("cc.debug.scheduler.now"), | |
142 "Scheduler::Now", | |
143 "now", | |
144 now); | |
145 return now; | |
146 } | |
147 | |
148 void Scheduler::CommitVSyncParameters(base::TimeTicks timebase, | |
149 base::TimeDelta interval) { | |
150 // TODO(brianderson): We should not be receiving 0 intervals. | |
151 if (interval == base::TimeDelta()) | |
152 interval = BeginFrameArgs::DefaultInterval(); | |
153 | |
154 if (vsync_observer_) | |
155 vsync_observer_->OnUpdateVSyncParameters(timebase, interval); | |
156 } | |
157 | |
158 void Scheduler::SetEstimatedParentDrawTime(base::TimeDelta draw_time) { | |
159 DCHECK_GE(draw_time.ToInternalValue(), 0); | |
160 estimated_parent_draw_time_ = draw_time; | |
161 } | |
162 | |
163 void Scheduler::SetCanStart() { | |
164 state_machine_.SetCanStart(); | |
165 ProcessScheduledActions(); | |
166 } | |
167 | |
168 void Scheduler::UpdateActiveFrameSource() { | |
169 if (state_machine_.visible()) { | |
170 if (throttle_frame_production_) { | |
171 frame_source_->SetActiveSource(primary_frame_source_); | |
172 } else { | |
173 frame_source_->SetActiveSource(unthrottled_frame_source_); | |
174 } | |
175 } else { | |
176 frame_source_->SetActiveSource(background_frame_source_); | |
177 } | |
178 ProcessScheduledActions(); | |
179 } | |
180 | |
181 void Scheduler::SetVisible(bool visible) { | |
182 state_machine_.SetVisible(visible); | |
183 UpdateActiveFrameSource(); | |
184 } | |
185 | |
186 void Scheduler::SetCanDraw(bool can_draw) { | |
187 state_machine_.SetCanDraw(can_draw); | |
188 ProcessScheduledActions(); | |
189 } | |
190 | |
191 void Scheduler::NotifyReadyToActivate() { | |
192 state_machine_.NotifyReadyToActivate(); | |
193 ProcessScheduledActions(); | |
194 } | |
195 | |
196 void Scheduler::NotifyReadyToDraw() { | |
197 // Empty for now, until we take action based on the notification as part of | |
198 // crbugs 352894, 383157, 421923. | |
199 } | |
200 | |
201 void Scheduler::SetThrottleFrameProduction(bool throttle) { | |
202 throttle_frame_production_ = throttle; | |
203 UpdateActiveFrameSource(); | |
204 } | |
205 | |
206 void Scheduler::SetNeedsCommit() { | |
207 state_machine_.SetNeedsCommit(); | |
208 ProcessScheduledActions(); | |
209 } | |
210 | |
211 void Scheduler::SetNeedsRedraw() { | |
212 state_machine_.SetNeedsRedraw(); | |
213 ProcessScheduledActions(); | |
214 } | |
215 | |
216 void Scheduler::SetNeedsAnimate() { | |
217 state_machine_.SetNeedsAnimate(); | |
218 ProcessScheduledActions(); | |
219 } | |
220 | |
221 void Scheduler::SetNeedsPrepareTiles() { | |
222 DCHECK(!IsInsideAction(SchedulerStateMachine::ACTION_PREPARE_TILES)); | |
223 state_machine_.SetNeedsPrepareTiles(); | |
224 ProcessScheduledActions(); | |
225 } | |
226 | |
227 void Scheduler::SetMaxSwapsPending(int max) { | |
228 state_machine_.SetMaxSwapsPending(max); | |
229 } | |
230 | |
231 void Scheduler::DidSwapBuffers() { | |
232 state_machine_.DidSwapBuffers(); | |
233 | |
234 // There is no need to call ProcessScheduledActions here because | |
235 // swapping should not trigger any new actions. | |
236 if (!inside_process_scheduled_actions_) { | |
237 DCHECK_EQ(state_machine_.NextAction(), SchedulerStateMachine::ACTION_NONE); | |
238 } | |
239 } | |
240 | |
241 void Scheduler::DidSwapBuffersComplete() { | |
242 state_machine_.DidSwapBuffersComplete(); | |
243 ProcessScheduledActions(); | |
244 } | |
245 | |
246 void Scheduler::SetImplLatencyTakesPriority(bool impl_latency_takes_priority) { | |
247 state_machine_.SetImplLatencyTakesPriority(impl_latency_takes_priority); | |
248 ProcessScheduledActions(); | |
249 } | |
250 | |
251 void Scheduler::NotifyReadyToCommit() { | |
252 TRACE_EVENT0("cc", "Scheduler::NotifyReadyToCommit"); | |
253 state_machine_.NotifyReadyToCommit(); | |
254 ProcessScheduledActions(); | |
255 } | |
256 | |
257 void Scheduler::BeginMainFrameAborted(CommitEarlyOutReason reason) { | |
258 TRACE_EVENT1("cc", "Scheduler::BeginMainFrameAborted", "reason", | |
259 CommitEarlyOutReasonToString(reason)); | |
260 state_machine_.BeginMainFrameAborted(reason); | |
261 ProcessScheduledActions(); | |
262 } | |
263 | |
264 void Scheduler::DidPrepareTiles() { | |
265 state_machine_.DidPrepareTiles(); | |
266 } | |
267 | |
268 void Scheduler::DidLoseOutputSurface() { | |
269 TRACE_EVENT0("cc", "Scheduler::DidLoseOutputSurface"); | |
270 begin_retro_frame_args_.clear(); | |
271 begin_retro_frame_task_.Cancel(); | |
272 state_machine_.DidLoseOutputSurface(); | |
273 ProcessScheduledActions(); | |
274 } | |
275 | |
276 void Scheduler::DidCreateAndInitializeOutputSurface() { | |
277 TRACE_EVENT0("cc", "Scheduler::DidCreateAndInitializeOutputSurface"); | |
278 DCHECK(!frame_source_->NeedsBeginFrames()); | |
279 DCHECK(begin_impl_frame_deadline_task_.IsCancelled()); | |
280 state_machine_.DidCreateAndInitializeOutputSurface(); | |
281 ProcessScheduledActions(); | |
282 } | |
283 | |
284 void Scheduler::NotifyBeginMainFrameStarted() { | |
285 TRACE_EVENT0("cc", "Scheduler::NotifyBeginMainFrameStarted"); | |
286 state_machine_.NotifyBeginMainFrameStarted(); | |
287 } | |
288 | |
289 base::TimeTicks Scheduler::AnticipatedDrawTime() const { | |
290 if (!frame_source_->NeedsBeginFrames() || | |
291 begin_impl_frame_args_.interval <= base::TimeDelta()) | |
292 return base::TimeTicks(); | |
293 | |
294 base::TimeTicks now = Now(); | |
295 base::TimeTicks timebase = std::max(begin_impl_frame_args_.frame_time, | |
296 begin_impl_frame_args_.deadline); | |
297 int64 intervals = 1 + ((now - timebase) / begin_impl_frame_args_.interval); | |
298 return timebase + (begin_impl_frame_args_.interval * intervals); | |
299 } | |
300 | |
301 base::TimeTicks Scheduler::LastBeginImplFrameTime() { | |
302 return begin_impl_frame_args_.frame_time; | |
303 } | |
304 | |
305 void Scheduler::SetupNextBeginFrameIfNeeded() { | |
306 // Never call SetNeedsBeginFrames if the frame source already has the right | |
307 // value. | |
308 if (frame_source_->NeedsBeginFrames() != state_machine_.BeginFrameNeeded()) { | |
309 if (state_machine_.BeginFrameNeeded()) { | |
310 // Call SetNeedsBeginFrames(true) as soon as possible. | |
311 frame_source_->SetNeedsBeginFrames(true); | |
312 } else if (state_machine_.begin_impl_frame_state() == | |
313 SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_IDLE) { | |
314 // Call SetNeedsBeginFrames(false) in between frames only. | |
315 frame_source_->SetNeedsBeginFrames(false); | |
316 client_->SendBeginMainFrameNotExpectedSoon(); | |
317 } | |
318 } | |
319 | |
320 PostBeginRetroFrameIfNeeded(); | |
321 } | |
322 | |
323 // We may need to poll when we can't rely on BeginFrame to advance certain | |
324 // state or to avoid deadlock. | |
325 void Scheduler::SetupPollingMechanisms() { | |
326 bool needs_advance_commit_state_timer = false; | |
327 // Setup PollForAnticipatedDrawTriggers if we need to monitor state but | |
328 // aren't expecting any more BeginFrames. This should only be needed by | |
329 // the synchronous compositor when BeginFrameNeeded is false. | |
330 if (state_machine_.ShouldPollForAnticipatedDrawTriggers()) { | |
331 DCHECK(!state_machine_.SupportsProactiveBeginFrame()); | |
332 if (poll_for_draw_triggers_task_.IsCancelled()) { | |
333 poll_for_draw_triggers_task_.Reset(poll_for_draw_triggers_closure_); | |
334 base::TimeDelta delay = begin_impl_frame_args_.IsValid() | |
335 ? begin_impl_frame_args_.interval | |
336 : BeginFrameArgs::DefaultInterval(); | |
337 task_runner_->PostDelayedTask( | |
338 FROM_HERE, poll_for_draw_triggers_task_.callback(), delay); | |
339 } | |
340 } else { | |
341 poll_for_draw_triggers_task_.Cancel(); | |
342 | |
343 // At this point we'd prefer to advance through the commit flow by | |
344 // drawing a frame, however it's possible that the frame rate controller | |
345 // will not give us a BeginFrame until the commit completes. See | |
346 // crbug.com/317430 for an example of a swap ack being held on commit. Thus | |
347 // we set a repeating timer to poll on ProcessScheduledActions until we | |
348 // successfully reach BeginFrame. Synchronous compositor does not use | |
349 // frame rate controller or have the circular wait in the bug. | |
350 if (IsBeginMainFrameSentOrStarted() && | |
351 !settings_.using_synchronous_renderer_compositor) { | |
352 needs_advance_commit_state_timer = true; | |
353 } | |
354 } | |
355 | |
356 if (needs_advance_commit_state_timer) { | |
357 if (advance_commit_state_task_.IsCancelled() && | |
358 begin_impl_frame_args_.IsValid()) { | |
359 // Since we'd rather get a BeginImplFrame by the normal mechanism, we | |
360 // set the interval to twice the interval from the previous frame. | |
361 advance_commit_state_task_.Reset(advance_commit_state_closure_); | |
362 task_runner_->PostDelayedTask(FROM_HERE, | |
363 advance_commit_state_task_.callback(), | |
364 begin_impl_frame_args_.interval * 2); | |
365 } | |
366 } else { | |
367 advance_commit_state_task_.Cancel(); | |
368 } | |
369 } | |
370 | |
371 // BeginFrame is the mechanism that tells us that now is a good time to start | |
372 // making a frame. Usually this means that user input for the frame is complete. | |
373 // If the scheduler is busy, we queue the BeginFrame to be handled later as | |
374 // a BeginRetroFrame. | |
375 bool Scheduler::OnBeginFrameMixInDelegate(const BeginFrameArgs& args) { | |
376 TRACE_EVENT1("cc,benchmark", "Scheduler::BeginFrame", "args", args.AsValue()); | |
377 | |
378 // Deliver BeginFrames to children. | |
379 if (state_machine_.children_need_begin_frames()) { | |
380 BeginFrameArgs adjusted_args_for_children(args); | |
381 // Adjust a deadline for child schedulers. | |
382 // TODO(simonhong): Once we have commitless update, we can get rid of | |
383 // BeginMainFrameToCommitDurationEstimate() + | |
384 // CommitToActivateDurationEstimate(). | |
385 adjusted_args_for_children.deadline -= | |
386 (client_->BeginMainFrameToCommitDurationEstimate() + | |
387 client_->CommitToActivateDurationEstimate() + | |
388 client_->DrawDurationEstimate() + EstimatedParentDrawTime()); | |
389 client_->SendBeginFramesToChildren(adjusted_args_for_children); | |
390 } | |
391 | |
392 // We have just called SetNeedsBeginFrame(true) and the BeginFrameSource has | |
393 // sent us the last BeginFrame we have missed. As we might not be able to | |
394 // actually make rendering for this call, handle it like a "retro frame". | |
395 // TODO(brainderson): Add a test for this functionality ASAP! | |
396 if (args.type == BeginFrameArgs::MISSED) { | |
397 begin_retro_frame_args_.push_back(args); | |
398 PostBeginRetroFrameIfNeeded(); | |
399 return true; | |
400 } | |
401 | |
402 BeginFrameArgs adjusted_args(args); | |
403 adjusted_args.deadline -= EstimatedParentDrawTime(); | |
404 | |
405 bool should_defer_begin_frame; | |
406 if (settings_.using_synchronous_renderer_compositor) { | |
407 should_defer_begin_frame = false; | |
408 } else { | |
409 should_defer_begin_frame = | |
410 !begin_retro_frame_args_.empty() || | |
411 !begin_retro_frame_task_.IsCancelled() || | |
412 !frame_source_->NeedsBeginFrames() || | |
413 (state_machine_.begin_impl_frame_state() != | |
414 SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_IDLE); | |
415 } | |
416 | |
417 if (should_defer_begin_frame) { | |
418 begin_retro_frame_args_.push_back(adjusted_args); | |
419 TRACE_EVENT_INSTANT0( | |
420 "cc", "Scheduler::BeginFrame deferred", TRACE_EVENT_SCOPE_THREAD); | |
421 // Queuing the frame counts as "using it", so we need to return true. | |
422 } else { | |
423 BeginImplFrame(adjusted_args); | |
424 } | |
425 return true; | |
426 } | |
427 | |
428 void Scheduler::SetChildrenNeedBeginFrames(bool children_need_begin_frames) { | |
429 state_machine_.SetChildrenNeedBeginFrames(children_need_begin_frames); | |
430 ProcessScheduledActions(); | |
431 } | |
432 | |
433 // BeginRetroFrame is called for BeginFrames that we've deferred because | |
434 // the scheduler was in the middle of processing a previous BeginFrame. | |
435 void Scheduler::BeginRetroFrame() { | |
436 TRACE_EVENT0("cc,benchmark", "Scheduler::BeginRetroFrame"); | |
437 DCHECK(!settings_.using_synchronous_renderer_compositor); | |
438 DCHECK(!begin_retro_frame_args_.empty()); | |
439 DCHECK(!begin_retro_frame_task_.IsCancelled()); | |
440 DCHECK_EQ(state_machine_.begin_impl_frame_state(), | |
441 SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_IDLE); | |
442 | |
443 begin_retro_frame_task_.Cancel(); | |
444 | |
445 // Discard expired BeginRetroFrames | |
446 // Today, we should always end up with at most one un-expired BeginRetroFrame | |
447 // because deadlines will not be greater than the next frame time. We don't | |
448 // DCHECK though because some systems don't always have monotonic timestamps. | |
449 // TODO(brianderson): In the future, long deadlines could result in us not | |
450 // draining the queue if we don't catch up. If we consistently can't catch | |
451 // up, our fallback should be to lower our frame rate. | |
452 base::TimeTicks now = Now(); | |
453 | |
454 while (!begin_retro_frame_args_.empty()) { | |
455 const BeginFrameArgs& args = begin_retro_frame_args_.front(); | |
456 base::TimeTicks expiration_time = args.frame_time + args.interval; | |
457 if (now <= expiration_time) | |
458 break; | |
459 TRACE_EVENT_INSTANT2( | |
460 "cc", "Scheduler::BeginRetroFrame discarding", TRACE_EVENT_SCOPE_THREAD, | |
461 "expiration_time - now", (expiration_time - now).InMillisecondsF(), | |
462 "BeginFrameArgs", begin_retro_frame_args_.front().AsValue()); | |
463 begin_retro_frame_args_.pop_front(); | |
464 frame_source_->DidFinishFrame(begin_retro_frame_args_.size()); | |
465 } | |
466 | |
467 if (begin_retro_frame_args_.empty()) { | |
468 TRACE_EVENT_INSTANT0("cc", | |
469 "Scheduler::BeginRetroFrames all expired", | |
470 TRACE_EVENT_SCOPE_THREAD); | |
471 } else { | |
472 BeginFrameArgs front = begin_retro_frame_args_.front(); | |
473 begin_retro_frame_args_.pop_front(); | |
474 BeginImplFrame(front); | |
475 } | |
476 } | |
477 | |
478 // There could be a race between the posted BeginRetroFrame and a new | |
479 // BeginFrame arriving via the normal mechanism. Scheduler::BeginFrame | |
480 // will check if there is a pending BeginRetroFrame to ensure we handle | |
481 // BeginFrames in FIFO order. | |
482 void Scheduler::PostBeginRetroFrameIfNeeded() { | |
483 TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("cc.debug.scheduler"), | |
484 "Scheduler::PostBeginRetroFrameIfNeeded", | |
485 "state", | |
486 AsValue()); | |
487 if (!frame_source_->NeedsBeginFrames()) | |
488 return; | |
489 | |
490 if (begin_retro_frame_args_.empty() || !begin_retro_frame_task_.IsCancelled()) | |
491 return; | |
492 | |
493 // begin_retro_frame_args_ should always be empty for the | |
494 // synchronous compositor. | |
495 DCHECK(!settings_.using_synchronous_renderer_compositor); | |
496 | |
497 if (state_machine_.begin_impl_frame_state() != | |
498 SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_IDLE) | |
499 return; | |
500 | |
501 begin_retro_frame_task_.Reset(begin_retro_frame_closure_); | |
502 | |
503 task_runner_->PostTask(FROM_HERE, begin_retro_frame_task_.callback()); | |
504 } | |
505 | |
506 // BeginImplFrame starts a compositor frame that will wait up until a deadline | |
507 // for a BeginMainFrame+activation to complete before it times out and draws | |
508 // any asynchronous animation and scroll/pinch updates. | |
509 void Scheduler::BeginImplFrame(const BeginFrameArgs& args) { | |
510 bool main_thread_is_in_high_latency_mode = | |
511 state_machine_.MainThreadIsInHighLatencyMode(); | |
512 TRACE_EVENT2("cc,benchmark", | |
513 "Scheduler::BeginImplFrame", | |
514 "args", | |
515 args.AsValue(), | |
516 "main_thread_is_high_latency", | |
517 main_thread_is_in_high_latency_mode); | |
518 TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("cc.debug.scheduler"), | |
519 "MainThreadLatency", | |
520 main_thread_is_in_high_latency_mode); | |
521 DCHECK_EQ(state_machine_.begin_impl_frame_state(), | |
522 SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_IDLE); | |
523 DCHECK(state_machine_.HasInitializedOutputSurface()); | |
524 | |
525 advance_commit_state_task_.Cancel(); | |
526 | |
527 begin_impl_frame_args_ = args; | |
528 begin_impl_frame_args_.deadline -= client_->DrawDurationEstimate(); | |
529 | |
530 if (!state_machine_.impl_latency_takes_priority() && | |
531 main_thread_is_in_high_latency_mode && | |
532 CanCommitAndActivateBeforeDeadline()) { | |
533 state_machine_.SetSkipNextBeginMainFrameToReduceLatency(); | |
534 } | |
535 | |
536 state_machine_.OnBeginImplFrame(); | |
537 client_->WillBeginImplFrame(begin_impl_frame_args_); | |
538 | |
539 ProcessScheduledActions(); | |
540 | |
541 state_machine_.OnBeginImplFrameDeadlinePending(); | |
542 | |
543 if (settings_.using_synchronous_renderer_compositor) { | |
544 // The synchronous renderer compositor has to make its GL calls | |
545 // within this call. | |
546 // TODO(brianderson): Have the OutputSurface initiate the deadline tasks | |
547 // so the synchronous renderer compositor can take advantage of splitting | |
548 // up the BeginImplFrame and deadline as well. | |
549 OnBeginImplFrameDeadline(); | |
550 } else { | |
551 ScheduleBeginImplFrameDeadline(); | |
552 } | |
553 } | |
554 | |
555 void Scheduler::ScheduleBeginImplFrameDeadline() { | |
556 // The synchronous compositor does not post a deadline task. | |
557 DCHECK(!settings_.using_synchronous_renderer_compositor); | |
558 | |
559 begin_impl_frame_deadline_task_.Cancel(); | |
560 begin_impl_frame_deadline_task_.Reset(begin_impl_frame_deadline_closure_); | |
561 | |
562 begin_impl_frame_deadline_mode_ = | |
563 state_machine_.CurrentBeginImplFrameDeadlineMode(); | |
564 | |
565 base::TimeTicks deadline; | |
566 switch (begin_impl_frame_deadline_mode_) { | |
567 case SchedulerStateMachine::BEGIN_IMPL_FRAME_DEADLINE_MODE_IMMEDIATE: | |
568 // We are ready to draw a new active tree immediately. | |
569 // We don't use Now() here because it's somewhat expensive to call. | |
570 deadline = base::TimeTicks(); | |
571 break; | |
572 case SchedulerStateMachine::BEGIN_IMPL_FRAME_DEADLINE_MODE_REGULAR: | |
573 // We are animating on the impl thread but we can wait for some time. | |
574 deadline = begin_impl_frame_args_.deadline; | |
575 break; | |
576 case SchedulerStateMachine::BEGIN_IMPL_FRAME_DEADLINE_MODE_LATE: | |
577 // We are blocked for one reason or another and we should wait. | |
578 // TODO(brianderson): Handle long deadlines (that are past the next | |
579 // frame's frame time) properly instead of using this hack. | |
580 deadline = | |
581 begin_impl_frame_args_.frame_time + begin_impl_frame_args_.interval; | |
582 break; | |
583 } | |
584 | |
585 TRACE_EVENT1( | |
586 "cc", "Scheduler::ScheduleBeginImplFrameDeadline", "deadline", deadline); | |
587 | |
588 base::TimeDelta delta = deadline - Now(); | |
589 if (delta <= base::TimeDelta()) | |
590 delta = base::TimeDelta(); | |
591 task_runner_->PostDelayedTask( | |
592 FROM_HERE, begin_impl_frame_deadline_task_.callback(), delta); | |
593 } | |
594 | |
595 void Scheduler::RescheduleBeginImplFrameDeadlineIfNeeded() { | |
596 if (settings_.using_synchronous_renderer_compositor) | |
597 return; | |
598 | |
599 if (state_machine_.begin_impl_frame_state() != | |
600 SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_INSIDE_BEGIN_FRAME) | |
601 return; | |
602 | |
603 if (begin_impl_frame_deadline_mode_ != | |
604 state_machine_.CurrentBeginImplFrameDeadlineMode()) | |
605 ScheduleBeginImplFrameDeadline(); | |
606 } | |
607 | |
608 void Scheduler::OnBeginImplFrameDeadline() { | |
609 TRACE_EVENT0("cc,benchmark", "Scheduler::OnBeginImplFrameDeadline"); | |
610 begin_impl_frame_deadline_task_.Cancel(); | |
611 // We split the deadline actions up into two phases so the state machine | |
612 // has a chance to trigger actions that should occur durring and after | |
613 // the deadline separately. For example: | |
614 // * Sending the BeginMainFrame will not occur after the deadline in | |
615 // order to wait for more user-input before starting the next commit. | |
616 // * Creating a new OuputSurface will not occur during the deadline in | |
617 // order to allow the state machine to "settle" first. | |
618 | |
619 // TODO(robliao): Remove ScopedTracker below once crbug.com/461509 is fixed. | |
620 tracked_objects::ScopedTracker tracking_profile1( | |
621 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
622 "461509 Scheduler::OnBeginImplFrameDeadline1")); | |
623 state_machine_.OnBeginImplFrameDeadline(); | |
624 ProcessScheduledActions(); | |
625 state_machine_.OnBeginImplFrameIdle(); | |
626 ProcessScheduledActions(); | |
627 | |
628 client_->DidBeginImplFrameDeadline(); | |
629 frame_source_->DidFinishFrame(begin_retro_frame_args_.size()); | |
630 } | |
631 | |
632 void Scheduler::PollForAnticipatedDrawTriggers() { | |
633 TRACE_EVENT0("cc", "Scheduler::PollForAnticipatedDrawTriggers"); | |
634 poll_for_draw_triggers_task_.Cancel(); | |
635 state_machine_.DidEnterPollForAnticipatedDrawTriggers(); | |
636 ProcessScheduledActions(); | |
637 state_machine_.DidLeavePollForAnticipatedDrawTriggers(); | |
638 } | |
639 | |
640 void Scheduler::PollToAdvanceCommitState() { | |
641 TRACE_EVENT0("cc", "Scheduler::PollToAdvanceCommitState"); | |
642 advance_commit_state_task_.Cancel(); | |
643 ProcessScheduledActions(); | |
644 } | |
645 | |
646 void Scheduler::DrawAndSwapIfPossible() { | |
647 DrawResult result = client_->ScheduledActionDrawAndSwapIfPossible(); | |
648 state_machine_.DidDrawIfPossibleCompleted(result); | |
649 } | |
650 | |
651 void Scheduler::SetDeferCommits(bool defer_commits) { | |
652 TRACE_EVENT1("cc", "Scheduler::SetDeferCommits", | |
653 "defer_commits", | |
654 defer_commits); | |
655 state_machine_.SetDeferCommits(defer_commits); | |
656 ProcessScheduledActions(); | |
657 } | |
658 | |
659 void Scheduler::ProcessScheduledActions() { | |
660 // We do not allow ProcessScheduledActions to be recursive. | |
661 // The top-level call will iteratively execute the next action for us anyway. | |
662 if (inside_process_scheduled_actions_) | |
663 return; | |
664 | |
665 base::AutoReset<bool> mark_inside(&inside_process_scheduled_actions_, true); | |
666 | |
667 SchedulerStateMachine::Action action; | |
668 do { | |
669 action = state_machine_.NextAction(); | |
670 TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("cc.debug.scheduler"), | |
671 "SchedulerStateMachine", | |
672 "state", | |
673 AsValue()); | |
674 VLOG(2) << "Scheduler::ProcessScheduledActions: " | |
675 << SchedulerStateMachine::ActionToString(action) << " " | |
676 << state_machine_.GetStatesForDebugging(); | |
677 state_machine_.UpdateState(action); | |
678 base::AutoReset<SchedulerStateMachine::Action> | |
679 mark_inside_action(&inside_action_, action); | |
680 switch (action) { | |
681 case SchedulerStateMachine::ACTION_NONE: | |
682 break; | |
683 case SchedulerStateMachine::ACTION_ANIMATE: | |
684 client_->ScheduledActionAnimate(); | |
685 break; | |
686 case SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME: | |
687 client_->ScheduledActionSendBeginMainFrame(); | |
688 break; | |
689 case SchedulerStateMachine::ACTION_COMMIT: { | |
690 // TODO(robliao): Remove ScopedTracker below once crbug.com/461509 is | |
691 // fixed. | |
692 tracked_objects::ScopedTracker tracking_profile4( | |
693 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
694 "461509 Scheduler::ProcessScheduledActions4")); | |
695 client_->ScheduledActionCommit(); | |
696 break; | |
697 } | |
698 case SchedulerStateMachine::ACTION_ACTIVATE_SYNC_TREE: | |
699 client_->ScheduledActionActivateSyncTree(); | |
700 break; | |
701 case SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE: { | |
702 // TODO(robliao): Remove ScopedTracker below once crbug.com/461509 is | |
703 // fixed. | |
704 tracked_objects::ScopedTracker tracking_profile6( | |
705 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
706 "461509 Scheduler::ProcessScheduledActions6")); | |
707 DrawAndSwapIfPossible(); | |
708 break; | |
709 } | |
710 case SchedulerStateMachine::ACTION_DRAW_AND_SWAP_FORCED: | |
711 client_->ScheduledActionDrawAndSwapForced(); | |
712 break; | |
713 case SchedulerStateMachine::ACTION_DRAW_AND_SWAP_ABORT: | |
714 // No action is actually performed, but this allows the state machine to | |
715 // advance out of its waiting to draw state without actually drawing. | |
716 break; | |
717 case SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION: | |
718 client_->ScheduledActionBeginOutputSurfaceCreation(); | |
719 break; | |
720 case SchedulerStateMachine::ACTION_PREPARE_TILES: | |
721 client_->ScheduledActionPrepareTiles(); | |
722 break; | |
723 } | |
724 } while (action != SchedulerStateMachine::ACTION_NONE); | |
725 | |
726 SetupPollingMechanisms(); | |
727 | |
728 client_->DidAnticipatedDrawTimeChange(AnticipatedDrawTime()); | |
729 | |
730 RescheduleBeginImplFrameDeadlineIfNeeded(); | |
731 | |
732 SetupNextBeginFrameIfNeeded(); | |
733 } | |
734 | |
735 scoped_refptr<base::trace_event::ConvertableToTraceFormat> Scheduler::AsValue() | |
736 const { | |
737 scoped_refptr<base::trace_event::TracedValue> state = | |
738 new base::trace_event::TracedValue(); | |
739 AsValueInto(state.get()); | |
740 return state; | |
741 } | |
742 | |
743 void Scheduler::AsValueInto(base::trace_event::TracedValue* state) const { | |
744 state->BeginDictionary("state_machine"); | |
745 state_machine_.AsValueInto(state); | |
746 state->EndDictionary(); | |
747 | |
748 // Only trace frame sources when explicitly enabled - http://crbug.com/420607 | |
749 bool frame_tracing_enabled = false; | |
750 TRACE_EVENT_CATEGORY_GROUP_ENABLED( | |
751 TRACE_DISABLED_BY_DEFAULT("cc.debug.scheduler.frames"), | |
752 &frame_tracing_enabled); | |
753 if (frame_tracing_enabled) { | |
754 state->BeginDictionary("frame_source_"); | |
755 frame_source_->AsValueInto(state); | |
756 state->EndDictionary(); | |
757 } | |
758 | |
759 state->BeginDictionary("scheduler_state"); | |
760 state->SetDouble("time_until_anticipated_draw_time_ms", | |
761 (AnticipatedDrawTime() - Now()).InMillisecondsF()); | |
762 state->SetDouble("estimated_parent_draw_time_ms", | |
763 estimated_parent_draw_time_.InMillisecondsF()); | |
764 state->SetBoolean("last_set_needs_begin_frame_", | |
765 frame_source_->NeedsBeginFrames()); | |
766 state->SetInteger("begin_retro_frame_args_", begin_retro_frame_args_.size()); | |
767 state->SetBoolean("begin_retro_frame_task_", | |
768 !begin_retro_frame_task_.IsCancelled()); | |
769 state->SetBoolean("begin_impl_frame_deadline_task_", | |
770 !begin_impl_frame_deadline_task_.IsCancelled()); | |
771 state->SetBoolean("poll_for_draw_triggers_task_", | |
772 !poll_for_draw_triggers_task_.IsCancelled()); | |
773 state->SetBoolean("advance_commit_state_task_", | |
774 !advance_commit_state_task_.IsCancelled()); | |
775 state->BeginDictionary("begin_impl_frame_args"); | |
776 begin_impl_frame_args_.AsValueInto(state); | |
777 state->EndDictionary(); | |
778 | |
779 base::TimeTicks now = Now(); | |
780 base::TimeTicks frame_time = begin_impl_frame_args_.frame_time; | |
781 base::TimeTicks deadline = begin_impl_frame_args_.deadline; | |
782 base::TimeDelta interval = begin_impl_frame_args_.interval; | |
783 state->BeginDictionary("major_timestamps_in_ms"); | |
784 state->SetDouble("0_interval", interval.InMillisecondsF()); | |
785 state->SetDouble("1_now_to_deadline", (deadline - now).InMillisecondsF()); | |
786 state->SetDouble("2_frame_time_to_now", (now - frame_time).InMillisecondsF()); | |
787 state->SetDouble("3_frame_time_to_deadline", | |
788 (deadline - frame_time).InMillisecondsF()); | |
789 state->SetDouble("4_now", (now - base::TimeTicks()).InMillisecondsF()); | |
790 state->SetDouble("5_frame_time", | |
791 (frame_time - base::TimeTicks()).InMillisecondsF()); | |
792 state->SetDouble("6_deadline", | |
793 (deadline - base::TimeTicks()).InMillisecondsF()); | |
794 state->EndDictionary(); | |
795 | |
796 state->EndDictionary(); | |
797 | |
798 state->BeginDictionary("client_state"); | |
799 state->SetDouble("draw_duration_estimate_ms", | |
800 client_->DrawDurationEstimate().InMillisecondsF()); | |
801 state->SetDouble( | |
802 "begin_main_frame_to_commit_duration_estimate_ms", | |
803 client_->BeginMainFrameToCommitDurationEstimate().InMillisecondsF()); | |
804 state->SetDouble( | |
805 "commit_to_activate_duration_estimate_ms", | |
806 client_->CommitToActivateDurationEstimate().InMillisecondsF()); | |
807 state->EndDictionary(); | |
808 } | |
809 | |
810 bool Scheduler::CanCommitAndActivateBeforeDeadline() const { | |
811 // Check if the main thread computation and commit can be finished before the | |
812 // impl thread's deadline. | |
813 base::TimeTicks estimated_draw_time = | |
814 begin_impl_frame_args_.frame_time + | |
815 client_->BeginMainFrameToCommitDurationEstimate() + | |
816 client_->CommitToActivateDurationEstimate(); | |
817 | |
818 TRACE_EVENT2( | |
819 TRACE_DISABLED_BY_DEFAULT("cc.debug.scheduler"), | |
820 "CanCommitAndActivateBeforeDeadline", | |
821 "time_left_after_drawing_ms", | |
822 (begin_impl_frame_args_.deadline - estimated_draw_time).InMillisecondsF(), | |
823 "state", | |
824 AsValue()); | |
825 | |
826 return estimated_draw_time < begin_impl_frame_args_.deadline; | |
827 } | |
828 | |
829 bool Scheduler::IsBeginMainFrameSentOrStarted() const { | |
830 return (state_machine_.commit_state() == | |
831 SchedulerStateMachine::COMMIT_STATE_BEGIN_MAIN_FRAME_SENT || | |
832 state_machine_.commit_state() == | |
833 SchedulerStateMachine::COMMIT_STATE_BEGIN_MAIN_FRAME_STARTED); | |
834 } | |
835 | |
836 } // namespace cc | |
OLD | NEW |