| 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 |