OLD | NEW |
| (Empty) |
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. | |
4 | |
5 #include "content/renderer/scheduler/renderer_scheduler_impl.h" | |
6 | |
7 #include "base/bind.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/child/scheduler/nestable_single_thread_task_runner.h" | |
13 #include "content/child/scheduler/prioritizing_task_queue_selector.h" | |
14 #include "ui/gfx/frame_time.h" | |
15 | |
16 namespace content { | |
17 | |
18 RendererSchedulerImpl::RendererSchedulerImpl( | |
19 scoped_refptr<NestableSingleThreadTaskRunner> main_task_runner) | |
20 : helper_(main_task_runner, | |
21 this, | |
22 "renderer.scheduler", | |
23 TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"), | |
24 "RendererSchedulerIdlePeriod", | |
25 TASK_QUEUE_COUNT, | |
26 base::TimeDelta()), | |
27 control_task_runner_(helper_.ControlTaskRunner()), | |
28 compositor_task_runner_( | |
29 helper_.TaskRunnerForQueue(COMPOSITOR_TASK_QUEUE)), | |
30 loading_task_runner_(helper_.TaskRunnerForQueue(LOADING_TASK_QUEUE)), | |
31 timer_task_runner_(helper_.TaskRunnerForQueue(TIMER_TASK_QUEUE)), | |
32 delayed_update_policy_runner_( | |
33 base::Bind(&RendererSchedulerImpl::UpdatePolicy, | |
34 base::Unretained(this)), | |
35 helper_.ControlTaskRunner()), | |
36 current_policy_(Policy::NORMAL), | |
37 renderer_hidden_(false), | |
38 last_input_type_(blink::WebInputEvent::Undefined), | |
39 input_stream_state_(InputStreamState::INACTIVE), | |
40 policy_may_need_update_(&incoming_signals_lock_), | |
41 timer_queue_suspend_count_(0), | |
42 weak_factory_(this) { | |
43 update_policy_closure_ = base::Bind(&RendererSchedulerImpl::UpdatePolicy, | |
44 weak_factory_.GetWeakPtr()); | |
45 end_renderer_hidden_idle_period_closure_.Reset(base::Bind( | |
46 &RendererSchedulerImpl::EndIdlePeriod, weak_factory_.GetWeakPtr())); | |
47 | |
48 for (size_t i = SchedulerHelper::TASK_QUEUE_COUNT; | |
49 i < TASK_QUEUE_COUNT; | |
50 i++) { | |
51 helper_.SetQueueName(i, TaskQueueIdToString(static_cast<QueueId>(i))); | |
52 } | |
53 TRACE_EVENT_OBJECT_CREATED_WITH_ID( | |
54 TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"), "RendererScheduler", | |
55 this); | |
56 } | |
57 | |
58 RendererSchedulerImpl::~RendererSchedulerImpl() { | |
59 TRACE_EVENT_OBJECT_DELETED_WITH_ID( | |
60 TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"), "RendererScheduler", | |
61 this); | |
62 } | |
63 | |
64 void RendererSchedulerImpl::Shutdown() { | |
65 helper_.Shutdown(); | |
66 } | |
67 | |
68 scoped_refptr<base::SingleThreadTaskRunner> | |
69 RendererSchedulerImpl::DefaultTaskRunner() { | |
70 return helper_.DefaultTaskRunner(); | |
71 } | |
72 | |
73 scoped_refptr<base::SingleThreadTaskRunner> | |
74 RendererSchedulerImpl::CompositorTaskRunner() { | |
75 helper_.CheckOnValidThread(); | |
76 return compositor_task_runner_; | |
77 } | |
78 | |
79 scoped_refptr<SingleThreadIdleTaskRunner> | |
80 RendererSchedulerImpl::IdleTaskRunner() { | |
81 return helper_.IdleTaskRunner(); | |
82 } | |
83 | |
84 scoped_refptr<base::SingleThreadTaskRunner> | |
85 RendererSchedulerImpl::LoadingTaskRunner() { | |
86 helper_.CheckOnValidThread(); | |
87 return loading_task_runner_; | |
88 } | |
89 | |
90 scoped_refptr<base::SingleThreadTaskRunner> | |
91 RendererSchedulerImpl::TimerTaskRunner() { | |
92 helper_.CheckOnValidThread(); | |
93 return timer_task_runner_; | |
94 } | |
95 | |
96 bool RendererSchedulerImpl::CanExceedIdleDeadlineIfRequired() const { | |
97 return helper_.CanExceedIdleDeadlineIfRequired(); | |
98 } | |
99 | |
100 void RendererSchedulerImpl::AddTaskObserver( | |
101 base::MessageLoop::TaskObserver* task_observer) { | |
102 helper_.AddTaskObserver(task_observer); | |
103 } | |
104 | |
105 void RendererSchedulerImpl::RemoveTaskObserver( | |
106 base::MessageLoop::TaskObserver* task_observer) { | |
107 helper_.RemoveTaskObserver(task_observer); | |
108 } | |
109 | |
110 void RendererSchedulerImpl::WillBeginFrame(const cc::BeginFrameArgs& args) { | |
111 TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"), | |
112 "RendererSchedulerImpl::WillBeginFrame", "args", args.AsValue()); | |
113 helper_.CheckOnValidThread(); | |
114 if (helper_.IsShutdown()) | |
115 return; | |
116 | |
117 EndIdlePeriod(); | |
118 estimated_next_frame_begin_ = args.frame_time + args.interval; | |
119 // TODO(skyostil): Wire up real notification of input events processing | |
120 // instead of this approximation. | |
121 DidProcessInputEvent(args.frame_time); | |
122 } | |
123 | |
124 void RendererSchedulerImpl::DidCommitFrameToCompositor() { | |
125 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"), | |
126 "RendererSchedulerImpl::DidCommitFrameToCompositor"); | |
127 helper_.CheckOnValidThread(); | |
128 if (helper_.IsShutdown()) | |
129 return; | |
130 | |
131 base::TimeTicks now(helper_.Now()); | |
132 if (now < estimated_next_frame_begin_) { | |
133 // TODO(rmcilroy): Consider reducing the idle period based on the runtime of | |
134 // the next pending delayed tasks (as currently done in for long idle times) | |
135 helper_.StartIdlePeriod( | |
136 SchedulerHelper::IdlePeriodState::IN_SHORT_IDLE_PERIOD, | |
137 now, | |
138 estimated_next_frame_begin_, | |
139 true); | |
140 } | |
141 } | |
142 | |
143 void RendererSchedulerImpl::BeginFrameNotExpectedSoon() { | |
144 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"), | |
145 "RendererSchedulerImpl::BeginFrameNotExpectedSoon"); | |
146 helper_.CheckOnValidThread(); | |
147 if (helper_.IsShutdown()) | |
148 return; | |
149 | |
150 // TODO(skyostil): Wire up real notification of input events processing | |
151 // instead of this approximation. | |
152 DidProcessInputEvent(base::TimeTicks()); | |
153 | |
154 helper_.EnableLongIdlePeriod(); | |
155 } | |
156 | |
157 void RendererSchedulerImpl::OnRendererHidden() { | |
158 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"), | |
159 "RendererSchedulerImpl::OnRendererHidden"); | |
160 helper_.CheckOnValidThread(); | |
161 if (helper_.IsShutdown() || renderer_hidden_) | |
162 return; | |
163 | |
164 helper_.EnableLongIdlePeriod(); | |
165 | |
166 // Ensure that we stop running idle tasks after a few seconds of being hidden. | |
167 end_renderer_hidden_idle_period_closure_.Cancel(); | |
168 base::TimeDelta end_idle_when_hidden_delay = | |
169 base::TimeDelta::FromMilliseconds(kEndIdleWhenHiddenDelayMillis); | |
170 control_task_runner_->PostDelayedTask( | |
171 FROM_HERE, | |
172 end_renderer_hidden_idle_period_closure_.callback(), | |
173 end_idle_when_hidden_delay); | |
174 renderer_hidden_ = true; | |
175 | |
176 TRACE_EVENT_OBJECT_SNAPSHOT_WITH_ID( | |
177 TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"), "RendererScheduler", | |
178 this, AsValueLocked(helper_.Now())); | |
179 } | |
180 | |
181 void RendererSchedulerImpl::OnRendererVisible() { | |
182 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"), | |
183 "RendererSchedulerImpl::OnRendererVisible"); | |
184 helper_.CheckOnValidThread(); | |
185 if (helper_.IsShutdown() || !renderer_hidden_) | |
186 return; | |
187 | |
188 end_renderer_hidden_idle_period_closure_.Cancel(); | |
189 renderer_hidden_ = false; | |
190 EndIdlePeriod(); | |
191 | |
192 TRACE_EVENT_OBJECT_SNAPSHOT_WITH_ID( | |
193 TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"), "RendererScheduler", | |
194 this, AsValueLocked(helper_.Now())); | |
195 } | |
196 | |
197 void RendererSchedulerImpl::EndIdlePeriod() { | |
198 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"), | |
199 "RendererSchedulerImpl::EndIdlePeriod"); | |
200 helper_.CheckOnValidThread(); | |
201 helper_.EndIdlePeriod(); | |
202 } | |
203 | |
204 void RendererSchedulerImpl::DidReceiveInputEventOnCompositorThread( | |
205 const blink::WebInputEvent& web_input_event) { | |
206 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"), | |
207 "RendererSchedulerImpl::DidReceiveInputEventOnCompositorThread"); | |
208 // We regard MouseMove events with the left mouse button down as a signal | |
209 // that the user is doing something requiring a smooth frame rate. | |
210 if (web_input_event.type == blink::WebInputEvent::MouseMove && | |
211 (web_input_event.modifiers & blink::WebInputEvent::LeftButtonDown)) { | |
212 UpdateForInputEvent(web_input_event.type); | |
213 return; | |
214 } | |
215 // Ignore all other mouse events because they probably don't signal user | |
216 // interaction needing a smooth framerate. NOTE isMouseEventType returns false | |
217 // for mouse wheel events, hence we regard them as user input. | |
218 // Ignore keyboard events because it doesn't really make sense to enter | |
219 // compositor priority for them. | |
220 if (blink::WebInputEvent::isMouseEventType(web_input_event.type) || | |
221 blink::WebInputEvent::isKeyboardEventType(web_input_event.type)) { | |
222 return; | |
223 } | |
224 UpdateForInputEvent(web_input_event.type); | |
225 } | |
226 | |
227 void RendererSchedulerImpl::DidAnimateForInputOnCompositorThread() { | |
228 UpdateForInputEvent(blink::WebInputEvent::Undefined); | |
229 } | |
230 | |
231 void RendererSchedulerImpl::UpdateForInputEvent( | |
232 blink::WebInputEvent::Type type) { | |
233 base::AutoLock lock(incoming_signals_lock_); | |
234 | |
235 InputStreamState new_input_stream_state = | |
236 ComputeNewInputStreamState(input_stream_state_, type, last_input_type_); | |
237 | |
238 if (input_stream_state_ != new_input_stream_state) { | |
239 // Update scheduler policy if we should start a new policy mode. | |
240 input_stream_state_ = new_input_stream_state; | |
241 EnsureUrgentPolicyUpdatePostedOnMainThread(FROM_HERE); | |
242 } | |
243 last_input_receipt_time_on_compositor_ = helper_.Now(); | |
244 // Clear the last known input processing time so that we know an input event | |
245 // is still queued up. This timestamp will be updated the next time the | |
246 // compositor commits or becomes quiescent. Note that this function is always | |
247 // called before the input event is processed either on the compositor or | |
248 // main threads. | |
249 last_input_process_time_on_main_ = base::TimeTicks(); | |
250 last_input_type_ = type; | |
251 } | |
252 | |
253 void RendererSchedulerImpl::DidProcessInputEvent( | |
254 base::TimeTicks begin_frame_time) { | |
255 helper_.CheckOnValidThread(); | |
256 base::AutoLock lock(incoming_signals_lock_); | |
257 if (input_stream_state_ == InputStreamState::INACTIVE) | |
258 return; | |
259 // Avoid marking input that arrived after the BeginFrame as processed. | |
260 if (!begin_frame_time.is_null() && | |
261 begin_frame_time < last_input_receipt_time_on_compositor_) | |
262 return; | |
263 last_input_process_time_on_main_ = helper_.Now(); | |
264 UpdatePolicyLocked(UpdateType::MAY_EARLY_OUT_IF_POLICY_UNCHANGED); | |
265 } | |
266 | |
267 bool RendererSchedulerImpl::IsHighPriorityWorkAnticipated() { | |
268 helper_.CheckOnValidThread(); | |
269 if (helper_.IsShutdown()) | |
270 return false; | |
271 | |
272 MaybeUpdatePolicy(); | |
273 // The touchstart and compositor policies indicate a strong likelihood of | |
274 // high-priority work in the near future. | |
275 return SchedulerPolicy() == Policy::COMPOSITOR_PRIORITY || | |
276 SchedulerPolicy() == Policy::TOUCHSTART_PRIORITY; | |
277 } | |
278 | |
279 bool RendererSchedulerImpl::ShouldYieldForHighPriorityWork() { | |
280 helper_.CheckOnValidThread(); | |
281 if (helper_.IsShutdown()) | |
282 return false; | |
283 | |
284 MaybeUpdatePolicy(); | |
285 // We only yield if we are in the compositor priority and there is compositor | |
286 // work outstanding, or if we are in the touchstart response priority. | |
287 // Note: even though the control queue is higher priority we don't yield for | |
288 // it since these tasks are not user-provided work and they are only intended | |
289 // to run before the next task, not interrupt the tasks. | |
290 switch (SchedulerPolicy()) { | |
291 case Policy::NORMAL: | |
292 return false; | |
293 | |
294 case Policy::COMPOSITOR_PRIORITY: | |
295 return !helper_.IsQueueEmpty(COMPOSITOR_TASK_QUEUE); | |
296 | |
297 case Policy::TOUCHSTART_PRIORITY: | |
298 return true; | |
299 | |
300 default: | |
301 NOTREACHED(); | |
302 return false; | |
303 } | |
304 } | |
305 | |
306 base::TimeTicks | |
307 RendererSchedulerImpl::CurrentIdleTaskDeadlineForTesting() const { | |
308 base::TimeTicks deadline; | |
309 helper_.CurrentIdleTaskDeadlineCallback(&deadline); | |
310 return deadline; | |
311 } | |
312 | |
313 RendererSchedulerImpl::Policy RendererSchedulerImpl::SchedulerPolicy() const { | |
314 helper_.CheckOnValidThread(); | |
315 return current_policy_; | |
316 } | |
317 | |
318 void RendererSchedulerImpl::MaybeUpdatePolicy() { | |
319 helper_.CheckOnValidThread(); | |
320 if (policy_may_need_update_.IsSet()) { | |
321 UpdatePolicy(); | |
322 } | |
323 } | |
324 | |
325 void RendererSchedulerImpl::EnsureUrgentPolicyUpdatePostedOnMainThread( | |
326 const tracked_objects::Location& from_here) { | |
327 // TODO(scheduler-dev): Check that this method isn't called from the main | |
328 // thread. | |
329 incoming_signals_lock_.AssertAcquired(); | |
330 if (!policy_may_need_update_.IsSet()) { | |
331 policy_may_need_update_.SetWhileLocked(true); | |
332 control_task_runner_->PostTask(from_here, update_policy_closure_); | |
333 } | |
334 } | |
335 | |
336 void RendererSchedulerImpl::UpdatePolicy() { | |
337 base::AutoLock lock(incoming_signals_lock_); | |
338 UpdatePolicyLocked(UpdateType::MAY_EARLY_OUT_IF_POLICY_UNCHANGED); | |
339 } | |
340 | |
341 void RendererSchedulerImpl::ForceUpdatePolicy() { | |
342 base::AutoLock lock(incoming_signals_lock_); | |
343 UpdatePolicyLocked(UpdateType::FORCE_UPDATE); | |
344 } | |
345 | |
346 void RendererSchedulerImpl::UpdatePolicyLocked(UpdateType update_type) { | |
347 helper_.CheckOnValidThread(); | |
348 incoming_signals_lock_.AssertAcquired(); | |
349 if (helper_.IsShutdown()) | |
350 return; | |
351 | |
352 base::TimeTicks now = helper_.Now(); | |
353 policy_may_need_update_.SetWhileLocked(false); | |
354 | |
355 base::TimeDelta new_policy_duration; | |
356 Policy new_policy = ComputeNewPolicy(now, &new_policy_duration); | |
357 if (new_policy_duration > base::TimeDelta()) { | |
358 current_policy_expiration_time_ = now + new_policy_duration; | |
359 delayed_update_policy_runner_.SetDeadline(FROM_HERE, new_policy_duration, | |
360 now); | |
361 } else { | |
362 current_policy_expiration_time_ = base::TimeTicks(); | |
363 } | |
364 | |
365 if (update_type == UpdateType::MAY_EARLY_OUT_IF_POLICY_UNCHANGED && | |
366 new_policy == current_policy_) | |
367 return; | |
368 | |
369 PrioritizingTaskQueueSelector* task_queue_selector = | |
370 helper_.SchedulerTaskQueueSelector(); | |
371 bool policy_disables_timers = false; | |
372 | |
373 switch (new_policy) { | |
374 case Policy::COMPOSITOR_PRIORITY: | |
375 task_queue_selector->SetQueuePriority( | |
376 COMPOSITOR_TASK_QUEUE, PrioritizingTaskQueueSelector::HIGH_PRIORITY); | |
377 // TODO(scheduler-dev): Add a task priority between HIGH and BEST_EFFORT | |
378 // that still has some guarantee of running. | |
379 task_queue_selector->SetQueuePriority( | |
380 LOADING_TASK_QUEUE, | |
381 PrioritizingTaskQueueSelector::BEST_EFFORT_PRIORITY); | |
382 break; | |
383 case Policy::TOUCHSTART_PRIORITY: | |
384 task_queue_selector->SetQueuePriority( | |
385 COMPOSITOR_TASK_QUEUE, PrioritizingTaskQueueSelector::HIGH_PRIORITY); | |
386 task_queue_selector->DisableQueue(LOADING_TASK_QUEUE); | |
387 // TODO(alexclarke): Set policy_disables_timers once the blink TimerBase | |
388 // refactor is safely landed. | |
389 break; | |
390 case Policy::NORMAL: | |
391 task_queue_selector->SetQueuePriority( | |
392 COMPOSITOR_TASK_QUEUE, | |
393 PrioritizingTaskQueueSelector::NORMAL_PRIORITY); | |
394 task_queue_selector->SetQueuePriority( | |
395 LOADING_TASK_QUEUE, PrioritizingTaskQueueSelector::NORMAL_PRIORITY); | |
396 break; | |
397 } | |
398 if (timer_queue_suspend_count_ != 0 || policy_disables_timers) { | |
399 task_queue_selector->DisableQueue(TIMER_TASK_QUEUE); | |
400 } else { | |
401 helper_.SchedulerTaskQueueSelector()->SetQueuePriority( | |
402 TIMER_TASK_QUEUE, PrioritizingTaskQueueSelector::NORMAL_PRIORITY); | |
403 } | |
404 DCHECK(task_queue_selector->IsQueueEnabled(COMPOSITOR_TASK_QUEUE)); | |
405 if (new_policy != Policy::TOUCHSTART_PRIORITY) | |
406 DCHECK(task_queue_selector->IsQueueEnabled(LOADING_TASK_QUEUE)); | |
407 | |
408 current_policy_ = new_policy; | |
409 | |
410 TRACE_EVENT_OBJECT_SNAPSHOT_WITH_ID( | |
411 TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"), "RendererScheduler", | |
412 this, AsValueLocked(now)); | |
413 TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"), | |
414 "RendererScheduler.policy", current_policy_); | |
415 } | |
416 | |
417 RendererSchedulerImpl::Policy RendererSchedulerImpl::ComputeNewPolicy( | |
418 base::TimeTicks now, | |
419 base::TimeDelta* new_policy_duration) { | |
420 helper_.CheckOnValidThread(); | |
421 incoming_signals_lock_.AssertAcquired(); | |
422 | |
423 Policy new_policy = Policy::NORMAL; | |
424 *new_policy_duration = base::TimeDelta(); | |
425 | |
426 if (input_stream_state_ == InputStreamState::INACTIVE) | |
427 return new_policy; | |
428 | |
429 Policy input_priority_policy = | |
430 input_stream_state_ == | |
431 InputStreamState::ACTIVE_AND_AWAITING_TOUCHSTART_RESPONSE | |
432 ? Policy::TOUCHSTART_PRIORITY | |
433 : Policy::COMPOSITOR_PRIORITY; | |
434 base::TimeDelta time_left_in_policy = TimeLeftInInputEscalatedPolicy(now); | |
435 if (time_left_in_policy > base::TimeDelta()) { | |
436 new_policy = input_priority_policy; | |
437 *new_policy_duration = time_left_in_policy; | |
438 } else { | |
439 // Reset |input_stream_state_| to ensure | |
440 // DidReceiveInputEventOnCompositorThread will post an UpdatePolicy task | |
441 // when it's next called. | |
442 input_stream_state_ = InputStreamState::INACTIVE; | |
443 } | |
444 return new_policy; | |
445 } | |
446 | |
447 base::TimeDelta RendererSchedulerImpl::TimeLeftInInputEscalatedPolicy( | |
448 base::TimeTicks now) const { | |
449 helper_.CheckOnValidThread(); | |
450 // TODO(rmcilroy): Change this to DCHECK_EQ when crbug.com/463869 is fixed. | |
451 DCHECK(input_stream_state_ != InputStreamState::INACTIVE); | |
452 incoming_signals_lock_.AssertAcquired(); | |
453 | |
454 base::TimeDelta escalated_priority_duration = | |
455 base::TimeDelta::FromMilliseconds(kPriorityEscalationAfterInputMillis); | |
456 base::TimeDelta time_left_in_policy; | |
457 if (last_input_process_time_on_main_.is_null() && | |
458 !helper_.IsQueueEmpty(COMPOSITOR_TASK_QUEUE)) { | |
459 // If the input event is still pending, go into input prioritized policy | |
460 // and check again later. | |
461 time_left_in_policy = escalated_priority_duration; | |
462 } else { | |
463 // Otherwise make sure the input prioritization policy ends on time. | |
464 base::TimeTicks new_priority_end( | |
465 std::max(last_input_receipt_time_on_compositor_, | |
466 last_input_process_time_on_main_) + | |
467 escalated_priority_duration); | |
468 time_left_in_policy = new_priority_end - now; | |
469 } | |
470 return time_left_in_policy; | |
471 } | |
472 | |
473 bool RendererSchedulerImpl::CanEnterLongIdlePeriod( | |
474 base::TimeTicks now, | |
475 base::TimeDelta* next_long_idle_period_delay_out) { | |
476 helper_.CheckOnValidThread(); | |
477 | |
478 MaybeUpdatePolicy(); | |
479 if (SchedulerPolicy() == Policy::TOUCHSTART_PRIORITY) { | |
480 // Don't start a long idle task in touch start priority, try again when | |
481 // the policy is scheduled to end. | |
482 *next_long_idle_period_delay_out = current_policy_expiration_time_ - now; | |
483 return false; | |
484 } | |
485 return true; | |
486 } | |
487 | |
488 SchedulerHelper* RendererSchedulerImpl::GetSchedulerHelperForTesting() { | |
489 return &helper_; | |
490 } | |
491 | |
492 void RendererSchedulerImpl::SetWorkBatchSizeForTesting(size_t work_batch_size) { | |
493 helper_.SetWorkBatchSizeForTesting(work_batch_size); | |
494 } | |
495 | |
496 RendererSchedulerImpl::PollableNeedsUpdateFlag::PollableNeedsUpdateFlag( | |
497 base::Lock* write_lock_) | |
498 : flag_(false), write_lock_(write_lock_) { | |
499 } | |
500 | |
501 RendererSchedulerImpl::PollableNeedsUpdateFlag::~PollableNeedsUpdateFlag() { | |
502 } | |
503 | |
504 void RendererSchedulerImpl::PollableNeedsUpdateFlag::SetWhileLocked( | |
505 bool value) { | |
506 write_lock_->AssertAcquired(); | |
507 base::subtle::Release_Store(&flag_, value); | |
508 } | |
509 | |
510 bool RendererSchedulerImpl::PollableNeedsUpdateFlag::IsSet() const { | |
511 return base::subtle::Acquire_Load(&flag_) != 0; | |
512 } | |
513 | |
514 void RendererSchedulerImpl::SuspendTimerQueue() { | |
515 helper_.CheckOnValidThread(); | |
516 timer_queue_suspend_count_++; | |
517 ForceUpdatePolicy(); | |
518 DCHECK(!helper_.SchedulerTaskQueueSelector()->IsQueueEnabled( | |
519 TIMER_TASK_QUEUE)); | |
520 } | |
521 | |
522 void RendererSchedulerImpl::ResumeTimerQueue() { | |
523 helper_.CheckOnValidThread(); | |
524 timer_queue_suspend_count_--; | |
525 DCHECK_GE(timer_queue_suspend_count_, 0); | |
526 ForceUpdatePolicy(); | |
527 } | |
528 | |
529 // static | |
530 const char* RendererSchedulerImpl::TaskQueueIdToString(QueueId queue_id) { | |
531 switch (queue_id) { | |
532 case COMPOSITOR_TASK_QUEUE: | |
533 return "compositor_tq"; | |
534 case LOADING_TASK_QUEUE: | |
535 return "loading_tq"; | |
536 case TIMER_TASK_QUEUE: | |
537 return "timer_tq"; | |
538 default: | |
539 return SchedulerHelper::TaskQueueIdToString( | |
540 static_cast<SchedulerHelper::QueueId>(queue_id)); | |
541 } | |
542 } | |
543 | |
544 // static | |
545 const char* RendererSchedulerImpl::PolicyToString(Policy policy) { | |
546 switch (policy) { | |
547 case Policy::NORMAL: | |
548 return "normal"; | |
549 case Policy::COMPOSITOR_PRIORITY: | |
550 return "compositor"; | |
551 case Policy::TOUCHSTART_PRIORITY: | |
552 return "touchstart"; | |
553 default: | |
554 NOTREACHED(); | |
555 return nullptr; | |
556 } | |
557 } | |
558 | |
559 const char* RendererSchedulerImpl::InputStreamStateToString( | |
560 InputStreamState state) { | |
561 switch (state) { | |
562 case InputStreamState::INACTIVE: | |
563 return "inactive"; | |
564 case InputStreamState::ACTIVE: | |
565 return "active"; | |
566 case InputStreamState::ACTIVE_AND_AWAITING_TOUCHSTART_RESPONSE: | |
567 return "active_and_awaiting_touchstart_response"; | |
568 default: | |
569 NOTREACHED(); | |
570 return nullptr; | |
571 } | |
572 } | |
573 | |
574 scoped_refptr<base::trace_event::ConvertableToTraceFormat> | |
575 RendererSchedulerImpl::AsValueLocked(base::TimeTicks optional_now) const { | |
576 helper_.CheckOnValidThread(); | |
577 incoming_signals_lock_.AssertAcquired(); | |
578 | |
579 if (optional_now.is_null()) | |
580 optional_now = helper_.Now(); | |
581 scoped_refptr<base::trace_event::TracedValue> state = | |
582 new base::trace_event::TracedValue(); | |
583 | |
584 state->SetString("current_policy", PolicyToString(current_policy_)); | |
585 state->SetString("idle_period_state", | |
586 SchedulerHelper::IdlePeriodStateToString( | |
587 helper_.SchedulerIdlePeriodState())); | |
588 state->SetBoolean("renderer_hidden_", renderer_hidden_); | |
589 state->SetString("input_stream_state", | |
590 InputStreamStateToString(input_stream_state_)); | |
591 state->SetDouble("now", (optional_now - base::TimeTicks()).InMillisecondsF()); | |
592 state->SetDouble("last_input_receipt_time_on_compositor_", | |
593 (last_input_receipt_time_on_compositor_ - base::TimeTicks()) | |
594 .InMillisecondsF()); | |
595 state->SetDouble( | |
596 "last_input_process_time_on_main_", | |
597 (last_input_process_time_on_main_ - base::TimeTicks()).InMillisecondsF()); | |
598 state->SetDouble( | |
599 "estimated_next_frame_begin", | |
600 (estimated_next_frame_begin_ - base::TimeTicks()).InMillisecondsF()); | |
601 | |
602 return state; | |
603 } | |
604 | |
605 RendererSchedulerImpl::InputStreamState | |
606 RendererSchedulerImpl::ComputeNewInputStreamState( | |
607 InputStreamState current_state, | |
608 blink::WebInputEvent::Type new_input_type, | |
609 blink::WebInputEvent::Type last_input_type) { | |
610 switch (new_input_type) { | |
611 case blink::WebInputEvent::TouchStart: | |
612 return InputStreamState::ACTIVE_AND_AWAITING_TOUCHSTART_RESPONSE; | |
613 | |
614 case blink::WebInputEvent::TouchMove: | |
615 // Observation of consecutive touchmoves is a strong signal that the page | |
616 // is consuming the touch sequence, in which case touchstart response | |
617 // prioritization is no longer necessary. Otherwise, the initial touchmove | |
618 // should preserve the touchstart response pending state. | |
619 if (current_state == | |
620 InputStreamState::ACTIVE_AND_AWAITING_TOUCHSTART_RESPONSE) { | |
621 return last_input_type == blink::WebInputEvent::TouchMove | |
622 ? InputStreamState::ACTIVE | |
623 : InputStreamState::ACTIVE_AND_AWAITING_TOUCHSTART_RESPONSE; | |
624 } | |
625 break; | |
626 | |
627 case blink::WebInputEvent::GestureTapDown: | |
628 case blink::WebInputEvent::GestureShowPress: | |
629 case blink::WebInputEvent::GestureFlingCancel: | |
630 case blink::WebInputEvent::GestureScrollEnd: | |
631 // With no observable effect, these meta events do not indicate a | |
632 // meaningful touchstart response and should not impact task priority. | |
633 return current_state; | |
634 | |
635 default: | |
636 break; | |
637 } | |
638 return InputStreamState::ACTIVE; | |
639 } | |
640 | |
641 } // namespace content | |
OLD | NEW |