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 "content/renderer/scheduler/renderer_scheduler_impl.h"
8 #include "base/message_loop/message_loop_proxy.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 "content/renderer/scheduler/renderer_task_queue_selector.h"
13 #include "ui/gfx/frame_time.h"
17 RendererSchedulerImpl::RendererSchedulerImpl(
18 scoped_refptr
<base::SingleThreadTaskRunner
> main_task_runner
)
19 : renderer_task_queue_selector_(new RendererTaskQueueSelector()),
21 new TaskQueueManager(TASK_QUEUE_COUNT
,
23 renderer_task_queue_selector_
.get())),
25 task_queue_manager_
->TaskRunnerForQueue(CONTROL_TASK_QUEUE
)),
27 task_queue_manager_
->TaskRunnerForQueue(DEFAULT_TASK_QUEUE
)),
28 compositor_task_runner_(
29 task_queue_manager_
->TaskRunnerForQueue(COMPOSITOR_TASK_QUEUE
)),
31 task_queue_manager_
->TaskRunnerForQueue(LOADING_TASK_QUEUE
)),
32 current_policy_(NORMAL_PRIORITY_POLICY
),
33 policy_may_need_update_(&incoming_signals_lock_
),
35 weak_renderer_scheduler_ptr_
= weak_factory_
.GetWeakPtr();
36 update_policy_closure_
= base::Bind(&RendererSchedulerImpl::UpdatePolicy
,
37 weak_renderer_scheduler_ptr_
);
38 end_idle_period_closure_
.Reset(base::Bind(
39 &RendererSchedulerImpl::EndIdlePeriod
, weak_renderer_scheduler_ptr_
));
40 idle_task_runner_
= make_scoped_refptr(new SingleThreadIdleTaskRunner(
41 task_queue_manager_
->TaskRunnerForQueue(IDLE_TASK_QUEUE
),
42 base::Bind(&RendererSchedulerImpl::CurrentIdleTaskDeadlineCallback
,
43 weak_renderer_scheduler_ptr_
)));
44 renderer_task_queue_selector_
->SetQueuePriority(
45 CONTROL_TASK_QUEUE
, RendererTaskQueueSelector::CONTROL_PRIORITY
);
46 renderer_task_queue_selector_
->DisableQueue(IDLE_TASK_QUEUE
);
47 task_queue_manager_
->SetAutoPump(IDLE_TASK_QUEUE
, false);
48 // TODO(skyostil): Increase this to 4 (crbug.com/444764).
49 task_queue_manager_
->SetWorkBatchSize(1);
51 for (size_t i
= 0; i
< TASK_QUEUE_COUNT
; i
++) {
52 task_queue_manager_
->SetQueueName(
53 i
, TaskQueueIdToString(static_cast<QueueId
>(i
)));
55 TRACE_EVENT_OBJECT_CREATED_WITH_ID(
56 TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"), "RendererScheduler",
60 RendererSchedulerImpl::~RendererSchedulerImpl() {
61 TRACE_EVENT_OBJECT_DELETED_WITH_ID(
62 TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"), "RendererScheduler",
66 void RendererSchedulerImpl::Shutdown() {
67 main_thread_checker_
.CalledOnValidThread();
68 task_queue_manager_
.reset();
71 scoped_refptr
<base::SingleThreadTaskRunner
>
72 RendererSchedulerImpl::DefaultTaskRunner() {
73 main_thread_checker_
.CalledOnValidThread();
74 return default_task_runner_
;
77 scoped_refptr
<base::SingleThreadTaskRunner
>
78 RendererSchedulerImpl::CompositorTaskRunner() {
79 main_thread_checker_
.CalledOnValidThread();
80 return compositor_task_runner_
;
83 scoped_refptr
<SingleThreadIdleTaskRunner
>
84 RendererSchedulerImpl::IdleTaskRunner() {
85 main_thread_checker_
.CalledOnValidThread();
86 return idle_task_runner_
;
89 scoped_refptr
<base::SingleThreadTaskRunner
>
90 RendererSchedulerImpl::LoadingTaskRunner() {
91 main_thread_checker_
.CalledOnValidThread();
92 return loading_task_runner_
;
95 void RendererSchedulerImpl::WillBeginFrame(const cc::BeginFrameArgs
& args
) {
96 TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
97 "RendererSchedulerImpl::WillBeginFrame", "args", args
.AsValue());
98 main_thread_checker_
.CalledOnValidThread();
99 if (!task_queue_manager_
)
103 estimated_next_frame_begin_
= args
.frame_time
+ args
.interval
;
106 void RendererSchedulerImpl::DidCommitFrameToCompositor() {
107 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
108 "RendererSchedulerImpl::DidCommitFrameToCompositor");
109 main_thread_checker_
.CalledOnValidThread();
110 if (!task_queue_manager_
)
113 base::TimeTicks
now(Now());
114 if (now
< estimated_next_frame_begin_
) {
116 control_task_runner_
->PostDelayedTask(FROM_HERE
,
117 end_idle_period_closure_
.callback(),
118 estimated_next_frame_begin_
- now
);
122 void RendererSchedulerImpl::DidReceiveInputEventOnCompositorThread(
123 blink::WebInputEvent::Type type
) {
124 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
125 "RendererSchedulerImpl::DidReceiveInputEventOnCompositorThread");
126 // Ignore mouse events because on windows these can very frequent.
127 // Ignore keyboard events because it doesn't really make sense to enter
128 // compositor priority for them.
129 if (blink::WebInputEvent::isMouseEventType(type
) ||
130 blink::WebInputEvent::isKeyboardEventType(type
)) {
133 UpdateForInputEvent();
136 void RendererSchedulerImpl::DidAnimateForInputOnCompositorThread() {
137 UpdateForInputEvent();
140 void RendererSchedulerImpl::UpdateForInputEvent() {
141 base::AutoLock
lock(incoming_signals_lock_
);
142 if (last_input_time_
.is_null()) {
143 // Update scheduler policy if should start a new compositor policy mode.
144 policy_may_need_update_
.SetLocked(true);
145 PostUpdatePolicyOnControlRunner(base::TimeDelta());
147 last_input_time_
= Now();
150 bool RendererSchedulerImpl::ShouldYieldForHighPriorityWork() {
151 main_thread_checker_
.CalledOnValidThread();
152 if (!task_queue_manager_
)
156 // We only yield if we are in the compositor priority and there is compositor
157 // work outstanding. Note: even though the control queue is higher priority
158 // we don't yield for it since these tasks are not user-provided work and they
159 // are only intended to run before the next task, not interrupt the tasks.
160 return SchedulerPolicy() == COMPOSITOR_PRIORITY_POLICY
&&
161 !task_queue_manager_
->IsQueueEmpty(COMPOSITOR_TASK_QUEUE
);
164 void RendererSchedulerImpl::CurrentIdleTaskDeadlineCallback(
165 base::TimeTicks
* deadline_out
) const {
166 main_thread_checker_
.CalledOnValidThread();
167 *deadline_out
= estimated_next_frame_begin_
;
170 RendererSchedulerImpl::Policy
RendererSchedulerImpl::SchedulerPolicy() const {
171 main_thread_checker_
.CalledOnValidThread();
172 return current_policy_
;
175 void RendererSchedulerImpl::MaybeUpdatePolicy() {
176 main_thread_checker_
.CalledOnValidThread();
177 if (policy_may_need_update_
.IsSet()) {
182 void RendererSchedulerImpl::PostUpdatePolicyOnControlRunner(
183 base::TimeDelta delay
) {
184 control_task_runner_
->PostDelayedTask(
185 FROM_HERE
, update_policy_closure_
, delay
);
188 void RendererSchedulerImpl::UpdatePolicy() {
189 main_thread_checker_
.CalledOnValidThread();
190 if (!task_queue_manager_
)
193 base::AutoLock
lock(incoming_signals_lock_
);
195 policy_may_need_update_
.SetLocked(false);
197 Policy new_policy
= NORMAL_PRIORITY_POLICY
;
198 if (!last_input_time_
.is_null()) {
199 base::TimeDelta compositor_priority_duration
=
200 base::TimeDelta::FromMilliseconds(kCompositorPriorityAfterTouchMillis
);
201 base::TimeTicks
compositor_priority_end(last_input_time_
+
202 compositor_priority_duration
);
204 if (compositor_priority_end
> now
) {
205 PostUpdatePolicyOnControlRunner(compositor_priority_end
- now
);
206 new_policy
= COMPOSITOR_PRIORITY_POLICY
;
208 // Null out last_input_time_ to ensure
209 // DidReceiveInputEventOnCompositorThread will post an
210 // UpdatePolicy task when it's next called.
211 last_input_time_
= base::TimeTicks();
215 if (new_policy
== current_policy_
)
218 switch (new_policy
) {
219 case COMPOSITOR_PRIORITY_POLICY
:
220 renderer_task_queue_selector_
->SetQueuePriority(
221 COMPOSITOR_TASK_QUEUE
, RendererTaskQueueSelector::HIGH_PRIORITY
);
222 // TODO(scheduler-dev): Add a task priority between HIGH and BEST_EFFORT
223 // that still has some guarantee of running.
224 renderer_task_queue_selector_
->SetQueuePriority(
225 LOADING_TASK_QUEUE
, RendererTaskQueueSelector::BEST_EFFORT_PRIORITY
);
227 case NORMAL_PRIORITY_POLICY
:
228 renderer_task_queue_selector_
->SetQueuePriority(
229 COMPOSITOR_TASK_QUEUE
, RendererTaskQueueSelector::NORMAL_PRIORITY
);
230 renderer_task_queue_selector_
->SetQueuePriority(
231 LOADING_TASK_QUEUE
, RendererTaskQueueSelector::NORMAL_PRIORITY
);
234 current_policy_
= new_policy
;
236 TRACE_EVENT_OBJECT_SNAPSHOT_WITH_ID(
237 TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"), "RendererScheduler",
238 this, AsValueLocked(now
));
239 TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
240 "RendererScheduler.policy", current_policy_
);
243 void RendererSchedulerImpl::StartIdlePeriod() {
244 TRACE_EVENT_ASYNC_BEGIN0("renderer.scheduler",
245 "RendererSchedulerIdlePeriod", this);
246 main_thread_checker_
.CalledOnValidThread();
247 renderer_task_queue_selector_
->EnableQueue(
248 IDLE_TASK_QUEUE
, RendererTaskQueueSelector::BEST_EFFORT_PRIORITY
);
249 task_queue_manager_
->PumpQueue(IDLE_TASK_QUEUE
);
252 void RendererSchedulerImpl::EndIdlePeriod() {
253 TRACE_EVENT_ASYNC_END0("renderer.scheduler",
254 "RendererSchedulerIdlePeriod", this);
255 main_thread_checker_
.CalledOnValidThread();
256 end_idle_period_closure_
.Cancel();
257 renderer_task_queue_selector_
->DisableQueue(IDLE_TASK_QUEUE
);
260 void RendererSchedulerImpl::SetTimeSourceForTesting(
261 scoped_refptr
<cc::TestNowSource
> time_source
) {
262 main_thread_checker_
.CalledOnValidThread();
263 time_source_
= time_source
;
264 task_queue_manager_
->SetTimeSourceForTesting(time_source
);
267 base::TimeTicks
RendererSchedulerImpl::Now() const {
268 return UNLIKELY(time_source_
) ? time_source_
->Now() : base::TimeTicks::Now();
271 RendererSchedulerImpl::PollableNeedsUpdateFlag::PollableNeedsUpdateFlag(
272 base::Lock
* write_lock_
)
273 : flag_(false), write_lock_(write_lock_
) {
276 RendererSchedulerImpl::PollableNeedsUpdateFlag::~PollableNeedsUpdateFlag() {
279 void RendererSchedulerImpl::PollableNeedsUpdateFlag::SetLocked(bool value
) {
280 write_lock_
->AssertAcquired();
281 base::subtle::Release_Store(&flag_
, value
);
284 bool RendererSchedulerImpl::PollableNeedsUpdateFlag::IsSet() const {
285 thread_checker_
.CalledOnValidThread();
286 return base::subtle::Acquire_Load(&flag_
) != 0;
290 const char* RendererSchedulerImpl::TaskQueueIdToString(QueueId queue_id
) {
292 case DEFAULT_TASK_QUEUE
:
294 case COMPOSITOR_TASK_QUEUE
:
295 return "compositor_tq";
296 case IDLE_TASK_QUEUE
:
298 case CONTROL_TASK_QUEUE
:
300 case LOADING_TASK_QUEUE
:
309 const char* RendererSchedulerImpl::PolicyToString(Policy policy
) {
311 case NORMAL_PRIORITY_POLICY
:
313 case COMPOSITOR_PRIORITY_POLICY
:
321 scoped_refptr
<base::debug::ConvertableToTraceFormat
>
322 RendererSchedulerImpl::AsValueLocked(base::TimeTicks optional_now
) const {
323 main_thread_checker_
.CalledOnValidThread();
324 incoming_signals_lock_
.AssertAcquired();
326 if (optional_now
.is_null())
327 optional_now
= Now();
328 scoped_refptr
<base::debug::TracedValue
> state
=
329 new base::debug::TracedValue();
331 state
->SetString("current_policy", PolicyToString(current_policy_
));
332 state
->SetDouble("now", (optional_now
- base::TimeTicks()).InMillisecondsF());
333 state
->SetDouble("last_input_time",
334 (last_input_time_
- base::TimeTicks()).InMillisecondsF());
336 "estimated_next_frame_begin",
337 (estimated_next_frame_begin_
- base::TimeTicks()).InMillisecondsF());
342 } // namespace content