Repository navigation
Expand file tree
/
Copy path01-input-priority.patch
More file actions
130 lines (127 loc) · 6.61 KB
/
Copy path01-input-priority.patch
File metadata and controls
130 lines (127 loc) · 6.61 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.cc b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.cc
index 6ff8280cbb..8427b61194 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.cc
@@ -92,8 +92,25 @@ namespace scheduler {
BASE_FEATURE(kBusyLoopAggressiveAfterCommittedLoad,
base::FEATURE_DISABLED_BY_DEFAULT);
+// Kute: input task queues at normal instead of highest priority and the
+// compositor priority capped at normal. Without it, continuous mouse input
+// with --disable-frame-rate-limit starves WebSocket and worker messages on a
+// busy main thread (the aim freeze, crbug 415071737). Off, the stock
+// priorities apply. Enabled by Kute with
+// --enable-features=KuteInputNormalPriority.
+BASE_FEATURE(kKuteInputNormalPriority,
+ "KuteInputNormalPriority",
+ base::FEATURE_DISABLED_BY_DEFAULT);
+
namespace {
constexpr base::TimeDelta kBusyLoopAggressiveTime = base::Milliseconds(500);
+
+// Read once and cache, the priority is recomputed on every policy update.
+bool KuteInputNormalPriority() {
+ static const bool value =
+ base::FeatureList::IsEnabled(kKuteInputNormalPriority);
+ return value;
+}
}
// When scrolling and the main thread is not expected to be blocking, decrease
@@ -2656,7 +2673,16 @@ TaskPriority MainThreadSchedulerImpl::ComputePriority(
case MainThreadTaskQueue::QueueTraits::PrioritisationType::kCompositor:
return main_thread_only().compositor_priority;
case MainThreadTaskQueue::QueueTraits::PrioritisationType::kInput:
- return TaskPriority::kHighestPriority;
+ // Kute: lowered from kHighestPriority to kNormalPriority to prevent
+ // input tasks from starving WebSocket/Worker message dispatch when
+ // --disable-frame-rate-limit is active. Without cross-priority
+ // anti-starvation in the task queue selector, ANY priority above
+ // kNormalPriority causes starvation during continuous mouse input.
+ // Testing shows kNormalPriority actually improves both frame rate
+ // and input throughput vs kHighestPriority, because it prevents the
+ // input->compositor priority cascade from monopolizing the thread.
+ return KuteInputNormalPriority() ? TaskPriority::kNormalPriority
+ : TaskPriority::kHighestPriority;
case MainThreadTaskQueue::QueueTraits::PrioritisationType::kBestEffort:
return TaskPriority::kBestEffortPriority;
case MainThreadTaskQueue::QueueTraits::PrioritisationType::kRegular:
@@ -2724,31 +2750,59 @@ TaskPriority MainThreadSchedulerImpl::ComputeCompositorPriority() const {
ComputeCompositorPriorityForMainFrame();
std::optional<TaskPriority> use_case_priority =
ComputeCompositorPriorityFromUseCase();
+
+ if (!KuteInputNormalPriority()) {
+ // Stock Chromium.
+ if (!targeted_main_frame_priority && !use_case_priority) {
+ return TaskPriority::kNormalPriority;
+ } else if (!use_case_priority) {
+ return *targeted_main_frame_priority;
+ } else if (!targeted_main_frame_priority) {
+ return *use_case_priority;
+ }
+
+ // Both are set, so some reconciliation is needed.
+ CHECK(targeted_main_frame_priority && use_case_priority);
+ // If either votes for the highest priority, use that to simplify the
+ // remaining case.
+ if (*targeted_main_frame_priority == TaskPriority::kHighestPriority ||
+ *use_case_priority == TaskPriority::kHighestPriority) {
+ return TaskPriority::kHighestPriority;
+ }
+ // Otherwise, this must be a combination of UseCase::kCompositorGesture and
+ // rendering starvation since all other use cases set the priority to
+ // highest.
+ CHECK(current_use_case() == UseCase::kCompositorGesture &&
+ (main_thread_only().main_frame_prioritization_state ==
+ RenderingPrioritizationState::kRenderingStarved ||
+ main_thread_only().main_frame_prioritization_state ==
+ RenderingPrioritizationState::kRenderingStarvedByRenderBlocking));
+ CHECK_LE(*targeted_main_frame_priority, *use_case_priority);
+ return *targeted_main_frame_priority;
+ }
+
+ TaskPriority result;
if (!targeted_main_frame_priority && !use_case_priority) {
- return TaskPriority::kNormalPriority;
+ result = TaskPriority::kNormalPriority;
} else if (!use_case_priority) {
- return *targeted_main_frame_priority;
+ result = *targeted_main_frame_priority;
} else if (!targeted_main_frame_priority) {
- return *use_case_priority;
- }
-
- // Both are set, so some reconciliation is needed.
- CHECK(targeted_main_frame_priority && use_case_priority);
- // If either votes for the highest priority, use that to simplify the
- // remaining case.
- if (*targeted_main_frame_priority == TaskPriority::kHighestPriority ||
- *use_case_priority == TaskPriority::kHighestPriority) {
- return TaskPriority::kHighestPriority;
- }
- // Otherwise, this must be a combination of UseCase::kCompositorGesture and
- // rendering starvation since all other use cases set the priority to highest.
- CHECK(current_use_case() == UseCase::kCompositorGesture &&
- (main_thread_only().main_frame_prioritization_state ==
- RenderingPrioritizationState::kRenderingStarved ||
- main_thread_only().main_frame_prioritization_state ==
- RenderingPrioritizationState::kRenderingStarvedByRenderBlocking));
- CHECK_LE(*targeted_main_frame_priority, *use_case_priority);
- return *targeted_main_frame_priority;
+ result = *use_case_priority;
+ } else {
+ // Both are set, take the higher priority (lower numeric value).
+ result = std::min(*targeted_main_frame_priority, *use_case_priority);
+ }
+
+ // Kute: cap compositor priority to kNormalPriority. Without this cap,
+ // back-to-back BeginFrame tasks at kHighestPriority (triggered by
+ // continuous mouse input + --disable-frame-rate-limit) create a tight
+ // compositor loop that permanently starves kNormalPriority tasks
+ // (WebSocket onmessage, Worker postMessage). The task queue selector
+ // has no cross-priority anti-starvation, so any priority above kNormal
+ // causes indefinite deferral of lower-priority work. Rendering starvation
+ // detection in ComputeCompositorPriorityForMainFrame() is sufficient to
+ // protect against actual frame drops when compositor priority is capped.
+ return std::max(result, TaskPriority::kNormalPriority);
}
void MainThreadSchedulerImpl::UpdateCompositorTaskQueuePriority() {