Commit 2e3be401 authored by Alexander Timin's avatar Alexander Timin Committed by Commit Bot

[scheduler] Add experiment controls to disable input heuristics

Add support for disabling input heuristics (or only expensive task blocking)
via Finch.

R=skyostil@chromium.org,alexclarke@chromium.org
BUG=874836

Change-Id: I48837a46b8381d8e805f69899ef57efbda08cf38
Reviewed-on: https://chromium-review.googlesource.com/1187150
Commit-Queue: Alexander Timin <altimin@chromium.org>
Reviewed-by: default avatarAlex Clarke <alexclarke@chromium.org>
Reviewed-by: default avatarSami Kyöstilä <skyostil@chromium.org>
Cr-Commit-Position: refs/heads/master@{#587890}
parent 218c3921
...@@ -143,6 +143,22 @@ const base::Feature kThrottleAndFreezeTaskTypes{ ...@@ -143,6 +143,22 @@ const base::Feature kThrottleAndFreezeTaskTypes{
extern const char PLATFORM_EXPORT kThrottleableTaskTypesListParam[]; extern const char PLATFORM_EXPORT kThrottleableTaskTypesListParam[];
extern const char PLATFORM_EXPORT kFreezableTaskTypesListParam[]; extern const char PLATFORM_EXPORT kFreezableTaskTypesListParam[];
// https://crbug.com/874836: Experiment-controlled removal of input heuristics.
// Expensive task blocking as a part of input handling heuristics, so disabling
// input heuristics implicitly disables expensive task blocking. Expensive task
// blocking is tested separately as it's less risky. Touchstart and
// non-touchstart input heuristics are separated because non-touchstart are
// seen as less ricky.
const base::Feature kDisableExpensiveTaskBlocking{
"BlinkSchedulerDisableExpensiveTaskBlocking",
base::FEATURE_DISABLED_BY_DEFAULT};
const base::Feature kDisableNonTouchstartInputHeuristics{
"BlinkSchedulerDisableNonTouchstartInputHeuristics",
base::FEATURE_DISABLED_BY_DEFAULT};
const base::Feature kDisableTouchstartInputHeuristics{
"BlinkSchedulerDisableTouchstartInputHeuristics",
base::FEATURE_DISABLED_BY_DEFAULT};
} // namespace scheduler } // namespace scheduler
} // namespace blink } // namespace blink
......
...@@ -616,6 +616,13 @@ MainThreadSchedulerImpl::SchedulingSettings::SchedulingSettings() { ...@@ -616,6 +616,13 @@ MainThreadSchedulerImpl::SchedulingSettings::SchedulingSettings() {
FrameSchedulerImpl::InitializeTaskTypeQueueTraitsMap( FrameSchedulerImpl::InitializeTaskTypeQueueTraitsMap(
frame_task_types_to_queue_traits); frame_task_types_to_queue_traits);
disable_expensive_task_blocking =
base::FeatureList::IsEnabled(kDisableExpensiveTaskBlocking);
disable_non_touchstart_input_heuristics =
base::FeatureList::IsEnabled(kDisableNonTouchstartInputHeuristics);
disable_touchstart_input_heuristics =
base::FeatureList::IsEnabled(kDisableTouchstartInputHeuristics);
} }
MainThreadSchedulerImpl::AnyThread::~AnyThread() = default; MainThreadSchedulerImpl::AnyThread::~AnyThread() = default;
...@@ -1549,6 +1556,9 @@ void MainThreadSchedulerImpl::UpdatePolicyLocked(UpdateType update_type) { ...@@ -1549,6 +1556,9 @@ void MainThreadSchedulerImpl::UpdatePolicyLocked(UpdateType update_type) {
expensive_task_policy = ExpensiveTaskPolicy::kRun; expensive_task_policy = ExpensiveTaskPolicy::kRun;
} }
if (scheduling_settings().disable_expensive_task_blocking)
expensive_task_policy = ExpensiveTaskPolicy::kRun;
switch (expensive_task_policy) { switch (expensive_task_policy) {
case ExpensiveTaskPolicy::kRun: case ExpensiveTaskPolicy::kRun:
break; break;
...@@ -1690,7 +1700,8 @@ UseCase MainThreadSchedulerImpl::ComputeCurrentUseCase( ...@@ -1690,7 +1700,8 @@ UseCase MainThreadSchedulerImpl::ComputeCurrentUseCase(
// Special case for flings. This is needed because we don't get notification // Special case for flings. This is needed because we don't get notification
// of a fling ending (although we do for cancellation). // of a fling ending (although we do for cancellation).
if (any_thread().fling_compositor_escalation_deadline > now && if (any_thread().fling_compositor_escalation_deadline > now &&
!any_thread().awaiting_touch_start_response) { !any_thread().awaiting_touch_start_response &&
!scheduling_settings().disable_non_touchstart_input_heuristics) {
*expected_use_case_duration = *expected_use_case_duration =
any_thread().fling_compositor_escalation_deadline - now; any_thread().fling_compositor_escalation_deadline - now;
return UseCase::kCompositorGesture; return UseCase::kCompositorGesture;
...@@ -1700,7 +1711,8 @@ UseCase MainThreadSchedulerImpl::ComputeCurrentUseCase( ...@@ -1700,7 +1711,8 @@ UseCase MainThreadSchedulerImpl::ComputeCurrentUseCase(
any_thread().user_model.TimeLeftInUserGesture(now); any_thread().user_model.TimeLeftInUserGesture(now);
if (*expected_use_case_duration > base::TimeDelta()) { if (*expected_use_case_duration > base::TimeDelta()) {
// Has a gesture been fully established? // Has a gesture been fully established?
if (any_thread().awaiting_touch_start_response) { if (any_thread().awaiting_touch_start_response &&
!scheduling_settings().disable_touchstart_input_heuristics) {
// No, so arrange for compositor tasks to be run at the highest priority. // No, so arrange for compositor tasks to be run at the highest priority.
return UseCase::kTouchstart; return UseCase::kTouchstart;
} }
...@@ -1715,18 +1727,20 @@ UseCase MainThreadSchedulerImpl::ComputeCurrentUseCase( ...@@ -1715,18 +1727,20 @@ UseCase MainThreadSchedulerImpl::ComputeCurrentUseCase(
// stream of input events and has prevented a default gesture from being // stream of input events and has prevented a default gesture from being
// started. // started.
// 4. SYNCHRONIZED_GESTURE where the gesture is processed on both threads. // 4. SYNCHRONIZED_GESTURE where the gesture is processed on both threads.
if (any_thread().last_gesture_was_compositor_driven) { if (!scheduling_settings().disable_non_touchstart_input_heuristics) {
if (any_thread().begin_main_frame_on_critical_path) { if (any_thread().last_gesture_was_compositor_driven) {
return UseCase::kSynchronizedGesture; if (any_thread().begin_main_frame_on_critical_path) {
return UseCase::kSynchronizedGesture;
} else {
return UseCase::kCompositorGesture;
}
}
if (any_thread().default_gesture_prevented) {
return UseCase::kMainThreadCustomInputHandling;
} else { } else {
return UseCase::kCompositorGesture; return UseCase::kMainThreadGesture;
} }
} }
if (any_thread().default_gesture_prevented) {
return UseCase::kMainThreadCustomInputHandling;
} else {
return UseCase::kMainThreadGesture;
}
} }
// Occasionally the meaningful paint fails to be detected, so as a fallback we // Occasionally the meaningful paint fails to be detected, so as a fallback we
......
...@@ -132,6 +132,10 @@ class PLATFORM_EXPORT MainThreadSchedulerImpl ...@@ -132,6 +132,10 @@ class PLATFORM_EXPORT MainThreadSchedulerImpl
// entries. This is initialized early with all valid entries. Entries that // entries. This is initialized early with all valid entries. Entries that
// aren't valid task types, i.e. non-frame level, are base::nullopt. // aren't valid task types, i.e. non-frame level, are base::nullopt.
FrameTaskTypeToQueueTraitsArray frame_task_types_to_queue_traits; FrameTaskTypeToQueueTraitsArray frame_task_types_to_queue_traits;
bool disable_expensive_task_blocking;
bool disable_non_touchstart_input_heuristics;
bool disable_touchstart_input_heuristics;
}; };
static const char* UseCaseToString(UseCase use_case); static const char* UseCaseToString(UseCase use_case);
......
...@@ -806,6 +806,27 @@ class MainThreadSchedulerImplTest : public testing::Test { ...@@ -806,6 +806,27 @@ class MainThreadSchedulerImplTest : public testing::Test {
DISALLOW_COPY_AND_ASSIGN(MainThreadSchedulerImplTest); DISALLOW_COPY_AND_ASSIGN(MainThreadSchedulerImplTest);
}; };
class MainThreadSchedulerImplTestWithoutTouchstartInputHeuristics
: public MainThreadSchedulerImplTest {
public:
MainThreadSchedulerImplTestWithoutTouchstartInputHeuristics()
: MainThreadSchedulerImplTest({kDisableTouchstartInputHeuristics,
kDisableNonTouchstartInputHeuristics},
{kPrioritizeCompositingAfterInput}) {}
~MainThreadSchedulerImplTestWithoutTouchstartInputHeuristics() override =
default;
};
class MainThreadSchedulerImplTestWithoutNonTouchstartInputHeuristics
: public MainThreadSchedulerImplTest {
public:
MainThreadSchedulerImplTestWithoutNonTouchstartInputHeuristics()
: MainThreadSchedulerImplTest({kDisableNonTouchstartInputHeuristics},
{kPrioritizeCompositingAfterInput}) {}
~MainThreadSchedulerImplTestWithoutNonTouchstartInputHeuristics() override =
default;
};
TEST_F(MainThreadSchedulerImplTest, TestPostDefaultTask) { TEST_F(MainThreadSchedulerImplTest, TestPostDefaultTask) {
std::vector<std::string> run_order; std::vector<std::string> run_order;
PostTestTasks(&run_order, "D1 D2 D3 D4"); PostTestTasks(&run_order, "D1 D2 D3 D4");
...@@ -1008,6 +1029,22 @@ TEST_F(MainThreadSchedulerImplTest, ...@@ -1008,6 +1029,22 @@ TEST_F(MainThreadSchedulerImplTest,
EXPECT_EQ(UseCase::kCompositorGesture, CurrentUseCase()); EXPECT_EQ(UseCase::kCompositorGesture, CurrentUseCase());
} }
TEST_F(MainThreadSchedulerImplTestWithoutNonTouchstartInputHeuristics,
TestCompositorPolicy_CompositorHandlesInput_WithTouchHandler) {
std::vector<std::string> run_order;
PostTestTasks(&run_order, "L1 I1 D1 C1 D2 C2");
scheduler_->SetHasVisibleRenderWidgetWithTouchHandler(true);
EnableIdleTasks();
SimulateCompositorGestureStart(TouchEventPolicy::kSendTouchStart);
base::RunLoop().RunUntilIdle();
EXPECT_THAT(run_order,
testing::ElementsAre(std::string("L1"), std::string("D1"),
std::string("C1"), std::string("D2"),
std::string("C2"), std::string("I1")));
EXPECT_EQ(UseCase::kNone, CurrentUseCase());
}
TEST_F(MainThreadSchedulerImplTest, TEST_F(MainThreadSchedulerImplTest,
TestCompositorPolicy_MainThreadHandlesInput_WithoutScrollUpdates) { TestCompositorPolicy_MainThreadHandlesInput_WithoutScrollUpdates) {
std::vector<std::string> run_order; std::vector<std::string> run_order;
...@@ -1174,6 +1211,52 @@ TEST_F( ...@@ -1174,6 +1211,52 @@ TEST_F(
EXPECT_EQ(UseCase::kTouchstart, CurrentUseCase()); EXPECT_EQ(UseCase::kTouchstart, CurrentUseCase());
} }
TEST_F(
MainThreadSchedulerImplTestWithoutTouchstartInputHeuristics,
TestCompositorPolicy_MainThreadHandlesInput_SingleEvent_NoPreventDefault) {
std::vector<std::string> run_order;
PostTestTasks(&run_order, "L1 I1 D1 C1 D2 C2");
scheduler_->SetHasVisibleRenderWidgetWithTouchHandler(true);
EnableIdleTasks();
scheduler_->DidHandleInputEventOnCompositorThread(
FakeTouchEvent(blink::WebInputEvent::kTouchStart),
InputEventState::EVENT_FORWARDED_TO_MAIN_THREAD);
scheduler_->DidHandleInputEventOnMainThread(
FakeTouchEvent(blink::WebInputEvent::kTouchStart),
WebInputEventResult::kHandledSystem);
base::RunLoop().RunUntilIdle();
EXPECT_THAT(run_order,
testing::ElementsAre(std::string("L1"), std::string("D1"),
std::string("C1"), std::string("D2"),
std::string("C2"), std::string("I1")));
EXPECT_EQ(UseCase::kNone, CurrentUseCase());
}
TEST_F(
MainThreadSchedulerImplTestWithoutNonTouchstartInputHeuristics,
TestCompositorPolicy_MainThreadHandlesInput_SingleEvent_NoPreventDefault) {
std::vector<std::string> run_order;
PostTestTasks(&run_order, "L1 I1 D1 C1 D2 C2");
scheduler_->SetHasVisibleRenderWidgetWithTouchHandler(true);
EnableIdleTasks();
scheduler_->DidHandleInputEventOnCompositorThread(
FakeTouchEvent(blink::WebInputEvent::kTouchStart),
InputEventState::EVENT_FORWARDED_TO_MAIN_THREAD);
scheduler_->DidHandleInputEventOnMainThread(
FakeTouchEvent(blink::WebInputEvent::kTouchStart),
WebInputEventResult::kHandledSystem);
base::RunLoop().RunUntilIdle();
// Because we are still waiting for the touchstart to be processed,
// non-essential tasks like loading tasks are blocked.
EXPECT_THAT(run_order,
testing::ElementsAre(std::string("C1"), std::string("C2"),
std::string("D1"), std::string("D2"),
std::string("I1")));
EXPECT_EQ(UseCase::kTouchstart, CurrentUseCase());
}
TEST_F(MainThreadSchedulerImplTest, TestCompositorPolicy_DidAnimateForInput) { TEST_F(MainThreadSchedulerImplTest, TestCompositorPolicy_DidAnimateForInput) {
std::vector<std::string> run_order; std::vector<std::string> run_order;
PostTestTasks(&run_order, "I1 D1 C1 D2 C2"); PostTestTasks(&run_order, "I1 D1 C1 D2 C2");
...@@ -1232,6 +1315,34 @@ TEST_F(MainThreadSchedulerImplTest, ...@@ -1232,6 +1315,34 @@ TEST_F(MainThreadSchedulerImplTest,
EXPECT_THAT(run_order, testing::ElementsAre(std::string("C1"))); EXPECT_THAT(run_order, testing::ElementsAre(std::string("C1")));
} }
class MainThreadSchedulerImplTestWithoutExpensiveTaskBlocking
: public MainThreadSchedulerImplTest {
public:
MainThreadSchedulerImplTestWithoutExpensiveTaskBlocking()
: MainThreadSchedulerImplTest({kDisableExpensiveTaskBlocking}, {}) {}
~MainThreadSchedulerImplTestWithoutExpensiveTaskBlocking() override = default;
};
TEST_F(MainThreadSchedulerImplTestWithoutExpensiveTaskBlocking,
ExpensiveTimersRunWhenMainThreadScrolling) {
std::vector<std::string> run_order;
scheduler_->SetHasVisibleRenderWidgetWithTouchHandler(true);
SimulateExpensiveTasks(timer_task_runner_);
DoMainFrame();
SimulateMainThreadGestureStart(TouchEventPolicy::kSendTouchStart,
blink::WebInputEvent::kGestureScrollUpdate);
PostTestTasks(&run_order, "C1 T1");
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(BlockingInputExpectedSoon());
EXPECT_EQ(UseCase::kMainThreadGesture, CurrentUseCase());
EXPECT_THAT(run_order,
testing::ElementsAre(std::string("C1"), std::string("T1")));
}
TEST_F(MainThreadSchedulerImplTest, TEST_F(MainThreadSchedulerImplTest,
ExpensiveTimersDoRunWhenMainThreadInputHandling) { ExpensiveTimersDoRunWhenMainThreadInputHandling) {
std::vector<std::string> run_order; std::vector<std::string> run_order;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment