Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(225)

Side by Side Diff: cc/scheduler/scheduler.cc

Issue 794293002: cc: Unify BeginRetroFrames and non-RetroFrame paths in scheduler. [WIP] (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 6 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | cc/scheduler/scheduler_state_machine.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2011 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "cc/scheduler/scheduler.h" 5 #include "cc/scheduler/scheduler.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 8
9 #include "base/auto_reset.h" 9 #include "base/auto_reset.h"
10 #include "base/debug/trace_event.h" 10 #include "base/debug/trace_event.h"
(...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after
83 primary_frame_source_(NULL), 83 primary_frame_source_(NULL),
84 background_frame_source_(NULL), 84 background_frame_source_(NULL),
85 primary_frame_source_internal_(external_begin_frame_source.Pass()), 85 primary_frame_source_internal_(external_begin_frame_source.Pass()),
86 background_frame_source_internal_(), 86 background_frame_source_internal_(),
87 vsync_observer_(NULL), 87 vsync_observer_(NULL),
88 settings_(scheduler_settings), 88 settings_(scheduler_settings),
89 client_(client), 89 client_(client),
90 layer_tree_host_id_(layer_tree_host_id), 90 layer_tree_host_id_(layer_tree_host_id),
91 task_runner_(task_runner), 91 task_runner_(task_runner),
92 power_monitor_(power_monitor), 92 power_monitor_(power_monitor),
93 begin_retro_frame_posted_(false), 93 begin_frame_process_posted_(false),
94 state_machine_(scheduler_settings), 94 state_machine_(scheduler_settings),
95 inside_process_scheduled_actions_(false), 95 inside_process_scheduled_actions_(false),
96 inside_action_(SchedulerStateMachine::ACTION_NONE), 96 inside_action_(SchedulerStateMachine::ACTION_NONE),
97 weak_factory_(this) { 97 weak_factory_(this) {
98 TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("cc.debug.scheduler"), 98 TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("cc.debug.scheduler"),
99 "Scheduler::Scheduler", 99 "Scheduler::Scheduler",
100 "settings", 100 "settings",
101 settings_.AsValue()); 101 settings_.AsValue());
102 DCHECK(client_); 102 DCHECK(client_);
103 DCHECK(!state_machine_.BeginFrameNeeded()); 103 DCHECK(!state_machine_.BeginFrameNeeded());
104 104
105 begin_retro_frame_closure_ = 105 begin_frame_process_closure_ =
106 base::Bind(&Scheduler::BeginRetroFrame, weak_factory_.GetWeakPtr()); 106 base::Bind(&Scheduler::ProcessBeginFrames, weak_factory_.GetWeakPtr());
107 begin_impl_frame_deadline_closure_ = base::Bind( 107 begin_impl_frame_deadline_closure_ = base::Bind(
108 &Scheduler::OnBeginImplFrameDeadline, weak_factory_.GetWeakPtr()); 108 &Scheduler::OnBeginImplFrameDeadline, weak_factory_.GetWeakPtr());
109 poll_for_draw_triggers_closure_ = base::Bind( 109 poll_for_draw_triggers_closure_ = base::Bind(
110 &Scheduler::PollForAnticipatedDrawTriggers, weak_factory_.GetWeakPtr()); 110 &Scheduler::PollForAnticipatedDrawTriggers, weak_factory_.GetWeakPtr());
111 advance_commit_state_closure_ = base::Bind( 111 advance_commit_state_closure_ = base::Bind(
112 &Scheduler::PollToAdvanceCommitState, weak_factory_.GetWeakPtr()); 112 &Scheduler::PollToAdvanceCommitState, weak_factory_.GetWeakPtr());
113 113
114 frame_source_ = BeginFrameSourceMultiplexer::Create(); 114 frame_source_ = BeginFrameSourceMultiplexer::Create();
115 frame_source_->AddObserver(this); 115 frame_source_->AddObserver(this);
116 116
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
152 } 152 }
153 } 153 }
154 154
155 void Scheduler::TeardownPowerMonitoring() { 155 void Scheduler::TeardownPowerMonitoring() {
156 if (settings_.disable_hi_res_timer_tasks_on_battery) { 156 if (settings_.disable_hi_res_timer_tasks_on_battery) {
157 DCHECK(power_monitor_); 157 DCHECK(power_monitor_);
158 power_monitor_->RemoveObserver(this); 158 power_monitor_->RemoveObserver(this);
159 } 159 }
160 } 160 }
161 161
162 void Scheduler::OnPowerStateChange(bool on_battery_power) {
163 DCHECK(settings_.disable_hi_res_timer_tasks_on_battery);
164 state_machine_.SetImplLatencyTakesPriorityOnBattery(on_battery_power);
165 }
166
167 void Scheduler::CommitVSyncParameters(base::TimeTicks timebase, 162 void Scheduler::CommitVSyncParameters(base::TimeTicks timebase,
168 base::TimeDelta interval) { 163 base::TimeDelta interval) {
169 // TODO(brianderson): We should not be receiving 0 intervals. 164 // TODO(brianderson): We should not be receiving 0 intervals.
170 if (interval == base::TimeDelta()) 165 if (interval == base::TimeDelta())
171 interval = BeginFrameArgs::DefaultInterval(); 166 interval = BeginFrameArgs::DefaultInterval();
172 167
173 if (vsync_observer_) 168 if (vsync_observer_)
174 vsync_observer_->OnUpdateVSyncParameters(timebase, interval); 169 vsync_observer_->OnUpdateVSyncParameters(timebase, interval);
175 } 170 }
176 171
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after
242 if (!inside_process_scheduled_actions_) { 237 if (!inside_process_scheduled_actions_) {
243 DCHECK_EQ(state_machine_.NextAction(), SchedulerStateMachine::ACTION_NONE); 238 DCHECK_EQ(state_machine_.NextAction(), SchedulerStateMachine::ACTION_NONE);
244 } 239 }
245 } 240 }
246 241
247 void Scheduler::DidSwapBuffersComplete() { 242 void Scheduler::DidSwapBuffersComplete() {
248 state_machine_.DidSwapBuffersComplete(); 243 state_machine_.DidSwapBuffersComplete();
249 ProcessScheduledActions(); 244 ProcessScheduledActions();
250 } 245 }
251 246
252 void Scheduler::SetImplLatencyTakesPriority(bool impl_latency_takes_priority) {
253 state_machine_.SetImplLatencyTakesPriority(impl_latency_takes_priority);
254 ProcessScheduledActions();
255 }
256
257 void Scheduler::NotifyReadyToCommit() { 247 void Scheduler::NotifyReadyToCommit() {
258 TRACE_EVENT0("cc", "Scheduler::NotifyReadyToCommit"); 248 TRACE_EVENT0("cc", "Scheduler::NotifyReadyToCommit");
259 state_machine_.NotifyReadyToCommit(); 249 state_machine_.NotifyReadyToCommit();
260 ProcessScheduledActions(); 250 ProcessScheduledActions();
261 } 251 }
262 252
263 void Scheduler::BeginMainFrameAborted(bool did_handle) { 253 void Scheduler::BeginMainFrameAborted(bool did_handle) {
264 TRACE_EVENT0("cc", "Scheduler::BeginMainFrameAborted"); 254 TRACE_EVENT0("cc", "Scheduler::BeginMainFrameAborted");
265 state_machine_.BeginMainFrameAborted(did_handle); 255 state_machine_.BeginMainFrameAborted(did_handle);
266 ProcessScheduledActions(); 256 ProcessScheduledActions();
267 } 257 }
268 258
269 void Scheduler::DidManageTiles() { 259 void Scheduler::DidManageTiles() {
270 state_machine_.DidManageTiles(); 260 state_machine_.DidManageTiles();
271 } 261 }
272 262
273 void Scheduler::DidLoseOutputSurface() { 263 void Scheduler::DidLoseOutputSurface() {
274 TRACE_EVENT0("cc", "Scheduler::DidLoseOutputSurface"); 264 TRACE_EVENT0("cc", "Scheduler::DidLoseOutputSurface");
275 state_machine_.DidLoseOutputSurface(); 265 state_machine_.DidLoseOutputSurface();
276 if (frame_source_->NeedsBeginFrames()) 266 if (frame_source_->NeedsBeginFrames())
277 frame_source_->SetNeedsBeginFrames(false); 267 frame_source_->SetNeedsBeginFrames(false);
278 begin_retro_frame_args_.clear(); 268 pending_begin_frame_args_.clear();
279 ProcessScheduledActions(); 269 ProcessScheduledActions();
280 } 270 }
281 271
282 void Scheduler::DidCreateAndInitializeOutputSurface() { 272 void Scheduler::DidCreateAndInitializeOutputSurface() {
283 TRACE_EVENT0("cc", "Scheduler::DidCreateAndInitializeOutputSurface"); 273 TRACE_EVENT0("cc", "Scheduler::DidCreateAndInitializeOutputSurface");
284 DCHECK(!frame_source_->NeedsBeginFrames()); 274 DCHECK(!frame_source_->NeedsBeginFrames());
285 DCHECK(begin_impl_frame_deadline_task_.IsCancelled()); 275 DCHECK(begin_impl_frame_deadline_task_.IsCancelled());
276
286 state_machine_.DidCreateAndInitializeOutputSurface(); 277 state_machine_.DidCreateAndInitializeOutputSurface();
287 ProcessScheduledActions(); 278 ProcessScheduledActions();
279
280 // We want to start the first commit ASAP, so we post the LastBeginFrameArgs
281 // onto the retro queue and process it.
282 BeginFrameArgs missed = frame_source_->LastBeginFrameArgs();
283 missed.type = BeginFrameArgs::MISSED;
284 // Adjust the deadline
285 //missed.deadline = XXXXX
286 OnBeginFrame(missed);
288 } 287 }
289 288
290 void Scheduler::NotifyBeginMainFrameStarted() { 289 void Scheduler::NotifyBeginMainFrameStarted() {
291 TRACE_EVENT0("cc", "Scheduler::NotifyBeginMainFrameStarted"); 290 TRACE_EVENT0("cc", "Scheduler::NotifyBeginMainFrameStarted");
292 state_machine_.NotifyBeginMainFrameStarted(); 291 state_machine_.NotifyBeginMainFrameStarted();
293 } 292 }
294 293
295 base::TimeTicks Scheduler::AnticipatedDrawTime() const {
296 if (!frame_source_->NeedsBeginFrames() ||
297 begin_impl_frame_args_.interval <= base::TimeDelta())
298 return base::TimeTicks();
299
300 base::TimeTicks now = Now();
301 base::TimeTicks timebase = std::max(begin_impl_frame_args_.frame_time,
302 begin_impl_frame_args_.deadline);
303 int64 intervals = 1 + ((now - timebase) / begin_impl_frame_args_.interval);
304 return timebase + (begin_impl_frame_args_.interval * intervals);
305 }
306
307 base::TimeTicks Scheduler::LastBeginImplFrameTime() {
308 return begin_impl_frame_args_.frame_time;
309 }
310
311 void Scheduler::SetupNextBeginFrameIfNeeded() { 294 void Scheduler::SetupNextBeginFrameIfNeeded() {
312 if (!task_runner_.get()) 295 if (!task_runner_.get())
313 return; 296 return;
314 297
315 bool needs_begin_frame = state_machine_.BeginFrameNeeded(); 298 bool needs_begin_frame = state_machine_.BeginFrameNeeded();
316 299
317 bool at_end_of_deadline = 300 bool at_end_of_deadline =
318 (state_machine_.begin_impl_frame_state() == 301 (state_machine_.begin_impl_frame_state() ==
319 SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_INSIDE_DEADLINE); 302 SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_INSIDE_DEADLINE);
320 303
321 bool should_call_set_needs_begin_frame = 304 bool should_call_set_needs_begin_frame =
322 // Always request the BeginFrame immediately if it wasn't needed before. 305 // Always request the BeginFrame immediately if it wasn't needed before.
323 (needs_begin_frame && !frame_source_->NeedsBeginFrames()) || 306 (needs_begin_frame && !frame_source_->NeedsBeginFrames()) ||
324 // Only stop requesting BeginFrames after a deadline. 307 // Only stop requesting BeginFrames after a deadline.
325 (!needs_begin_frame && frame_source_->NeedsBeginFrames() && 308 (!needs_begin_frame && frame_source_->NeedsBeginFrames() &&
326 at_end_of_deadline); 309 at_end_of_deadline);
327 310
328 if (should_call_set_needs_begin_frame) { 311 if (should_call_set_needs_begin_frame) {
329 frame_source_->SetNeedsBeginFrames(needs_begin_frame); 312 frame_source_->SetNeedsBeginFrames(needs_begin_frame);
330 } 313 }
331 314
332 if (at_end_of_deadline) { 315 if (at_end_of_deadline) {
333 frame_source_->DidFinishFrame(begin_retro_frame_args_.size()); 316 frame_source_->DidFinishFrame(pending_begin_frame_args_.size());
334 } 317 }
335 318
336 PostBeginRetroFrameIfNeeded();
337 SetupPollingMechanisms(needs_begin_frame); 319 SetupPollingMechanisms(needs_begin_frame);
338 } 320 }
339 321
340 // We may need to poll when we can't rely on BeginFrame to advance certain 322 // We may need to poll when we can't rely on BeginFrame to advance certain
341 // state or to avoid deadlock. 323 // state or to avoid deadlock.
342 void Scheduler::SetupPollingMechanisms(bool needs_begin_frame) { 324 void Scheduler::SetupPollingMechanisms(bool needs_begin_frame) {
343 bool needs_advance_commit_state_timer = false; 325 bool needs_advance_commit_state_timer = false;
344 // Setup PollForAnticipatedDrawTriggers if we need to monitor state but 326 // Setup PollForAnticipatedDrawTriggers if we need to monitor state but
345 // aren't expecting any more BeginFrames. This should only be needed by 327 // aren't expecting any more BeginFrames. This should only be needed by
346 // the synchronous compositor when BeginFrameNeeded is false. 328 // the synchronous compositor when BeginFrameNeeded is false.
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
379 advance_commit_state_task_.Reset(advance_commit_state_closure_); 361 advance_commit_state_task_.Reset(advance_commit_state_closure_);
380 task_runner_->PostDelayedTask(FROM_HERE, 362 task_runner_->PostDelayedTask(FROM_HERE,
381 advance_commit_state_task_.callback(), 363 advance_commit_state_task_.callback(),
382 begin_impl_frame_args_.interval * 2); 364 begin_impl_frame_args_.interval * 2);
383 } 365 }
384 } else { 366 } else {
385 advance_commit_state_task_.Cancel(); 367 advance_commit_state_task_.Cancel();
386 } 368 }
387 } 369 }
388 370
371 base::TimeDelta Scheduler::TimeForDrawing() {
372 return EstimatedParentDrawTime() + client_->DrawDurationEstimate();
373 }
374
375 base::TimeTicks Scheduler::CalculateDeadline(base::TimeTicks now, const BeginFra meArgs& args) {
376 // Synchronous renderer should doesn't use deadlines.
377 if (settings_.using_synchronous_renderer_compositor) {
378 return base::TimeTicks();
379 }
380
381 base::TimeTicks deadline = args.deadline;
382
383 // Decrease the deadline enough to give drawing enough time to happen.
384 deadline -= TimeForDrawing();
385
386 if (false) { //is_low_end()) {
387 // On low end devices, if we start main thread rendering it can stave us
388 // of CPU. Hence, if we don't think the main will make this frame we should
389 // run the impl tasks right now.
390 base::TimeTicks main_thread_time =
391 client_->BeginMainFrameToCommitDurationEstimate() +
392 client_->CommitToActivateDurationEstimate();
393
394 if (now + main_thread_time > deadline) {
395 deadline = now;
396 }
397 }
398
399 return deadline;
400 }
401
389 // BeginFrame is the mechanism that tells us that now is a good time to start 402 // BeginFrame is the mechanism that tells us that now is a good time to start
390 // making a frame. Usually this means that user input for the frame is complete. 403 // making a frame. Usually this means that user input for the frame is complete.
391 // If the scheduler is busy, we queue the BeginFrame to be handled later as
392 // a BeginRetroFrame.
393 bool Scheduler::OnBeginFrameMixInDelegate(const BeginFrameArgs& args) { 404 bool Scheduler::OnBeginFrameMixInDelegate(const BeginFrameArgs& args) {
394 TRACE_EVENT1("cc", "Scheduler::BeginFrame", "args", args.AsValue()); 405 TRACE_EVENT1("cc", "Scheduler::BeginFrame", "args", args.AsValue());
395 406
407 // Push the BeginFrame onto a queue for processing.
408 begin_retro_frame_args.push_back(args);
409
410 // If we aren't already processing frames, start processing now.
411 if (state_machine_.begin_impl_frame_state() == SchedulerStateMachine::BEGIN_IM PL_FRAME_STATE_IDLE) {
412 ProcessBeginFrames(nullptr);
413 // Otherwise, post a task to process the frames after the current processing
414 // is finished.
415 } else if (!begin_frame_process_posted_) {
416 begin_frame_process_posted_ = true;
417 task_runner_->PostTask(FROM_HERE, begin_frame_process_closure_, &begin_frame _process_posted_);
418 }
419
420 return true;
421 }
422
423 void Scheduler::ProcessBeginFrames(bool* clear_posted) {
424 DCHECK(!begin_retro_frame_args.empty());
425
426 if (clear_posted) {
427 *clear_posted = false;
428 }
429
430 // Capture now to use for all the deadline calculations
431 base::TimeTicks now = Now();
432
433 // If we have multiple frames to process, discard anything which we can't
434 // make it's deadline.
435 while (pending_begin_frame_args_.size() > 1) {
436 const BeginFrameArgs& args = pending_begin_frame_args_.front();
437
438 base::TimeTicks deadline = CalculateDeadline(now, args);
439 if (now >= deadline) {
440 TRACE_EVENT_INSTANT2(
441 "cc", "Scheduler::BeginFrame discarding", TRACE_EVENT_SCOPE_THREAD,
442 "expiration_time - now", (deadline - now).InMillisecondsF(),
443 "BeginFrameArgs", args.AsValue());
444 pending_begin_frame_args_.pop_front();
445 frame_source_->DidFinishFrame(pending_begin_frame_args_.size());
446 }
447 }
448
449 // Process the head of the queue.
450 BeginFrameArgs front_args = pending_begin_frame_args_.front();
451 pending_begin_frame_args_.pop_front();
452 front_args.deadline = CalculateDeadline(now, front_args);
453 BeginImplFrame(now, front_args);
454 }
455
456
457 void Scheduler::BeginImplFrame(base::TimeTicks now, const BeginFrameArgs& args) {
458 // Clear any pending impl frame deadline
459 begin_impl_frame_deadline_task_.Cancel();
460 begin_impl_frame_deadline_task_.Reset(begin_impl_frame_deadline_closure_);
461
462 // TODO(mithro): ?????
396 // Deliver BeginFrames to children. 463 // Deliver BeginFrames to children.
397 if (settings_.forward_begin_frames_to_children && 464 if (settings_.forward_begin_frames_to_children &&
398 state_machine_.children_need_begin_frames()) { 465 state_machine_.children_need_begin_frames()) {
399 BeginFrameArgs adjusted_args_for_children(args); 466 BeginFrameArgs adjusted_args_for_children(args);
400 // Adjust a deadline for child schedulers. 467 // Adjust a deadline for child schedulers.
401 // TODO(simonhong): Once we have commitless update, we can get rid of 468 // TODO(simonhong): Once we have commitless update, we can get rid of
402 // BeginMainFrameToCommitDurationEstimate() + 469 // BeginMainFrameToCommitDurationEstimate() +
403 // CommitToActivateDurationEstimate(). 470 // CommitToActivateDurationEstimate().
404 adjusted_args_for_children.deadline -= 471 adjusted_args_for_children.deadline -=
405 (client_->BeginMainFrameToCommitDurationEstimate() + 472 (client_->BeginMainFrameToCommitDurationEstimate() +
406 client_->CommitToActivateDurationEstimate() + 473 client_->CommitToActivateDurationEstimate() +
407 client_->DrawDurationEstimate() + EstimatedParentDrawTime()); 474 TimeForDrawing());
408 client_->SendBeginFramesToChildren(adjusted_args_for_children); 475 client_->SendBeginFramesToChildren(adjusted_args_for_children);
409 } 476 }
410 477
411 // We have just called SetNeedsBeginFrame(true) and the BeginFrameSource has 478 // Start the ImplFrame
412 // sent us the last BeginFrame we have missed. As we might not be able to 479 client_->BeginImplFrame(args);
413 // actually make rendering for this call, handle it like a "retro frame". 480
414 // TODO(brainderson): Add a test for this functionality ASAP! 481 // If we are asked to schedule a deadline in the past, just run the deadline
415 if (args.type == BeginFrameArgs::MISSED) { 482 // right now.
416 begin_retro_frame_args_.push_back(args); 483 if (now >= args.deadline) {
417 PostBeginRetroFrameIfNeeded(); 484 OnBeginImplFrameDeadline();
418 return true; 485 return;
419 } 486 }
420 487
421 BeginFrameArgs adjusted_args(args); 488 // Else post a task for the deadline
422 adjusted_args.deadline -= EstimatedParentDrawTime();
423
424 bool should_defer_begin_frame;
425 if (settings_.using_synchronous_renderer_compositor) {
426 should_defer_begin_frame = false;
427 } else {
428 should_defer_begin_frame =
429 !begin_retro_frame_args_.empty() || begin_retro_frame_posted_ ||
430 !frame_source_->NeedsBeginFrames() ||
431 (state_machine_.begin_impl_frame_state() !=
432 SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_IDLE);
433 }
434
435 if (should_defer_begin_frame) {
436 begin_retro_frame_args_.push_back(adjusted_args);
437 TRACE_EVENT_INSTANT0(
438 "cc", "Scheduler::BeginFrame deferred", TRACE_EVENT_SCOPE_THREAD);
439 // Queuing the frame counts as "using it", so we need to return true.
440 } else {
441 BeginImplFrame(adjusted_args);
442 }
443 return true;
444 }
445
446 void Scheduler::SetChildrenNeedBeginFrames(bool children_need_begin_frames) {
447 DCHECK(settings_.forward_begin_frames_to_children);
448 state_machine_.SetChildrenNeedBeginFrames(children_need_begin_frames);
449 DCHECK_EQ(state_machine_.NextAction(), SchedulerStateMachine::ACTION_NONE);
450 }
451
452 // BeginRetroFrame is called for BeginFrames that we've deferred because
453 // the scheduler was in the middle of processing a previous BeginFrame.
454 void Scheduler::BeginRetroFrame() {
455 TRACE_EVENT0("cc", "Scheduler::BeginRetroFrame");
456 DCHECK(!settings_.using_synchronous_renderer_compositor);
457 DCHECK(begin_retro_frame_posted_);
458 begin_retro_frame_posted_ = false;
459
460 // If there aren't any retroactive BeginFrames, then we've lost the
461 // OutputSurface and should abort.
462 if (begin_retro_frame_args_.empty())
463 return;
464
465 // Discard expired BeginRetroFrames
466 // Today, we should always end up with at most one un-expired BeginRetroFrame
467 // because deadlines will not be greater than the next frame time. We don't
468 // DCHECK though because some systems don't always have monotonic timestamps.
469 // TODO(brianderson): In the future, long deadlines could result in us not
470 // draining the queue if we don't catch up. If we consistently can't catch
471 // up, our fallback should be to lower our frame rate.
472 base::TimeTicks now = Now();
473
474 while (!begin_retro_frame_args_.empty()) {
475 const BeginFrameArgs& args = begin_retro_frame_args_.front();
476 base::TimeTicks expiration_time = args.frame_time + args.interval;
477 if (now <= expiration_time)
478 break;
479 TRACE_EVENT_INSTANT2(
480 "cc", "Scheduler::BeginRetroFrame discarding", TRACE_EVENT_SCOPE_THREAD,
481 "expiration_time - now", (expiration_time - now).InMillisecondsF(),
482 "BeginFrameArgs", begin_retro_frame_args_.front().AsValue());
483 begin_retro_frame_args_.pop_front();
484 frame_source_->DidFinishFrame(begin_retro_frame_args_.size());
485 }
486
487 if (begin_retro_frame_args_.empty()) {
488 TRACE_EVENT_INSTANT0("cc",
489 "Scheduler::BeginRetroFrames all expired",
490 TRACE_EVENT_SCOPE_THREAD);
491 } else {
492 BeginFrameArgs front = begin_retro_frame_args_.front();
493 begin_retro_frame_args_.pop_front();
494 BeginImplFrame(front);
495 }
496 }
497
498 // There could be a race between the posted BeginRetroFrame and a new
499 // BeginFrame arriving via the normal mechanism. Scheduler::BeginFrame
500 // will check if there is a pending BeginRetroFrame to ensure we handle
501 // BeginFrames in FIFO order.
502 void Scheduler::PostBeginRetroFrameIfNeeded() {
503 TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("cc.debug.scheduler"),
504 "Scheduler::PostBeginRetroFrameIfNeeded",
505 "state",
506 AsValue());
507 if (!frame_source_->NeedsBeginFrames())
508 return;
509
510 if (begin_retro_frame_args_.empty() || begin_retro_frame_posted_)
511 return;
512
513 // begin_retro_frame_args_ should always be empty for the
514 // synchronous compositor.
515 DCHECK(!settings_.using_synchronous_renderer_compositor);
516
517 if (state_machine_.begin_impl_frame_state() !=
518 SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_IDLE)
519 return;
520
521 begin_retro_frame_posted_ = true;
522 task_runner_->PostTask(FROM_HERE, begin_retro_frame_closure_);
523 }
524
525 // BeginImplFrame starts a compositor frame that will wait up until a deadline
526 // for a BeginMainFrame+activation to complete before it times out and draws
527 // any asynchronous animation and scroll/pinch updates.
528 void Scheduler::BeginImplFrame(const BeginFrameArgs& args) {
529 bool main_thread_is_in_high_latency_mode =
530 state_machine_.MainThreadIsInHighLatencyMode();
531 TRACE_EVENT2("cc",
532 "Scheduler::BeginImplFrame",
533 "args",
534 args.AsValue(),
535 "main_thread_is_high_latency",
536 main_thread_is_in_high_latency_mode);
537 TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("cc.debug.scheduler"),
538 "MainThreadLatency",
539 main_thread_is_in_high_latency_mode);
540 DCHECK_EQ(state_machine_.begin_impl_frame_state(),
541 SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_IDLE);
542 DCHECK(state_machine_.HasInitializedOutputSurface());
543
544 advance_commit_state_task_.Cancel();
545
546 base::TimeDelta draw_duration_estimate = client_->DrawDurationEstimate();
547 begin_impl_frame_args_ = args;
548 begin_impl_frame_args_.deadline -= draw_duration_estimate;
549
550 if (!state_machine_.impl_latency_takes_priority() &&
551 main_thread_is_in_high_latency_mode &&
552 CanCommitAndActivateBeforeDeadline()) {
553 state_machine_.SetSkipNextBeginMainFrameToReduceLatency();
554 }
555
556 client_->WillBeginImplFrame(begin_impl_frame_args_);
557 state_machine_.OnBeginImplFrame(begin_impl_frame_args_);
558 devtools_instrumentation::DidBeginFrame(layer_tree_host_id_);
559
560 ProcessScheduledActions();
561
562 state_machine_.OnBeginImplFrameDeadlinePending();
563
564 if (settings_.using_synchronous_renderer_compositor) {
565 // The synchronous renderer compositor has to make its GL calls
566 // within this call.
567 // TODO(brianderson): Have the OutputSurface initiate the deadline tasks
568 // so the synchronous renderer compositor can take advantage of splitting
569 // up the BeginImplFrame and deadline as well.
570 OnBeginImplFrameDeadline();
571 } else {
572 ScheduleBeginImplFrameDeadline(
573 AdjustedBeginImplFrameDeadline(args, draw_duration_estimate));
574 }
575 }
576
577 base::TimeTicks Scheduler::AdjustedBeginImplFrameDeadline(
578 const BeginFrameArgs& args,
579 base::TimeDelta draw_duration_estimate) const {
580 // The synchronous compositor does not post a deadline task.
581 DCHECK(!settings_.using_synchronous_renderer_compositor);
582 if (state_machine_.ShouldTriggerBeginImplFrameDeadlineEarly()) {
583 // We are ready to draw a new active tree immediately.
584 // We don't use Now() here because it's somewhat expensive to call.
585 return base::TimeTicks();
586 } else if (state_machine_.needs_redraw()) {
587 // We have an animation or fast input path on the impl thread that wants
588 // to draw, so don't wait too long for a new active tree.
589 return args.deadline - draw_duration_estimate;
590 } else {
591 // The impl thread doesn't have anything it wants to draw and we are just
592 // waiting for a new active tree, so post the deadline for the next
593 // expected BeginImplFrame start. This allows us to draw immediately when
594 // there is a new active tree, instead of waiting for the next
595 // BeginImplFrame.
596 // TODO(brianderson): Handle long deadlines (that are past the next frame's
597 // frame time) properly instead of using this hack.
598 return args.frame_time + args.interval;
599 }
600 }
601
602 void Scheduler::ScheduleBeginImplFrameDeadline(base::TimeTicks deadline) {
603 TRACE_EVENT1(
604 "cc", "Scheduler::ScheduleBeginImplFrameDeadline", "deadline", deadline);
605
606 begin_impl_frame_deadline_task_.Cancel();
607 begin_impl_frame_deadline_task_.Reset(begin_impl_frame_deadline_closure_);
608
609 base::TimeDelta delta = deadline - Now();
610 if (delta <= base::TimeDelta())
611 delta = base::TimeDelta();
612 task_runner_->PostDelayedTask( 489 task_runner_->PostDelayedTask(
613 FROM_HERE, begin_impl_frame_deadline_task_.callback(), delta); 490 FROM_HERE, begin_impl_frame_deadline_task_.callback(), now - deadline);
614 } 491 }
615 492
616 void Scheduler::OnBeginImplFrameDeadline() { 493 void Scheduler::OnBeginImplFrameDeadline() {
617 TRACE_EVENT0("cc", "Scheduler::OnBeginImplFrameDeadline"); 494 TRACE_EVENT0("cc", "Scheduler::OnBeginImplFrameDeadline");
618 begin_impl_frame_deadline_task_.Cancel(); 495 begin_impl_frame_deadline_task_.Cancel();
619 496
497 last_begin_impl_frame_args_ = current_begin_impl_frame_args_;
498
620 // We split the deadline actions up into two phases so the state machine 499 // We split the deadline actions up into two phases so the state machine
621 // has a chance to trigger actions that should occur durring and after 500 // has a chance to trigger actions that should occur during and after the
622 // the deadline separately. For example: 501 // deadline separately. For example:
623 // * Sending the BeginMainFrame will not occur after the deadline in 502 // * Sending the BeginMainFrame will not occur after the deadline in
624 // order to wait for more user-input before starting the next commit. 503 // order to wait for more user-input before starting the next commit.
625 // * Creating a new OuputSurface will not occur during the deadline in 504 // * Creating a new OuputSurface will not occur during the deadline in
626 // order to allow the state machine to "settle" first. 505 // order to allow the state machine to "settle" first.
627 state_machine_.OnBeginImplFrameDeadline(); 506 state_machine_.OnBeginImplFrameDeadline();
628 ProcessScheduledActions(); 507 ProcessScheduledActions();
629 state_machine_.OnBeginImplFrameIdle(); 508 state_machine_.OnBeginImplFrameIdle();
630 ProcessScheduledActions(); 509 ProcessScheduledActions();
631 510
632 client_->DidBeginImplFrameDeadline(); 511 client_->DidBeginImplFrameDeadline();
633 } 512 }
634 513
514 void Scheduler::DidActivateSyncTree() {
515 // Main frame is committed and activated
516 last_main_frame_args_ = current_main_frame_args_;
517 current_main_frame_args_ = BeginFrameArgs();
518 }
519
520 BeginFrameArgs Scheduler::GetBeginMainFrameArgs() {
521 // Did the previous main frame meet the deadline?
522 bool last_main_frame_meet_deadline = (last_main_frame_args_.frame_time == last _impl_frame_args_.frame_time);
523
524 BeginFrameArgs args(current_impl_frame_args_);
525 // We aren't inside an active impl frame deadline, so we use the values of
526 // the last impl frame.
527 // TODO(mithro): This includes deadline adjustment for draw time which could
528 // instead be given to the main frame.
529 if (!args.is_valid()) {
530 args = last_impl_frame_args_;
531 }
532
533 // Do we think the main frame can make the deadline?
534 base::TimeDelta main_thread_time =
535 client_->BeginMainFrameToCommitDurationEstimate() +
536 client_->CommitToActivateDurationEstimate();
537 if (now + main_thread_time <= args.deadline) {
538 // If we think the BeginMainFrame will make the deadline, we should
539 // definitely go ahead.
540 return args;
541 }
542
543 base::TimeDelta will_missed_deadline_by = now + main_thread_time - args.deadli ne;
544
545 /*
546 // If we think the BeginMainFrame will only miss by a little and we made the
547 // last deadline, we are going to give it a go and see if we actually miss.
548 // This exists because our estimates are rough and there is jitter in the
549 // system.
550 bool close_deadline_miss = (will_missed_deadline_by / base::TimeDelta::FromMil liseconds(1)) == 0;
551 if (last_main_frame_meet_deadline && close_deadline_miss) {
552 return args;
553 }
554 */
555
556 // Otherwise, we don't think the BeginMainFrame will make the deadline and
557 // have already missed the last frame. What we do depends on if we think
558 // sending a BeginMainFrame now will effect the next frame's ability to meet
559 // the deadline.
560
561 // Work out what percentage of an normal interval the main thread rendering
562 // will take.
563 base::TimeDelta time_for_rendering = args.interval - (EstimatedParentDrawTime( ) + client_->DrawDurationEstimate());
564 double main_percentage_of_rendering_time = main_thread_time.InMillisecondsF() / time_for_rendering.InMillisecondsF();
565
566 if (main_percentage_of_rendering_time < 0.5) {
567 // As the main thread can render twice a frame, starting rendering is
568 // unlikely to cause us to miss the next frame. Let's try and catch the
569 // main thread up to the impl thread to help with latency.
570 // We keep the expired deadline so main thread knows it not to run any
571 // unimportant tasks. It will have time after we catch back up.
572 return args;
573 }
574
575 if (main_percentage_of_rendering_time > 1.0) {
576 // Values greater than 1.0 mean the main thread will never make a normal
577 // deadline.
578
579 // If we could meet 50% of the interval, then we assume that we will do
580 // that and start rendering ASAP.
581 if (main_percentage_of_rendering_time < 2.0) {
582 // Adjust the deadline to the next deadline we think the main thread will
583 // probably make.
584 while (args.deadline + time_for_rendering >= now) {
585 args.deadline += args.interval;
586 }
587 return args;
588 }
589
590 // If the main thread isn't even going to make 50% frame rate, just target
591 // throughput and render as fast as we can. We clear the deadline to inform
592 // the main thread that we are targeting throughput and want to minimize
593 // context switches.
594 args.deadline = base::TimeTicks();
595 return args;
596 }
597
598 // If main_percentage_of_rendering_time is between 0.5 and 1.0, then if we
599 // start rendering too late it will break the next frame too. However, we
600 // have to be careful we don't drop *every* main frame on the floor.
601
602 // If we are missing the deadline by the "slack" in the next frame, then we
603 // should go ahead.
604 base::TimeDelta slack_in_next_frame = time_for_rendering - main_thread_time;
605 if (will_miss_deadline_by < slack_in_next_frame) {
606 // We don't adjust the deadline because we want this main frame to return
607 // as early as possible to give the next main frame as much chance as
608 // possible.
609 return args;
610 }
611
612 if (last_main_frame_meet_deadline == DROPPED) {
613 // Drop this main frame to allow the next main frame to start as soon as
614 // the BeginFrame message occurs.
615 return args;
616 } else {
617 return BeginFrameArgs();
618 }
619 }
620
621 void Scheduler::SetChildrenNeedBeginFrames(bool children_need_begin_frames) {
622 DCHECK(settings_.forward_begin_frames_to_children);
623 state_machine_.SetChildrenNeedBeginFrames(children_need_begin_frames);
624 DCHECK_EQ(state_machine_.NextAction(), SchedulerStateMachine::ACTION_NONE);
625 }
626
627
635 void Scheduler::PollForAnticipatedDrawTriggers() { 628 void Scheduler::PollForAnticipatedDrawTriggers() {
636 TRACE_EVENT0("cc", "Scheduler::PollForAnticipatedDrawTriggers"); 629 TRACE_EVENT0("cc", "Scheduler::PollForAnticipatedDrawTriggers");
637 poll_for_draw_triggers_task_.Cancel(); 630 poll_for_draw_triggers_task_.Cancel();
638 state_machine_.DidEnterPollForAnticipatedDrawTriggers(); 631 state_machine_.DidEnterPollForAnticipatedDrawTriggers();
639 ProcessScheduledActions(); 632 ProcessScheduledActions();
640 state_machine_.DidLeavePollForAnticipatedDrawTriggers(); 633 state_machine_.DidLeavePollForAnticipatedDrawTriggers();
641 } 634 }
642 635
643 void Scheduler::PollToAdvanceCommitState() { 636 void Scheduler::PollToAdvanceCommitState() {
644 TRACE_EVENT0("cc", "Scheduler::PollToAdvanceCommitState"); 637 TRACE_EVENT0("cc", "Scheduler::PollToAdvanceCommitState");
(...skipping 27 matching lines...) Expand all
672 state_machine_.UpdateState(action); 665 state_machine_.UpdateState(action);
673 base::AutoReset<SchedulerStateMachine::Action> 666 base::AutoReset<SchedulerStateMachine::Action>
674 mark_inside_action(&inside_action_, action); 667 mark_inside_action(&inside_action_, action);
675 switch (action) { 668 switch (action) {
676 case SchedulerStateMachine::ACTION_NONE: 669 case SchedulerStateMachine::ACTION_NONE:
677 break; 670 break;
678 case SchedulerStateMachine::ACTION_ANIMATE: 671 case SchedulerStateMachine::ACTION_ANIMATE:
679 client_->ScheduledActionAnimate(); 672 client_->ScheduledActionAnimate();
680 break; 673 break;
681 case SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME: 674 case SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME:
675
676 // FIXME:!!!!!!!
677 BeginFrameArgs args = GetBeginMainFrameArgs();
678 if (!args.IsValid())
679 continue;
680 current_main_frame_args_ = args;
681 // FIXME:!!!!!!!
682
682 client_->ScheduledActionSendBeginMainFrame(); 683 client_->ScheduledActionSendBeginMainFrame();
683 break; 684 break;
684 case SchedulerStateMachine::ACTION_COMMIT: 685 case SchedulerStateMachine::ACTION_COMMIT:
685 client_->ScheduledActionCommit(); 686 client_->ScheduledActionCommit();
686 break; 687 break;
687 case SchedulerStateMachine::ACTION_ACTIVATE_SYNC_TREE: 688 case SchedulerStateMachine::ACTION_ACTIVATE_SYNC_TREE:
688 client_->ScheduledActionActivateSyncTree(); 689 client_->ScheduledActionActivateSyncTree();
689 break; 690 break;
690 case SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE: 691 case SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE:
691 DrawAndSwapIfPossible(); 692 DrawAndSwapIfPossible();
692 break; 693 break;
693 case SchedulerStateMachine::ACTION_DRAW_AND_SWAP_FORCED: 694 case SchedulerStateMachine::ACTION_DRAW_AND_SWAP_FORCED:
694 client_->ScheduledActionDrawAndSwapForced(); 695 client_->ScheduledActionDrawAndSwapForced();
695 break; 696 break;
696 case SchedulerStateMachine::ACTION_DRAW_AND_SWAP_ABORT: 697 case SchedulerStateMachine::ACTION_DRAW_AND_SWAP_ABORT:
697 // No action is actually performed, but this allows the state machine to 698 // No action is actually performed, but this allows the state machine to
698 // advance out of its waiting to draw state without actually drawing. 699 // advance out of its waiting to draw state without actually drawing.
699 break; 700 break;
700 case SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION: 701 case SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION:
701 client_->ScheduledActionBeginOutputSurfaceCreation(); 702 client_->ScheduledActionBeginOutputSurfaceCreation();
702 break; 703 break;
703 case SchedulerStateMachine::ACTION_MANAGE_TILES: 704 case SchedulerStateMachine::ACTION_MANAGE_TILES:
704 client_->ScheduledActionManageTiles(); 705 client_->ScheduledActionManageTiles();
705 break; 706 break;
706 } 707 }
707 } while (action != SchedulerStateMachine::ACTION_NONE); 708 } while (action != SchedulerStateMachine::ACTION_NONE);
708 709
709 SetupNextBeginFrameIfNeeded(); 710 SetupNextBeginFrameIfNeeded();
710 client_->DidAnticipatedDrawTimeChange(AnticipatedDrawTime());
711
712 if (state_machine_.ShouldTriggerBeginImplFrameDeadlineEarly()) { 711 if (state_machine_.ShouldTriggerBeginImplFrameDeadlineEarly()) {
713 DCHECK(!settings_.using_synchronous_renderer_compositor); 712 DCHECK(!settings_.using_synchronous_renderer_compositor);
714 ScheduleBeginImplFrameDeadline(base::TimeTicks()); 713 OnBeginImplFrameDeadline();
715 } 714 }
716 } 715 }
717 716
718 bool Scheduler::WillDrawIfNeeded() const { 717 bool Scheduler::WillDrawIfNeeded() const {
719 return !state_machine_.PendingDrawsShouldBeAborted(); 718 return !state_machine_.PendingDrawsShouldBeAborted();
720 } 719 }
721 720
722 scoped_refptr<base::debug::ConvertableToTraceFormat> Scheduler::AsValue() 721 scoped_refptr<base::debug::ConvertableToTraceFormat> Scheduler::AsValue()
723 const { 722 const {
724 scoped_refptr<base::debug::TracedValue> state = 723 scoped_refptr<base::debug::TracedValue> state =
(...skipping 18 matching lines...) Expand all
743 state->EndDictionary(); 742 state->EndDictionary();
744 } 743 }
745 744
746 state->BeginDictionary("scheduler_state"); 745 state->BeginDictionary("scheduler_state");
747 state->SetDouble("time_until_anticipated_draw_time_ms", 746 state->SetDouble("time_until_anticipated_draw_time_ms",
748 (AnticipatedDrawTime() - Now()).InMillisecondsF()); 747 (AnticipatedDrawTime() - Now()).InMillisecondsF());
749 state->SetDouble("estimated_parent_draw_time_ms", 748 state->SetDouble("estimated_parent_draw_time_ms",
750 estimated_parent_draw_time_.InMillisecondsF()); 749 estimated_parent_draw_time_.InMillisecondsF());
751 state->SetBoolean("last_set_needs_begin_frame_", 750 state->SetBoolean("last_set_needs_begin_frame_",
752 frame_source_->NeedsBeginFrames()); 751 frame_source_->NeedsBeginFrames());
753 state->SetBoolean("begin_retro_frame_posted_", begin_retro_frame_posted_); 752 state->SetBoolean("begin_frame_process_posted_", begin_frame_process_posted_);
754 state->SetInteger("begin_retro_frame_args_", begin_retro_frame_args_.size()); 753 state->SetInteger("pending_begin_frame_args_", pending_begin_frame_args_.size( ));
755 state->SetBoolean("begin_impl_frame_deadline_task_", 754 state->SetBoolean("begin_impl_frame_deadline_task_",
756 !begin_impl_frame_deadline_task_.IsCancelled()); 755 !begin_impl_frame_deadline_task_.IsCancelled());
757 state->SetBoolean("poll_for_draw_triggers_task_", 756 state->SetBoolean("poll_for_draw_triggers_task_",
758 !poll_for_draw_triggers_task_.IsCancelled()); 757 !poll_for_draw_triggers_task_.IsCancelled());
759 state->SetBoolean("advance_commit_state_task_", 758 state->SetBoolean("advance_commit_state_task_",
760 !advance_commit_state_task_.IsCancelled()); 759 !advance_commit_state_task_.IsCancelled());
761 state->BeginDictionary("begin_impl_frame_args"); 760 state->BeginDictionary("begin_impl_frame_args");
762 begin_impl_frame_args_.AsValueInto(state); 761 begin_impl_frame_args_.AsValueInto(state);
763 state->EndDictionary(); 762 state->EndDictionary();
764 763
765 state->EndDictionary(); 764 state->EndDictionary();
766 765
767 state->BeginDictionary("client_state"); 766 state->BeginDictionary("client_state");
768 state->SetDouble("draw_duration_estimate_ms", 767 state->SetDouble("draw_duration_estimate_ms",
769 client_->DrawDurationEstimate().InMillisecondsF()); 768 client_->DrawDurationEstimate().InMillisecondsF());
770 state->SetDouble( 769 state->SetDouble(
771 "begin_main_frame_to_commit_duration_estimate_ms", 770 "begin_main_frame_to_commit_duration_estimate_ms",
772 client_->BeginMainFrameToCommitDurationEstimate().InMillisecondsF()); 771 client_->BeginMainFrameToCommitDurationEstimate().InMillisecondsF());
773 state->SetDouble( 772 state->SetDouble(
774 "commit_to_activate_duration_estimate_ms", 773 "commit_to_activate_duration_estimate_ms",
775 client_->CommitToActivateDurationEstimate().InMillisecondsF()); 774 client_->CommitToActivateDurationEstimate().InMillisecondsF());
776 state->EndDictionary(); 775 state->EndDictionary();
777 } 776 }
778 777
779 bool Scheduler::CanCommitAndActivateBeforeDeadline() const {
780 // Check if the main thread computation and commit can be finished before the
781 // impl thread's deadline.
782 base::TimeTicks estimated_draw_time =
783 begin_impl_frame_args_.frame_time +
784 client_->BeginMainFrameToCommitDurationEstimate() +
785 client_->CommitToActivateDurationEstimate();
786
787 TRACE_EVENT2(
788 TRACE_DISABLED_BY_DEFAULT("cc.debug.scheduler"),
789 "CanCommitAndActivateBeforeDeadline",
790 "time_left_after_drawing_ms",
791 (begin_impl_frame_args_.deadline - estimated_draw_time).InMillisecondsF(),
792 "state",
793 AsValue());
794
795 return estimated_draw_time < begin_impl_frame_args_.deadline;
796 }
797
798 bool Scheduler::IsBeginMainFrameSentOrStarted() const { 778 bool Scheduler::IsBeginMainFrameSentOrStarted() const {
799 return (state_machine_.commit_state() == 779 return (state_machine_.commit_state() ==
800 SchedulerStateMachine::COMMIT_STATE_BEGIN_MAIN_FRAME_SENT || 780 SchedulerStateMachine::COMMIT_STATE_BEGIN_MAIN_FRAME_SENT ||
801 state_machine_.commit_state() == 781 state_machine_.commit_state() ==
802 SchedulerStateMachine::COMMIT_STATE_BEGIN_MAIN_FRAME_STARTED); 782 SchedulerStateMachine::COMMIT_STATE_BEGIN_MAIN_FRAME_STARTED);
803 } 783 }
804 784
805 } // namespace cc 785 } // namespace cc
OLDNEW
« no previous file with comments | « no previous file | cc/scheduler/scheduler_state_machine.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698