Changeset View
Changeset View
Standalone View
Standalone View
source/blender/depsgraph/intern/eval/deg_eval.cc
| Show First 20 Lines • Show All 55 Lines • ▼ Show 20 Lines | |||||
| #include "intern/node/deg_node_time.h" | #include "intern/node/deg_node_time.h" | ||||
| namespace DEG { | namespace DEG { | ||||
| namespace { | namespace { | ||||
| struct DepsgraphEvalState; | struct DepsgraphEvalState; | ||||
| void deg_task_run_func(TaskPool *pool, void *taskdata, int thread_id); | void deg_task_run_func(TaskPool *pool, void *taskdata); | ||||
| template<typename ScheduleFunction, typename... ScheduleFunctionArgs> | template<typename ScheduleFunction, typename... ScheduleFunctionArgs> | ||||
| void schedule_children(DepsgraphEvalState *state, | void schedule_children(DepsgraphEvalState *state, | ||||
| OperationNode *node, | OperationNode *node, | ||||
| const int thread_id, | |||||
| ScheduleFunction *schedule_function, | ScheduleFunction *schedule_function, | ||||
| ScheduleFunctionArgs... schedule_function_args); | ScheduleFunctionArgs... schedule_function_args); | ||||
| void schedule_node_to_pool(OperationNode *node, const int thread_id, TaskPool *pool) | void schedule_node_to_pool(OperationNode *node, const int UNUSED(thread_id), TaskPool *pool) | ||||
| { | { | ||||
| BLI_task_pool_push_from_thread(pool, deg_task_run_func, node, false, NULL, thread_id); | BLI_task_pool_push(pool, deg_task_run_func, node, false, NULL); | ||||
| } | } | ||||
| /* Denotes which part of dependency graph is being evaluated. */ | /* Denotes which part of dependency graph is being evaluated. */ | ||||
| enum class EvaluationStage { | enum class EvaluationStage { | ||||
| /* Stage 1: Only Copy-on-Write operations are to be evaluated, prior to anything else. | /* Stage 1: Only Copy-on-Write operations are to be evaluated, prior to anything else. | ||||
| * This allows other operations to access its dependencies when there is a dependency cycle | * This allows other operations to access its dependencies when there is a dependency cycle | ||||
| * involved. */ | * involved. */ | ||||
| COPY_ON_WRITE, | COPY_ON_WRITE, | ||||
| Show All 27 Lines | if (state->do_stats) { | ||||
| operation_node->evaluate(depsgraph); | operation_node->evaluate(depsgraph); | ||||
| operation_node->stats.current_time += PIL_check_seconds_timer() - start_time; | operation_node->stats.current_time += PIL_check_seconds_timer() - start_time; | ||||
| } | } | ||||
| else { | else { | ||||
| operation_node->evaluate(depsgraph); | operation_node->evaluate(depsgraph); | ||||
| } | } | ||||
| } | } | ||||
| void deg_task_run_func(TaskPool *pool, void *taskdata, int thread_id) | void deg_task_run_func(TaskPool *pool, void *taskdata) | ||||
| { | { | ||||
| void *userdata_v = BLI_task_pool_userdata(pool); | void *userdata_v = BLI_task_pool_user_data(pool); | ||||
| DepsgraphEvalState *state = (DepsgraphEvalState *)userdata_v; | DepsgraphEvalState *state = (DepsgraphEvalState *)userdata_v; | ||||
| /* Evaluate node. */ | /* Evaluate node. */ | ||||
| OperationNode *operation_node = reinterpret_cast<OperationNode *>(taskdata); | OperationNode *operation_node = reinterpret_cast<OperationNode *>(taskdata); | ||||
| evaluate_node(state, operation_node); | evaluate_node(state, operation_node); | ||||
| /* Schedule children. */ | /* Schedule children. */ | ||||
| BLI_task_pool_delayed_push_begin(pool, thread_id); | schedule_children(state, operation_node, schedule_node_to_pool, pool); | ||||
| schedule_children(state, operation_node, thread_id, schedule_node_to_pool, pool); | |||||
| BLI_task_pool_delayed_push_end(pool, thread_id); | |||||
| } | } | ||||
| bool check_operation_node_visible(OperationNode *op_node) | bool check_operation_node_visible(OperationNode *op_node) | ||||
| { | { | ||||
| const ComponentNode *comp_node = op_node->owner; | const ComponentNode *comp_node = op_node->owner; | ||||
| /* Special exception, copy on write component is to be always evaluated, | /* Special exception, copy on write component is to be always evaluated, | ||||
| * to keep copied "database" in a consistent state. */ | * to keep copied "database" in a consistent state. */ | ||||
| if (comp_node->type == NodeType::COPY_ON_WRITE) { | if (comp_node->type == NodeType::COPY_ON_WRITE) { | ||||
| ▲ Show 20 Lines • Show All 93 Lines • ▼ Show 20 Lines | |||||
| /* Schedule a node if it needs evaluation. | /* Schedule a node if it needs evaluation. | ||||
| * dec_parents: Decrement pending parents count, true when child nodes are | * dec_parents: Decrement pending parents count, true when child nodes are | ||||
| * scheduled after a task has been completed. | * scheduled after a task has been completed. | ||||
| */ | */ | ||||
| template<typename ScheduleFunction, typename... ScheduleFunctionArgs> | template<typename ScheduleFunction, typename... ScheduleFunctionArgs> | ||||
| void schedule_node(DepsgraphEvalState *state, | void schedule_node(DepsgraphEvalState *state, | ||||
| OperationNode *node, | OperationNode *node, | ||||
| bool dec_parents, | bool dec_parents, | ||||
| const int thread_id, | |||||
| ScheduleFunction *schedule_function, | ScheduleFunction *schedule_function, | ||||
| ScheduleFunctionArgs... schedule_function_args) | ScheduleFunctionArgs... schedule_function_args) | ||||
| { | { | ||||
| /* No need to schedule nodes of invisible ID. */ | /* No need to schedule nodes of invisible ID. */ | ||||
| if (!check_operation_node_visible(node)) { | if (!check_operation_node_visible(node)) { | ||||
| return; | return; | ||||
| } | } | ||||
| /* No need to schedule operations which are not tagged for update, they are | /* No need to schedule operations which are not tagged for update, they are | ||||
| Show All 16 Lines | void schedule_node(DepsgraphEvalState *state, | ||||
| if (!need_evaluate_operation_at_stage(state, node)) { | if (!need_evaluate_operation_at_stage(state, node)) { | ||||
| return; | return; | ||||
| } | } | ||||
| /* Actually schedule the node. */ | /* Actually schedule the node. */ | ||||
| bool is_scheduled = atomic_fetch_and_or_uint8((uint8_t *)&node->scheduled, (uint8_t) true); | bool is_scheduled = atomic_fetch_and_or_uint8((uint8_t *)&node->scheduled, (uint8_t) true); | ||||
| if (!is_scheduled) { | if (!is_scheduled) { | ||||
| if (node->is_noop()) { | if (node->is_noop()) { | ||||
| /* skip NOOP node, schedule children right away */ | /* skip NOOP node, schedule children right away */ | ||||
| schedule_children(state, node, thread_id, schedule_function, schedule_function_args...); | schedule_children(state, node, schedule_function, schedule_function_args...); | ||||
| } | } | ||||
| else { | else { | ||||
| /* children are scheduled once this task is completed */ | /* children are scheduled once this task is completed */ | ||||
| schedule_function(node, thread_id, schedule_function_args...); | schedule_function(node, 0, schedule_function_args...); | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| template<typename ScheduleFunction, typename... ScheduleFunctionArgs> | template<typename ScheduleFunction, typename... ScheduleFunctionArgs> | ||||
| void schedule_graph(DepsgraphEvalState *state, | void schedule_graph(DepsgraphEvalState *state, | ||||
| ScheduleFunction *schedule_function, | ScheduleFunction *schedule_function, | ||||
| ScheduleFunctionArgs... schedule_function_args) | ScheduleFunctionArgs... schedule_function_args) | ||||
| { | { | ||||
| for (OperationNode *node : state->graph->operations) { | for (OperationNode *node : state->graph->operations) { | ||||
| schedule_node(state, node, false, -1, schedule_function, schedule_function_args...); | schedule_node(state, node, false, schedule_function, schedule_function_args...); | ||||
| } | } | ||||
| } | } | ||||
| template<typename ScheduleFunction, typename... ScheduleFunctionArgs> | template<typename ScheduleFunction, typename... ScheduleFunctionArgs> | ||||
| void schedule_children(DepsgraphEvalState *state, | void schedule_children(DepsgraphEvalState *state, | ||||
| OperationNode *node, | OperationNode *node, | ||||
| const int thread_id, | |||||
| ScheduleFunction *schedule_function, | ScheduleFunction *schedule_function, | ||||
| ScheduleFunctionArgs... schedule_function_args) | ScheduleFunctionArgs... schedule_function_args) | ||||
| { | { | ||||
| for (Relation *rel : node->outlinks) { | for (Relation *rel : node->outlinks) { | ||||
| OperationNode *child = (OperationNode *)rel->to; | OperationNode *child = (OperationNode *)rel->to; | ||||
| BLI_assert(child->type == NodeType::OPERATION); | BLI_assert(child->type == NodeType::OPERATION); | ||||
| if (child->scheduled) { | if (child->scheduled) { | ||||
| /* Happens when having cyclic dependencies. */ | /* Happens when having cyclic dependencies. */ | ||||
| continue; | continue; | ||||
| } | } | ||||
| schedule_node(state, | schedule_node(state, | ||||
| child, | child, | ||||
| (rel->flag & RELATION_FLAG_CYCLIC) == 0, | (rel->flag & RELATION_FLAG_CYCLIC) == 0, | ||||
| thread_id, | |||||
| schedule_function, | schedule_function, | ||||
| schedule_function_args...); | schedule_function_args...); | ||||
| } | } | ||||
| } | } | ||||
| void schedule_node_to_queue(OperationNode *node, | void schedule_node_to_queue(OperationNode *node, | ||||
| const int /*thread_id*/, | const int /*thread_id*/, | ||||
| GSQueue *evaluation_queue) | GSQueue *evaluation_queue) | ||||
| { | { | ||||
| BLI_gsqueue_push(evaluation_queue, &node); | BLI_gsqueue_push(evaluation_queue, &node); | ||||
| } | } | ||||
| void evaluate_graph_single_threaded(DepsgraphEvalState *state) | void evaluate_graph_single_threaded(DepsgraphEvalState *state) | ||||
| { | { | ||||
| GSQueue *evaluation_queue = BLI_gsqueue_new(sizeof(OperationNode *)); | GSQueue *evaluation_queue = BLI_gsqueue_new(sizeof(OperationNode *)); | ||||
| schedule_graph(state, schedule_node_to_queue, evaluation_queue); | schedule_graph(state, schedule_node_to_queue, evaluation_queue); | ||||
| while (!BLI_gsqueue_is_empty(evaluation_queue)) { | while (!BLI_gsqueue_is_empty(evaluation_queue)) { | ||||
| OperationNode *operation_node; | OperationNode *operation_node; | ||||
| BLI_gsqueue_pop(evaluation_queue, &operation_node); | BLI_gsqueue_pop(evaluation_queue, &operation_node); | ||||
| evaluate_node(state, operation_node); | evaluate_node(state, operation_node); | ||||
| schedule_children(state, operation_node, 0, schedule_node_to_queue, evaluation_queue); | schedule_children(state, operation_node, schedule_node_to_queue, evaluation_queue); | ||||
| } | } | ||||
| BLI_gsqueue_free(evaluation_queue); | BLI_gsqueue_free(evaluation_queue); | ||||
| } | } | ||||
| void depsgraph_ensure_view_layer(Depsgraph *graph) | void depsgraph_ensure_view_layer(Depsgraph *graph) | ||||
| { | { | ||||
| /* We update copy-on-write scene in the following cases: | /* We update copy-on-write scene in the following cases: | ||||
| * - It was not expanded yet. | * - It was not expanded yet. | ||||
| * - It was tagged for update of CoW component. | * - It was tagged for update of CoW component. | ||||
| * This allows us to have proper view layer pointer. */ | * This allows us to have proper view layer pointer. */ | ||||
| Scene *scene_cow = graph->scene_cow; | Scene *scene_cow = graph->scene_cow; | ||||
| if (deg_copy_on_write_is_expanded(&scene_cow->id) && | if (deg_copy_on_write_is_expanded(&scene_cow->id) && | ||||
| (scene_cow->id.recalc & ID_RECALC_COPY_ON_WRITE) == 0) { | (scene_cow->id.recalc & ID_RECALC_COPY_ON_WRITE) == 0) { | ||||
| return; | return; | ||||
| } | } | ||||
| const IDNode *scene_id_node = graph->find_id_node(&graph->scene->id); | const IDNode *scene_id_node = graph->find_id_node(&graph->scene->id); | ||||
| deg_update_copy_on_write_datablock(graph, scene_id_node); | deg_update_copy_on_write_datablock(graph, scene_id_node); | ||||
| } | } | ||||
| } // namespace | } // namespace | ||||
| static TaskPool *deg_evaluate_task_pool_create(DepsgraphEvalState *state) | |||||
| { | |||||
| if (G.debug & G_DEBUG_DEPSGRAPH_NO_THREADS) { | |||||
| return BLI_task_pool_create_no_threads(state); | |||||
| } | |||||
| else { | |||||
| return BLI_task_pool_create(state, TASK_PRIORITY_HIGH); | |||||
| } | |||||
| } | |||||
| /** | /** | ||||
| * Evaluate all nodes tagged for updating, | * Evaluate all nodes tagged for updating, | ||||
| * \warning This is usually done as part of main loop, but may also be | * \warning This is usually done as part of main loop, but may also be | ||||
| * called from frame-change update. | * called from frame-change update. | ||||
| * | * | ||||
| * \note Time sources should be all valid! | * \note Time sources should be all valid! | ||||
| */ | */ | ||||
| void deg_evaluate_on_refresh(Depsgraph *graph) | void deg_evaluate_on_refresh(Depsgraph *graph) | ||||
| { | { | ||||
| /* Nothing to update, early out. */ | /* Nothing to update, early out. */ | ||||
| if (BLI_gset_len(graph->entry_tags) == 0) { | if (BLI_gset_len(graph->entry_tags) == 0) { | ||||
| return; | return; | ||||
| } | } | ||||
| graph->debug.begin_graph_evaluation(); | graph->debug.begin_graph_evaluation(); | ||||
| graph->is_evaluating = true; | graph->is_evaluating = true; | ||||
| depsgraph_ensure_view_layer(graph); | depsgraph_ensure_view_layer(graph); | ||||
| /* Set up evaluation state. */ | /* Set up evaluation state. */ | ||||
| DepsgraphEvalState state; | DepsgraphEvalState state; | ||||
| state.graph = graph; | state.graph = graph; | ||||
| state.do_stats = graph->debug.do_time_debug(); | state.do_stats = graph->debug.do_time_debug(); | ||||
| state.need_single_thread_pass = false; | state.need_single_thread_pass = false; | ||||
| /* Set up task scheduler and pull for threaded evaluation. */ | |||||
| TaskScheduler *task_scheduler; | |||||
| bool need_free_scheduler; | |||||
| if (G.debug & G_DEBUG_DEPSGRAPH_NO_THREADS) { | |||||
| task_scheduler = BLI_task_scheduler_create(1); | |||||
| need_free_scheduler = true; | |||||
| } | |||||
| else { | |||||
| task_scheduler = BLI_task_scheduler_get(); | |||||
| need_free_scheduler = false; | |||||
| } | |||||
| TaskPool *task_pool = BLI_task_pool_create_suspended(task_scheduler, &state, TASK_PRIORITY_HIGH); | |||||
| /* Prepare all nodes for evaluation. */ | /* Prepare all nodes for evaluation. */ | ||||
| initialize_execution(&state, graph); | initialize_execution(&state, graph); | ||||
| /* Do actual evaluation now. */ | /* Do actual evaluation now. */ | ||||
| /* First, process all Copy-On-Write nodes. */ | /* First, process all Copy-On-Write nodes. */ | ||||
| state.stage = EvaluationStage::COPY_ON_WRITE; | state.stage = EvaluationStage::COPY_ON_WRITE; | ||||
| TaskPool *task_pool = deg_evaluate_task_pool_create(&state); | |||||
| schedule_graph(&state, schedule_node_to_pool, task_pool); | schedule_graph(&state, schedule_node_to_pool, task_pool); | ||||
| BLI_task_pool_work_wait_and_reset(task_pool); | BLI_task_pool_work_and_wait(task_pool); | ||||
| /* After that, process all other nodes. */ | /* After that, process all other nodes. */ | ||||
| state.stage = EvaluationStage::THREADED_EVALUATION; | state.stage = EvaluationStage::THREADED_EVALUATION; | ||||
| schedule_graph(&state, schedule_node_to_pool, task_pool); | schedule_graph(&state, schedule_node_to_pool, task_pool); | ||||
| BLI_task_pool_work_and_wait(task_pool); | BLI_task_pool_work_and_wait(task_pool); | ||||
| BLI_task_pool_free(task_pool); | BLI_task_pool_free(task_pool); | ||||
| if (state.need_single_thread_pass) { | if (state.need_single_thread_pass) { | ||||
| state.stage = EvaluationStage::SINGLE_THREADED_WORKAROUND; | state.stage = EvaluationStage::SINGLE_THREADED_WORKAROUND; | ||||
| evaluate_graph_single_threaded(&state); | evaluate_graph_single_threaded(&state); | ||||
| } | } | ||||
| /* Finalize statistics gathering. This is because we only gather single | /* Finalize statistics gathering. This is because we only gather single | ||||
| * operation timing here, without aggregating anything to avoid any extra | * operation timing here, without aggregating anything to avoid any extra | ||||
| * synchronization. */ | * synchronization. */ | ||||
| if (state.do_stats) { | if (state.do_stats) { | ||||
| deg_eval_stats_aggregate(graph); | deg_eval_stats_aggregate(graph); | ||||
| } | } | ||||
| /* Clear any uncleared tags - just in case. */ | /* Clear any uncleared tags - just in case. */ | ||||
| deg_graph_clear_tags(graph); | deg_graph_clear_tags(graph); | ||||
| if (need_free_scheduler) { | |||||
| BLI_task_scheduler_free(task_scheduler); | |||||
| } | |||||
| graph->is_evaluating = false; | graph->is_evaluating = false; | ||||
| graph->debug.end_graph_evaluation(); | graph->debug.end_graph_evaluation(); | ||||
| } | } | ||||
| } // namespace DEG | } // namespace DEG | ||||