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