| Index: components/scheduler/renderer/renderer_scheduler_impl.cc
|
| diff --git a/components/scheduler/renderer/renderer_scheduler_impl.cc b/components/scheduler/renderer/renderer_scheduler_impl.cc
|
| index e58e62cbe7cc5d9b598cfa4ae59b03c91b51df50..f05fc4994421f2659ea92e318bd8790098ae1f19 100644
|
| --- a/components/scheduler/renderer/renderer_scheduler_impl.cc
|
| +++ b/components/scheduler/renderer/renderer_scheduler_impl.cc
|
| @@ -15,10 +15,8 @@
|
|
|
| namespace scheduler {
|
| namespace {
|
| -const int kLoadingTaskEstimationSampleCount = 200;
|
| -const double kLoadingTaskEstimationPercentile = 90;
|
| -const int kTimerTaskEstimationSampleCount = 200;
|
| -const double kTimerTaskEstimationPercentile = 90;
|
| +const int kTimerTaskEstimationSampleCount = 4 * 60;
|
| +const double kTimerTaskEstimationPercentile = 80;
|
| const int kShortIdlePeriodDurationSampleCount = 10;
|
| const double kShortIdlePeriodDurationPercentile = 20;
|
| }
|
| @@ -60,19 +58,12 @@
|
| base::Bind(&RendererSchedulerImpl::SuspendTimerQueueWhenBackgrounded,
|
| weak_factory_.GetWeakPtr()));
|
|
|
| - loading_task_runner_->AddTaskObserver(
|
| - &MainThreadOnly().loading_task_cost_estimator);
|
| -
|
| timer_task_runner_->AddTaskObserver(
|
| - &MainThreadOnly().timer_task_cost_estimator);
|
| + &MainThreadOnly().timer_task_cost_estimator_);
|
|
|
| TRACE_EVENT_OBJECT_CREATED_WITH_ID(
|
| TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"), "RendererScheduler",
|
| this);
|
| -
|
| - // Make sure that we don't initially assume there is no idle time.
|
| - MainThreadOnly().short_idle_period_duration.InsertSample(
|
| - cc::BeginFrameArgs::DefaultInterval());
|
| }
|
|
|
| RendererSchedulerImpl::~RendererSchedulerImpl() {
|
| @@ -80,55 +71,44 @@
|
| TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"), "RendererScheduler",
|
| this);
|
| timer_task_runner_->RemoveTaskObserver(
|
| - &MainThreadOnly().timer_task_cost_estimator);
|
| - loading_task_runner_->RemoveTaskObserver(
|
| - &MainThreadOnly().loading_task_cost_estimator);
|
| + &MainThreadOnly().timer_task_cost_estimator_);
|
| // Ensure the renderer scheduler was shut down explicitly, because otherwise
|
| // we could end up having stale pointers to the Blink heap which has been
|
| // terminated by this point.
|
| - DCHECK(MainThreadOnly().was_shutdown);
|
| -}
|
| -
|
| -RendererSchedulerImpl::Policy::Policy()
|
| - : compositor_queue_priority(TaskQueue::NORMAL_PRIORITY),
|
| - loading_queue_priority(TaskQueue::NORMAL_PRIORITY),
|
| - timer_queue_priority(TaskQueue::NORMAL_PRIORITY),
|
| - default_queue_priority(TaskQueue::NORMAL_PRIORITY) {}
|
| + DCHECK(MainThreadOnly().was_shutdown_);
|
| +}
|
|
|
| RendererSchedulerImpl::MainThreadOnly::MainThreadOnly()
|
| - : loading_task_cost_estimator(kLoadingTaskEstimationSampleCount,
|
| - kLoadingTaskEstimationPercentile),
|
| - timer_task_cost_estimator(kTimerTaskEstimationSampleCount,
|
| - kTimerTaskEstimationPercentile),
|
| - short_idle_period_duration(kShortIdlePeriodDurationSampleCount),
|
| - current_use_case(UseCase::NONE),
|
| - timer_queue_suspend_count(0),
|
| - renderer_hidden(false),
|
| - renderer_backgrounded(false),
|
| - timer_queue_suspension_when_backgrounded_enabled(false),
|
| - timer_queue_suspended_when_backgrounded(false),
|
| - was_shutdown(false),
|
| - loading_tasks_seem_expensive(false),
|
| - timer_tasks_seem_expensive(false),
|
| - touchstart_expected_soon(false),
|
| - have_seen_a_begin_main_frame(false) {}
|
| + : timer_task_cost_estimator_(kTimerTaskEstimationSampleCount,
|
| + kTimerTaskEstimationPercentile),
|
| + short_idle_period_duration_(kShortIdlePeriodDurationSampleCount),
|
| + current_policy_(Policy::NORMAL),
|
| + timer_queue_suspend_count_(0),
|
| + renderer_hidden_(false),
|
| + renderer_backgrounded_(false),
|
| + timer_queue_suspension_when_backgrounded_enabled_(false),
|
| + timer_queue_suspended_when_backgrounded_(false),
|
| + was_shutdown_(false) {}
|
|
|
| RendererSchedulerImpl::MainThreadOnly::~MainThreadOnly() {}
|
|
|
| RendererSchedulerImpl::AnyThread::AnyThread()
|
| - : awaiting_touch_start_response(false),
|
| - in_idle_period(false),
|
| - begin_main_frame_on_critical_path(false) {}
|
| + : pending_main_thread_input_event_count_(0),
|
| + awaiting_touch_start_response_(false),
|
| + in_idle_period_(false),
|
| + begin_main_frame_on_critical_path_(false),
|
| + timer_tasks_seem_expensive_(false) {}
|
|
|
| RendererSchedulerImpl::CompositorThreadOnly::CompositorThreadOnly()
|
| - : last_input_type(blink::WebInputEvent::Undefined) {}
|
| + : last_input_type_(blink::WebInputEvent::Undefined) {
|
| +}
|
|
|
| RendererSchedulerImpl::CompositorThreadOnly::~CompositorThreadOnly() {
|
| }
|
|
|
| void RendererSchedulerImpl::Shutdown() {
|
| helper_.Shutdown();
|
| - MainThreadOnly().was_shutdown = true;
|
| + MainThreadOnly().was_shutdown_ = true;
|
| }
|
|
|
| scoped_refptr<TaskQueue> RendererSchedulerImpl::DefaultTaskRunner() {
|
| @@ -179,11 +159,11 @@
|
| return;
|
|
|
| EndIdlePeriod();
|
| - MainThreadOnly().estimated_next_frame_begin = args.frame_time + args.interval;
|
| - MainThreadOnly().have_seen_a_begin_main_frame = true;
|
| + MainThreadOnly().estimated_next_frame_begin_ =
|
| + args.frame_time + args.interval;
|
| {
|
| base::AutoLock lock(any_thread_lock_);
|
| - AnyThread().begin_main_frame_on_critical_path = args.on_critical_path;
|
| + AnyThread().begin_main_frame_on_critical_path_ = args.on_critical_path;
|
| }
|
| }
|
|
|
| @@ -195,22 +175,18 @@
|
| return;
|
|
|
| base::TimeTicks now(helper_.Now());
|
| - if (now < MainThreadOnly().estimated_next_frame_begin) {
|
| + if (now < MainThreadOnly().estimated_next_frame_begin_) {
|
| // TODO(rmcilroy): Consider reducing the idle period based on the runtime of
|
| // the next pending delayed tasks (as currently done in for long idle times)
|
| idle_helper_.StartIdlePeriod(
|
| IdleHelper::IdlePeriodState::IN_SHORT_IDLE_PERIOD, now,
|
| - MainThreadOnly().estimated_next_frame_begin);
|
| - MainThreadOnly().short_idle_period_duration.InsertSample(
|
| - MainThreadOnly().estimated_next_frame_begin - now);
|
| - } else {
|
| - // There was no idle time :(
|
| - MainThreadOnly().short_idle_period_duration.InsertSample(base::TimeDelta());
|
| - }
|
| -
|
| - MainThreadOnly().expected_short_idle_period_duration =
|
| - MainThreadOnly().short_idle_period_duration.Percentile(
|
| - kShortIdlePeriodDurationPercentile);
|
| + MainThreadOnly().estimated_next_frame_begin_);
|
| + MainThreadOnly().short_idle_period_duration_.InsertSample(
|
| + MainThreadOnly().estimated_next_frame_begin_ - now);
|
| + MainThreadOnly().expected_short_idle_period_duration_ =
|
| + MainThreadOnly().short_idle_period_duration_.Percentile(
|
| + kShortIdlePeriodDurationPercentile);
|
| + }
|
| }
|
|
|
| void RendererSchedulerImpl::BeginFrameNotExpectedSoon() {
|
| @@ -227,7 +203,7 @@
|
| TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
|
| "RendererSchedulerImpl::OnRendererHidden");
|
| helper_.CheckOnValidThread();
|
| - if (helper_.IsShutdown() || MainThreadOnly().renderer_hidden)
|
| + if (helper_.IsShutdown() || MainThreadOnly().renderer_hidden_)
|
| return;
|
|
|
| idle_helper_.EnableLongIdlePeriod();
|
| @@ -239,7 +215,7 @@
|
| control_task_runner_->PostDelayedTask(
|
| FROM_HERE, end_renderer_hidden_idle_period_closure_.callback(),
|
| end_idle_when_hidden_delay);
|
| - MainThreadOnly().renderer_hidden = true;
|
| + MainThreadOnly().renderer_hidden_ = true;
|
|
|
| TRACE_EVENT_OBJECT_SNAPSHOT_WITH_ID(
|
| TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"), "RendererScheduler",
|
| @@ -250,11 +226,11 @@
|
| TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
|
| "RendererSchedulerImpl::OnRendererVisible");
|
| helper_.CheckOnValidThread();
|
| - if (helper_.IsShutdown() || !MainThreadOnly().renderer_hidden)
|
| + if (helper_.IsShutdown() || !MainThreadOnly().renderer_hidden_)
|
| return;
|
|
|
| end_renderer_hidden_idle_period_closure_.Cancel();
|
| - MainThreadOnly().renderer_hidden = false;
|
| + MainThreadOnly().renderer_hidden_ = false;
|
| EndIdlePeriod();
|
|
|
| TRACE_EVENT_OBJECT_SNAPSHOT_WITH_ID(
|
| @@ -266,11 +242,11 @@
|
| TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
|
| "RendererSchedulerImpl::OnRendererBackgrounded");
|
| helper_.CheckOnValidThread();
|
| - if (helper_.IsShutdown() || MainThreadOnly().renderer_backgrounded)
|
| - return;
|
| -
|
| - MainThreadOnly().renderer_backgrounded = true;
|
| - if (!MainThreadOnly().timer_queue_suspension_when_backgrounded_enabled)
|
| + if (helper_.IsShutdown() || MainThreadOnly().renderer_backgrounded_)
|
| + return;
|
| +
|
| + MainThreadOnly().renderer_backgrounded_ = true;
|
| + if (!MainThreadOnly().timer_queue_suspension_when_backgrounded_enabled_)
|
| return;
|
|
|
| suspend_timers_when_backgrounded_closure_.Cancel();
|
| @@ -286,10 +262,10 @@
|
| TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
|
| "RendererSchedulerImpl::OnRendererForegrounded");
|
| helper_.CheckOnValidThread();
|
| - if (helper_.IsShutdown() || !MainThreadOnly().renderer_backgrounded)
|
| - return;
|
| -
|
| - MainThreadOnly().renderer_backgrounded = false;
|
| + if (helper_.IsShutdown() || !MainThreadOnly().renderer_backgrounded_)
|
| + return;
|
| +
|
| + MainThreadOnly().renderer_backgrounded_ = false;
|
| suspend_timers_when_backgrounded_closure_.Cancel();
|
| ResumeTimerQueueWhenForegrounded();
|
| }
|
| @@ -346,27 +322,14 @@
|
| InputEventState input_event_state) {
|
| base::AutoLock lock(any_thread_lock_);
|
| base::TimeTicks now = helper_.Now();
|
| -
|
| - // TODO(alexclarke): Move WebInputEventTraits where we can access it from here
|
| - // and record the name rather than the integer representation.
|
| - TRACE_EVENT2(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
|
| - "RendererSchedulerImpl::UpdateForInputEventOnCompositorThread",
|
| - "type", static_cast<int>(type), "input_event_state",
|
| - InputEventStateToString(input_event_state));
|
| -
|
| - bool gesture_already_in_progress = InputSignalsSuggestGestureInProgress(now);
|
| + bool was_in_compositor_priority = InputSignalsSuggestCompositorPriority(now);
|
| bool was_awaiting_touch_start_response =
|
| - AnyThread().awaiting_touch_start_response;
|
| -
|
| - AnyThread().user_model.DidStartProcessingInputEvent(type, now);
|
| -
|
| - if (input_event_state == InputEventState::EVENT_CONSUMED_BY_COMPOSITOR)
|
| - AnyThread().user_model.DidFinishProcessingInputEvent(now);
|
| + AnyThread().awaiting_touch_start_response_;
|
|
|
| if (type) {
|
| switch (type) {
|
| case blink::WebInputEvent::TouchStart:
|
| - AnyThread().awaiting_touch_start_response = true;
|
| + AnyThread().awaiting_touch_start_response_ = true;
|
| break;
|
|
|
| case blink::WebInputEvent::TouchMove:
|
| @@ -375,10 +338,10 @@
|
| // response prioritization is no longer necessary. Otherwise, the
|
| // initial touchmove should preserve the touchstart response pending
|
| // state.
|
| - if (AnyThread().awaiting_touch_start_response &&
|
| - CompositorThreadOnly().last_input_type ==
|
| + if (AnyThread().awaiting_touch_start_response_ &&
|
| + CompositorThreadOnly().last_input_type_ ==
|
| blink::WebInputEvent::TouchMove) {
|
| - AnyThread().awaiting_touch_start_response = false;
|
| + AnyThread().awaiting_touch_start_response_ = false;
|
| }
|
| break;
|
|
|
| @@ -392,18 +355,22 @@
|
| break;
|
|
|
| default:
|
| - AnyThread().awaiting_touch_start_response = false;
|
| + AnyThread().awaiting_touch_start_response_ = false;
|
| break;
|
| }
|
| }
|
|
|
| - // Avoid unnecessary policy updates, while a gesture is already in progress.
|
| - if (!gesture_already_in_progress ||
|
| + // Avoid unnecessary policy updates, while in compositor priority.
|
| + if (!was_in_compositor_priority ||
|
| was_awaiting_touch_start_response !=
|
| - AnyThread().awaiting_touch_start_response) {
|
| + AnyThread().awaiting_touch_start_response_) {
|
| EnsureUrgentPolicyUpdatePostedOnMainThread(FROM_HERE);
|
| }
|
| - CompositorThreadOnly().last_input_type = type;
|
| + AnyThread().last_input_signal_time_ = now;
|
| + CompositorThreadOnly().last_input_type_ = type;
|
| +
|
| + if (input_event_state == InputEventState::EVENT_FORWARDED_TO_MAIN_THREAD)
|
| + AnyThread().pending_main_thread_input_event_count_++;
|
| }
|
|
|
| void RendererSchedulerImpl::DidHandleInputEventOnMainThread(
|
| @@ -413,7 +380,9 @@
|
| helper_.CheckOnValidThread();
|
| if (ShouldPrioritizeInputEvent(web_input_event)) {
|
| base::AutoLock lock(any_thread_lock_);
|
| - AnyThread().user_model.DidFinishProcessingInputEvent(helper_.Now());
|
| + AnyThread().last_input_signal_time_ = helper_.Now();
|
| + if (AnyThread().pending_main_thread_input_event_count_ > 0)
|
| + AnyThread().pending_main_thread_input_event_count_--;
|
| }
|
| }
|
|
|
| @@ -423,12 +392,12 @@
|
| return false;
|
|
|
| MaybeUpdatePolicy();
|
| - // The touchstart and main-thread gesture use cases indicate a strong
|
| - // likelihood of high-priority work in the near future.
|
| - UseCase use_case = MainThreadOnly().current_use_case;
|
| - return MainThreadOnly().touchstart_expected_soon ||
|
| - use_case == UseCase::TOUCHSTART ||
|
| - use_case == UseCase::MAIN_THREAD_GESTURE;
|
| + // The touchstart and compositor policies indicate a strong likelihood of
|
| + // high-priority work in the near future.
|
| + return MainThreadOnly().current_policy_ == Policy::COMPOSITOR_PRIORITY ||
|
| + MainThreadOnly().current_policy_ ==
|
| + Policy::COMPOSITOR_CRITICAL_PATH_PRIORITY ||
|
| + MainThreadOnly().current_policy_ == Policy::TOUCHSTART_PRIORITY;
|
| }
|
|
|
| bool RendererSchedulerImpl::ShouldYieldForHighPriorityWork() {
|
| @@ -437,26 +406,25 @@
|
| return false;
|
|
|
| MaybeUpdatePolicy();
|
| - // We only yield if there's a urgent task to be run now, or we are expecting
|
| - // one soon (touch start).
|
| - // Note: even though the control queue has the highest priority we don't yield
|
| - // for it since these tasks are not user-provided work and they are only
|
| - // intended to run before the next task, not interrupt the tasks.
|
| - switch (MainThreadOnly().current_use_case) {
|
| - case UseCase::NONE:
|
| - return MainThreadOnly().touchstart_expected_soon;
|
| -
|
| - case UseCase::COMPOSITOR_GESTURE:
|
| - return MainThreadOnly().touchstart_expected_soon;
|
| -
|
| - case UseCase::MAIN_THREAD_GESTURE:
|
| - return !compositor_task_runner_->IsQueueEmpty() ||
|
| - MainThreadOnly().touchstart_expected_soon;
|
| -
|
| - case UseCase::TOUCHSTART:
|
| + // We only yield if we are in the compositor priority and there is compositor
|
| + // work outstanding, or if we are in the touchstart response priority.
|
| + // Note: even though the control queue is higher priority we don't yield for
|
| + // it since these tasks are not user-provided work and they are only intended
|
| + // to run before the next task, not interrupt the tasks.
|
| + switch (MainThreadOnly().current_policy_) {
|
| + case Policy::NORMAL:
|
| + return false;
|
| +
|
| + case Policy::COMPOSITOR_PRIORITY:
|
| + return !compositor_task_runner_->IsQueueEmpty();
|
| +
|
| + case Policy::COMPOSITOR_CRITICAL_PATH_PRIORITY:
|
| + return !compositor_task_runner_->IsQueueEmpty();
|
| +
|
| + case Policy::TOUCHSTART_PRIORITY:
|
| return true;
|
|
|
| - case UseCase::LOADING:
|
| + case Policy::LOADING_PRIORITY:
|
| return false;
|
|
|
| default:
|
| @@ -507,142 +475,92 @@
|
| base::TimeTicks now = helper_.Now();
|
| policy_may_need_update_.SetWhileLocked(false);
|
|
|
| - base::TimeDelta expected_use_case_duration;
|
| - UseCase use_case = ComputeCurrentUseCase(now, &expected_use_case_duration);
|
| - MainThreadOnly().current_use_case = use_case;
|
| -
|
| - // TODO(alexclarke): We should wire up a signal from blink to let us know if
|
| - // there are any touch handlers registerd or not, and only call
|
| - // TouchStartExpectedSoon if there is at least one. NOTE a TouchStart will
|
| - // only actually get sent if there is a touch handler.
|
| - base::TimeDelta touchstart_expected_flag_valid_for_duration;
|
| - bool touchstart_expected_soon = AnyThread().user_model.IsGestureExpectedSoon(
|
| - use_case, now, &touchstart_expected_flag_valid_for_duration);
|
| - MainThreadOnly().touchstart_expected_soon = touchstart_expected_soon;
|
| -
|
| - bool loading_tasks_seem_expensive =
|
| - MainThreadOnly().loading_task_cost_estimator.expected_task_duration() >
|
| - MainThreadOnly().expected_short_idle_period_duration;
|
| - MainThreadOnly().loading_tasks_seem_expensive = loading_tasks_seem_expensive;
|
| -
|
| - bool timer_tasks_seem_expensive =
|
| - MainThreadOnly().timer_task_cost_estimator.expected_task_duration() >
|
| - MainThreadOnly().expected_short_idle_period_duration;
|
| - MainThreadOnly().timer_tasks_seem_expensive = timer_tasks_seem_expensive;
|
| -
|
| - // The |new_policy_duration| is the minimum of |expected_use_case_duration|
|
| - // and |touchstart_expected_flag_valid_for_duration| unless one is zero in
|
| - // which case we choose the other.
|
| - base::TimeDelta new_policy_duration = expected_use_case_duration;
|
| - if (new_policy_duration == base::TimeDelta() ||
|
| - (touchstart_expected_flag_valid_for_duration > base::TimeDelta() &&
|
| - new_policy_duration > touchstart_expected_flag_valid_for_duration)) {
|
| - new_policy_duration = touchstart_expected_flag_valid_for_duration;
|
| - }
|
| -
|
| + AnyThread().timer_tasks_seem_expensive_ =
|
| + MainThreadOnly().expected_short_idle_period_duration_ >
|
| + base::TimeDelta() &&
|
| + MainThreadOnly().timer_task_cost_estimator_.expected_task_duration() >
|
| + MainThreadOnly().expected_short_idle_period_duration_;
|
| +
|
| + base::TimeDelta new_policy_duration;
|
| + Policy new_policy = ComputeNewPolicy(now, &new_policy_duration);
|
| if (new_policy_duration > base::TimeDelta()) {
|
| - MainThreadOnly().current_policy_expiration_time = now + new_policy_duration;
|
| + MainThreadOnly().current_policy_expiration_time_ =
|
| + now + new_policy_duration;
|
| delayed_update_policy_runner_.SetDeadline(FROM_HERE, new_policy_duration,
|
| now);
|
| } else {
|
| - MainThreadOnly().current_policy_expiration_time = base::TimeTicks();
|
| - }
|
| -
|
| - Policy new_policy;
|
| - bool block_expensive_tasks = false;
|
| - switch (use_case) {
|
| - case UseCase::COMPOSITOR_GESTURE:
|
| - if (touchstart_expected_soon) {
|
| - block_expensive_tasks = true;
|
| - } else {
|
| - // What we really want to do is priorize loading tasks, but that doesn't
|
| - // seem to be safe. Instead we do that by proxy by deprioritizing
|
| - // compositor tasks. This should be safe since we've already gone to the
|
| - // pain of fixing ordering issues with them.
|
| - new_policy.compositor_queue_priority = TaskQueue::BEST_EFFORT_PRIORITY;
|
| - }
|
| + MainThreadOnly().current_policy_expiration_time_ = base::TimeTicks();
|
| + }
|
| +
|
| + if (update_type == UpdateType::MAY_EARLY_OUT_IF_POLICY_UNCHANGED &&
|
| + new_policy == MainThreadOnly().current_policy_)
|
| + return;
|
| +
|
| + TaskQueue::QueuePriority compositor_queue_priority =
|
| + TaskQueue::NORMAL_PRIORITY;
|
| + TaskQueue::QueuePriority loading_queue_priority = TaskQueue::NORMAL_PRIORITY;
|
| + TaskQueue::QueuePriority timer_queue_priority = TaskQueue::NORMAL_PRIORITY;
|
| +
|
| + if (MainThreadOnly().timer_queue_suspend_count_ != 0 ||
|
| + MainThreadOnly().timer_queue_suspended_when_backgrounded_) {
|
| + timer_queue_priority = TaskQueue::DISABLED_PRIORITY;
|
| + }
|
| +
|
| + switch (new_policy) {
|
| + case Policy::COMPOSITOR_PRIORITY:
|
| + compositor_queue_priority = TaskQueue::HIGH_PRIORITY;
|
| break;
|
| -
|
| - case UseCase::MAIN_THREAD_GESTURE:
|
| - new_policy.compositor_queue_priority = TaskQueue::HIGH_PRIORITY;
|
| - block_expensive_tasks = true;
|
| + case Policy::COMPOSITOR_CRITICAL_PATH_PRIORITY:
|
| + compositor_queue_priority = TaskQueue::HIGH_PRIORITY;
|
| + loading_queue_priority = TaskQueue::DISABLED_PRIORITY;
|
| + timer_queue_priority = TaskQueue::DISABLED_PRIORITY;
|
| break;
|
| -
|
| - case UseCase::TOUCHSTART:
|
| - new_policy.compositor_queue_priority = TaskQueue::HIGH_PRIORITY;
|
| - new_policy.loading_queue_priority = TaskQueue::DISABLED_PRIORITY;
|
| - new_policy.timer_queue_priority = TaskQueue::DISABLED_PRIORITY;
|
| - block_expensive_tasks = true; // NOTE this is a nop due to the above.
|
| + case Policy::TOUCHSTART_PRIORITY:
|
| + compositor_queue_priority = TaskQueue::HIGH_PRIORITY;
|
| + loading_queue_priority = TaskQueue::DISABLED_PRIORITY;
|
| + timer_queue_priority = TaskQueue::DISABLED_PRIORITY;
|
| break;
|
| -
|
| - case UseCase::NONE:
|
| - if (touchstart_expected_soon)
|
| - block_expensive_tasks = true;
|
| + case Policy::NORMAL:
|
| break;
|
| -
|
| - case UseCase::LOADING:
|
| - new_policy.loading_queue_priority = TaskQueue::HIGH_PRIORITY;
|
| - new_policy.default_queue_priority = TaskQueue::HIGH_PRIORITY;
|
| + case Policy::LOADING_PRIORITY:
|
| + // We prioritize loading tasks by deprioritizing compositing and timers.
|
| + compositor_queue_priority = TaskQueue::BEST_EFFORT_PRIORITY;
|
| + timer_queue_priority = TaskQueue::BEST_EFFORT_PRIORITY;
|
| + // TODO(alexclarke): See if we can safely mark the loading task queue as
|
| + // high priority.
|
| break;
|
| -
|
| default:
|
| NOTREACHED();
|
| }
|
|
|
| - // Don't block expensive tasks unless we have actually seen something.
|
| - if (!MainThreadOnly().have_seen_a_begin_main_frame)
|
| - block_expensive_tasks = false;
|
| -
|
| - if (block_expensive_tasks && loading_tasks_seem_expensive)
|
| - new_policy.loading_queue_priority = TaskQueue::DISABLED_PRIORITY;
|
| -
|
| - if ((block_expensive_tasks && timer_tasks_seem_expensive) ||
|
| - MainThreadOnly().timer_queue_suspend_count != 0 ||
|
| - MainThreadOnly().timer_queue_suspended_when_backgrounded) {
|
| - new_policy.timer_queue_priority = TaskQueue::DISABLED_PRIORITY;
|
| - }
|
| -
|
| - // Tracing is done before the early out check, because it's quite possible we
|
| - // will otherwise miss this information in traces.
|
| + compositor_task_runner_->SetQueuePriority(compositor_queue_priority);
|
| + loading_task_runner_->SetQueuePriority(loading_queue_priority);
|
| + timer_task_runner_->SetQueuePriority(timer_queue_priority);
|
| +
|
| + DCHECK(compositor_task_runner_->IsQueueEnabled());
|
| + if (new_policy != Policy::TOUCHSTART_PRIORITY &&
|
| + new_policy != Policy::COMPOSITOR_CRITICAL_PATH_PRIORITY) {
|
| + DCHECK(loading_task_runner_->IsQueueEnabled());
|
| + }
|
| + MainThreadOnly().current_policy_ = new_policy;
|
| +
|
| TRACE_EVENT_OBJECT_SNAPSHOT_WITH_ID(
|
| TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"), "RendererScheduler",
|
| this, AsValueLocked(now));
|
| - TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"), "use_case",
|
| - use_case);
|
| TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
|
| - "RendererScheduler.loading_tasks_seem_expensive",
|
| - MainThreadOnly().loading_tasks_seem_expensive);
|
| + "RendererScheduler.policy", MainThreadOnly().current_policy_);
|
| TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
|
| "RendererScheduler.timer_tasks_seem_expensive",
|
| - MainThreadOnly().timer_tasks_seem_expensive);
|
| -
|
| - if (update_type == UpdateType::MAY_EARLY_OUT_IF_POLICY_UNCHANGED &&
|
| - new_policy == MainThreadOnly().current_policy) {
|
| - return;
|
| - }
|
| -
|
| - compositor_task_runner_->SetQueuePriority(
|
| - new_policy.compositor_queue_priority);
|
| - loading_task_runner_->SetQueuePriority(new_policy.loading_queue_priority);
|
| - timer_task_runner_->SetQueuePriority(new_policy.timer_queue_priority);
|
| -
|
| - // TODO(alexclarke): We shouldn't have to prioritize the default queue, but it
|
| - // appears to be necessary since the order of loading tasks and IPCs (which
|
| - // are mostly dispatched on the default queue) need to be preserved.
|
| - helper_.DefaultTaskRunner()->SetQueuePriority(
|
| - new_policy.default_queue_priority);
|
| -
|
| - DCHECK(compositor_task_runner_->IsQueueEnabled());
|
| - MainThreadOnly().current_policy = new_policy;
|
| -}
|
| -
|
| -bool RendererSchedulerImpl::InputSignalsSuggestGestureInProgress(
|
| + AnyThread().timer_tasks_seem_expensive_);
|
| +}
|
| +
|
| +bool RendererSchedulerImpl::InputSignalsSuggestCompositorPriority(
|
| base::TimeTicks now) const {
|
| base::TimeDelta unused_policy_duration;
|
| - switch (ComputeCurrentUseCase(now, &unused_policy_duration)) {
|
| - case UseCase::COMPOSITOR_GESTURE:
|
| - case UseCase::MAIN_THREAD_GESTURE:
|
| - case UseCase::TOUCHSTART:
|
| + switch (ComputeNewPolicy(now, &unused_policy_duration)) {
|
| + case Policy::TOUCHSTART_PRIORITY:
|
| + case Policy::COMPOSITOR_PRIORITY:
|
| + case Policy::COMPOSITOR_CRITICAL_PATH_PRIORITY:
|
| return true;
|
|
|
| default:
|
| @@ -651,35 +569,52 @@
|
| return false;
|
| }
|
|
|
| -RendererSchedulerImpl::UseCase RendererSchedulerImpl::ComputeCurrentUseCase(
|
| +RendererSchedulerImpl::Policy RendererSchedulerImpl::ComputeNewPolicy(
|
| base::TimeTicks now,
|
| - base::TimeDelta* expected_use_case_duration) const {
|
| + base::TimeDelta* new_policy_duration) const {
|
| any_thread_lock_.AssertAcquired();
|
| // Above all else we want to be responsive to user input.
|
| - *expected_use_case_duration =
|
| - AnyThread().user_model.TimeLeftInUserGesture(now);
|
| - if (*expected_use_case_duration > base::TimeDelta()) {
|
| - // Has scrolling been fully established?
|
| - if (AnyThread().awaiting_touch_start_response) {
|
| - // No, so arrange for compositor tasks to be run at the highest priority.
|
| - return UseCase::TOUCHSTART;
|
| - }
|
| - // Yes scrolling has been established. If BeginMainFrame is on the critical
|
| - // path, compositor tasks need to be prioritized, otherwise now might be a
|
| - // good time to run potentially expensive work.
|
| + *new_policy_duration = TimeLeftInInputEscalatedPolicy(now);
|
| + if (*new_policy_duration > base::TimeDelta()) {
|
| + if (AnyThread().awaiting_touch_start_response_)
|
| + return Policy::TOUCHSTART_PRIORITY;
|
| + // If BeginMainFrame is on the critical path, we want to try and prevent
|
| + // timers and loading tasks from running if we think they might be
|
| + // expensive.
|
| // TODO(skyostil): Consider removing in_idle_period_ and
|
| // HadAnIdlePeriodRecently() unless we need them here.
|
| - if (AnyThread().begin_main_frame_on_critical_path) {
|
| - return UseCase::MAIN_THREAD_GESTURE;
|
| - } else {
|
| - return UseCase::COMPOSITOR_GESTURE;
|
| + if (AnyThread().timer_tasks_seem_expensive_ &&
|
| + AnyThread().begin_main_frame_on_critical_path_) {
|
| + return Policy::COMPOSITOR_CRITICAL_PATH_PRIORITY;
|
| }
|
| - }
|
| -
|
| - // TODO(alexclarke): return UseCase::LOADING if signals suggest the system is
|
| - // in the initial 1s of RAIL loading.
|
| -
|
| - return UseCase::NONE;
|
| + return Policy::COMPOSITOR_PRIORITY;
|
| + }
|
| +
|
| + if (AnyThread().rails_loading_priority_deadline_ > now) {
|
| + *new_policy_duration = AnyThread().rails_loading_priority_deadline_ - now;
|
| + return Policy::LOADING_PRIORITY;
|
| + }
|
| +
|
| + return Policy::NORMAL;
|
| +}
|
| +
|
| +base::TimeDelta RendererSchedulerImpl::TimeLeftInInputEscalatedPolicy(
|
| + base::TimeTicks now) const {
|
| + any_thread_lock_.AssertAcquired();
|
| +
|
| + base::TimeDelta escalated_priority_duration =
|
| + base::TimeDelta::FromMilliseconds(kPriorityEscalationAfterInputMillis);
|
| +
|
| + // If the input event is still pending, go into input prioritized policy
|
| + // and check again later.
|
| + if (AnyThread().pending_main_thread_input_event_count_ > 0)
|
| + return escalated_priority_duration;
|
| + if (AnyThread().last_input_signal_time_.is_null() ||
|
| + AnyThread().last_input_signal_time_ + escalated_priority_duration < now) {
|
| + return base::TimeDelta();
|
| + }
|
| + return AnyThread().last_input_signal_time_ + escalated_priority_duration -
|
| + now;
|
| }
|
|
|
| bool RendererSchedulerImpl::CanEnterLongIdlePeriod(
|
| @@ -688,11 +623,11 @@
|
| helper_.CheckOnValidThread();
|
|
|
| MaybeUpdatePolicy();
|
| - if (MainThreadOnly().current_use_case == UseCase::TOUCHSTART) {
|
| + if (MainThreadOnly().current_policy_ == Policy::TOUCHSTART_PRIORITY) {
|
| // Don't start a long idle task in touch start priority, try again when
|
| // the policy is scheduled to end.
|
| *next_long_idle_period_delay_out =
|
| - MainThreadOnly().current_policy_expiration_time - now;
|
| + MainThreadOnly().current_policy_expiration_time_ - now;
|
| return false;
|
| }
|
| return true;
|
| @@ -703,21 +638,40 @@
|
| }
|
|
|
| void RendererSchedulerImpl::SuspendTimerQueue() {
|
| - MainThreadOnly().timer_queue_suspend_count++;
|
| + MainThreadOnly().timer_queue_suspend_count_++;
|
| ForceUpdatePolicy();
|
| DCHECK(!timer_task_runner_->IsQueueEnabled());
|
| }
|
|
|
| void RendererSchedulerImpl::ResumeTimerQueue() {
|
| - MainThreadOnly().timer_queue_suspend_count--;
|
| - DCHECK_GE(MainThreadOnly().timer_queue_suspend_count, 0);
|
| + MainThreadOnly().timer_queue_suspend_count_--;
|
| + DCHECK_GE(MainThreadOnly().timer_queue_suspend_count_, 0);
|
| ForceUpdatePolicy();
|
| }
|
|
|
| void RendererSchedulerImpl::SetTimerQueueSuspensionWhenBackgroundedEnabled(
|
| bool enabled) {
|
| // Note that this will only take effect for the next backgrounded signal.
|
| - MainThreadOnly().timer_queue_suspension_when_backgrounded_enabled = enabled;
|
| + MainThreadOnly().timer_queue_suspension_when_backgrounded_enabled_ = enabled;
|
| +}
|
| +
|
| +// static
|
| +const char* RendererSchedulerImpl::PolicyToString(Policy policy) {
|
| + switch (policy) {
|
| + case Policy::NORMAL:
|
| + return "normal";
|
| + case Policy::COMPOSITOR_PRIORITY:
|
| + return "compositor";
|
| + case Policy::COMPOSITOR_CRITICAL_PATH_PRIORITY:
|
| + return "compositor_critical_path";
|
| + case Policy::TOUCHSTART_PRIORITY:
|
| + return "touchstart";
|
| + case Policy::LOADING_PRIORITY:
|
| + return "loading";
|
| + default:
|
| + NOTREACHED();
|
| + return nullptr;
|
| + }
|
| }
|
|
|
| scoped_refptr<base::trace_event::ConvertableToTraceFormat>
|
| @@ -736,117 +690,95 @@
|
| scoped_refptr<base::trace_event::TracedValue> state =
|
| new base::trace_event::TracedValue();
|
|
|
| - state->SetString("current_use_case",
|
| - UseCaseToString(MainThreadOnly().current_use_case));
|
| - state->SetBoolean("loading_tasks_seem_expensive",
|
| - MainThreadOnly().loading_tasks_seem_expensive);
|
| - state->SetBoolean("timer_tasks_seem_expensive",
|
| - MainThreadOnly().timer_tasks_seem_expensive);
|
| - state->SetBoolean("touchstart_expected_soon",
|
| - MainThreadOnly().touchstart_expected_soon);
|
| + state->SetString("current_policy",
|
| + PolicyToString(MainThreadOnly().current_policy_));
|
| state->SetString("idle_period_state",
|
| IdleHelper::IdlePeriodStateToString(
|
| idle_helper_.SchedulerIdlePeriodState()));
|
| - state->SetBoolean("renderer_hidden", MainThreadOnly().renderer_hidden);
|
| + state->SetBoolean("renderer_hidden", MainThreadOnly().renderer_hidden_);
|
| state->SetBoolean("renderer_backgrounded",
|
| - MainThreadOnly().renderer_backgrounded);
|
| + MainThreadOnly().renderer_backgrounded_);
|
| state->SetBoolean("timer_queue_suspended_when_backgrounded",
|
| - MainThreadOnly().timer_queue_suspended_when_backgrounded);
|
| + MainThreadOnly().timer_queue_suspended_when_backgrounded_);
|
| state->SetInteger("timer_queue_suspend_count",
|
| - MainThreadOnly().timer_queue_suspend_count);
|
| + MainThreadOnly().timer_queue_suspend_count_);
|
| state->SetDouble("now", (optional_now - base::TimeTicks()).InMillisecondsF());
|
| - state->SetDouble(
|
| - "rails_loading_priority_deadline",
|
| - (AnyThread().rails_loading_priority_deadline - base::TimeTicks())
|
| - .InMillisecondsF());
|
| + state->SetDouble("last_input_signal_time",
|
| + (AnyThread().last_input_signal_time_ - base::TimeTicks())
|
| + .InMillisecondsF());
|
| + state->SetDouble("rails_loading_priority_deadline",
|
| + (AnyThread().rails_loading_priority_deadline_ -
|
| + base::TimeTicks()).InMillisecondsF());
|
| state->SetDouble("last_idle_period_end_time",
|
| - (AnyThread().last_idle_period_end_time - base::TimeTicks())
|
| + (AnyThread().last_idle_period_end_time_ - base::TimeTicks())
|
| .InMillisecondsF());
|
| + state->SetInteger("pending_main_thread_input_event_count",
|
| + AnyThread().pending_main_thread_input_event_count_);
|
| state->SetBoolean("awaiting_touch_start_response",
|
| - AnyThread().awaiting_touch_start_response);
|
| + AnyThread().awaiting_touch_start_response_);
|
| state->SetBoolean("begin_main_frame_on_critical_path",
|
| - AnyThread().begin_main_frame_on_critical_path);
|
| - state->SetDouble("expected_loading_task_duration",
|
| - MainThreadOnly()
|
| - .loading_task_cost_estimator.expected_task_duration()
|
| - .InMillisecondsF());
|
| + AnyThread().begin_main_frame_on_critical_path_);
|
| state->SetDouble("expected_timer_task_duration",
|
| MainThreadOnly()
|
| - .timer_task_cost_estimator.expected_task_duration()
|
| + .timer_task_cost_estimator_.expected_task_duration()
|
| .InMillisecondsF());
|
| // TODO(skyostil): Can we somehow trace how accurate these estimates were?
|
| state->SetDouble(
|
| "expected_short_idle_period_duration",
|
| - MainThreadOnly().expected_short_idle_period_duration.InMillisecondsF());
|
| - state->SetDouble(
|
| - "estimated_next_frame_begin",
|
| - (MainThreadOnly().estimated_next_frame_begin - base::TimeTicks())
|
| - .InMillisecondsF());
|
| - state->SetBoolean("in_idle_period", AnyThread().in_idle_period);
|
| - AnyThread().user_model.AsValueInto(state.get());
|
| + MainThreadOnly().expected_short_idle_period_duration_.InMillisecondsF());
|
| + state->SetBoolean("timer_tasks_seem_expensive",
|
| + AnyThread().timer_tasks_seem_expensive_);
|
| + state->SetDouble("estimated_next_frame_begin",
|
| + (MainThreadOnly().estimated_next_frame_begin_ -
|
| + base::TimeTicks()).InMillisecondsF());
|
| + state->SetBoolean("in_idle_period", AnyThread().in_idle_period_);
|
|
|
| return state;
|
| }
|
|
|
| void RendererSchedulerImpl::OnIdlePeriodStarted() {
|
| base::AutoLock lock(any_thread_lock_);
|
| - AnyThread().in_idle_period = true;
|
| + AnyThread().in_idle_period_ = true;
|
| UpdatePolicyLocked(UpdateType::MAY_EARLY_OUT_IF_POLICY_UNCHANGED);
|
| }
|
|
|
| void RendererSchedulerImpl::OnIdlePeriodEnded() {
|
| base::AutoLock lock(any_thread_lock_);
|
| - AnyThread().last_idle_period_end_time = helper_.Now();
|
| - AnyThread().in_idle_period = false;
|
| + AnyThread().last_idle_period_end_time_ = helper_.Now();
|
| + AnyThread().in_idle_period_ = false;
|
| UpdatePolicyLocked(UpdateType::MAY_EARLY_OUT_IF_POLICY_UNCHANGED);
|
| }
|
|
|
| void RendererSchedulerImpl::OnPageLoadStarted() {
|
| - TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
|
| - "RendererSchedulerImpl::OnPageLoadStarted");
|
| base::AutoLock lock(any_thread_lock_);
|
| - AnyThread().rails_loading_priority_deadline =
|
| + AnyThread().rails_loading_priority_deadline_ =
|
| helper_.Now() + base::TimeDelta::FromMilliseconds(
|
| kRailsInitialLoadingPrioritizationMillis);
|
| - ResetForNavigationLocked();
|
| + UpdatePolicyLocked(UpdateType::MAY_EARLY_OUT_IF_POLICY_UNCHANGED);
|
| }
|
|
|
| bool RendererSchedulerImpl::HadAnIdlePeriodRecently(base::TimeTicks now) const {
|
| - return (now - AnyThread().last_idle_period_end_time) <=
|
| + return (now - AnyThread().last_idle_period_end_time_) <=
|
| base::TimeDelta::FromMilliseconds(
|
| kIdlePeriodStarvationThresholdMillis);
|
| }
|
|
|
| void RendererSchedulerImpl::SuspendTimerQueueWhenBackgrounded() {
|
| - DCHECK(MainThreadOnly().renderer_backgrounded);
|
| - if (MainThreadOnly().timer_queue_suspended_when_backgrounded)
|
| - return;
|
| -
|
| - MainThreadOnly().timer_queue_suspended_when_backgrounded = true;
|
| + DCHECK(MainThreadOnly().renderer_backgrounded_);
|
| + if (MainThreadOnly().timer_queue_suspended_when_backgrounded_)
|
| + return;
|
| +
|
| + MainThreadOnly().timer_queue_suspended_when_backgrounded_ = true;
|
| ForceUpdatePolicy();
|
| }
|
|
|
| void RendererSchedulerImpl::ResumeTimerQueueWhenForegrounded() {
|
| - DCHECK(!MainThreadOnly().renderer_backgrounded);
|
| - if (!MainThreadOnly().timer_queue_suspended_when_backgrounded)
|
| - return;
|
| -
|
| - MainThreadOnly().timer_queue_suspended_when_backgrounded = false;
|
| + DCHECK(!MainThreadOnly().renderer_backgrounded_);
|
| + if (!MainThreadOnly().timer_queue_suspended_when_backgrounded_)
|
| + return;
|
| +
|
| + MainThreadOnly().timer_queue_suspended_when_backgrounded_ = false;
|
| ForceUpdatePolicy();
|
| }
|
|
|
| -void RendererSchedulerImpl::ResetForNavigationLocked() {
|
| - helper_.CheckOnValidThread();
|
| - any_thread_lock_.AssertAcquired();
|
| - MainThreadOnly().loading_task_cost_estimator.Clear();
|
| - MainThreadOnly().timer_task_cost_estimator.Clear();
|
| - MainThreadOnly().short_idle_period_duration.Clear();
|
| - // Make sure that we don't initially assume there is no idle time.
|
| - MainThreadOnly().short_idle_period_duration.InsertSample(
|
| - cc::BeginFrameArgs::DefaultInterval());
|
| - AnyThread().user_model.Reset();
|
| - MainThreadOnly().have_seen_a_begin_main_frame = false;
|
| - UpdatePolicyLocked(UpdateType::MAY_EARLY_OUT_IF_POLICY_UNCHANGED);
|
| -}
|
| -
|
| } // namespace scheduler
|
|
|