| Index: cc/raster/task_graph_work_queue.cc
|
| diff --git a/cc/raster/task_graph_work_queue.cc b/cc/raster/task_graph_work_queue.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..655a005b024804117fc9052a16ca20dd523868ca
|
| --- /dev/null
|
| +++ b/cc/raster/task_graph_work_queue.cc
|
| @@ -0,0 +1,231 @@
|
| +// Copyright 2014 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +#include "cc/raster/task_graph_work_queue.h"
|
| +
|
| +#include <algorithm>
|
| +#include <utility>
|
| +
|
| +#include "base/trace_event/trace_event.h"
|
| +
|
| +namespace cc {
|
| +
|
| +TaskGraphWorkQueue::TaskNamespace::TaskNamespace() {}
|
| +
|
| +TaskGraphWorkQueue::TaskNamespace::~TaskNamespace() {}
|
| +
|
| +TaskGraphWorkQueue::TaskGraphWorkQueue() : next_namespace_id_(1) {}
|
| +TaskGraphWorkQueue::~TaskGraphWorkQueue() {}
|
| +
|
| +NamespaceToken TaskGraphWorkQueue::GetNamespaceToken() {
|
| + NamespaceToken token(next_namespace_id_++);
|
| + DCHECK(namespaces_.find(token) == namespaces_.end());
|
| + return token;
|
| +}
|
| +
|
| +void TaskGraphWorkQueue::ScheduleTasks(NamespaceToken token, TaskGraph* graph) {
|
| + TaskNamespace& task_namespace = namespaces_[token];
|
| +
|
| + // First adjust number of dependencies to reflect completed tasks.
|
| + for (const scoped_refptr<Task>& task : task_namespace.completed_tasks) {
|
| + for (DependentIterator node_it(graph, task.get()); node_it; ++node_it) {
|
| + TaskGraph::Node& node = *node_it;
|
| + DCHECK_LT(0u, node.dependencies);
|
| + node.dependencies--;
|
| + }
|
| + }
|
| +
|
| + // Build new "ready to run" queue and remove nodes from old graph.
|
| + task_namespace.ready_to_run_tasks.clear();
|
| + for (const TaskGraph::Node& node : graph->nodes) {
|
| + // Remove any old nodes that are associated with this task. The result is
|
| + // that the old graph is left with all nodes not present in this graph,
|
| + // which we use below to determine what tasks need to be canceled.
|
| + TaskGraph::Node::Vector::iterator old_it = std::find_if(
|
| + task_namespace.graph.nodes.begin(), task_namespace.graph.nodes.end(),
|
| + [node](const TaskGraph::Node& other) {
|
| + return node.task == other.task;
|
| + });
|
| + if (old_it != task_namespace.graph.nodes.end()) {
|
| + std::swap(*old_it, task_namespace.graph.nodes.back());
|
| + task_namespace.graph.nodes.pop_back();
|
| + }
|
| +
|
| + // Task is not ready to run if dependencies are not yet satisfied.
|
| + if (node.dependencies)
|
| + continue;
|
| +
|
| + // Skip if already finished running task.
|
| + if (node.task->HasFinishedRunning())
|
| + continue;
|
| +
|
| + // Skip if already running.
|
| + if (std::find(task_namespace.running_tasks.begin(),
|
| + task_namespace.running_tasks.end(),
|
| + node.task) != task_namespace.running_tasks.end())
|
| + continue;
|
| +
|
| + task_namespace.ready_to_run_tasks.push_back(
|
| + PrioritizedTask(node.task, &task_namespace, node.priority));
|
| + }
|
| +
|
| + // Rearrange the elements in |ready_to_run_tasks| in such a way that they
|
| + // form a heap.
|
| + std::make_heap(task_namespace.ready_to_run_tasks.begin(),
|
| + task_namespace.ready_to_run_tasks.end(), CompareTaskPriority);
|
| +
|
| + // Swap task graph.
|
| + task_namespace.graph.Swap(graph);
|
| +
|
| + // Determine what tasks in old graph need to be canceled.
|
| + for (TaskGraph::Node::Vector::iterator it = graph->nodes.begin();
|
| + it != graph->nodes.end(); ++it) {
|
| + TaskGraph::Node& node = *it;
|
| +
|
| + // Skip if already finished running task.
|
| + if (node.task->HasFinishedRunning())
|
| + continue;
|
| +
|
| + // Skip if already running.
|
| + if (std::find(task_namespace.running_tasks.begin(),
|
| + task_namespace.running_tasks.end(),
|
| + node.task) != task_namespace.running_tasks.end())
|
| + continue;
|
| +
|
| + DCHECK(std::find(task_namespace.completed_tasks.begin(),
|
| + task_namespace.completed_tasks.end(),
|
| + node.task) == task_namespace.completed_tasks.end());
|
| + task_namespace.completed_tasks.push_back(node.task);
|
| + }
|
| +
|
| + // Build new "ready to run" task namespaces queue.
|
| + ready_to_run_namespaces_.clear();
|
| + for (auto& it : namespaces_) {
|
| + if (!it.second.ready_to_run_tasks.empty())
|
| + ready_to_run_namespaces_.push_back(&it.second);
|
| + }
|
| +
|
| + // Rearrange the task namespaces in |ready_to_run_namespaces| in such a
|
| + // way that they form a heap.
|
| + std::make_heap(ready_to_run_namespaces_.begin(),
|
| + ready_to_run_namespaces_.end(), CompareTaskNamespacePriority);
|
| +}
|
| +
|
| +TaskGraphWorkQueue::PrioritizedTask TaskGraphWorkQueue::GetNextTaskToRun() {
|
| + DCHECK(!ready_to_run_namespaces_.empty());
|
| +
|
| + // Take top priority TaskNamespace from |ready_to_run_namespaces_|.
|
| + std::pop_heap(ready_to_run_namespaces_.begin(),
|
| + ready_to_run_namespaces_.end(), CompareTaskNamespacePriority);
|
| + TaskNamespace* task_namespace = ready_to_run_namespaces_.back();
|
| + ready_to_run_namespaces_.pop_back();
|
| + DCHECK(!task_namespace->ready_to_run_tasks.empty());
|
| +
|
| + // Take top priority task from |ready_to_run_tasks|.
|
| + std::pop_heap(task_namespace->ready_to_run_tasks.begin(),
|
| + task_namespace->ready_to_run_tasks.end(), CompareTaskPriority);
|
| + PrioritizedTask task = task_namespace->ready_to_run_tasks.back();
|
| + task_namespace->ready_to_run_tasks.pop_back();
|
| +
|
| + // Add task namespace back to |ready_to_run_namespaces_| if not empty after
|
| + // taking top priority task.
|
| + if (!task_namespace->ready_to_run_tasks.empty()) {
|
| + ready_to_run_namespaces_.push_back(task_namespace);
|
| + std::push_heap(ready_to_run_namespaces_.begin(),
|
| + ready_to_run_namespaces_.end(),
|
| + CompareTaskNamespacePriority);
|
| + }
|
| +
|
| + // Add task to |running_tasks|.
|
| + task_namespace->running_tasks.push_back(task.task);
|
| +
|
| + return task;
|
| +}
|
| +
|
| +void TaskGraphWorkQueue::CompleteTask(const PrioritizedTask& completed_task) {
|
| + TaskNamespace* task_namespace = completed_task.task_namespace;
|
| + scoped_refptr<Task> task(completed_task.task);
|
| +
|
| + // Remove task from |running_tasks|.
|
| + auto it = std::find(task_namespace->running_tasks.begin(),
|
| + task_namespace->running_tasks.end(), task);
|
| + DCHECK(it != task_namespace->running_tasks.end());
|
| + std::swap(*it, task_namespace->running_tasks.back());
|
| + task_namespace->running_tasks.pop_back();
|
| +
|
| + // Now iterate over all dependents to decrement dependencies and check if they
|
| + // are ready to run.
|
| + bool ready_to_run_namespaces_has_heap_properties = true;
|
| + for (DependentIterator it(&task_namespace->graph, task.get()); it; ++it) {
|
| + TaskGraph::Node& dependent_node = *it;
|
| +
|
| + DCHECK_LT(0u, dependent_node.dependencies);
|
| + dependent_node.dependencies--;
|
| + // Task is ready if it has no dependencies. Add it to |ready_to_run_tasks_|.
|
| + if (!dependent_node.dependencies) {
|
| + bool was_empty = task_namespace->ready_to_run_tasks.empty();
|
| + task_namespace->ready_to_run_tasks.push_back(PrioritizedTask(
|
| + dependent_node.task, task_namespace, dependent_node.priority));
|
| + std::push_heap(task_namespace->ready_to_run_tasks.begin(),
|
| + task_namespace->ready_to_run_tasks.end(),
|
| + CompareTaskPriority);
|
| + // Task namespace is ready if it has at least one ready to run task. Add
|
| + // it to |ready_to_run_namespaces_| if it just become ready.
|
| + if (was_empty) {
|
| + DCHECK(std::find(ready_to_run_namespaces_.begin(),
|
| + ready_to_run_namespaces_.end(),
|
| + task_namespace) == ready_to_run_namespaces_.end());
|
| + ready_to_run_namespaces_.push_back(task_namespace);
|
| + }
|
| + ready_to_run_namespaces_has_heap_properties = false;
|
| + }
|
| + }
|
| +
|
| + // Rearrange the task namespaces in |ready_to_run_namespaces_| in such a way
|
| + // that they yet again form a heap.
|
| + if (!ready_to_run_namespaces_has_heap_properties) {
|
| + std::make_heap(ready_to_run_namespaces_.begin(),
|
| + ready_to_run_namespaces_.end(),
|
| + CompareTaskNamespacePriority);
|
| + }
|
| +
|
| + // Finally add task to |completed_tasks_|.
|
| + task_namespace->completed_tasks.push_back(task);
|
| +}
|
| +
|
| +void TaskGraphWorkQueue::CollectCompletedTasks(NamespaceToken token,
|
| + Task::Vector* completed_tasks) {
|
| + TaskNamespaceMap::iterator it = namespaces_.find(token);
|
| + if (it == namespaces_.end())
|
| + return;
|
| +
|
| + TaskNamespace& task_namespace = it->second;
|
| +
|
| + DCHECK_EQ(0u, completed_tasks->size());
|
| + completed_tasks->swap(task_namespace.completed_tasks);
|
| + if (!HasFinishedRunningTasksInNamespace(&task_namespace))
|
| + return;
|
| +
|
| + // Remove namespace if finished running tasks.
|
| + DCHECK_EQ(0u, task_namespace.completed_tasks.size());
|
| + DCHECK_EQ(0u, task_namespace.ready_to_run_tasks.size());
|
| + DCHECK_EQ(0u, task_namespace.running_tasks.size());
|
| + namespaces_.erase(it);
|
| +}
|
| +
|
| +bool TaskGraphWorkQueue::DependencyMismatch(const TaskGraph* graph) {
|
| + // Value storage will be 0-initialized.
|
| + base::hash_map<const Task*, size_t> dependents;
|
| + for (const TaskGraph::Edge& edge : graph->edges)
|
| + dependents[edge.dependent]++;
|
| +
|
| + for (const TaskGraph::Node& node : graph->nodes) {
|
| + if (dependents[node.task] != node.dependencies)
|
| + return true;
|
| + }
|
| +
|
| + return false;
|
| +}
|
| +
|
| +} // namespace cc
|
|
|