diff options
Diffstat (limited to 'source/blender/depsgraph/intern/eval/deg_eval_flush.cc')
-rw-r--r-- | source/blender/depsgraph/intern/eval/deg_eval_flush.cc | 345 |
1 files changed, 223 insertions, 122 deletions
diff --git a/source/blender/depsgraph/intern/eval/deg_eval_flush.cc b/source/blender/depsgraph/intern/eval/deg_eval_flush.cc index 7c6c25bef0d..e7764cf5a27 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval_flush.cc +++ b/source/blender/depsgraph/intern/eval/deg_eval_flush.cc @@ -35,18 +35,19 @@ // TODO(sergey): Use some sort of wrapper. #include <deque> -extern "C" { -#include "DNA_object_types.h" - #include "BLI_utildefines.h" #include "BLI_task.h" #include "BLI_ghash.h" -#include "DEG_depsgraph.h" +extern "C" { +#include "DNA_object_types.h" } /* extern "C" */ +#include "DEG_depsgraph.h" + #include "intern/nodes/deg_node.h" #include "intern/nodes/deg_node_component.h" +#include "intern/nodes/deg_node_id.h" #include "intern/nodes/deg_node_operation.h" #include "intern/depsgraph_intern.h" @@ -54,158 +55,253 @@ extern "C" { namespace DEG { +enum { + ID_STATE_NONE = 0, + ID_STATE_MODIFIED = 1, +}; + +enum { + COMPONENT_STATE_NONE = 0, + COMPONENT_STATE_SCHEDULED = 1, + COMPONENT_STATE_DONE = 2, +}; + +typedef std::deque<OperationDepsNode *> FlushQueue; + namespace { // TODO(sergey): De-duplicate with depsgraph_tag,cc void lib_id_recalc_tag(Main *bmain, ID *id) { - id->tag |= LIB_TAG_ID_RECALC; + id->recalc |= ID_RECALC; DEG_id_type_tag(bmain, GS(id->name)); } void lib_id_recalc_data_tag(Main *bmain, ID *id) { - id->tag |= LIB_TAG_ID_RECALC_DATA; + id->recalc |= ID_RECALC_DATA; DEG_id_type_tag(bmain, GS(id->name)); } -} /* namespace */ - -typedef std::deque<OperationDepsNode *> FlushQueue; - -static void flush_init_func(void *data_v, int i) +void flush_init_operation_node_func( + void *__restrict data_v, + const int i, + const ParallelRangeTLS *__restrict /*tls*/) { - /* ID node's done flag is used to avoid multiple editors update - * for the same ID. - */ Depsgraph *graph = (Depsgraph *)data_v; OperationDepsNode *node = graph->operations[i]; - ComponentDepsNode *comp_node = node->owner; - IDDepsNode *id_node = comp_node->owner; - id_node->done = 0; - comp_node->done = 0; node->scheduled = false; } -/* Flush updates from tagged nodes outwards until all affected nodes - * are tagged. - */ -void deg_graph_flush_updates(Main *bmain, Depsgraph *graph) +void flush_init_id_node_func( + void *__restrict data_v, + const int i, + const ParallelRangeTLS *__restrict /*tls*/) { - /* Sanity check. */ - if (graph == NULL) { - return; - } + Depsgraph *graph = (Depsgraph *)data_v; + IDDepsNode *id_node = graph->id_nodes[i]; + id_node->done = ID_STATE_NONE; + GHASH_FOREACH_BEGIN(ComponentDepsNode *, comp_node, id_node->components) + comp_node->done = COMPONENT_STATE_NONE; + GHASH_FOREACH_END(); +} - /* Nothing to update, early out. */ - if (BLI_gset_size(graph->entry_tags) == 0) { - return; +BLI_INLINE void flush_prepare(Depsgraph *graph) +{ + { + const int num_operations = graph->operations.size(); + ParallelRangeSettings settings; + BLI_parallel_range_settings_defaults(&settings); + settings.min_iter_per_thread = 1024; + BLI_task_parallel_range(0, num_operations, + graph, + flush_init_operation_node_func, + &settings); } + { + const int num_id_nodes = graph->id_nodes.size(); + ParallelRangeSettings settings; + BLI_parallel_range_settings_defaults(&settings); + settings.min_iter_per_thread = 1024; + BLI_task_parallel_range(0, num_id_nodes, + graph, + flush_init_id_node_func, + &settings); + } +} - /* TODO(sergey): With a bit of flag magic we can get rid of this - * extra loop. - */ - const int num_operations = graph->operations.size(); - const bool do_threads = num_operations > 256; - BLI_task_parallel_range(0, - num_operations, - graph, - flush_init_func, - do_threads); - - FlushQueue queue; - /* Starting from the tagged "entry" nodes, flush outwards... */ - /* NOTE: Also need to ensure that for each of these, there is a path back to - * root, or else they won't be done. - * NOTE: Count how many nodes we need to handle - entry nodes may be - * component nodes which don't count for this purpose! - */ - GSET_FOREACH_BEGIN(OperationDepsNode *, node, graph->entry_tags) +BLI_INLINE void flush_schedule_entrypoints(Depsgraph *graph, FlushQueue *queue) +{ + GSET_FOREACH_BEGIN(OperationDepsNode *, op_node, graph->entry_tags) { - queue.push_back(node); - node->scheduled = true; + queue->push_back(op_node); + op_node->scheduled = true; } GSET_FOREACH_END(); +} - int num_flushed_objects = 0; - while (!queue.empty()) { - OperationDepsNode *node = queue.front(); - queue.pop_front(); - - for (;;) { - node->flag |= DEPSOP_FLAG_NEEDS_UPDATE; - - ComponentDepsNode *comp_node = node->owner; - IDDepsNode *id_node = comp_node->owner; - - ID *id = id_node->id; - if(id_node->done == 0) { - deg_editors_id_update(bmain, id); - lib_id_recalc_tag(bmain, id); - /* TODO(sergey): For until we've got proper data nodes in the graph. */ - lib_id_recalc_data_tag(bmain, id); - } +BLI_INLINE void flush_handle_id_node(IDDepsNode *id_node) +{ + id_node->done = ID_STATE_MODIFIED; +} - if(comp_node->done == 0) { - Object *object = NULL; - if (GS(id->name) == ID_OB) { - object = (Object *)id; - if(id_node->done == 0) { - ++num_flushed_objects; - } - } - foreach (OperationDepsNode *op, comp_node->operations) { - op->flag |= DEPSOP_FLAG_NEEDS_UPDATE; - } - if (object != NULL) { - /* This code is used to preserve those areas which does - * direct object update, - * - * Plus it ensures visibility changes and relations and - * layers visibility update has proper flags to work with. - */ - if (comp_node->type == DEPSNODE_TYPE_ANIMATION) { - object->recalc |= OB_RECALC_TIME; - } - else if (comp_node->type == DEPSNODE_TYPE_TRANSFORM) { - object->recalc |= OB_RECALC_OB; - } - else { - object->recalc |= OB_RECALC_DATA; - } - } - } +/* TODO(sergey): We can reduce number of arguments here. */ +BLI_INLINE void flush_handle_component_node(Depsgraph * /*graph*/, + IDDepsNode *id_node, + ComponentDepsNode *comp_node, + FlushQueue *queue) +{ + /* We only handle component once. */ + if (comp_node->done == COMPONENT_STATE_DONE) { + return; + } + comp_node->done = COMPONENT_STATE_DONE; + /* Tag all required operations in component for update. */ + foreach (OperationDepsNode *op, comp_node->operations) { + op->flag |= DEPSOP_FLAG_NEEDS_UPDATE; + } + if (GS(id_node->id->name) == ID_OB) { + Object *object = (Object *)id_node->id; + /* This code is used to preserve those areas which does + * direct object update, + * + * Plus it ensures visibility changes and relations and + * layers visibility update has proper flags to work with. + */ + switch (comp_node->type) { + case DEG_NODE_TYPE_UNDEFINED: + case DEG_NODE_TYPE_OPERATION: + case DEG_NODE_TYPE_TIMESOURCE: + case DEG_NODE_TYPE_ID_REF: + case DEG_NODE_TYPE_SEQUENCER: + case NUM_DEG_NODE_TYPES: + /* Ignore, does not translate to object component. */ + BLI_assert(!"This should never happen!"); + break; + case DEG_NODE_TYPE_ANIMATION: + object->recalc |= OB_RECALC_TIME; + break; + case DEG_NODE_TYPE_TRANSFORM: + object->recalc |= OB_RECALC_OB; + break; + case DEG_NODE_TYPE_GEOMETRY: + case DEG_NODE_TYPE_EVAL_POSE: + case DEG_NODE_TYPE_BONE: + case DEG_NODE_TYPE_EVAL_PARTICLES: + case DEG_NODE_TYPE_SHADING: + case DEG_NODE_TYPE_CACHE: + case DEG_NODE_TYPE_PROXY: + object->recalc |= OB_RECALC_DATA; + break; + case DEG_NODE_TYPE_PARAMETERS: + break; + } + } + /* When some target changes bone, we might need to re-run the + * whole IK solver, otherwise result might be unpredictable. + */ + if (comp_node->type == DEG_NODE_TYPE_BONE) { + ComponentDepsNode *pose_comp = + id_node->find_component(DEG_NODE_TYPE_EVAL_POSE); + BLI_assert(pose_comp != NULL); + if (pose_comp->done == COMPONENT_STATE_NONE) { + queue->push_front(pose_comp->get_entry_operation()); + pose_comp->done = COMPONENT_STATE_SCHEDULED; + } + } +} - id_node->done = 1; - comp_node->done = 1; - - /* Flush to nodes along links... */ - if (node->outlinks.size() == 1) { - OperationDepsNode *to_node = (OperationDepsNode *)node->outlinks[0]->to; - if (to_node->scheduled == false) { - to_node->scheduled = true; - node = to_node; - } - else { - break; - } +/* Schedule children of the given operation node for traversal. + * + * One of the children will by-pass the queue and will be returned as a function + * return value, so it can start being handled right away, without building too + * much of a queue. + */ +BLI_INLINE OperationDepsNode *flush_schedule_children( + OperationDepsNode *op_node, + FlushQueue *queue) +{ + OperationDepsNode *result = NULL; + foreach (DepsRelation *rel, op_node->outlinks) { + if (rel->flag & DEPSREL_FLAG_NO_FLUSH) { + continue; + } + OperationDepsNode *to_node = (OperationDepsNode *)rel->to; + if (to_node->scheduled == false) { + if (result != NULL) { + queue->push_front(to_node); } else { - foreach (DepsRelation *rel, node->outlinks) { - OperationDepsNode *to_node = (OperationDepsNode *)rel->to; - if (to_node->scheduled == false) { - queue.push_front(to_node); - to_node->scheduled = true; - } - } - break; + result = to_node; } + to_node->scheduled = true; } } - DEG_DEBUG_PRINTF("Update flushed to %d objects\n", num_flushed_objects); + return result; } -static void graph_clear_func(void *data_v, int i) +BLI_INLINE void flush_editors_id_update(Main *bmain, + Depsgraph *graph) +{ + foreach (IDDepsNode *id_node, graph->id_nodes) { + if (id_node->done != ID_STATE_MODIFIED) { + continue; + } + /* TODO(sergey): Do we need to pass original or evaluated ID here? */ + ID *id = id_node->id; + deg_editors_id_update(bmain, id); + lib_id_recalc_tag(bmain, id); + /* TODO(sergey): For until we've got proper data nodes in the graph. */ + lib_id_recalc_data_tag(bmain, id); + } +} + +} // namespace + +/* Flush updates from tagged nodes outwards until all affected nodes + * are tagged. + */ +void deg_graph_flush_updates(Main *bmain, Depsgraph *graph) +{ + /* Sanity checks. */ + BLI_assert(bmain != NULL); + BLI_assert(graph != NULL); + /* Nothing to update, early out. */ + if (BLI_gset_len(graph->entry_tags) == 0) { + return; + } + /* Reset all flags, get ready for the flush. */ + flush_prepare(graph); + /* Starting from the tagged "entry" nodes, flush outwards. */ + FlushQueue queue; + flush_schedule_entrypoints(graph, &queue); + /* Do actual flush. */ + while (!queue.empty()) { + OperationDepsNode *op_node = queue.front(); + queue.pop_front(); + while (op_node != NULL) { + /* Tag operation as required for update. */ + op_node->flag |= DEPSOP_FLAG_NEEDS_UPDATE; + /* Inform corresponding ID and component nodes about the change. */ + ComponentDepsNode *comp_node = op_node->owner; + IDDepsNode *id_node = comp_node->owner; + flush_handle_id_node(id_node); + flush_handle_component_node(graph, + id_node, + comp_node, + &queue); + /* Flush to nodes along links. */ + op_node = flush_schedule_children(op_node, &queue); + } + } + /* Inform editors about all changes. */ + flush_editors_id_update(bmain, graph); +} + +static void graph_clear_func( + void *__restrict data_v, + const int i, + const ParallelRangeTLS *__restrict /*tls*/) { Depsgraph *graph = (Depsgraph *)data_v; OperationDepsNode *node = graph->operations[i]; @@ -218,8 +314,13 @@ void deg_graph_clear_tags(Depsgraph *graph) { /* Go over all operation nodes, clearing tags. */ const int num_operations = graph->operations.size(); - const bool do_threads = num_operations > 256; - BLI_task_parallel_range(0, num_operations, graph, graph_clear_func, do_threads); + ParallelRangeSettings settings; + BLI_parallel_range_settings_defaults(&settings); + settings.min_iter_per_thread = 1024; + BLI_task_parallel_range(0, num_operations, + graph, + graph_clear_func, + &settings); /* Clear any entry tags which haven't been flushed. */ BLI_gset_clear(graph->entry_tags, NULL); } |