Changeset View
Changeset View
Standalone View
Standalone View
source/blender/blenlib/intern/task_graph.cc
- This file was added.
| /* | |||||
| * This program is free software; you can redistribute it and/or | |||||
| * modify it under the terms of the GNU General Public License | |||||
| * as published by the Free Software Foundation; either version 2 | |||||
| * of the License, or (at your option) any later version. | |||||
| * | |||||
| * This program is distributed in the hope that it will be useful, | |||||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||||
| * GNU General Public License for more details. | |||||
| * | |||||
| * You should have received a copy of the GNU General Public License | |||||
| * along with this program; if not, write to the Free Software Foundation, | |||||
| * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||||
| */ | |||||
| /** \file | |||||
| * \ingroup bli | |||||
| * | |||||
| * Task graph. | |||||
| */ | |||||
| #include "MEM_guardedalloc.h" | |||||
| #include "BLI_task.h" | |||||
| #include <list> | |||||
| #include <memory> | |||||
| #include <vector> | |||||
| #ifdef WITH_TBB | |||||
| /* Quiet top level deprecation message, unrelated to API usage here. */ | |||||
| # define TBB_SUPPRESS_DEPRECATED_MESSAGES 1 | |||||
| # include <tbb/flow_graph.h> | |||||
| # include <tbb/tbb.h> | |||||
| #endif | |||||
| /* Task Graph */ | |||||
| struct TaskGraph { | |||||
| #ifdef WITH_TBB | |||||
| /* the flow graph from TBB */ | |||||
| tbb::flow::graph tbb_graph; | |||||
| #endif | |||||
| std::vector<std::unique_ptr<TaskNode>> nodes; | |||||
| TaskNode *add_node(TaskGraphNodeRunFunction run, | |||||
| void *user_data, | |||||
| TaskGraphNodeFreeFunction free_func); | |||||
| void work_and_wait(); | |||||
| #ifdef WITH_CXX_GUARDEDALLOC | |||||
| MEM_CXX_CLASS_ALLOC_FUNCS("task_graph:TaskGraph") | |||||
| #endif | |||||
| }; | |||||
| /* TaskNode - a node in the task graph. */ | |||||
| struct TaskNode { | |||||
| /* Reference to the task_graph this node belongs to. */ | |||||
| TaskGraph *task_graph; | |||||
| /* The user function that will be executed. */ | |||||
| TaskGraphNodeRunFunction run_func; | |||||
| /* Task Data this node will forward to the run function. */ | |||||
| void *task_data; | |||||
| /* Free function of the task_data. The data will be freed when the graph is freed. When sharing | |||||
| * `task_data` between nodes make sure that only a single TaskNode does the actual freeing. */ | |||||
| TaskGraphNodeFreeFunction free_func; | |||||
| std::vector<TaskNode *> children; | |||||
| #ifdef WITH_TBB | |||||
| tbb::flow::continue_node<tbb::flow::continue_msg> tbb_node; | |||||
| tbb::flow::continue_msg run(const tbb::flow::continue_msg UNUSED(input)) | |||||
| { | |||||
| tbb::this_task_arena::isolate([this] { run_func(task_data); }); | |||||
| return tbb::flow::continue_msg(); | |||||
| } | |||||
| TaskNode(TaskGraph *task_graph, | |||||
| TaskGraphNodeRunFunction run_func, | |||||
| void *task_data, | |||||
| TaskGraphNodeFreeFunction free_func) | |||||
| : task_graph(task_graph), | |||||
| run_func(run_func), | |||||
| task_data(task_data), | |||||
| free_func(free_func), | |||||
| tbb_node(task_graph->tbb_graph, | |||||
| tbb::flow::unlimited, | |||||
| std::bind(&TaskNode::run, this, std::placeholders::_1)) | |||||
| { | |||||
| } | |||||
| #else | |||||
| TaskNode(TaskGraph *task_graph, | |||||
| TaskGraphNodeRunFunction run_func, | |||||
| void *task_data, | |||||
| TaskGraphNodeFreeFunction free_func) | |||||
| : task_graph(task_graph), run_func(run_func), task_data(task_data), free_func(free_func) | |||||
| { | |||||
| } | |||||
| #endif | |||||
| void run_single_threaded() | |||||
| { | |||||
| run_func(task_data); | |||||
| for (TaskNode *child : children) { | |||||
| child->run_single_threaded(); | |||||
| } | |||||
| } | |||||
| ~TaskNode() | |||||
| { | |||||
| if (task_data && free_func) { | |||||
| free_func(task_data); | |||||
| } | |||||
| } | |||||
| #ifdef WITH_CXX_GUARDEDALLOC | |||||
| MEM_CXX_CLASS_ALLOC_FUNCS("task_graph:TaskNode") | |||||
| #endif | |||||
| }; | |||||
| TaskNode *TaskGraph::add_node(TaskGraphNodeRunFunction run, | |||||
| void *task_data, | |||||
| TaskGraphNodeFreeFunction free_func) | |||||
| { | |||||
| TaskNode *task_node = new TaskNode(this, run, task_data, free_func); | |||||
| nodes.push_back(std::unique_ptr<TaskNode>(task_node)); | |||||
| return task_node; | |||||
| } | |||||
| void TaskGraph::work_and_wait() | |||||
| { | |||||
| #ifdef WITH_TBB | |||||
| tbb_graph.wait_for_all(); | |||||
| #endif | |||||
| } | |||||
| TaskGraph *BLI_task_graph_create(void) | |||||
| { | |||||
| return new TaskGraph(); | |||||
| } | |||||
| void BLI_task_graph_free(TaskGraph *task_graph) | |||||
| { | |||||
| BLI_assert(task_graph); | |||||
| delete task_graph; | |||||
| } | |||||
| void BLI_task_graph_work_and_wait(TaskGraph *task_graph) | |||||
| { | |||||
| BLI_assert(task_graph); | |||||
| task_graph->work_and_wait(); | |||||
| } | |||||
| struct TaskNode *BLI_task_graph_node_create(struct TaskGraph *task_graph, | |||||
| TaskGraphNodeRunFunction run, | |||||
| void *user_data, | |||||
| TaskGraphNodeFreeFunction free_func) | |||||
| { | |||||
| BLI_assert(task_graph); | |||||
| return task_graph->add_node(run, user_data, free_func); | |||||
| } | |||||
| bool BLI_task_graph_node_push_work(struct TaskNode *task_node) | |||||
| { | |||||
| BLI_assert(task_node); | |||||
| #ifdef WITH_TBB | |||||
| if (BLI_task_scheduler_num_threads() > 1) { | |||||
| return task_node->tbb_node.try_put(tbb::flow::continue_msg()); | |||||
| } | |||||
| #endif | |||||
| task_node->run_single_threaded(); | |||||
| return true; | |||||
| } | |||||
| void BLI_task_graph_edge_create(struct TaskNode *from_node, struct TaskNode *to_node) | |||||
| { | |||||
| BLI_assert(from_node); | |||||
| BLI_assert(to_node); | |||||
| BLI_assert(from_node->task_graph == to_node->task_graph); | |||||
| from_node->children.push_back(to_node); | |||||
| #ifdef WITH_TBB | |||||
| tbb::flow::make_edge(from_node->tbb_node, to_node->tbb_node); | |||||
| #endif | |||||
| } | |||||
| No newline at end of file | |||||