OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "cc/raster/task_graph_work_queue.h" | 5 #include "cc/raster/task_graph_work_queue.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
| 8 #include <map> |
8 #include <utility> | 9 #include <utility> |
9 | 10 |
10 #include "base/trace_event/trace_event.h" | 11 #include "base/trace_event/trace_event.h" |
11 | 12 |
12 namespace cc { | 13 namespace cc { |
| 14 namespace { |
| 15 |
| 16 bool CompareTaskPriority(const TaskGraphWorkQueue::PrioritizedTask& a, |
| 17 const TaskGraphWorkQueue::PrioritizedTask& b) { |
| 18 // In this system, numerically lower priority is run first. |
| 19 return a.priority > b.priority; |
| 20 } |
| 21 |
| 22 class CompareTaskNamespacePriority { |
| 23 public: |
| 24 explicit CompareTaskNamespacePriority(uint16_t category) |
| 25 : category_(category) {} |
| 26 |
| 27 bool operator()(const TaskGraphWorkQueue::TaskNamespace* a, |
| 28 const TaskGraphWorkQueue::TaskNamespace* b) { |
| 29 DCHECK(!a->ready_to_run_tasks.at(category_).empty()); |
| 30 DCHECK(!b->ready_to_run_tasks.at(category_).empty()); |
| 31 |
| 32 // Compare based on task priority of the ready_to_run_tasks heap .front() |
| 33 // will hold the max element of the heap, except after pop_heap, when max |
| 34 // element is moved to .back(). |
| 35 return CompareTaskPriority(a->ready_to_run_tasks.at(category_).front(), |
| 36 b->ready_to_run_tasks.at(category_).front()); |
| 37 } |
| 38 |
| 39 private: |
| 40 uint16_t category_; |
| 41 }; |
| 42 } // namespace |
13 | 43 |
14 TaskGraphWorkQueue::TaskNamespace::TaskNamespace() {} | 44 TaskGraphWorkQueue::TaskNamespace::TaskNamespace() {} |
15 | 45 |
16 TaskGraphWorkQueue::TaskNamespace::~TaskNamespace() {} | 46 TaskGraphWorkQueue::TaskNamespace::~TaskNamespace() {} |
17 | 47 |
18 TaskGraphWorkQueue::TaskGraphWorkQueue() : next_namespace_id_(1) {} | 48 TaskGraphWorkQueue::TaskGraphWorkQueue() : next_namespace_id_(1) {} |
19 TaskGraphWorkQueue::~TaskGraphWorkQueue() {} | 49 TaskGraphWorkQueue::~TaskGraphWorkQueue() {} |
20 | 50 |
21 NamespaceToken TaskGraphWorkQueue::GetNamespaceToken() { | 51 NamespaceToken TaskGraphWorkQueue::GetNamespaceToken() { |
22 NamespaceToken token(next_namespace_id_++); | 52 NamespaceToken token(next_namespace_id_++); |
23 DCHECK(namespaces_.find(token) == namespaces_.end()); | 53 DCHECK(namespaces_.find(token) == namespaces_.end()); |
24 return token; | 54 return token; |
25 } | 55 } |
26 | 56 |
27 void TaskGraphWorkQueue::ScheduleTasks(NamespaceToken token, TaskGraph* graph) { | 57 void TaskGraphWorkQueue::ScheduleTasks(NamespaceToken token, TaskGraph* graph) { |
28 TaskNamespace& task_namespace = namespaces_[token]; | 58 TaskNamespace& task_namespace = namespaces_[token]; |
29 | 59 |
30 // First adjust number of dependencies to reflect completed tasks. | 60 // First adjust number of dependencies to reflect completed tasks. |
31 for (const scoped_refptr<Task>& task : task_namespace.completed_tasks) { | 61 for (const scoped_refptr<Task>& task : task_namespace.completed_tasks) { |
32 for (DependentIterator node_it(graph, task.get()); node_it; ++node_it) { | 62 for (DependentIterator node_it(graph, task.get()); node_it; ++node_it) { |
33 TaskGraph::Node& node = *node_it; | 63 TaskGraph::Node& node = *node_it; |
34 DCHECK_LT(0u, node.dependencies); | 64 DCHECK_LT(0u, node.dependencies); |
35 node.dependencies--; | 65 node.dependencies--; |
36 } | 66 } |
37 } | 67 } |
38 | 68 |
39 // Build new "ready to run" queue and remove nodes from old graph. | 69 // Build new "ready to run" queue and remove nodes from old graph. |
40 task_namespace.ready_to_run_tasks.clear(); | 70 for (auto& ready_to_run_tasks_it : task_namespace.ready_to_run_tasks) { |
| 71 ready_to_run_tasks_it.second.clear(); |
| 72 } |
41 for (const TaskGraph::Node& node : graph->nodes) { | 73 for (const TaskGraph::Node& node : graph->nodes) { |
42 // Remove any old nodes that are associated with this task. The result is | 74 // Remove any old nodes that are associated with this task. The result is |
43 // that the old graph is left with all nodes not present in this graph, | 75 // that the old graph is left with all nodes not present in this graph, |
44 // which we use below to determine what tasks need to be canceled. | 76 // which we use below to determine what tasks need to be canceled. |
45 TaskGraph::Node::Vector::iterator old_it = std::find_if( | 77 TaskGraph::Node::Vector::iterator old_it = std::find_if( |
46 task_namespace.graph.nodes.begin(), task_namespace.graph.nodes.end(), | 78 task_namespace.graph.nodes.begin(), task_namespace.graph.nodes.end(), |
47 [node](const TaskGraph::Node& other) { | 79 [node](const TaskGraph::Node& other) { |
48 return node.task == other.task; | 80 return node.task == other.task; |
49 }); | 81 }); |
50 if (old_it != task_namespace.graph.nodes.end()) { | 82 if (old_it != task_namespace.graph.nodes.end()) { |
51 std::swap(*old_it, task_namespace.graph.nodes.back()); | 83 std::swap(*old_it, task_namespace.graph.nodes.back()); |
52 task_namespace.graph.nodes.pop_back(); | 84 task_namespace.graph.nodes.pop_back(); |
53 } | 85 } |
54 | 86 |
55 // Task is not ready to run if dependencies are not yet satisfied. | 87 // Task is not ready to run if dependencies are not yet satisfied. |
56 if (node.dependencies) | 88 if (node.dependencies) |
57 continue; | 89 continue; |
58 | 90 |
59 // Skip if already finished running task. | 91 // Skip if already finished running task. |
60 if (node.task->HasFinishedRunning()) | 92 if (node.task->HasFinishedRunning()) |
61 continue; | 93 continue; |
62 | 94 |
63 // Skip if already running. | 95 // Skip if already running. |
64 if (std::find(task_namespace.running_tasks.begin(), | 96 if (std::find(task_namespace.running_tasks.begin(), |
65 task_namespace.running_tasks.end(), | 97 task_namespace.running_tasks.end(), |
66 node.task) != task_namespace.running_tasks.end()) | 98 node.task) != task_namespace.running_tasks.end()) |
67 continue; | 99 continue; |
68 | 100 |
69 task_namespace.ready_to_run_tasks.push_back( | 101 task_namespace.ready_to_run_tasks[node.category].push_back(PrioritizedTask( |
70 PrioritizedTask(node.task, &task_namespace, node.priority)); | 102 node.task, &task_namespace, node.category, node.priority)); |
71 } | 103 } |
72 | 104 |
73 // Rearrange the elements in |ready_to_run_tasks| in such a way that they | 105 // Rearrange the elements in each vector within |ready_to_run_tasks| in such a |
74 // form a heap. | 106 // way that they form a heap. |
75 std::make_heap(task_namespace.ready_to_run_tasks.begin(), | 107 for (auto& it : task_namespace.ready_to_run_tasks) { |
76 task_namespace.ready_to_run_tasks.end(), CompareTaskPriority); | 108 auto& ready_to_run_tasks = it.second; |
| 109 std::make_heap(ready_to_run_tasks.begin(), ready_to_run_tasks.end(), |
| 110 CompareTaskPriority); |
| 111 } |
77 | 112 |
78 // Swap task graph. | 113 // Swap task graph. |
79 task_namespace.graph.Swap(graph); | 114 task_namespace.graph.Swap(graph); |
80 | 115 |
81 // Determine what tasks in old graph need to be canceled. | 116 // Determine what tasks in old graph need to be canceled. |
82 for (TaskGraph::Node::Vector::iterator it = graph->nodes.begin(); | 117 for (TaskGraph::Node::Vector::iterator it = graph->nodes.begin(); |
83 it != graph->nodes.end(); ++it) { | 118 it != graph->nodes.end(); ++it) { |
84 TaskGraph::Node& node = *it; | 119 TaskGraph::Node& node = *it; |
85 | 120 |
86 // Skip if already finished running task. | 121 // Skip if already finished running task. |
87 if (node.task->HasFinishedRunning()) | 122 if (node.task->HasFinishedRunning()) |
88 continue; | 123 continue; |
89 | 124 |
90 // Skip if already running. | 125 // Skip if already running. |
91 if (std::find(task_namespace.running_tasks.begin(), | 126 if (std::find(task_namespace.running_tasks.begin(), |
92 task_namespace.running_tasks.end(), | 127 task_namespace.running_tasks.end(), |
93 node.task) != task_namespace.running_tasks.end()) | 128 node.task) != task_namespace.running_tasks.end()) |
94 continue; | 129 continue; |
95 | 130 |
96 DCHECK(std::find(task_namespace.completed_tasks.begin(), | 131 DCHECK(std::find(task_namespace.completed_tasks.begin(), |
97 task_namespace.completed_tasks.end(), | 132 task_namespace.completed_tasks.end(), |
98 node.task) == task_namespace.completed_tasks.end()); | 133 node.task) == task_namespace.completed_tasks.end()); |
99 task_namespace.completed_tasks.push_back(node.task); | 134 task_namespace.completed_tasks.push_back(node.task); |
100 } | 135 } |
101 | 136 |
102 // Build new "ready to run" task namespaces queue. | 137 // Build new "ready to run" task namespaces queue. |
103 ready_to_run_namespaces_.clear(); | 138 for (auto& ready_to_run_namespaces_it : ready_to_run_namespaces_) { |
104 for (auto& it : namespaces_) { | 139 ready_to_run_namespaces_it.second.clear(); |
105 if (!it.second.ready_to_run_tasks.empty()) | 140 } |
106 ready_to_run_namespaces_.push_back(&it.second); | 141 for (auto& namespace_it : namespaces_) { |
| 142 auto& task_namespace = namespace_it.second; |
| 143 for (auto& ready_to_run_tasks_it : task_namespace.ready_to_run_tasks) { |
| 144 auto& ready_to_run_tasks = ready_to_run_tasks_it.second; |
| 145 uint16_t category = ready_to_run_tasks_it.first; |
| 146 if (!ready_to_run_tasks.empty()) { |
| 147 ready_to_run_namespaces_[category].push_back(&task_namespace); |
| 148 } |
| 149 } |
107 } | 150 } |
108 | 151 |
109 // Rearrange the task namespaces in |ready_to_run_namespaces| in such a | 152 // Rearrange the task namespaces in |ready_to_run_namespaces| in such a |
110 // way that they form a heap. | 153 // way that they form a heap. |
111 std::make_heap(ready_to_run_namespaces_.begin(), | 154 for (auto& it : ready_to_run_namespaces_) { |
112 ready_to_run_namespaces_.end(), CompareTaskNamespacePriority); | 155 uint16_t category = it.first; |
| 156 auto& task_namespace = it.second; |
| 157 std::make_heap(task_namespace.begin(), task_namespace.end(), |
| 158 CompareTaskNamespacePriority(category)); |
| 159 } |
113 } | 160 } |
114 | 161 |
115 TaskGraphWorkQueue::PrioritizedTask TaskGraphWorkQueue::GetNextTaskToRun() { | 162 TaskGraphWorkQueue::PrioritizedTask TaskGraphWorkQueue::GetNextTaskToRun( |
116 DCHECK(!ready_to_run_namespaces_.empty()); | 163 uint16_t category) { |
| 164 TaskNamespace::Vector& ready_to_run_namespaces = |
| 165 ready_to_run_namespaces_[category]; |
| 166 DCHECK(!ready_to_run_namespaces.empty()); |
117 | 167 |
118 // Take top priority TaskNamespace from |ready_to_run_namespaces_|. | 168 // Take top priority TaskNamespace from |ready_to_run_namespaces|. |
119 std::pop_heap(ready_to_run_namespaces_.begin(), | 169 std::pop_heap(ready_to_run_namespaces.begin(), ready_to_run_namespaces.end(), |
120 ready_to_run_namespaces_.end(), CompareTaskNamespacePriority); | 170 CompareTaskNamespacePriority(category)); |
121 TaskNamespace* task_namespace = ready_to_run_namespaces_.back(); | 171 TaskNamespace* task_namespace = ready_to_run_namespaces.back(); |
122 ready_to_run_namespaces_.pop_back(); | 172 ready_to_run_namespaces.pop_back(); |
123 DCHECK(!task_namespace->ready_to_run_tasks.empty()); | 173 |
| 174 PrioritizedTask::Vector& ready_to_run_tasks = |
| 175 task_namespace->ready_to_run_tasks[category]; |
| 176 DCHECK(!ready_to_run_tasks.empty()); |
124 | 177 |
125 // Take top priority task from |ready_to_run_tasks|. | 178 // Take top priority task from |ready_to_run_tasks|. |
126 std::pop_heap(task_namespace->ready_to_run_tasks.begin(), | 179 std::pop_heap(ready_to_run_tasks.begin(), ready_to_run_tasks.end(), |
127 task_namespace->ready_to_run_tasks.end(), CompareTaskPriority); | 180 CompareTaskPriority); |
128 PrioritizedTask task = task_namespace->ready_to_run_tasks.back(); | 181 PrioritizedTask task = ready_to_run_tasks.back(); |
129 task_namespace->ready_to_run_tasks.pop_back(); | 182 ready_to_run_tasks.pop_back(); |
130 | 183 |
131 // Add task namespace back to |ready_to_run_namespaces_| if not empty after | 184 // Add task namespace back to |ready_to_run_namespaces| if not empty after |
132 // taking top priority task. | 185 // taking top priority task. |
133 if (!task_namespace->ready_to_run_tasks.empty()) { | 186 if (!ready_to_run_tasks.empty()) { |
134 ready_to_run_namespaces_.push_back(task_namespace); | 187 ready_to_run_namespaces.push_back(task_namespace); |
135 std::push_heap(ready_to_run_namespaces_.begin(), | 188 std::push_heap(ready_to_run_namespaces.begin(), |
136 ready_to_run_namespaces_.end(), | 189 ready_to_run_namespaces.end(), |
137 CompareTaskNamespacePriority); | 190 CompareTaskNamespacePriority(category)); |
138 } | 191 } |
139 | 192 |
140 // Add task to |running_tasks|. | 193 // Add task to |running_tasks|. |
141 task_namespace->running_tasks.push_back(task.task); | 194 task_namespace->running_tasks.push_back(task.task); |
142 | 195 |
143 return task; | 196 return task; |
144 } | 197 } |
145 | 198 |
146 void TaskGraphWorkQueue::CompleteTask(const PrioritizedTask& completed_task) { | 199 void TaskGraphWorkQueue::CompleteTask(const PrioritizedTask& completed_task) { |
147 TaskNamespace* task_namespace = completed_task.task_namespace; | 200 TaskNamespace* task_namespace = completed_task.task_namespace; |
148 scoped_refptr<Task> task(completed_task.task); | 201 scoped_refptr<Task> task(completed_task.task); |
149 | 202 |
150 // Remove task from |running_tasks|. | 203 // Remove task from |running_tasks|. |
151 auto it = std::find(task_namespace->running_tasks.begin(), | 204 auto it = std::find(task_namespace->running_tasks.begin(), |
152 task_namespace->running_tasks.end(), task); | 205 task_namespace->running_tasks.end(), task); |
153 DCHECK(it != task_namespace->running_tasks.end()); | 206 DCHECK(it != task_namespace->running_tasks.end()); |
154 std::swap(*it, task_namespace->running_tasks.back()); | 207 std::swap(*it, task_namespace->running_tasks.back()); |
155 task_namespace->running_tasks.pop_back(); | 208 task_namespace->running_tasks.pop_back(); |
156 | 209 |
157 // Now iterate over all dependents to decrement dependencies and check if they | 210 // Now iterate over all dependents to decrement dependencies and check if they |
158 // are ready to run. | 211 // are ready to run. |
159 bool ready_to_run_namespaces_has_heap_properties = true; | 212 bool ready_to_run_namespaces_has_heap_properties = true; |
160 for (DependentIterator it(&task_namespace->graph, task.get()); it; ++it) { | 213 for (DependentIterator it(&task_namespace->graph, task.get()); it; ++it) { |
161 TaskGraph::Node& dependent_node = *it; | 214 TaskGraph::Node& dependent_node = *it; |
162 | 215 |
163 DCHECK_LT(0u, dependent_node.dependencies); | 216 DCHECK_LT(0u, dependent_node.dependencies); |
164 dependent_node.dependencies--; | 217 dependent_node.dependencies--; |
165 // Task is ready if it has no dependencies. Add it to |ready_to_run_tasks_|. | 218 // Task is ready if it has no dependencies. Add it to |ready_to_run_tasks_|. |
166 if (!dependent_node.dependencies) { | 219 if (!dependent_node.dependencies) { |
167 bool was_empty = task_namespace->ready_to_run_tasks.empty(); | 220 PrioritizedTask::Vector& ready_to_run_tasks = |
168 task_namespace->ready_to_run_tasks.push_back(PrioritizedTask( | 221 task_namespace->ready_to_run_tasks[dependent_node.category]; |
169 dependent_node.task, task_namespace, dependent_node.priority)); | 222 |
170 std::push_heap(task_namespace->ready_to_run_tasks.begin(), | 223 bool was_empty = ready_to_run_tasks.empty(); |
171 task_namespace->ready_to_run_tasks.end(), | 224 ready_to_run_tasks.push_back( |
| 225 PrioritizedTask(dependent_node.task, task_namespace, |
| 226 dependent_node.category, dependent_node.priority)); |
| 227 std::push_heap(ready_to_run_tasks.begin(), ready_to_run_tasks.end(), |
172 CompareTaskPriority); | 228 CompareTaskPriority); |
| 229 |
173 // Task namespace is ready if it has at least one ready to run task. Add | 230 // Task namespace is ready if it has at least one ready to run task. Add |
174 // it to |ready_to_run_namespaces_| if it just become ready. | 231 // it to |ready_to_run_namespaces_| if it just become ready. |
175 if (was_empty) { | 232 if (was_empty) { |
176 DCHECK(std::find(ready_to_run_namespaces_.begin(), | 233 TaskNamespace::Vector& ready_to_run_namespaces = |
177 ready_to_run_namespaces_.end(), | 234 ready_to_run_namespaces_[dependent_node.category]; |
178 task_namespace) == ready_to_run_namespaces_.end()); | 235 |
179 ready_to_run_namespaces_.push_back(task_namespace); | 236 DCHECK(std::find(ready_to_run_namespaces.begin(), |
| 237 ready_to_run_namespaces.end(), |
| 238 task_namespace) == ready_to_run_namespaces.end()); |
| 239 ready_to_run_namespaces.push_back(task_namespace); |
180 } | 240 } |
181 ready_to_run_namespaces_has_heap_properties = false; | 241 ready_to_run_namespaces_has_heap_properties = false; |
182 } | 242 } |
183 } | 243 } |
184 | 244 |
185 // Rearrange the task namespaces in |ready_to_run_namespaces_| in such a way | 245 // Rearrange the task namespaces in |ready_to_run_namespaces_| in such a way |
186 // that they yet again form a heap. | 246 // that they yet again form a heap. |
187 if (!ready_to_run_namespaces_has_heap_properties) { | 247 if (!ready_to_run_namespaces_has_heap_properties) { |
188 std::make_heap(ready_to_run_namespaces_.begin(), | 248 for (auto& it : ready_to_run_namespaces_) { |
189 ready_to_run_namespaces_.end(), | 249 uint16_t category = it.first; |
190 CompareTaskNamespacePriority); | 250 auto& ready_to_run_namespaces = it.second; |
| 251 std::make_heap(ready_to_run_namespaces.begin(), |
| 252 ready_to_run_namespaces.end(), |
| 253 CompareTaskNamespacePriority(category)); |
| 254 } |
191 } | 255 } |
192 | 256 |
193 // Finally add task to |completed_tasks_|. | 257 // Finally add task to |completed_tasks_|. |
194 task_namespace->completed_tasks.push_back(task); | 258 task_namespace->completed_tasks.push_back(task); |
195 } | 259 } |
196 | 260 |
197 void TaskGraphWorkQueue::CollectCompletedTasks(NamespaceToken token, | 261 void TaskGraphWorkQueue::CollectCompletedTasks(NamespaceToken token, |
198 Task::Vector* completed_tasks) { | 262 Task::Vector* completed_tasks) { |
199 TaskNamespaceMap::iterator it = namespaces_.find(token); | 263 TaskNamespaceMap::iterator it = namespaces_.find(token); |
200 if (it == namespaces_.end()) | 264 if (it == namespaces_.end()) |
201 return; | 265 return; |
202 | 266 |
203 TaskNamespace& task_namespace = it->second; | 267 TaskNamespace& task_namespace = it->second; |
204 | 268 |
205 DCHECK_EQ(0u, completed_tasks->size()); | 269 DCHECK_EQ(0u, completed_tasks->size()); |
206 completed_tasks->swap(task_namespace.completed_tasks); | 270 completed_tasks->swap(task_namespace.completed_tasks); |
207 if (!HasFinishedRunningTasksInNamespace(&task_namespace)) | 271 if (!HasFinishedRunningTasksInNamespace(&task_namespace)) |
208 return; | 272 return; |
209 | 273 |
210 // Remove namespace if finished running tasks. | 274 // Remove namespace if finished running tasks. |
211 DCHECK_EQ(0u, task_namespace.completed_tasks.size()); | 275 DCHECK_EQ(0u, task_namespace.completed_tasks.size()); |
212 DCHECK_EQ(0u, task_namespace.ready_to_run_tasks.size()); | 276 DCHECK(!HasReadyToRunTasksInNamespace(&task_namespace)); |
213 DCHECK_EQ(0u, task_namespace.running_tasks.size()); | 277 DCHECK_EQ(0u, task_namespace.running_tasks.size()); |
214 namespaces_.erase(it); | 278 namespaces_.erase(it); |
215 } | 279 } |
216 | 280 |
217 bool TaskGraphWorkQueue::DependencyMismatch(const TaskGraph* graph) { | 281 bool TaskGraphWorkQueue::DependencyMismatch(const TaskGraph* graph) { |
218 // Value storage will be 0-initialized. | 282 // Value storage will be 0-initialized. |
219 base::hash_map<const Task*, size_t> dependents; | 283 base::hash_map<const Task*, size_t> dependents; |
220 for (const TaskGraph::Edge& edge : graph->edges) | 284 for (const TaskGraph::Edge& edge : graph->edges) |
221 dependents[edge.dependent]++; | 285 dependents[edge.dependent]++; |
222 | 286 |
223 for (const TaskGraph::Node& node : graph->nodes) { | 287 for (const TaskGraph::Node& node : graph->nodes) { |
224 if (dependents[node.task] != node.dependencies) | 288 if (dependents[node.task] != node.dependencies) |
225 return true; | 289 return true; |
226 } | 290 } |
227 | 291 |
228 return false; | 292 return false; |
229 } | 293 } |
230 | 294 |
231 } // namespace cc | 295 } // namespace cc |
OLD | NEW |