Roll src/third_party/WebKit 9f7fb92:f103b33 (svn 202621:202622)
[chromium-blink-merge.git] / components / scheduler / renderer / renderer_scheduler_impl.cc
blobbb5dc7224f7cbf2650d316d3e55060fae20b09b2
1 // Copyright 2014 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 "components/scheduler/renderer/renderer_scheduler_impl.h"
7 #include "base/bind.h"
8 #include "base/debug/stack_trace.h"
9 #include "base/trace_event/trace_event.h"
10 #include "base/trace_event/trace_event_argument.h"
11 #include "cc/output/begin_frame_args.h"
12 #include "components/scheduler/child/scheduler_task_runner_delegate.h"
13 #include "components/scheduler/child/task_queue_impl.h"
14 #include "components/scheduler/child/task_queue_selector.h"
16 namespace scheduler {
17 namespace {
18 const int kLoadingTaskEstimationSampleCount = 200;
19 const double kLoadingTaskEstimationPercentile = 90;
20 const int kTimerTaskEstimationSampleCount = 200;
21 const double kTimerTaskEstimationPercentile = 90;
22 const int kShortIdlePeriodDurationSampleCount = 10;
23 const double kShortIdlePeriodDurationPercentile = 20;
26 RendererSchedulerImpl::RendererSchedulerImpl(
27 scoped_refptr<SchedulerTaskRunnerDelegate> main_task_runner)
28 : helper_(main_task_runner,
29 "renderer.scheduler",
30 TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
31 TRACE_DISABLED_BY_DEFAULT("renderer.scheduler.debug")),
32 idle_helper_(&helper_,
33 this,
34 "renderer.scheduler",
35 TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
36 "RendererSchedulerIdlePeriod",
37 base::TimeDelta()),
38 control_task_runner_(helper_.ControlTaskRunner()),
39 compositor_task_runner_(
40 helper_.NewTaskQueue(TaskQueue::Spec("compositor_tq")
41 .SetShouldMonitorQuiescence(true))),
42 loading_task_runner_(
43 helper_.NewTaskQueue(TaskQueue::Spec("loading_tq")
44 .SetShouldMonitorQuiescence(true))),
45 timer_task_runner_(
46 helper_.NewTaskQueue(TaskQueue::Spec("timer_tq")
47 .SetShouldMonitorQuiescence(true))),
48 delayed_update_policy_runner_(
49 base::Bind(&RendererSchedulerImpl::UpdatePolicy,
50 base::Unretained(this)),
51 helper_.ControlTaskRunner()),
52 policy_may_need_update_(&any_thread_lock_),
53 weak_factory_(this) {
54 update_policy_closure_ = base::Bind(&RendererSchedulerImpl::UpdatePolicy,
55 weak_factory_.GetWeakPtr());
56 end_renderer_hidden_idle_period_closure_.Reset(base::Bind(
57 &RendererSchedulerImpl::EndIdlePeriod, weak_factory_.GetWeakPtr()));
59 suspend_timers_when_backgrounded_closure_.Reset(
60 base::Bind(&RendererSchedulerImpl::SuspendTimerQueueWhenBackgrounded,
61 weak_factory_.GetWeakPtr()));
63 loading_task_runner_->AddTaskObserver(
64 &MainThreadOnly().loading_task_cost_estimator);
66 timer_task_runner_->AddTaskObserver(
67 &MainThreadOnly().timer_task_cost_estimator);
69 TRACE_EVENT_OBJECT_CREATED_WITH_ID(
70 TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"), "RendererScheduler",
71 this);
73 // Make sure that we don't initially assume there is no idle time.
74 MainThreadOnly().short_idle_period_duration.InsertSample(
75 cc::BeginFrameArgs::DefaultInterval());
78 RendererSchedulerImpl::~RendererSchedulerImpl() {
79 TRACE_EVENT_OBJECT_DELETED_WITH_ID(
80 TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"), "RendererScheduler",
81 this);
82 timer_task_runner_->RemoveTaskObserver(
83 &MainThreadOnly().timer_task_cost_estimator);
84 loading_task_runner_->RemoveTaskObserver(
85 &MainThreadOnly().loading_task_cost_estimator);
86 // Ensure the renderer scheduler was shut down explicitly, because otherwise
87 // we could end up having stale pointers to the Blink heap which has been
88 // terminated by this point.
89 DCHECK(MainThreadOnly().was_shutdown);
92 RendererSchedulerImpl::Policy::Policy()
93 : compositor_queue_priority(TaskQueue::NORMAL_PRIORITY),
94 loading_queue_priority(TaskQueue::NORMAL_PRIORITY),
95 timer_queue_priority(TaskQueue::NORMAL_PRIORITY),
96 default_queue_priority(TaskQueue::NORMAL_PRIORITY) {}
98 RendererSchedulerImpl::MainThreadOnly::MainThreadOnly()
99 : loading_task_cost_estimator(kLoadingTaskEstimationSampleCount,
100 kLoadingTaskEstimationPercentile),
101 timer_task_cost_estimator(kTimerTaskEstimationSampleCount,
102 kTimerTaskEstimationPercentile),
103 short_idle_period_duration(kShortIdlePeriodDurationSampleCount),
104 current_use_case(UseCase::NONE),
105 timer_queue_suspend_count(0),
106 renderer_hidden(false),
107 renderer_backgrounded(false),
108 timer_queue_suspension_when_backgrounded_enabled(false),
109 timer_queue_suspended_when_backgrounded(false),
110 was_shutdown(false),
111 loading_tasks_seem_expensive(false),
112 timer_tasks_seem_expensive(false),
113 touchstart_expected_soon(false),
114 have_seen_a_begin_main_frame(false) {}
116 RendererSchedulerImpl::MainThreadOnly::~MainThreadOnly() {}
118 RendererSchedulerImpl::AnyThread::AnyThread()
119 : awaiting_touch_start_response(false),
120 in_idle_period(false),
121 begin_main_frame_on_critical_path(false) {}
123 RendererSchedulerImpl::CompositorThreadOnly::CompositorThreadOnly()
124 : last_input_type(blink::WebInputEvent::Undefined) {}
126 RendererSchedulerImpl::CompositorThreadOnly::~CompositorThreadOnly() {
129 void RendererSchedulerImpl::Shutdown() {
130 helper_.Shutdown();
131 MainThreadOnly().was_shutdown = true;
134 scoped_refptr<TaskQueue> RendererSchedulerImpl::DefaultTaskRunner() {
135 return helper_.DefaultTaskRunner();
138 scoped_refptr<base::SingleThreadTaskRunner>
139 RendererSchedulerImpl::CompositorTaskRunner() {
140 helper_.CheckOnValidThread();
141 return compositor_task_runner_;
144 scoped_refptr<SingleThreadIdleTaskRunner>
145 RendererSchedulerImpl::IdleTaskRunner() {
146 return idle_helper_.IdleTaskRunner();
149 scoped_refptr<base::SingleThreadTaskRunner>
150 RendererSchedulerImpl::LoadingTaskRunner() {
151 helper_.CheckOnValidThread();
152 return loading_task_runner_;
155 scoped_refptr<TaskQueue> RendererSchedulerImpl::TimerTaskRunner() {
156 helper_.CheckOnValidThread();
157 return timer_task_runner_;
160 bool RendererSchedulerImpl::CanExceedIdleDeadlineIfRequired() const {
161 return idle_helper_.CanExceedIdleDeadlineIfRequired();
164 void RendererSchedulerImpl::AddTaskObserver(
165 base::MessageLoop::TaskObserver* task_observer) {
166 helper_.AddTaskObserver(task_observer);
169 void RendererSchedulerImpl::RemoveTaskObserver(
170 base::MessageLoop::TaskObserver* task_observer) {
171 helper_.RemoveTaskObserver(task_observer);
174 void RendererSchedulerImpl::WillBeginFrame(const cc::BeginFrameArgs& args) {
175 TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
176 "RendererSchedulerImpl::WillBeginFrame", "args", args.AsValue());
177 helper_.CheckOnValidThread();
178 if (helper_.IsShutdown())
179 return;
181 EndIdlePeriod();
182 MainThreadOnly().estimated_next_frame_begin = args.frame_time + args.interval;
183 MainThreadOnly().have_seen_a_begin_main_frame = true;
185 base::AutoLock lock(any_thread_lock_);
186 AnyThread().begin_main_frame_on_critical_path = args.on_critical_path;
190 void RendererSchedulerImpl::DidCommitFrameToCompositor() {
191 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
192 "RendererSchedulerImpl::DidCommitFrameToCompositor");
193 helper_.CheckOnValidThread();
194 if (helper_.IsShutdown())
195 return;
197 base::TimeTicks now(helper_.Now());
198 if (now < MainThreadOnly().estimated_next_frame_begin) {
199 // TODO(rmcilroy): Consider reducing the idle period based on the runtime of
200 // the next pending delayed tasks (as currently done in for long idle times)
201 idle_helper_.StartIdlePeriod(
202 IdleHelper::IdlePeriodState::IN_SHORT_IDLE_PERIOD, now,
203 MainThreadOnly().estimated_next_frame_begin);
204 MainThreadOnly().short_idle_period_duration.InsertSample(
205 MainThreadOnly().estimated_next_frame_begin - now);
206 } else {
207 // There was no idle time :(
208 MainThreadOnly().short_idle_period_duration.InsertSample(base::TimeDelta());
211 MainThreadOnly().expected_short_idle_period_duration =
212 MainThreadOnly().short_idle_period_duration.Percentile(
213 kShortIdlePeriodDurationPercentile);
216 void RendererSchedulerImpl::BeginFrameNotExpectedSoon() {
217 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
218 "RendererSchedulerImpl::BeginFrameNotExpectedSoon");
219 helper_.CheckOnValidThread();
220 if (helper_.IsShutdown())
221 return;
223 idle_helper_.EnableLongIdlePeriod();
226 void RendererSchedulerImpl::OnRendererHidden() {
227 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
228 "RendererSchedulerImpl::OnRendererHidden");
229 helper_.CheckOnValidThread();
230 if (helper_.IsShutdown() || MainThreadOnly().renderer_hidden)
231 return;
233 idle_helper_.EnableLongIdlePeriod();
235 // Ensure that we stop running idle tasks after a few seconds of being hidden.
236 end_renderer_hidden_idle_period_closure_.Cancel();
237 base::TimeDelta end_idle_when_hidden_delay =
238 base::TimeDelta::FromMilliseconds(kEndIdleWhenHiddenDelayMillis);
239 control_task_runner_->PostDelayedTask(
240 FROM_HERE, end_renderer_hidden_idle_period_closure_.callback(),
241 end_idle_when_hidden_delay);
242 MainThreadOnly().renderer_hidden = true;
244 TRACE_EVENT_OBJECT_SNAPSHOT_WITH_ID(
245 TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"), "RendererScheduler",
246 this, AsValue(helper_.Now()));
249 void RendererSchedulerImpl::OnRendererVisible() {
250 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
251 "RendererSchedulerImpl::OnRendererVisible");
252 helper_.CheckOnValidThread();
253 if (helper_.IsShutdown() || !MainThreadOnly().renderer_hidden)
254 return;
256 end_renderer_hidden_idle_period_closure_.Cancel();
257 MainThreadOnly().renderer_hidden = false;
258 EndIdlePeriod();
260 TRACE_EVENT_OBJECT_SNAPSHOT_WITH_ID(
261 TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"), "RendererScheduler",
262 this, AsValue(helper_.Now()));
265 void RendererSchedulerImpl::OnRendererBackgrounded() {
266 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
267 "RendererSchedulerImpl::OnRendererBackgrounded");
268 helper_.CheckOnValidThread();
269 if (helper_.IsShutdown() || MainThreadOnly().renderer_backgrounded)
270 return;
272 MainThreadOnly().renderer_backgrounded = true;
273 if (!MainThreadOnly().timer_queue_suspension_when_backgrounded_enabled)
274 return;
276 suspend_timers_when_backgrounded_closure_.Cancel();
277 base::TimeDelta suspend_timers_when_backgrounded_delay =
278 base::TimeDelta::FromMilliseconds(
279 kSuspendTimersWhenBackgroundedDelayMillis);
280 control_task_runner_->PostDelayedTask(
281 FROM_HERE, suspend_timers_when_backgrounded_closure_.callback(),
282 suspend_timers_when_backgrounded_delay);
285 void RendererSchedulerImpl::OnRendererForegrounded() {
286 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
287 "RendererSchedulerImpl::OnRendererForegrounded");
288 helper_.CheckOnValidThread();
289 if (helper_.IsShutdown() || !MainThreadOnly().renderer_backgrounded)
290 return;
292 MainThreadOnly().renderer_backgrounded = false;
293 suspend_timers_when_backgrounded_closure_.Cancel();
294 ResumeTimerQueueWhenForegrounded();
297 void RendererSchedulerImpl::EndIdlePeriod() {
298 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
299 "RendererSchedulerImpl::EndIdlePeriod");
300 helper_.CheckOnValidThread();
301 idle_helper_.EndIdlePeriod();
304 // static
305 bool RendererSchedulerImpl::ShouldPrioritizeInputEvent(
306 const blink::WebInputEvent& web_input_event) {
307 // We regard MouseMove events with the left mouse button down as a signal
308 // that the user is doing something requiring a smooth frame rate.
309 if (web_input_event.type == blink::WebInputEvent::MouseMove &&
310 (web_input_event.modifiers & blink::WebInputEvent::LeftButtonDown)) {
311 return true;
313 // Ignore all other mouse events because they probably don't signal user
314 // interaction needing a smooth framerate. NOTE isMouseEventType returns false
315 // for mouse wheel events, hence we regard them as user input.
316 // Ignore keyboard events because it doesn't really make sense to enter
317 // compositor priority for them.
318 if (blink::WebInputEvent::isMouseEventType(web_input_event.type) ||
319 blink::WebInputEvent::isKeyboardEventType(web_input_event.type)) {
320 return false;
322 return true;
325 void RendererSchedulerImpl::DidHandleInputEventOnCompositorThread(
326 const blink::WebInputEvent& web_input_event,
327 InputEventState event_state) {
328 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
329 "RendererSchedulerImpl::DidHandleInputEventOnCompositorThread");
330 if (!ShouldPrioritizeInputEvent(web_input_event))
331 return;
333 UpdateForInputEventOnCompositorThread(web_input_event.type, event_state);
336 void RendererSchedulerImpl::DidAnimateForInputOnCompositorThread() {
337 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
338 "RendererSchedulerImpl::DidAnimateForInputOnCompositorThread");
339 UpdateForInputEventOnCompositorThread(
340 blink::WebInputEvent::Undefined,
341 InputEventState::EVENT_CONSUMED_BY_COMPOSITOR);
344 void RendererSchedulerImpl::UpdateForInputEventOnCompositorThread(
345 blink::WebInputEvent::Type type,
346 InputEventState input_event_state) {
347 base::AutoLock lock(any_thread_lock_);
348 base::TimeTicks now = helper_.Now();
350 // TODO(alexclarke): Move WebInputEventTraits where we can access it from here
351 // and record the name rather than the integer representation.
352 TRACE_EVENT2(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
353 "RendererSchedulerImpl::UpdateForInputEventOnCompositorThread",
354 "type", static_cast<int>(type), "input_event_state",
355 InputEventStateToString(input_event_state));
357 bool gesture_already_in_progress = InputSignalsSuggestGestureInProgress(now);
358 bool was_awaiting_touch_start_response =
359 AnyThread().awaiting_touch_start_response;
361 AnyThread().user_model.DidStartProcessingInputEvent(type, now);
363 if (input_event_state == InputEventState::EVENT_CONSUMED_BY_COMPOSITOR)
364 AnyThread().user_model.DidFinishProcessingInputEvent(now);
366 if (type) {
367 switch (type) {
368 case blink::WebInputEvent::TouchStart:
369 AnyThread().awaiting_touch_start_response = true;
370 break;
372 case blink::WebInputEvent::TouchMove:
373 // Observation of consecutive touchmoves is a strong signal that the
374 // page is consuming the touch sequence, in which case touchstart
375 // response prioritization is no longer necessary. Otherwise, the
376 // initial touchmove should preserve the touchstart response pending
377 // state.
378 if (AnyThread().awaiting_touch_start_response &&
379 CompositorThreadOnly().last_input_type ==
380 blink::WebInputEvent::TouchMove) {
381 AnyThread().awaiting_touch_start_response = false;
383 break;
385 case blink::WebInputEvent::Undefined:
386 case blink::WebInputEvent::GestureTapDown:
387 case blink::WebInputEvent::GestureShowPress:
388 case blink::WebInputEvent::GestureFlingCancel:
389 case blink::WebInputEvent::GestureScrollEnd:
390 // With no observable effect, these meta events do not indicate a
391 // meaningful touchstart response and should not impact task priority.
392 break;
394 default:
395 AnyThread().awaiting_touch_start_response = false;
396 break;
400 // Avoid unnecessary policy updates, while a gesture is already in progress.
401 if (!gesture_already_in_progress ||
402 was_awaiting_touch_start_response !=
403 AnyThread().awaiting_touch_start_response) {
404 EnsureUrgentPolicyUpdatePostedOnMainThread(FROM_HERE);
406 CompositorThreadOnly().last_input_type = type;
409 void RendererSchedulerImpl::DidHandleInputEventOnMainThread(
410 const blink::WebInputEvent& web_input_event) {
411 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
412 "RendererSchedulerImpl::DidHandleInputEventOnMainThread");
413 helper_.CheckOnValidThread();
414 if (ShouldPrioritizeInputEvent(web_input_event)) {
415 base::AutoLock lock(any_thread_lock_);
416 AnyThread().user_model.DidFinishProcessingInputEvent(helper_.Now());
420 bool RendererSchedulerImpl::IsHighPriorityWorkAnticipated() {
421 helper_.CheckOnValidThread();
422 if (helper_.IsShutdown())
423 return false;
425 MaybeUpdatePolicy();
426 // The touchstart and main-thread gesture use cases indicate a strong
427 // likelihood of high-priority work in the near future.
428 UseCase use_case = MainThreadOnly().current_use_case;
429 return MainThreadOnly().touchstart_expected_soon ||
430 use_case == UseCase::TOUCHSTART ||
431 use_case == UseCase::MAIN_THREAD_GESTURE;
434 bool RendererSchedulerImpl::ShouldYieldForHighPriorityWork() {
435 helper_.CheckOnValidThread();
436 if (helper_.IsShutdown())
437 return false;
439 MaybeUpdatePolicy();
440 // We only yield if there's a urgent task to be run now, or we are expecting
441 // one soon (touch start).
442 // Note: even though the control queue has the highest priority we don't yield
443 // for it since these tasks are not user-provided work and they are only
444 // intended to run before the next task, not interrupt the tasks.
445 switch (MainThreadOnly().current_use_case) {
446 case UseCase::NONE:
447 return MainThreadOnly().touchstart_expected_soon;
449 case UseCase::COMPOSITOR_GESTURE:
450 return MainThreadOnly().touchstart_expected_soon;
452 case UseCase::MAIN_THREAD_GESTURE:
453 return !compositor_task_runner_->IsQueueEmpty() ||
454 MainThreadOnly().touchstart_expected_soon;
456 case UseCase::TOUCHSTART:
457 return true;
459 case UseCase::LOADING:
460 return false;
462 default:
463 NOTREACHED();
464 return false;
468 base::TimeTicks RendererSchedulerImpl::CurrentIdleTaskDeadlineForTesting()
469 const {
470 return idle_helper_.CurrentIdleTaskDeadline();
473 void RendererSchedulerImpl::MaybeUpdatePolicy() {
474 helper_.CheckOnValidThread();
475 if (policy_may_need_update_.IsSet()) {
476 UpdatePolicy();
480 void RendererSchedulerImpl::EnsureUrgentPolicyUpdatePostedOnMainThread(
481 const tracked_objects::Location& from_here) {
482 // TODO(scheduler-dev): Check that this method isn't called from the main
483 // thread.
484 any_thread_lock_.AssertAcquired();
485 if (!policy_may_need_update_.IsSet()) {
486 policy_may_need_update_.SetWhileLocked(true);
487 control_task_runner_->PostTask(from_here, update_policy_closure_);
491 void RendererSchedulerImpl::UpdatePolicy() {
492 base::AutoLock lock(any_thread_lock_);
493 UpdatePolicyLocked(UpdateType::MAY_EARLY_OUT_IF_POLICY_UNCHANGED);
496 void RendererSchedulerImpl::ForceUpdatePolicy() {
497 base::AutoLock lock(any_thread_lock_);
498 UpdatePolicyLocked(UpdateType::FORCE_UPDATE);
501 void RendererSchedulerImpl::UpdatePolicyLocked(UpdateType update_type) {
502 helper_.CheckOnValidThread();
503 any_thread_lock_.AssertAcquired();
504 if (helper_.IsShutdown())
505 return;
507 base::TimeTicks now = helper_.Now();
508 policy_may_need_update_.SetWhileLocked(false);
510 base::TimeDelta expected_use_case_duration;
511 UseCase use_case = ComputeCurrentUseCase(now, &expected_use_case_duration);
512 MainThreadOnly().current_use_case = use_case;
514 // TODO(alexclarke): We should wire up a signal from blink to let us know if
515 // there are any touch handlers registerd or not, and only call
516 // TouchStartExpectedSoon if there is at least one. NOTE a TouchStart will
517 // only actually get sent if there is a touch handler.
518 base::TimeDelta touchstart_expected_flag_valid_for_duration;
519 bool touchstart_expected_soon = AnyThread().user_model.IsGestureExpectedSoon(
520 use_case, now, &touchstart_expected_flag_valid_for_duration);
521 MainThreadOnly().touchstart_expected_soon = touchstart_expected_soon;
523 bool loading_tasks_seem_expensive =
524 MainThreadOnly().loading_task_cost_estimator.expected_task_duration() >
525 MainThreadOnly().expected_short_idle_period_duration;
526 MainThreadOnly().loading_tasks_seem_expensive = loading_tasks_seem_expensive;
528 bool timer_tasks_seem_expensive =
529 MainThreadOnly().timer_task_cost_estimator.expected_task_duration() >
530 MainThreadOnly().expected_short_idle_period_duration;
531 MainThreadOnly().timer_tasks_seem_expensive = timer_tasks_seem_expensive;
533 // The |new_policy_duration| is the minimum of |expected_use_case_duration|
534 // and |touchstart_expected_flag_valid_for_duration| unless one is zero in
535 // which case we choose the other.
536 base::TimeDelta new_policy_duration = expected_use_case_duration;
537 if (new_policy_duration == base::TimeDelta() ||
538 (touchstart_expected_flag_valid_for_duration > base::TimeDelta() &&
539 new_policy_duration > touchstart_expected_flag_valid_for_duration)) {
540 new_policy_duration = touchstart_expected_flag_valid_for_duration;
543 if (new_policy_duration > base::TimeDelta()) {
544 MainThreadOnly().current_policy_expiration_time = now + new_policy_duration;
545 delayed_update_policy_runner_.SetDeadline(FROM_HERE, new_policy_duration,
546 now);
547 } else {
548 MainThreadOnly().current_policy_expiration_time = base::TimeTicks();
551 Policy new_policy;
552 bool block_expensive_tasks = false;
553 switch (use_case) {
554 case UseCase::COMPOSITOR_GESTURE:
555 if (touchstart_expected_soon) {
556 block_expensive_tasks = true;
557 } else {
558 // What we really want to do is priorize loading tasks, but that doesn't
559 // seem to be safe. Instead we do that by proxy by deprioritizing
560 // compositor tasks. This should be safe since we've already gone to the
561 // pain of fixing ordering issues with them.
562 new_policy.compositor_queue_priority = TaskQueue::BEST_EFFORT_PRIORITY;
564 break;
566 case UseCase::MAIN_THREAD_GESTURE:
567 new_policy.compositor_queue_priority = TaskQueue::HIGH_PRIORITY;
568 block_expensive_tasks = true;
569 break;
571 case UseCase::TOUCHSTART:
572 new_policy.compositor_queue_priority = TaskQueue::HIGH_PRIORITY;
573 new_policy.loading_queue_priority = TaskQueue::DISABLED_PRIORITY;
574 new_policy.timer_queue_priority = TaskQueue::DISABLED_PRIORITY;
575 block_expensive_tasks = true; // NOTE this is a nop due to the above.
576 break;
578 case UseCase::NONE:
579 if (touchstart_expected_soon)
580 block_expensive_tasks = true;
581 break;
583 case UseCase::LOADING:
584 new_policy.loading_queue_priority = TaskQueue::HIGH_PRIORITY;
585 new_policy.default_queue_priority = TaskQueue::HIGH_PRIORITY;
586 break;
588 default:
589 NOTREACHED();
592 // Don't block expensive tasks unless we have actually seen something.
593 if (!MainThreadOnly().have_seen_a_begin_main_frame)
594 block_expensive_tasks = false;
596 if (block_expensive_tasks && loading_tasks_seem_expensive)
597 new_policy.loading_queue_priority = TaskQueue::DISABLED_PRIORITY;
599 if ((block_expensive_tasks && timer_tasks_seem_expensive) ||
600 MainThreadOnly().timer_queue_suspend_count != 0 ||
601 MainThreadOnly().timer_queue_suspended_when_backgrounded) {
602 new_policy.timer_queue_priority = TaskQueue::DISABLED_PRIORITY;
605 // Tracing is done before the early out check, because it's quite possible we
606 // will otherwise miss this information in traces.
607 TRACE_EVENT_OBJECT_SNAPSHOT_WITH_ID(
608 TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"), "RendererScheduler",
609 this, AsValueLocked(now));
610 TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"), "use_case",
611 use_case);
612 TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
613 "RendererScheduler.loading_tasks_seem_expensive",
614 MainThreadOnly().loading_tasks_seem_expensive);
615 TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
616 "RendererScheduler.timer_tasks_seem_expensive",
617 MainThreadOnly().timer_tasks_seem_expensive);
619 if (update_type == UpdateType::MAY_EARLY_OUT_IF_POLICY_UNCHANGED &&
620 new_policy == MainThreadOnly().current_policy) {
621 return;
624 compositor_task_runner_->SetQueuePriority(
625 new_policy.compositor_queue_priority);
626 loading_task_runner_->SetQueuePriority(new_policy.loading_queue_priority);
627 timer_task_runner_->SetQueuePriority(new_policy.timer_queue_priority);
629 // TODO(alexclarke): We shouldn't have to prioritize the default queue, but it
630 // appears to be necessary since the order of loading tasks and IPCs (which
631 // are mostly dispatched on the default queue) need to be preserved.
632 helper_.DefaultTaskRunner()->SetQueuePriority(
633 new_policy.default_queue_priority);
635 DCHECK(compositor_task_runner_->IsQueueEnabled());
636 MainThreadOnly().current_policy = new_policy;
639 bool RendererSchedulerImpl::InputSignalsSuggestGestureInProgress(
640 base::TimeTicks now) const {
641 base::TimeDelta unused_policy_duration;
642 switch (ComputeCurrentUseCase(now, &unused_policy_duration)) {
643 case UseCase::COMPOSITOR_GESTURE:
644 case UseCase::MAIN_THREAD_GESTURE:
645 case UseCase::TOUCHSTART:
646 return true;
648 default:
649 break;
651 return false;
654 RendererSchedulerImpl::UseCase RendererSchedulerImpl::ComputeCurrentUseCase(
655 base::TimeTicks now,
656 base::TimeDelta* expected_use_case_duration) const {
657 any_thread_lock_.AssertAcquired();
658 // Above all else we want to be responsive to user input.
659 *expected_use_case_duration =
660 AnyThread().user_model.TimeLeftInUserGesture(now);
661 if (*expected_use_case_duration > base::TimeDelta()) {
662 // Has scrolling been fully established?
663 if (AnyThread().awaiting_touch_start_response) {
664 // No, so arrange for compositor tasks to be run at the highest priority.
665 return UseCase::TOUCHSTART;
667 // Yes scrolling has been established. If BeginMainFrame is on the critical
668 // path, compositor tasks need to be prioritized, otherwise now might be a
669 // good time to run potentially expensive work.
670 // TODO(skyostil): Consider removing in_idle_period_ and
671 // HadAnIdlePeriodRecently() unless we need them here.
672 if (AnyThread().begin_main_frame_on_critical_path) {
673 return UseCase::MAIN_THREAD_GESTURE;
674 } else {
675 return UseCase::COMPOSITOR_GESTURE;
679 // TODO(alexclarke): return UseCase::LOADING if signals suggest the system is
680 // in the initial 1s of RAIL loading.
682 return UseCase::NONE;
685 bool RendererSchedulerImpl::CanEnterLongIdlePeriod(
686 base::TimeTicks now,
687 base::TimeDelta* next_long_idle_period_delay_out) {
688 helper_.CheckOnValidThread();
690 MaybeUpdatePolicy();
691 if (MainThreadOnly().current_use_case == UseCase::TOUCHSTART) {
692 // Don't start a long idle task in touch start priority, try again when
693 // the policy is scheduled to end.
694 *next_long_idle_period_delay_out =
695 MainThreadOnly().current_policy_expiration_time - now;
696 return false;
698 return true;
701 SchedulerHelper* RendererSchedulerImpl::GetSchedulerHelperForTesting() {
702 return &helper_;
705 TaskCostEstimator*
706 RendererSchedulerImpl::GetLoadingTaskCostEstimatorForTesting() {
707 return &MainThreadOnly().loading_task_cost_estimator;
710 TaskCostEstimator*
711 RendererSchedulerImpl::GetTimerTaskCostEstimatorForTesting() {
712 return &MainThreadOnly().timer_task_cost_estimator;
715 void RendererSchedulerImpl::SuspendTimerQueue() {
716 MainThreadOnly().timer_queue_suspend_count++;
717 ForceUpdatePolicy();
718 DCHECK(!timer_task_runner_->IsQueueEnabled());
721 void RendererSchedulerImpl::ResumeTimerQueue() {
722 MainThreadOnly().timer_queue_suspend_count--;
723 DCHECK_GE(MainThreadOnly().timer_queue_suspend_count, 0);
724 ForceUpdatePolicy();
727 void RendererSchedulerImpl::SetTimerQueueSuspensionWhenBackgroundedEnabled(
728 bool enabled) {
729 // Note that this will only take effect for the next backgrounded signal.
730 MainThreadOnly().timer_queue_suspension_when_backgrounded_enabled = enabled;
733 scoped_refptr<base::trace_event::ConvertableToTraceFormat>
734 RendererSchedulerImpl::AsValue(base::TimeTicks optional_now) const {
735 base::AutoLock lock(any_thread_lock_);
736 return AsValueLocked(optional_now);
739 scoped_refptr<base::trace_event::ConvertableToTraceFormat>
740 RendererSchedulerImpl::AsValueLocked(base::TimeTicks optional_now) const {
741 helper_.CheckOnValidThread();
742 any_thread_lock_.AssertAcquired();
744 if (optional_now.is_null())
745 optional_now = helper_.Now();
746 scoped_refptr<base::trace_event::TracedValue> state =
747 new base::trace_event::TracedValue();
749 state->SetString("current_use_case",
750 UseCaseToString(MainThreadOnly().current_use_case));
751 state->SetBoolean("loading_tasks_seem_expensive",
752 MainThreadOnly().loading_tasks_seem_expensive);
753 state->SetBoolean("timer_tasks_seem_expensive",
754 MainThreadOnly().timer_tasks_seem_expensive);
755 state->SetBoolean("touchstart_expected_soon",
756 MainThreadOnly().touchstart_expected_soon);
757 state->SetString("idle_period_state",
758 IdleHelper::IdlePeriodStateToString(
759 idle_helper_.SchedulerIdlePeriodState()));
760 state->SetBoolean("renderer_hidden", MainThreadOnly().renderer_hidden);
761 state->SetBoolean("renderer_backgrounded",
762 MainThreadOnly().renderer_backgrounded);
763 state->SetBoolean("timer_queue_suspended_when_backgrounded",
764 MainThreadOnly().timer_queue_suspended_when_backgrounded);
765 state->SetInteger("timer_queue_suspend_count",
766 MainThreadOnly().timer_queue_suspend_count);
767 state->SetDouble("now", (optional_now - base::TimeTicks()).InMillisecondsF());
768 state->SetDouble(
769 "rails_loading_priority_deadline",
770 (AnyThread().rails_loading_priority_deadline - base::TimeTicks())
771 .InMillisecondsF());
772 state->SetDouble("last_idle_period_end_time",
773 (AnyThread().last_idle_period_end_time - base::TimeTicks())
774 .InMillisecondsF());
775 state->SetBoolean("awaiting_touch_start_response",
776 AnyThread().awaiting_touch_start_response);
777 state->SetBoolean("begin_main_frame_on_critical_path",
778 AnyThread().begin_main_frame_on_critical_path);
779 state->SetDouble("expected_loading_task_duration",
780 MainThreadOnly()
781 .loading_task_cost_estimator.expected_task_duration()
782 .InMillisecondsF());
783 state->SetDouble("expected_timer_task_duration",
784 MainThreadOnly()
785 .timer_task_cost_estimator.expected_task_duration()
786 .InMillisecondsF());
787 // TODO(skyostil): Can we somehow trace how accurate these estimates were?
788 state->SetDouble(
789 "expected_short_idle_period_duration",
790 MainThreadOnly().expected_short_idle_period_duration.InMillisecondsF());
791 state->SetDouble(
792 "estimated_next_frame_begin",
793 (MainThreadOnly().estimated_next_frame_begin - base::TimeTicks())
794 .InMillisecondsF());
795 state->SetBoolean("in_idle_period", AnyThread().in_idle_period);
796 AnyThread().user_model.AsValueInto(state.get());
798 return state;
801 void RendererSchedulerImpl::OnIdlePeriodStarted() {
802 base::AutoLock lock(any_thread_lock_);
803 AnyThread().in_idle_period = true;
804 UpdatePolicyLocked(UpdateType::MAY_EARLY_OUT_IF_POLICY_UNCHANGED);
807 void RendererSchedulerImpl::OnIdlePeriodEnded() {
808 base::AutoLock lock(any_thread_lock_);
809 AnyThread().last_idle_period_end_time = helper_.Now();
810 AnyThread().in_idle_period = false;
811 UpdatePolicyLocked(UpdateType::MAY_EARLY_OUT_IF_POLICY_UNCHANGED);
814 void RendererSchedulerImpl::OnPageLoadStarted() {
815 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
816 "RendererSchedulerImpl::OnPageLoadStarted");
817 base::AutoLock lock(any_thread_lock_);
818 AnyThread().rails_loading_priority_deadline =
819 helper_.Now() + base::TimeDelta::FromMilliseconds(
820 kRailsInitialLoadingPrioritizationMillis);
821 ResetForNavigationLocked();
824 bool RendererSchedulerImpl::HadAnIdlePeriodRecently(base::TimeTicks now) const {
825 return (now - AnyThread().last_idle_period_end_time) <=
826 base::TimeDelta::FromMilliseconds(
827 kIdlePeriodStarvationThresholdMillis);
830 void RendererSchedulerImpl::SuspendTimerQueueWhenBackgrounded() {
831 DCHECK(MainThreadOnly().renderer_backgrounded);
832 if (MainThreadOnly().timer_queue_suspended_when_backgrounded)
833 return;
835 MainThreadOnly().timer_queue_suspended_when_backgrounded = true;
836 ForceUpdatePolicy();
839 void RendererSchedulerImpl::ResumeTimerQueueWhenForegrounded() {
840 DCHECK(!MainThreadOnly().renderer_backgrounded);
841 if (!MainThreadOnly().timer_queue_suspended_when_backgrounded)
842 return;
844 MainThreadOnly().timer_queue_suspended_when_backgrounded = false;
845 ForceUpdatePolicy();
848 void RendererSchedulerImpl::ResetForNavigationLocked() {
849 helper_.CheckOnValidThread();
850 any_thread_lock_.AssertAcquired();
851 MainThreadOnly().loading_task_cost_estimator.Clear();
852 MainThreadOnly().timer_task_cost_estimator.Clear();
853 MainThreadOnly().short_idle_period_duration.Clear();
854 // Make sure that we don't initially assume there is no idle time.
855 MainThreadOnly().short_idle_period_duration.InsertSample(
856 cc::BeginFrameArgs::DefaultInterval());
857 AnyThread().user_model.Reset();
858 MainThreadOnly().have_seen_a_begin_main_frame = false;
859 UpdatePolicyLocked(UpdateType::MAY_EARLY_OUT_IF_POLICY_UNCHANGED);
862 } // namespace scheduler