diff options
Diffstat (limited to 'source/blender/depsgraph/intern')
36 files changed, 3506 insertions, 1062 deletions
diff --git a/source/blender/depsgraph/intern/builder/deg_builder.cc b/source/blender/depsgraph/intern/builder/deg_builder.cc index 5713297d658..2fcad233044 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder.cc @@ -30,142 +30,38 @@ #include "intern/builder/deg_builder.h" -#include "DNA_anim_types.h" #include "DNA_object_types.h" #include "DNA_ID.h" -#include "BLI_utildefines.h" -#include "BLI_ghash.h" -#include "BLI_stack.h" - #include "intern/depsgraph.h" #include "intern/depsgraph_types.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 "util/deg_util_foreach.h" -#include <cstdio> +#include "DEG_depsgraph.h" namespace DEG { -static bool check_object_needs_evaluation(Object *object) +void deg_graph_build_finalize(Main *bmain, Depsgraph *graph) { - if (object->recalc & OB_RECALC_ALL) { - /* Object is tagged for update anyway, no need to re-tag it. */ - return false; - } - if (object->type == OB_MESH) { - return object->derivedFinal == NULL; - } - else if (ELEM(object->type, - OB_CURVE, OB_SURF, OB_FONT, OB_MBALL, OB_LATTICE)) - { - return object->curve_cache == NULL; - } - return false; -} - -void deg_graph_build_flush_layers(Depsgraph *graph) -{ - BLI_Stack *stack = BLI_stack_new(sizeof(OperationDepsNode *), - "DEG flush layers stack"); - foreach (OperationDepsNode *node, graph->operations) { - IDDepsNode *id_node = node->owner->owner; - node->done = 0; - node->num_links_pending = 0; - foreach (DepsRelation *rel, node->outlinks) { - if ((rel->from->type == DEG_NODE_TYPE_OPERATION) && - (rel->flag & DEPSREL_FLAG_CYCLIC) == 0) - { - ++node->num_links_pending; - } - } - if (node->num_links_pending == 0) { - BLI_stack_push(stack, &node); - node->done = 1; - } - node->owner->layers = id_node->layers; - id_node->id->tag |= LIB_TAG_DOIT; - } - while (!BLI_stack_is_empty(stack)) { - OperationDepsNode *node; - BLI_stack_pop(stack, &node); - /* Flush layers to parents. */ - foreach (DepsRelation *rel, node->inlinks) { - if (rel->from->type == DEG_NODE_TYPE_OPERATION) { - OperationDepsNode *from = (OperationDepsNode *)rel->from; - from->owner->layers |= node->owner->layers; - } - } - /* Schedule parent nodes. */ - foreach (DepsRelation *rel, node->inlinks) { - if (rel->from->type == DEG_NODE_TYPE_OPERATION) { - OperationDepsNode *from = (OperationDepsNode *)rel->from; - if ((rel->flag & DEPSREL_FLAG_CYCLIC) == 0) { - BLI_assert(from->num_links_pending > 0); - --from->num_links_pending; - } - if (from->num_links_pending == 0 && from->done == 0) { - BLI_stack_push(stack, &from); - from->done = 1; - } - } - } - } - BLI_stack_free(stack); -} - -void deg_graph_build_finalize(Depsgraph *graph) -{ - /* STEP 1: Make sure new invisible dependencies are ready for use. - * - * TODO(sergey): This might do a bit of extra tagging, but it's kinda nice - * to do it ahead of a time and don't spend time on flushing updates on - * every frame change. - */ - foreach (IDDepsNode *id_node, graph->id_nodes) { - if (id_node->layers == 0) { - ID *id = id_node->id; - if (GS(id->name) == ID_OB) { - Object *object = (Object *)id; - if (check_object_needs_evaluation(object)) { - id_node->tag_update(graph); - } - } - } - } - /* STEP 2: Flush visibility layers from children to parent. */ - deg_graph_build_flush_layers(graph); - /* STEP 3: Re-tag IDs for update if it was tagged before the relations + const bool use_copy_on_write = DEG_depsgraph_use_copy_on_write(); + /* Re-tag IDs for update if it was tagged before the relations * update tag. */ foreach (IDDepsNode *id_node, graph->id_nodes) { - GHASH_FOREACH_BEGIN(ComponentDepsNode *, comp, id_node->components) - { - id_node->layers |= comp->layers; + ID *id = id_node->id_orig; + id_node->finalize_build(graph); + if ((id->recalc & ID_RECALC_ALL)) { + id_node->tag_update(graph); } - GHASH_FOREACH_END(); - - if ((id_node->layers & graph->layers) != 0 || graph->layers == 0) { - ID *id = id_node->id; - if ((id->recalc & ID_RECALC_ALL) && - (id->tag & LIB_TAG_DOIT)) - { - id_node->tag_update(graph); - id->tag &= ~LIB_TAG_DOIT; - } - else if (GS(id->name) == ID_OB) { - Object *object = (Object *)id; - if (object->recalc & OB_RECALC_ALL) { - id_node->tag_update(graph); - id->tag &= ~LIB_TAG_DOIT; - } - } + /* TODO(sergey): This is not ideal at all, since this forces + * re-evaluaiton of the whole tree. + */ + if (use_copy_on_write) { + DEG_id_tag_update_ex(bmain, id_node->id_orig, DEG_TAG_COPY_ON_WRITE); } - id_node->finalize_build(); } } diff --git a/source/blender/depsgraph/intern/builder/deg_builder.h b/source/blender/depsgraph/intern/builder/deg_builder.h index b8ea8c8e599..c7ff668504c 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder.h +++ b/source/blender/depsgraph/intern/builder/deg_builder.h @@ -30,15 +30,12 @@ #pragma once -#include "intern/depsgraph_types.h" - -struct FCurve; +struct Main; namespace DEG { struct Depsgraph; -void deg_graph_build_finalize(struct Depsgraph *graph); -void deg_graph_build_flush_layers(struct Depsgraph *graph); +void deg_graph_build_finalize(struct Main *bmain, struct Depsgraph *graph); } // namespace DEG diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc index b268d311a41..3502ca69414 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc @@ -62,6 +62,7 @@ extern "C" { #include "DNA_node_types.h" #include "DNA_particle_types.h" #include "DNA_object_types.h" +#include "DNA_lightprobe_types.h" #include "DNA_rigidbody_types.h" #include "DNA_scene_types.h" #include "DNA_texture_types.h" @@ -72,7 +73,6 @@ extern "C" { #include "BKE_animsys.h" #include "BKE_constraint.h" #include "BKE_curve.h" -#include "BKE_depsgraph.h" #include "BKE_effect.h" #include "BKE_fcurve.h" #include "BKE_idcode.h" @@ -103,6 +103,7 @@ extern "C" { #include "DEG_depsgraph_build.h" #include "intern/builder/deg_builder.h" +#include "intern/eval/deg_eval_copy_on_write.h" #include "intern/nodes/deg_node.h" #include "intern/nodes/deg_node_component.h" #include "intern/nodes/deg_node_id.h" @@ -127,7 +128,9 @@ static void modifier_walk(void *user_data, { BuilderWalkUserData *data = (BuilderWalkUserData *)user_data; if (*obpoin) { - data->builder->build_object(NULL, *obpoin); + data->builder->build_object(NULL, + *obpoin, + DEG_ID_LINKED_INDIRECTLY); } } @@ -140,11 +143,19 @@ void constraint_walk(bConstraint * /*con*/, if (*idpoin) { ID *id = *idpoin; if (GS(id->name) == ID_OB) { - data->builder->build_object(NULL, (Object *)id); + data->builder->build_object(NULL, + (Object *)id, + DEG_ID_LINKED_INDIRECTLY); } } } +void free_copy_on_write_datablock(void *id_v) +{ + ID *id = (ID *)id_v; + deg_free_copy_on_write_datablock(id); +} + } /* namespace */ /* ************ */ @@ -155,17 +166,51 @@ void constraint_walk(bConstraint * /*con*/, DepsgraphNodeBuilder::DepsgraphNodeBuilder(Main *bmain, Depsgraph *graph) : bmain_(bmain), graph_(graph), - scene_(NULL) + scene_(NULL), + cow_id_hash_(NULL) { } DepsgraphNodeBuilder::~DepsgraphNodeBuilder() { + if (cow_id_hash_ != NULL) { + BLI_ghash_free(cow_id_hash_, NULL, free_copy_on_write_datablock); + } } -IDDepsNode *DepsgraphNodeBuilder::add_id_node(ID *id) +IDDepsNode *DepsgraphNodeBuilder::add_id_node(ID *id, bool do_tag) { - return graph_->add_id_node(id, id->name); + if (!DEG_depsgraph_use_copy_on_write()) { + return graph_->add_id_node(id, do_tag); + } + IDDepsNode *id_node = NULL; + ID *id_cow = (ID *)BLI_ghash_lookup(cow_id_hash_, id); + if (id_cow != NULL) { + /* TODO(sergey): Is it possible to lookup and pop element from GHash + * at the same time? + */ + BLI_ghash_remove(cow_id_hash_, id, NULL, NULL); + } + id_node = graph_->add_id_node(id, do_tag, id_cow); + /* Currently all ID nodes are supposed to have copy-on-write logic. + * + * NOTE: Zero number of components indicates that ID node was just created. + */ + if (BLI_ghash_size(id_node->components) == 0) { + ComponentDepsNode *comp_cow = + id_node->add_component(DEG_NODE_TYPE_COPY_ON_WRITE); + OperationDepsNode *op_cow = comp_cow->add_operation( + function_bind(deg_evaluate_copy_on_write, _1, graph_, id_node), + DEG_OPCODE_COPY_ON_WRITE, + "", -1); + graph_->operations.push_back(op_cow); + } + return id_node; +} + +IDDepsNode *DepsgraphNodeBuilder::find_id_node(ID *id) +{ + return graph_->find_id_node(id); } TimeSourceDepsNode *DepsgraphNodeBuilder::add_time_source() @@ -276,6 +321,32 @@ OperationDepsNode *DepsgraphNodeBuilder::find_operation_node( return find_operation_node(id, comp_type, "", opcode, name, name_tag); } +ID *DepsgraphNodeBuilder::get_cow_id(const ID *id_orig) const +{ + return graph_->get_cow_id(id_orig); +} + +ID *DepsgraphNodeBuilder::ensure_cow_id(ID *id_orig) +{ + if (id_orig->tag & LIB_TAG_COPY_ON_WRITE) { + /* ID is already remapped to copy-on-write. */ + return id_orig; + } + IDDepsNode *id_node = add_id_node(id_orig, false); + return id_node->id_cow; +} + +ID *DepsgraphNodeBuilder::expand_cow_id(IDDepsNode *id_node) +{ + return deg_expand_copy_on_write_datablock(graph_, id_node, this, true); +} + +ID *DepsgraphNodeBuilder::expand_cow_id(ID *id_orig) +{ + IDDepsNode *id_node = add_id_node(id_orig); + return expand_cow_id(id_node); +} + /* **** Build functions for entity nodes **** */ void DepsgraphNodeBuilder::begin_build() { @@ -295,52 +366,116 @@ void DepsgraphNodeBuilder::begin_build() { } } FOREACH_NODETREE_END; + + if (DEG_depsgraph_use_copy_on_write()) { + /* Store existing copy-on-write versions of datablock, so we can re-use + * them for new ID nodes. + */ + cow_id_hash_ = BLI_ghash_ptr_new("Depsgraph id hash"); + foreach (IDDepsNode *id_node, graph_->id_nodes) { + if (deg_copy_on_write_is_expanded(id_node->id_cow)) { + BLI_ghash_insert(cow_id_hash_, + id_node->id_orig, + id_node->id_cow); + id_node->id_cow = NULL; + } + } + } + + GSET_FOREACH_BEGIN(OperationDepsNode *, op_node, graph_->entry_tags) + { + ComponentDepsNode *comp_node = op_node->owner; + IDDepsNode *id_node = comp_node->owner; + + SavedEntryTag entry_tag; + entry_tag.id = id_node->id_orig; + entry_tag.component_type = comp_node->type; + entry_tag.opcode = op_node->opcode; + saved_entry_tags_.push_back(entry_tag); + }; + GSET_FOREACH_END(); + + /* Make sure graph has no nodes left from previous state. */ + graph_->clear_all_nodes(); + graph_->operations.clear(); + BLI_gset_clear(graph_->entry_tags, NULL); +} + +void DepsgraphNodeBuilder::end_build() +{ + foreach (const SavedEntryTag& entry_tag, saved_entry_tags_) { + IDDepsNode *id_node = find_id_node(entry_tag.id); + if (id_node == NULL) { + continue; + } + ComponentDepsNode *comp_node = + id_node->find_component(entry_tag.component_type); + if (comp_node == NULL) { + continue; + } + OperationDepsNode *op_node = comp_node->find_operation(entry_tag.opcode); + if (op_node == NULL) { + continue; + } + op_node->tag_update(graph_); + } } -void DepsgraphNodeBuilder::build_group(Base *base, Group *group) +void DepsgraphNodeBuilder::build_group(Group *group) { ID *group_id = &group->id; if (group_id->tag & LIB_TAG_DOIT) { return; } group_id->tag |= LIB_TAG_DOIT; - - LINKLIST_FOREACH (GroupObject *, go, &group->gobject) { - build_object(base, go->ob); + /* Build group objects. */ + LINKLIST_FOREACH(Base *, base, &group->view_layer->object_bases) { + build_object(NULL, base->object, DEG_ID_LINKED_INDIRECTLY); } + /* Operation to evaluate the whole view layer. + * + * NOTE: We re-use DONE opcode even though the function does everything. + * This way we wouldn't need to worry about possible relations from DONE, + * regardless whether it's a group or scene or something else. + */ + add_id_node(group_id); + Group *group_cow = get_cow_datablock(group); + add_operation_node(group_id, + DEG_NODE_TYPE_LAYER_COLLECTIONS, + function_bind(BKE_group_eval_view_layers, + _1, + group_cow), + DEG_OPCODE_VIEW_LAYER_DONE); } -void DepsgraphNodeBuilder::build_object(Base *base, Object *object) +void DepsgraphNodeBuilder::build_object(Base *base, + Object *object, + eDepsNode_LinkedState_Type linked_state) { - const bool has_object = (object->id.tag & LIB_TAG_DOIT); - IDDepsNode *id_node = (has_object) - ? graph_->find_id_node(&object->id) - : add_id_node(&object->id); - /* Update node layers. - * Do it for both new and existing ID nodes. This is so because several - * bases might be sharing same object. - */ - if (base != NULL) { - id_node->layers |= base->lay; - } - if (object->type == OB_CAMERA) { - /* Camera should always be updated, it used directly by viewport. - * - * TODO(sergey): Make it only for active scene camera. - */ - id_node->layers |= (unsigned int)(-1); - } /* Skip rest of components if the ID node was already there. */ - if (has_object) { + if (object->id.tag & LIB_TAG_DOIT) { + IDDepsNode *id_node = find_id_node(&object->id); + /* We need to build some extra stuff if object becomes linked + * directly. + */ + if (id_node->linked_state == DEG_ID_LINKED_INDIRECTLY) { + build_object_flags(base, object, linked_state); + } + id_node->linked_state = max(id_node->linked_state, linked_state); return; } object->id.tag |= LIB_TAG_DOIT; + /* Create ID node for object and begin init. */ + IDDepsNode *id_node = add_id_node(&object->id); + id_node->linked_state = linked_state; object->customdata_mask = 0; + /* Various flags, flushing from bases/collections. */ + build_object_flags(base, object, linked_state); /* Transform. */ build_object_transform(object); /* Parent. */ if (object->parent != NULL) { - build_object(NULL, object->parent); + build_object(NULL, object->parent, DEG_ID_LINKED_INDIRECTLY); } /* Modifiers. */ if (object->modifiers.first != NULL) { @@ -374,12 +509,30 @@ void DepsgraphNodeBuilder::build_object(Base *base, Object *object) /* Object that this is a proxy for. */ if (object->proxy) { object->proxy->proxy_from = object; - build_object(base, object->proxy); + build_object(NULL, object->proxy, DEG_ID_LINKED_INDIRECTLY); } /* Object dupligroup. */ if (object->dup_group != NULL) { - build_group(base, object->dup_group); + build_group(object->dup_group); + } +} + +void DepsgraphNodeBuilder::build_object_flags( + Base *base, + Object *object, + eDepsNode_LinkedState_Type linked_state) +{ + if (base == NULL) { + return; } + /* TODO(sergey): Is this really best component to be used? */ + Object *object_cow = get_cow_datablock(object); + const bool is_from_set = (linked_state == DEG_ID_LINKED_VIA_SET); + add_operation_node(&object->id, + DEG_NODE_TYPE_LAYER_COLLECTIONS, + function_bind(BKE_object_eval_flush_base_flags, + _1, object_cow, base, is_from_set), + DEG_OPCODE_OBJECT_BASE_FLAGS); } void DepsgraphNodeBuilder::build_object_data(Object *object) @@ -388,9 +541,9 @@ void DepsgraphNodeBuilder::build_object_data(Object *object) return; } IDDepsNode *id_node = graph_->find_id_node(&object->id); - /* type-specific data... */ + /* type-specific data. */ switch (object->type) { - case OB_MESH: /* Geometry */ + case OB_MESH: case OB_CURVE: case OB_FONT: case OB_SURF: @@ -421,6 +574,9 @@ void DepsgraphNodeBuilder::build_object_data(Object *object) case OB_CAMERA: build_camera(object); break; + case OB_LIGHTPROBE: + build_lightprobe(object); + break; default: { ID *obdata = (ID *)object->data; @@ -435,39 +591,44 @@ void DepsgraphNodeBuilder::build_object_data(Object *object) void DepsgraphNodeBuilder::build_object_transform(Object *object) { OperationDepsNode *op_node; + Scene *scene_cow = get_cow_datablock(scene_); + Object *ob_cow = get_cow_datablock(object); /* local transforms (from transform channels - loc/rot/scale + deltas) */ op_node = add_operation_node(&object->id, DEG_NODE_TYPE_TRANSFORM, - function_bind(BKE_object_eval_local_transform, _1, object), + function_bind(BKE_object_eval_local_transform, + _1, + ob_cow), DEG_OPCODE_TRANSFORM_LOCAL); op_node->set_as_entry(); /* object parent */ - if (object->parent) { + if (object->parent != NULL) { add_operation_node(&object->id, DEG_NODE_TYPE_TRANSFORM, - function_bind(BKE_object_eval_parent, _1, scene_, object), + function_bind(BKE_object_eval_parent, + _1, + scene_cow, + ob_cow), DEG_OPCODE_TRANSFORM_PARENT); } /* object constraints */ - if (object->constraints.first) { + if (object->constraints.first != NULL) { build_object_constraints(object); } - /* Temporary uber-update node, which does everything. - * It is for the being we're porting old dependencies into the new system. - * We'll get rid of this node as soon as all the granular update functions - * are filled in. - * - * TODO(sergey): Get rid of this node. - */ + /* Rest of transformation update. */ add_operation_node(&object->id, DEG_NODE_TYPE_TRANSFORM, - function_bind(BKE_object_eval_uber_transform, _1, object), + function_bind(BKE_object_eval_uber_transform, + _1, + ob_cow), DEG_OPCODE_TRANSFORM_OBJECT_UBEREVAL); /* object transform is done */ op_node = add_operation_node(&object->id, DEG_NODE_TYPE_TRANSFORM, - function_bind(BKE_object_eval_done, _1, object), + function_bind(BKE_object_eval_done, + _1, + ob_cow), DEG_OPCODE_TRANSFORM_FINAL); op_node->set_as_exit(); } @@ -493,7 +654,10 @@ void DepsgraphNodeBuilder::build_object_constraints(Object *object) { /* create node for constraint stack */ add_operation_node(&object->id, DEG_NODE_TYPE_TRANSFORM, - function_bind(BKE_object_eval_constraints, _1, scene_, object), + function_bind(BKE_object_eval_constraints, + _1, + get_cow_datablock(scene_), + get_cow_datablock(object)), DEG_OPCODE_TRANSFORM_CONSTRAINTS); } @@ -504,23 +668,34 @@ void DepsgraphNodeBuilder::build_object_constraints(Object *object) void DepsgraphNodeBuilder::build_animdata(ID *id) { AnimData *adt = BKE_animdata_from_id(id); - - if (adt == NULL) + if (adt == NULL) { return; + } /* animation */ if (adt->action || adt->nla_tracks.first || adt->drivers.first) { - // XXX: Hook up specific update callbacks for special properties which may need it... + (void) add_id_node(id); + ID *id_cow = get_cow_id(id); - /* actions and NLA - as a single unit for now, as it gets complicated to schedule otherwise */ + // XXX: Hook up specific update callbacks for special properties which + // may need it... + + /* actions and NLA - as a single unit for now, as it gets complicated to + * schedule otherwise. + */ if ((adt->action) || (adt->nla_tracks.first)) { /* create the node */ add_operation_node(id, DEG_NODE_TYPE_ANIMATION, - function_bind(BKE_animsys_eval_animdata, _1, id), - DEG_OPCODE_ANIMATION, id->name); - - // TODO: for each channel affected, we might also want to add some support for running RNA update callbacks on them - // (which will be needed for proper handling of drivers later) + function_bind(BKE_animsys_eval_animdata, + _1, + id_cow), + DEG_OPCODE_ANIMATION, + id->name); + + /* TODO: for each channel affected, we might also want to add some + * support for running RNA update callbacks on them + * (which will be needed for proper handling of drivers later) + */ } /* drivers */ @@ -538,21 +713,28 @@ void DepsgraphNodeBuilder::build_animdata(ID *id) */ OperationDepsNode *DepsgraphNodeBuilder::build_driver(ID *id, FCurve *fcu) { + ID *id_cow = get_cow_id(id); + /* Create data node for this driver */ /* TODO(sergey): Avoid creating same operation multiple times, * in the future we need to avoid lookup of the operation as well * and use some tagging magic instead. */ - OperationDepsNode *driver_op = find_operation_node(id, - DEG_NODE_TYPE_PARAMETERS, - DEG_OPCODE_DRIVER, - fcu->rna_path ? fcu->rna_path : "", - fcu->array_index); + OperationDepsNode *driver_op = find_operation_node( + id, + DEG_NODE_TYPE_PARAMETERS, + DEG_OPCODE_DRIVER, + fcu->rna_path ? fcu->rna_path : "", + fcu->array_index); if (driver_op == NULL) { + /* TODO(sergey): Shall we use COW of fcu itself here? */ driver_op = add_operation_node(id, DEG_NODE_TYPE_PARAMETERS, - function_bind(BKE_animsys_eval_driver, _1, id, fcu), + function_bind(BKE_animsys_eval_driver, + _1, + id_cow, + fcu), DEG_OPCODE_DRIVER, fcu->rna_path ? fcu->rna_path : "", fcu->array_index); @@ -569,20 +751,19 @@ void DepsgraphNodeBuilder::build_world(World *world) if (world_id->tag & LIB_TAG_DOIT) { return; } - + /* Animation. */ build_animdata(world_id); - /* world itself */ add_operation_node(world_id, - DEG_NODE_TYPE_PARAMETERS, - NULL, - DEG_OPCODE_PARAMETERS_EVAL); - + DEG_NODE_TYPE_SHADING, + function_bind(BKE_world_eval, + _1, + get_cow_datablock(world)), + DEG_OPCODE_WORLD_UPDATE); /* textures */ build_texture_stack(world->mtex); - /* world's nodetree */ - if (world->nodetree) { + if (world->nodetree != NULL) { build_nodetree(world->nodetree); } } @@ -591,55 +772,66 @@ void DepsgraphNodeBuilder::build_world(World *world) void DepsgraphNodeBuilder::build_rigidbody(Scene *scene) { RigidBodyWorld *rbw = scene->rigidbody_world; + Scene *scene_cow = get_cow_datablock(scene); /** * Rigidbody Simulation Nodes * ========================== * * There are 3 nodes related to Rigidbody Simulation: - * 1) "Initialize/Rebuild World" - this is called sparingly, only when the simulation - * needs to be rebuilt (mainly after file reload, or moving back to start frame) - * 2) "Do Simulation" - perform a simulation step - interleaved between the evaluation - * steps for clusters of objects (i.e. between those affected and/or not affected by - * the sim for instance) + * 1) "Initialize/Rebuild World" - this is called sparingly, only when the + * simulation needs to be rebuilt (mainly after file reload, or moving + * back to start frame) + * 2) "Do Simulation" - perform a simulation step - interleaved between the + * evaluation steps for clusters of objects (i.e. between those affected + * and/or not affected by the sim for instance). * - * 3) "Pull Results" - grab the specific transforms applied for a specific object - - * performed as part of object's transform-stack building + * 3) "Pull Results" - grab the specific transforms applied for a specific + * object - performed as part of object's transform-stack building. */ - /* create nodes ------------------------------------------------------------------------ */ - /* XXX: is this the right component, or do we want to use another one instead? */ + /* Create nodes --------------------------------------------------------- */ + + /* XXX: is this the right component, or do we want to use another one + * instead? + */ /* init/rebuild operation */ - /*OperationDepsNode *init_node =*/ add_operation_node(&scene->id, DEG_NODE_TYPE_TRANSFORM, - function_bind(BKE_rigidbody_rebuild_sim, _1, scene), - DEG_OPCODE_RIGIDBODY_REBUILD); + /*OperationDepsNode *init_node =*/ add_operation_node( + &scene->id, DEG_NODE_TYPE_TRANSFORM, + function_bind(BKE_rigidbody_rebuild_sim, _1, scene_cow), + DEG_OPCODE_RIGIDBODY_REBUILD); /* do-sim operation */ // XXX: what happens if we need to split into several groups? - OperationDepsNode *sim_node = add_operation_node(&scene->id, DEG_NODE_TYPE_TRANSFORM, - function_bind(BKE_rigidbody_eval_simulation, _1, scene), - DEG_OPCODE_RIGIDBODY_SIM); + OperationDepsNode *sim_node = add_operation_node( + &scene->id, DEG_NODE_TYPE_TRANSFORM, + function_bind(BKE_rigidbody_eval_simulation, _1, scene_cow), + DEG_OPCODE_RIGIDBODY_SIM); - /* XXX: For now, the sim node is the only one that really matters here. If any other - * sims get added later, we may have to remove these hacks... + /* XXX: For now, the sim node is the only one that really matters here. + * If any other sims get added later, we may have to remove these hacks... */ sim_node->owner->entry_operation = sim_node; sim_node->owner->exit_operation = sim_node; - /* objects - simulation participants */ if (rbw->group) { - LINKLIST_FOREACH (GroupObject *, go, &rbw->group->gobject) { - Object *object = go->ob; + LINKLIST_FOREACH (Base *, base, &rbw->group->view_layer->object_bases) { + Object *object = base->object; if (!object || (object->type != OB_MESH)) continue; /* 2) create operation for flushing results */ - /* object's transform component - where the rigidbody operation lives */ + /* object's transform component - where the rigidbody operation + * lives. */ add_operation_node(&object->id, DEG_NODE_TYPE_TRANSFORM, - function_bind(BKE_rigidbody_object_sync_transforms, _1, scene, object), + function_bind( + BKE_rigidbody_object_sync_transforms, + _1, + scene_cow, + get_cow_datablock(object)), DEG_OPCODE_RIGIDBODY_TRANSFORM_COPY); } } @@ -666,41 +858,76 @@ void DepsgraphNodeBuilder::build_particles(Object *object) ComponentDepsNode *psys_comp = add_component_node(&object->id, DEG_NODE_TYPE_EVAL_PARTICLES); + /* TODO(sergey): Need to get COW of PSYS. */ + Scene *scene_cow = get_cow_datablock(scene_); + Object *ob_cow = get_cow_datablock(object); + add_operation_node(psys_comp, function_bind(BKE_particle_system_eval_init, _1, - scene_, - object), + scene_cow, + ob_cow), DEG_OPCODE_PARTICLE_SYSTEM_EVAL_INIT); /* particle systems */ LINKLIST_FOREACH (ParticleSystem *, psys, &object->particlesystem) { ParticleSettings *part = psys->part; - /* particle settings */ - // XXX: what if this is used more than once! - build_animdata(&part->id); + /* Build particle settings operations. + * + * NOTE: The call itself ensures settings are only build once. + */ + build_particle_settings(part); + + /* Update on particle settings change. */ + add_operation_node(psys_comp, + function_bind(BKE_particle_system_settings_eval, + _1, + psys), + DEG_OPCODE_PARTICLE_SETTINGS_EVAL, + psys->name); - /* this particle system */ - // TODO: for now, this will just be a placeholder "ubereval" node + /* Particle system evaluation. */ add_operation_node(psys_comp, NULL, DEG_OPCODE_PARTICLE_SYSTEM_EVAL, psys->name); } - /* pointcache */ - // TODO... + /* TODO(sergey): Do we need a point cache operations here? */ +} + +void DepsgraphNodeBuilder::build_particle_settings(ParticleSettings *part) { + ID *part_id = &part->id; + if (part_id->tag & LIB_TAG_DOIT) { + return; + } + part_id->tag |= LIB_TAG_DOIT; + /* Animation data. */ + build_animdata(part_id); + /* Parameters change. */ + add_operation_node(part_id, + DEG_NODE_TYPE_PARAMETERS, + NULL, + DEG_OPCODE_PARTICLE_SETTINGS_EVAL); + add_operation_node(part_id, + DEG_NODE_TYPE_PARAMETERS, + function_bind(BKE_particle_system_settings_recalc_clear, + _1, + part), + DEG_OPCODE_PARTICLE_SETTINGS_RECALC_CLEAR); } void DepsgraphNodeBuilder::build_cloth(Object *object) { + Scene *scene_cow = get_cow_datablock(scene_); + Object *object_cow = get_cow_datablock(object); add_operation_node(&object->id, DEG_NODE_TYPE_CACHE, function_bind(BKE_object_eval_cloth, _1, - scene_, - object), + scene_cow, + object_cow), DEG_OPCODE_GEOMETRY_CLOTH_MODIFIER); } @@ -718,8 +945,9 @@ void DepsgraphNodeBuilder::build_shapekeys(Key *key) // XXX: what happens if the datablock is shared! void DepsgraphNodeBuilder::build_obdata_geom(Object *object) { - ID *obdata = (ID *)object->data; OperationDepsNode *op_node; + Scene *scene_cow = get_cow_datablock(scene_); + Object *object_cow = get_cow_datablock(object); /* TODO(sergey): This way using this object's properties as driver target * works fine. @@ -743,8 +971,8 @@ void DepsgraphNodeBuilder::build_obdata_geom(Object *object) DEG_NODE_TYPE_GEOMETRY, function_bind(BKE_object_eval_uber_data, _1, - scene_, - object), + scene_cow, + object_cow), DEG_OPCODE_GEOMETRY_UBEREVAL); op_node->set_as_exit(); @@ -765,10 +993,21 @@ void DepsgraphNodeBuilder::build_obdata_geom(Object *object) } /* materials */ - for (int a = 1; a <= object->totcol; a++) { - Material *ma = give_current_material(object, a); - if (ma != NULL) { - build_material(ma); + if (object->totcol != 0) { + if (object->type == OB_MESH) { + add_operation_node(&object->id, + DEG_NODE_TYPE_SHADING, + function_bind(BKE_object_eval_update_shading, + _1, + object_cow), + DEG_OPCODE_SHADING); + } + + for (int a = 1; a <= object->totcol; a++) { + Material *ma = give_current_material(object, a); + if (ma != NULL) { + build_material(ma); + } } } @@ -777,9 +1016,14 @@ void DepsgraphNodeBuilder::build_obdata_geom(Object *object) // add geometry collider relations } + ID *obdata = (ID *)object->data; if (obdata->tag & LIB_TAG_DOIT) { return; } + obdata->tag |= LIB_TAG_DOIT; + /* Make sure we've got an ID node before requesting CoW pointer. */ + (void) add_id_node((ID *)obdata); + ID *obdata_cow = get_cow_id(obdata); /* ShapeKeys */ Key *key = BKE_key_from_object(object); @@ -802,7 +1046,7 @@ void DepsgraphNodeBuilder::build_obdata_geom(Object *object) DEG_NODE_TYPE_GEOMETRY, function_bind(BKE_mesh_eval_geometry, _1, - (Mesh *)obdata), + (Mesh *)obdata_cow), DEG_OPCODE_PLACEHOLDER, "Geometry Eval"); op_node->set_as_entry(); @@ -819,9 +1063,10 @@ void DepsgraphNodeBuilder::build_obdata_geom(Object *object) /* metaball evaluation operations */ op_node = add_operation_node(obdata, DEG_NODE_TYPE_GEOMETRY, - function_bind(BKE_mball_eval_geometry, - _1, - (MetaBall *)obdata), + function_bind( + BKE_mball_eval_geometry, + _1, + (MetaBall *)obdata_cow), DEG_OPCODE_PLACEHOLDER, "Geometry Eval"); } @@ -846,23 +1091,22 @@ void DepsgraphNodeBuilder::build_obdata_geom(Object *object) DEG_NODE_TYPE_GEOMETRY, function_bind(BKE_curve_eval_geometry, _1, - (Curve *)obdata), + (Curve *)obdata_cow), DEG_OPCODE_PLACEHOLDER, "Geometry Eval"); op_node->set_as_entry(); - /* Make sure objects used for bevel.taper are in the graph. * NOTE: This objects might be not linked to the scene. */ Curve *cu = (Curve *)obdata; if (cu->bevobj != NULL) { - build_object(NULL, cu->bevobj); + build_object(NULL, cu->bevobj, DEG_ID_LINKED_INDIRECTLY); } if (cu->taperobj != NULL) { - build_object(NULL, cu->taperobj); + build_object(NULL, cu->taperobj, DEG_ID_LINKED_INDIRECTLY); } if (object->type == OB_FONT && cu->textoncurve != NULL) { - build_object(NULL, cu->textoncurve); + build_object(NULL, cu->textoncurve, DEG_ID_LINKED_INDIRECTLY); } break; } @@ -874,7 +1118,7 @@ void DepsgraphNodeBuilder::build_obdata_geom(Object *object) DEG_NODE_TYPE_GEOMETRY, function_bind(BKE_lattice_eval_geometry, _1, - (Lattice *)obdata), + (Lattice *)obdata_cow), DEG_OPCODE_PLACEHOLDER, "Geometry Eval"); op_node->set_as_entry(); @@ -891,12 +1135,28 @@ void DepsgraphNodeBuilder::build_obdata_geom(Object *object) DEG_NODE_TYPE_PARAMETERS, NULL, DEG_OPCODE_PARAMETERS_EVAL); + + /* Batch cache. */ + add_operation_node(obdata, + DEG_NODE_TYPE_BATCH_CACHE, + function_bind(BKE_object_data_select_update, + _1, + obdata_cow), + DEG_OPCODE_GEOMETRY_SELECT_UPDATE); } /* Cameras */ void DepsgraphNodeBuilder::build_camera(Object *object) { - /* TODO: Link scene-camera links in somehow... */ + /* Object itself. */ + add_operation_node(&object->id, + DEG_NODE_TYPE_PARAMETERS, + NULL, + DEG_OPCODE_PARAMETERS_EVAL, + "Camera Parameters"); + + /* Object data. */ + /* TODO: Link scene-camera links in somehow. */ Camera *cam = (Camera *)object->data; ID *camera_id = &cam->id; if (camera_id->tag & LIB_TAG_DOIT) { @@ -909,17 +1169,19 @@ void DepsgraphNodeBuilder::build_camera(Object *object) DEG_NODE_TYPE_PARAMETERS, NULL, DEG_OPCODE_PARAMETERS_EVAL); - - if (cam->dof_ob != NULL) { - /* TODO(sergey): For now parametrs are on object level. */ - add_operation_node(&object->id, DEG_NODE_TYPE_PARAMETERS, NULL, - DEG_OPCODE_PLACEHOLDER, "Camera DOF"); - } } /* Lamps */ void DepsgraphNodeBuilder::build_lamp(Object *object) { + /* Object itself. */ + add_operation_node(&object->id, + DEG_NODE_TYPE_PARAMETERS, + NULL, + DEG_OPCODE_PARAMETERS_EVAL, + "Lamp Parameters"); + + /* Object data. */ Lamp *la = (Lamp *)object->data; ID *lamp_id = &la->id; if (lamp_id->tag & LIB_TAG_DOIT) { @@ -928,7 +1190,6 @@ void DepsgraphNodeBuilder::build_lamp(Object *object) build_animdata(&la->id); - /* TODO(sergey): Is it really how we're supposed to work with drivers? */ add_operation_node(lamp_id, DEG_NODE_TYPE_PARAMETERS, NULL, @@ -945,22 +1206,30 @@ void DepsgraphNodeBuilder::build_lamp(Object *object) void DepsgraphNodeBuilder::build_nodetree(bNodeTree *ntree) { - if (!ntree) + if (ntree == NULL) { return; - + } /* nodetree itself */ ID *ntree_id = &ntree->id; - OperationDepsNode *op_node; - + add_id_node(ntree_id); + bNodeTree *ntree_cow = get_cow_datablock(ntree); + /* Animation, */ build_animdata(ntree_id); - - /* Parameters for drivers. */ - op_node = add_operation_node(ntree_id, - DEG_NODE_TYPE_PARAMETERS, - NULL, - DEG_OPCODE_PARAMETERS_EVAL); - op_node->set_as_exit(); - + /* Shading update. */ + add_operation_node(ntree_id, + DEG_NODE_TYPE_SHADING, + NULL, + DEG_OPCODE_MATERIAL_UPDATE); + /* NOTE: We really pass original and CoW node trees here, this is how the + * callback works. Ideally we need to find a better way for that. + */ + add_operation_node(ntree_id, + DEG_NODE_TYPE_SHADING_PARAMETERS, + function_bind(BKE_nodetree_shading_params_eval, + _1, + ntree_cow, + ntree), + DEG_OPCODE_MATERIAL_UPDATE); /* nodetree's nodes... */ LINKLIST_FOREACH (bNode *, bnode, &ntree->nodes) { ID *id = bnode->id; @@ -978,7 +1247,7 @@ void DepsgraphNodeBuilder::build_nodetree(bNodeTree *ntree) build_image((Image *)id); } else if (id_type == ID_OB) { - build_object(NULL, (Object *)id); + build_object(NULL, (Object *)id, DEG_ID_LINKED_INDIRECTLY); } else if (id_type == ID_SCE) { /* Scenes are used by compositor trees, and handled by render @@ -1003,36 +1272,40 @@ void DepsgraphNodeBuilder::build_nodetree(bNodeTree *ntree) } /* Recursively build graph for material */ -void DepsgraphNodeBuilder::build_material(Material *ma) +void DepsgraphNodeBuilder::build_material(Material *material) { - ID *ma_id = &ma->id; - if (ma_id->tag & LIB_TAG_DOIT) { + ID *material_id = &material->id; + if (material_id->tag & LIB_TAG_DOIT) { return; } - - add_operation_node(ma_id, DEG_NODE_TYPE_SHADING, NULL, - DEG_OPCODE_PLACEHOLDER, "Material Update"); - - /* material animation */ - build_animdata(ma_id); - - /* textures */ - build_texture_stack(ma->mtex); - - /* material's nodetree */ - build_nodetree(ma->nodetree); + material_id->tag |= LIB_TAG_DOIT; + /* Material itself. */ + add_id_node(material_id); + Material *material_cow = get_cow_datablock(material); + /* Shading update. */ + add_operation_node(material_id, + DEG_NODE_TYPE_SHADING, + function_bind(BKE_material_eval, + _1, + material_cow), + DEG_OPCODE_MATERIAL_UPDATE); + /* Material animation. */ + build_animdata(material_id); + /* Textures. */ + build_texture_stack(material->mtex); + /* Material's nodetree. */ + build_nodetree(material->nodetree); } /* Texture-stack attached to some shading datablock */ void DepsgraphNodeBuilder::build_texture_stack(MTex **texture_stack) { - int i; - /* for now assume that all texture-stacks have same number of max items */ - for (i = 0; i < MAX_MTEX; i++) { + for (int i = 0; i < MAX_MTEX; i++) { MTex *mtex = texture_stack[i]; - if (mtex && mtex->tex) + if (mtex && mtex->tex) { build_texture(mtex->tex); + } } } @@ -1077,7 +1350,9 @@ void DepsgraphNodeBuilder::build_compositor(Scene *scene) // XXX: component type undefined! //graph->get_node(&scene->id, NULL, DEG_NODE_TYPE_COMPOSITING, NULL); - /* for now, nodetrees are just parameters; compositing occurs in internals of renderer... */ + /* for now, nodetrees are just parameters; compositing occurs in internals + * of renderer... + */ add_component_node(&scene->id, DEG_NODE_TYPE_PARAMETERS); build_nodetree(scene->nodetree); } @@ -1109,29 +1384,55 @@ void DepsgraphNodeBuilder::build_cachefile(CacheFile *cache_file) void DepsgraphNodeBuilder::build_mask(Mask *mask) { ID *mask_id = &mask->id; + Mask *mask_cow = get_cow_datablock(mask); /* F-Curve based animation. */ build_animdata(mask_id); /* Animation based on mask's shapes. */ add_operation_node(mask_id, DEG_NODE_TYPE_ANIMATION, - function_bind(BKE_mask_eval_animation, _1, mask), + function_bind(BKE_mask_eval_animation, _1, mask_cow), DEG_OPCODE_MASK_ANIMATION); /* Final mask evaluation. */ add_operation_node(mask_id, DEG_NODE_TYPE_PARAMETERS, - function_bind(BKE_mask_eval_update, _1, mask), + function_bind(BKE_mask_eval_update, _1, mask_cow), DEG_OPCODE_MASK_EVAL); } -void DepsgraphNodeBuilder::build_movieclip(MovieClip *clip) { +void DepsgraphNodeBuilder::build_movieclip(MovieClip *clip) +{ ID *clip_id = &clip->id; + MovieClip *clip_cow = get_cow_datablock(clip); /* Animation. */ build_animdata(clip_id); /* Movie clip evaluation. */ add_operation_node(clip_id, DEG_NODE_TYPE_PARAMETERS, - function_bind(BKE_movieclip_eval_update, _1, clip), + function_bind(BKE_movieclip_eval_update, _1, clip_cow), DEG_OPCODE_MOVIECLIP_EVAL); } +void DepsgraphNodeBuilder::build_lightprobe(Object *object) +{ + LightProbe *probe = (LightProbe *)object->data; + ID *probe_id = &probe->id; + if (probe_id->tag & LIB_TAG_DOIT) { + return; + } + probe_id->tag |= LIB_TAG_DOIT; + /* Placeholder so we can add relations and tag ID node for update. */ + add_operation_node(probe_id, + DEG_NODE_TYPE_PARAMETERS, + NULL, + DEG_OPCODE_PLACEHOLDER, + "LightProbe Eval"); + add_operation_node(&object->id, + DEG_NODE_TYPE_PARAMETERS, + NULL, + DEG_OPCODE_PLACEHOLDER, + "LightProbe Eval"); + + build_animdata(probe_id); +} + } // namespace DEG diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.h b/source/blender/depsgraph/intern/builder/deg_builder_nodes.h index 2fa9ba01cc7..c9bdd194227 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.h +++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.h @@ -32,6 +32,8 @@ #include "intern/depsgraph_types.h" +#include "DEG_depsgraph.h" /* used for DEG_depsgraph_use_copy_on_write() */ + struct Base; struct CacheFile; struct bGPdata; @@ -42,6 +44,7 @@ struct Image; struct FCurve; struct Group; struct Key; +struct LayerCollection; struct Main; struct Material; struct Mask; @@ -49,6 +52,8 @@ struct MTex; struct MovieClip; struct bNodeTree; struct Object; +struct ParticleSettings; +struct Probe; struct bPoseChannel; struct bConstraint; struct Scene; @@ -70,9 +75,45 @@ struct DepsgraphNodeBuilder { DepsgraphNodeBuilder(Main *bmain, Depsgraph *graph); ~DepsgraphNodeBuilder(); + /* For given original ID get ID which is created by CoW system. */ + ID *get_cow_id(const ID *id_orig) const; + /* Similar to above, but for the cases when there is no ID node we create + * one. + */ + ID *ensure_cow_id(ID *id_orig); + + /* Helper wrapper function which wraps get_cow_id with a needed type cast. */ + template<typename T> + T *get_cow_datablock(const T *orig) const { + return (T *)get_cow_id(&orig->id); + } + + /* Get fully expanded (ready for use) copy-on-write datablock for the given + * original datablock. + */ + ID *expand_cow_id(IDDepsNode *id_node); + ID *expand_cow_id(ID *id_orig); + template<typename T> + T *expand_cow_datablock(T *orig) { + return (T *)expand_cow_id(&orig->id); + } + + /* For a given COW datablock get corresponding original one. */ + template<typename T> + T *get_orig_datablock(const T *cow) const { + if (DEG_depsgraph_use_copy_on_write()) { + return (T *)cow->id.newid; + } + else { + return (T *)cow; + } + } + void begin_build(); + void end_build(); - IDDepsNode *add_id_node(ID *id); + IDDepsNode *add_id_node(ID *id, bool do_tag = true); + IDDepsNode *find_id_node(ID *id); TimeSourceDepsNode *add_time_source(); ComponentDepsNode *add_component_node(ID *id, @@ -118,15 +159,23 @@ struct DepsgraphNodeBuilder { const char *name = "", int name_tag = -1); - void build_scene(Scene *scene); - void build_group(Base *base, Group *group); - void build_object(Base *base, Object *object); + void build_view_layer(Scene *scene, + ViewLayer *view_layer, + eDepsNode_LinkedState_Type linked_state); + void build_group(Group *group); + void build_object(Base *base, + Object *object, + eDepsNode_LinkedState_Type linked_state); + void build_object_flags(Base *base, + Object *object, + eDepsNode_LinkedState_Type linked_state); void build_object_data(Object *object); void build_object_transform(Object *object); void build_object_constraints(Object *object); void build_pose_constraints(Object *object, bPoseChannel *pchan); void build_rigidbody(Scene *scene); void build_particles(Object *object); + void build_particle_settings(ParticleSettings *part); void build_cloth(Object *object); void build_animdata(ID *id); OperationDepsNode *build_driver(ID *id, FCurve *fcurve); @@ -153,14 +202,35 @@ struct DepsgraphNodeBuilder { void build_cachefile(CacheFile *cache_file); void build_mask(Mask *mask); void build_movieclip(MovieClip *clip); - + void build_lightprobe(Object *object); + + struct LayerCollectionState { + int index; + LayerCollection *parent; + }; + void build_layer_collection(ID *owner_id, + LayerCollection *layer_collection, + LayerCollectionState *state); + void build_layer_collections(ID *owner_id, + ListBase *layer_collections, + LayerCollectionState *state); + void build_view_layer_collections(ID *owner_id, ViewLayer *view_layer); protected: + struct SavedEntryTag { + ID *id; + eDepsNode_Type component_type; + eDepsOperation_Code opcode; + }; + vector<SavedEntryTag> saved_entry_tags_; + /* State which never changes, same for the whole builder time. */ Main *bmain_; Depsgraph *graph_; /* State which demotes currently built entities. */ Scene *scene_; + + GHash *cow_id_hash_; }; } // namespace DEG diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes_layer_collection.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes_layer_collection.cc new file mode 100644 index 00000000000..79316e47022 --- /dev/null +++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes_layer_collection.cc @@ -0,0 +1,126 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * 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. + * + * The Original Code is Copyright (C) 2013 Blender Foundation. + * All rights reserved. + * + * Original Author: Joshua Leung + * Contributor(s): Based on original depsgraph.c code - Blender Foundation (2005-2013) + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/depsgraph/intern/builder/deg_builder_nodes_layer_collection.cc + * \ingroup depsgraph + * + * Methods for constructing depsgraph's nodes + */ + +#include "intern/builder/deg_builder_nodes.h" + +#include <stdio.h> +#include <stdlib.h> + +#include "MEM_guardedalloc.h" + +extern "C" { +#include "BLI_utildefines.h" +#include "BLI_listbase.h" +#include "BLI_string.h" + +#include "BKE_layer.h" + +#include "DNA_scene_types.h" + +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_build.h" +} /* extern "C" */ + +#include "intern/builder/deg_builder.h" +#include "intern/eval/deg_eval_copy_on_write.h" +#include "intern/nodes/deg_node.h" +#include "intern/nodes/deg_node_component.h" +#include "intern/nodes/deg_node_operation.h" +#include "intern/depsgraph_types.h" +#include "intern/depsgraph_intern.h" +#include "util/deg_util_foreach.h" + +namespace DEG { + +void DepsgraphNodeBuilder::build_layer_collection( + ID *owner_id, + LayerCollection *layer_collection, + LayerCollectionState *state) +{ + /* TODO(sergey): This will attempt to create component for each collection. + * Harmless but could be optimized. + */ + ComponentDepsNode *comp = add_component_node( + owner_id, + DEG_NODE_TYPE_LAYER_COLLECTIONS); + + add_operation_node(comp, + function_bind(BKE_layer_eval_layer_collection, + _1, + layer_collection, + state->parent), + DEG_OPCODE_VIEW_LAYER_EVAL, + layer_collection->scene_collection->name, + state->index); + ++state->index; + + /* Recurs into nested layer collections. */ + LayerCollection *parent = state->parent; + state->parent = layer_collection; + build_layer_collections(owner_id, &layer_collection->layer_collections, state); + state->parent = parent; +} + +void DepsgraphNodeBuilder::build_layer_collections(ID *owner_id, + ListBase *layer_collections, + LayerCollectionState *state) +{ + LINKLIST_FOREACH (LayerCollection *, layer_collection, layer_collections) { + build_layer_collection(owner_id, layer_collection, state); + } +} + +void DepsgraphNodeBuilder::build_view_layer_collections( + ID *owner_id, + ViewLayer *view_layer) +{ + LayerCollectionState state; + state.index = 0; + ComponentDepsNode *comp = add_component_node( + owner_id, + DEG_NODE_TYPE_LAYER_COLLECTIONS); + add_operation_node(comp, + function_bind(BKE_layer_eval_layer_collection_pre, + _1, + owner_id, + view_layer), + DEG_OPCODE_VIEW_LAYER_INIT); + add_operation_node(comp, + function_bind(BKE_layer_eval_layer_collection_post, + _1, + view_layer), + DEG_OPCODE_VIEW_LAYER_DONE); + state.parent = NULL; + build_layer_collections(owner_id, &view_layer->layer_collections, &state); +} + +} // namespace DEG diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes_rig.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes_rig.cc index 9868a8d2208..531ea55cf5c 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_nodes_rig.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes_rig.cc @@ -46,6 +46,7 @@ extern "C" { #include "DNA_armature_types.h" #include "DNA_constraint_types.h" #include "DNA_object_types.h" +#include "DNA_scene_types.h" #include "BKE_action.h" #include "BKE_armature.h" @@ -55,6 +56,7 @@ extern "C" { #include "DEG_depsgraph_build.h" #include "intern/builder/deg_builder.h" +#include "intern/eval/deg_eval_copy_on_write.h" #include "intern/nodes/deg_node.h" #include "intern/nodes/deg_node_component.h" #include "intern/nodes/deg_node_operation.h" @@ -64,16 +66,23 @@ extern "C" { namespace DEG { -void DepsgraphNodeBuilder::build_pose_constraints(Object *object, bPoseChannel *pchan) +void DepsgraphNodeBuilder::build_pose_constraints(Object *object, + bPoseChannel *pchan) { /* create node for constraint stack */ add_operation_node(&object->id, DEG_NODE_TYPE_BONE, pchan->name, - function_bind(BKE_pose_constraints_evaluate, _1, scene_, object, pchan), + function_bind(BKE_pose_constraints_evaluate, + _1, + get_cow_datablock(scene_), + get_cow_datablock(object), + pchan), DEG_OPCODE_BONE_CONSTRAINTS); } /* IK Solver Eval Steps */ -void DepsgraphNodeBuilder::build_ik_pose(Object *object, bPoseChannel *pchan, bConstraint *con) +void DepsgraphNodeBuilder::build_ik_pose(Object *object, + bPoseChannel *pchan, + bConstraint *con) { bKinematicConstraint *data = (bKinematicConstraint *)con->data; @@ -91,12 +100,18 @@ void DepsgraphNodeBuilder::build_ik_pose(Object *object, bPoseChannel *pchan, bC /* Operation node for evaluating/running IK Solver. */ add_operation_node(&object->id, DEG_NODE_TYPE_EVAL_POSE, rootchan->name, - function_bind(BKE_pose_iktree_evaluate, _1, scene_, object, rootchan), + function_bind(BKE_pose_iktree_evaluate, + _1, + get_cow_datablock(scene_), + get_cow_datablock(object), + rootchan), DEG_OPCODE_POSE_IK_SOLVER); } /* Spline IK Eval Steps */ -void DepsgraphNodeBuilder::build_splineik_pose(Object *object, bPoseChannel *pchan, bConstraint *con) +void DepsgraphNodeBuilder::build_splineik_pose(Object *object, + bPoseChannel *pchan, + bConstraint *con) { bSplineIKConstraint *data = (bSplineIKConstraint *)con->data; @@ -104,30 +119,55 @@ void DepsgraphNodeBuilder::build_splineik_pose(Object *object, bPoseChannel *pch bPoseChannel *rootchan = BKE_armature_splineik_solver_find_root(pchan, data); /* Operation node for evaluating/running Spline IK Solver. - * Store the "root bone" of this chain in the solver, so it knows where to start. + * Store the "root bone" of this chain in the solver, so it knows where to + * start. */ add_operation_node(&object->id, DEG_NODE_TYPE_EVAL_POSE, rootchan->name, - function_bind(BKE_pose_splineik_evaluate, _1, scene_, object, rootchan), + function_bind(BKE_pose_splineik_evaluate, + _1, + get_cow_datablock(scene_), + get_cow_datablock(object), + rootchan), DEG_OPCODE_POSE_SPLINE_IK_SOLVER); } /* Pose/Armature Bones Graph */ void DepsgraphNodeBuilder::build_rig(Object *object) { - bArmature *arm = (bArmature *)object->data; + bArmature *armature = (bArmature *)object->data; + const short armature_tag = armature->id.tag; + Scene *scene_cow; + Object *object_cow; + bArmature *armature_cow; + if (DEG_depsgraph_use_copy_on_write()) { + /* NOTE: We need to expand both object and armature, so this way we can + * safely create object level pose. + */ + scene_cow = get_cow_datablock(scene_); + object_cow = expand_cow_datablock(object); + armature_cow = expand_cow_datablock(armature); + } + else { + scene_cow = scene_; + object_cow = object; + armature_cow = armature; + } OperationDepsNode *op_node; - /* animation and/or drivers linking posebones to base-armature used to define them + /* Animation and/or drivers linking posebones to base-armature used to + * define them. + * * NOTE: AnimData here is really used to control animated deform properties, - * which ideally should be able to be unique across different instances. - * Eventually, we need some type of proxy/isolation mechanism in-between here - * to ensure that we can use same rig multiple times in same scene... + * which ideally should be able to be unique across different + * instances. Eventually, we need some type of proxy/isolation + * mechanism in-between here to ensure that we can use same rig + * multiple times in same scene. */ - if ((arm->id.tag & LIB_TAG_DOIT) == 0) { - build_animdata(&arm->id); + if ((armature_tag & LIB_TAG_DOIT) == 0) { + build_animdata(&armature->id); /* Make sure pose is up-to-date with armature updates. */ - add_operation_node(&arm->id, + add_operation_node(&armature->id, DEG_NODE_TYPE_PARAMETERS, NULL, DEG_OPCODE_PLACEHOLDER, @@ -135,22 +175,22 @@ void DepsgraphNodeBuilder::build_rig(Object *object) } /* Rebuild pose if not up to date. */ - if (object->pose == NULL || (object->pose->flag & POSE_RECALC)) { - BKE_pose_rebuild_ex(object, arm, false); + if (object_cow->pose == NULL || (object->pose->flag & POSE_RECALC)) { + BKE_pose_rebuild(object_cow, armature_cow); /* XXX: Without this animation gets lost in certain circumstances * after loading file. Need to investigate further since it does * not happen with simple scenes.. */ - if (object->adt) { - object->adt->recalc |= ADT_RECALC_ANIM; + if (object_cow->adt) { + object_cow->adt->recalc |= ADT_RECALC_ANIM; } } /* speed optimization for animation lookups */ - if (object->pose) { - BKE_pose_channels_hash_make(object->pose); - if (object->pose->flag & POSE_CONSTRAINTS_NEED_UPDATE_FLAGS) { - BKE_pose_update_constraint_flags(object->pose); + if (object_cow->pose != NULL) { + BKE_pose_channels_hash_make(object_cow->pose); + if (object_cow->pose->flag & POSE_CONSTRAINTS_NEED_UPDATE_FLAGS) { + BKE_pose_update_constraint_flags(object_cow->pose); } } @@ -171,42 +211,58 @@ void DepsgraphNodeBuilder::build_rig(Object *object) * - Used for representing each bone within the rig * - Acts to encapsulate the evaluation operations (base matrix + parenting, * and constraint stack) so that they can be easily found. - * - Everything else which depends on bone-results hook up to the component only - * so that we can redirect those to point at either the the post-IK/ + * - Everything else which depends on bone-results hook up to the component + * only so that we can redirect those to point at either the the post-IK/ * post-constraint/post-matrix steps, as needed. */ /* pose eval context */ op_node = add_operation_node(&object->id, DEG_NODE_TYPE_EVAL_POSE, - function_bind(BKE_pose_eval_init, _1, scene_, object, object->pose), + function_bind(BKE_pose_eval_init, + _1, + scene_cow, + object_cow, + object_cow->pose), DEG_OPCODE_POSE_INIT); op_node->set_as_entry(); op_node = add_operation_node(&object->id, DEG_NODE_TYPE_EVAL_POSE, - function_bind(BKE_pose_eval_init_ik, _1, scene_, object, object->pose), + function_bind(BKE_pose_eval_init_ik, + _1, + scene_cow, + object_cow, + object_cow->pose), DEG_OPCODE_POSE_INIT_IK); op_node = add_operation_node(&object->id, DEG_NODE_TYPE_EVAL_POSE, - function_bind(BKE_pose_eval_flush, _1, scene_, object, object->pose), + function_bind(BKE_pose_eval_flush, + _1, + scene_cow, + object_cow, + object_cow->pose), DEG_OPCODE_POSE_DONE); op_node->set_as_exit(); /* bones */ - LINKLIST_FOREACH (bPoseChannel *, pchan, &object->pose->chanbase) { + LINKLIST_FOREACH (bPoseChannel *, pchan, &object_cow->pose->chanbase) { /* Node for bone evaluation. */ op_node = add_operation_node(&object->id, DEG_NODE_TYPE_BONE, pchan->name, NULL, DEG_OPCODE_BONE_LOCAL); op_node->set_as_entry(); add_operation_node(&object->id, DEG_NODE_TYPE_BONE, pchan->name, - function_bind(BKE_pose_eval_bone, _1, scene_, object, pchan), + function_bind(BKE_pose_eval_bone, _1, + scene_cow, + object_cow, + pchan), DEG_OPCODE_BONE_POSE_PARENT); + /* NOTE: Dedicated noop for easier relationship construction. */ add_operation_node(&object->id, DEG_NODE_TYPE_BONE, pchan->name, - NULL, /* NOTE: dedicated noop for easier relationship construction */ + NULL, DEG_OPCODE_BONE_READY); op_node = add_operation_node(&object->id, DEG_NODE_TYPE_BONE, pchan->name, @@ -221,7 +277,7 @@ void DepsgraphNodeBuilder::build_rig(Object *object) DEG_OPCODE_PARAMETERS_EVAL, pchan->name); } - /* Constraints. */ + /* Build constraints. */ if (pchan->constraints.first != NULL) { build_pose_constraints(object, pchan); } @@ -233,8 +289,9 @@ void DepsgraphNodeBuilder::build_rig(Object *object) * base transforms of a bunch of bones is done) * * Unsolved Issues: - * - Care is needed to ensure that multi-headed trees work out the same as in ik-tree building - * - Animated chain-lengths are a problem... + * - Care is needed to ensure that multi-headed trees work out the same + * as in ik-tree building + * - Animated chain-lengths are a problem. */ LINKLIST_FOREACH (bConstraint *, con, &pchan->constraints) { switch (con->type) { @@ -250,6 +307,13 @@ void DepsgraphNodeBuilder::build_rig(Object *object) break; } } + /* Custom shape. */ + /* NOTE: Custom shape datablock is already remapped to CoW version. */ + if (pchan->custom != NULL) { + build_object(NULL, + get_orig_datablock(pchan->custom), + DEG_ID_LINKED_INDIRECTLY); + } } } @@ -257,33 +321,52 @@ void DepsgraphNodeBuilder::build_proxy_rig(Object *object) { bArmature *arm = (bArmature *)object->data; OperationDepsNode *op_node; - - build_animdata(&arm->id); - + Object *object_cow; + if (DEG_depsgraph_use_copy_on_write()) { + /* NOTE: We need to expand both object and armature, so this way we can + * safely create object level pose. + */ + object_cow = expand_cow_datablock(object); + } + else { + object_cow = object; + } + /* Sanity check. */ BLI_assert(object->pose != NULL); - + /* Animation. */ + build_animdata(&arm->id); /* speed optimization for animation lookups */ BKE_pose_channels_hash_make(object->pose); - if (object->pose->flag & POSE_CONSTRAINTS_NEED_UPDATE_FLAGS) { - BKE_pose_update_constraint_flags(object->pose); + if (object_cow->pose->flag & POSE_CONSTRAINTS_NEED_UPDATE_FLAGS) { + BKE_pose_update_constraint_flags(object_cow->pose); } - op_node = add_operation_node(&object->id, DEG_NODE_TYPE_EVAL_POSE, - function_bind(BKE_pose_eval_proxy_copy, _1, object), + function_bind(BKE_pose_eval_proxy_copy, + _1, + object_cow), DEG_OPCODE_POSE_INIT); op_node->set_as_entry(); - - LINKLIST_FOREACH (bPoseChannel *, pchan, &object->pose->chanbase) { - op_node = add_operation_node(&object->id, DEG_NODE_TYPE_BONE, pchan->name, - NULL, DEG_OPCODE_BONE_LOCAL); + LINKLIST_FOREACH (bPoseChannel *, pchan, &object_cow->pose->chanbase) { + /* Local bone transform. */ + op_node = add_operation_node(&object->id, + DEG_NODE_TYPE_BONE, + pchan->name, + NULL, + DEG_OPCODE_BONE_LOCAL); op_node->set_as_entry(); - - add_operation_node(&object->id, DEG_NODE_TYPE_BONE, pchan->name, - NULL, DEG_OPCODE_BONE_READY); - - op_node = add_operation_node(&object->id, DEG_NODE_TYPE_BONE, pchan->name, - NULL, DEG_OPCODE_BONE_DONE); + /* Bone is ready for solvers. */ + add_operation_node(&object->id, + DEG_NODE_TYPE_BONE, + pchan->name, + NULL, + DEG_OPCODE_BONE_READY); + /* Bone is fully evaluated. */ + op_node = add_operation_node(&object->id, + DEG_NODE_TYPE_BONE, + pchan->name, + NULL, + DEG_OPCODE_BONE_DONE); op_node->set_as_exit(); /* Custom properties. */ @@ -295,9 +378,10 @@ void DepsgraphNodeBuilder::build_proxy_rig(Object *object) pchan->name); } } - - op_node = add_operation_node(&object->id, DEG_NODE_TYPE_EVAL_POSE, - NULL, DEG_OPCODE_POSE_DONE); + op_node = add_operation_node(&object->id, + DEG_NODE_TYPE_EVAL_POSE, + NULL, + DEG_OPCODE_POSE_DONE); op_node->set_as_exit(); } diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes_scene.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes_view_layer.cc index 3c523b1a23c..49772c4f852 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_nodes_scene.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes_view_layer.cc @@ -24,7 +24,7 @@ * ***** END GPL LICENSE BLOCK ***** */ -/** \file blender/depsgraph/intern/builder/deg_builder_nodes_scene.cc +/** \file blender/depsgraph/intern/builder/deg_builder_nodes_view_layer.cc * \ingroup depsgraph * * Methods for constructing depsgraph's nodes @@ -43,9 +43,11 @@ extern "C" { #include "DNA_node_types.h" +#include "DNA_layer_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" +#include "BKE_layer.h" #include "BKE_main.h" #include "BKE_node.h" } /* extern "C" */ @@ -63,24 +65,64 @@ extern "C" { namespace DEG { -void DepsgraphNodeBuilder::build_scene(Scene *scene) +void DepsgraphNodeBuilder::build_view_layer( + Scene *scene, + ViewLayer *view_layer, + eDepsNode_LinkedState_Type linked_state) { /* Scene ID block. */ add_id_node(&scene->id); - /* timesource */ + /* Time source. */ add_time_source(); - /* build subgraph for set, and link this in... */ - // XXX: depending on how this goes, that scene itself could probably store its - // own little partial depsgraph? - if (scene->set != NULL) { - build_scene(scene->set); - } /* Setup currently building context. */ scene_ = scene; - /* scene objects */ - LINKLIST_FOREACH (Base *, base, &scene->base) { - Object *object = base->object; - build_object(base, object); + /* Expand Scene Cow datablock to get proper pointers to bases. */ + Scene *scene_cow; + ViewLayer *view_layer_cow; + if (DEG_depsgraph_use_copy_on_write()) { + /* NOTE: We need to create ID nodes for all objects coming from bases, + * otherwise remapping will not replace objects with their CoW versions + * for CoW bases. + */ + LINKLIST_FOREACH(Base *, base, &view_layer->object_bases) { + Object *object = base->object; + add_id_node(&object->id, false); + } + /* Create ID node for nested ID of nodetree as well, otherwise remapping + * will not work correct either. + */ + if (scene->nodetree != NULL) { + add_id_node(&scene->nodetree->id, false); + } + /* Make sure we've got ID node, so we can get pointer to CoW datablock. + */ + scene_cow = expand_cow_datablock(scene); + view_layer_cow = (ViewLayer *)BLI_findstring( + &scene_cow->view_layers, + view_layer->name, + offsetof(ViewLayer, name)); + } + else { + scene_cow = scene; + view_layer_cow = view_layer; + } + /* Scene objects. */ + int select_color = 1; + /* NOTE: Base is used for function bindings as-is, so need to pass CoW base, + * but object is expected to be an original one. Hence we go into some + * tricks here iterating over the view layer. + */ + for (Base *base_orig = (Base *)view_layer->object_bases.first, + *base_cow = (Base *)view_layer_cow->object_bases.first; + base_orig != NULL; + base_orig = base_orig->next, base_cow = base_cow->next) + { + /* object itself */ + build_object(base_cow, base_orig->object, linked_state); + base_orig->object->select_color = select_color++; + } + if (scene->camera != NULL) { + build_object(NULL, scene->camera, DEG_ID_LINKED_INDIRECTLY); } /* Rigidbody. */ if (scene->rigidbody_world != NULL) { @@ -94,7 +136,7 @@ void DepsgraphNodeBuilder::build_scene(Scene *scene) if (scene->world != NULL) { build_world(scene->world); } - /* Compositor nodes. */ + /* Compositor nodes */ if (scene->nodetree != NULL) { build_compositor(scene); } @@ -114,12 +156,19 @@ void DepsgraphNodeBuilder::build_scene(Scene *scene) LINKLIST_FOREACH (MovieClip *, clip, &bmain_->movieclip) { build_movieclip(clip); } + /* Collections. */ + build_view_layer_collections(&scene->id, view_layer_cow); /* Parameters evaluation for scene relations mainly. */ add_operation_node(&scene->id, DEG_NODE_TYPE_PARAMETERS, NULL, DEG_OPCODE_PLACEHOLDER, "Scene Eval"); + /* Build all set scenes. */ + if (scene->set != NULL) { + ViewLayer *set_view_layer = BKE_view_layer_from_scene_get(scene->set); + build_view_layer(scene->set, set_view_layer, DEG_ID_LINKED_VIA_SET); + } } } // namespace DEG diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc index b6ef331442a..10cf6f906de 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc @@ -61,6 +61,7 @@ extern "C" { #include "DNA_movieclip_types.h" #include "DNA_node_types.h" #include "DNA_particle_types.h" +#include "DNA_lightprobe_types.h" #include "DNA_object_types.h" #include "DNA_rigidbody_types.h" #include "DNA_scene_types.h" @@ -127,7 +128,7 @@ void modifier_walk(void *user_data, { BuilderWalkUserData *data = (BuilderWalkUserData *)user_data; if (*obpoin) { - data->builder->build_object(*obpoin); + data->builder->build_object(NULL, *obpoin); } } @@ -140,7 +141,7 @@ void constraint_walk(bConstraint * /*con*/, if (*idpoin) { ID *id = *idpoin; if (GS(id->name) == ID_OB) { - data->builder->build_object((Object *)id); + data->builder->build_object(NULL, (Object *)id); } } } @@ -311,19 +312,16 @@ void DepsgraphRelationBuilder::add_collision_relations( Scene *scene, Object *object, Group *group, - int layer, bool dupli, const char *name) { unsigned int numcollobj; - Object **collobjs = get_collisionobjects_ext( - scene, - object, - group, - layer, - &numcollobj, - eModifierType_Collision, - dupli); + Object **collobjs = get_collisionobjects_ext(scene, + object, + group, + &numcollobj, + eModifierType_Collision, + dupli); for (unsigned int i = 0; i < numcollobj; i++) { Object *ob1 = collobjs[i]; @@ -347,7 +345,7 @@ void DepsgraphRelationBuilder::add_forcefield_relations( bool add_absorption, const char *name) { - ListBase *effectors = pdInitEffectors(scene, object, psys, eff, false); + ListBase *effectors = pdInitEffectors(NULL, scene, object, psys, eff, false); if (effectors != NULL) { LINKLIST_FOREACH(EffectorCache *, eff, effectors) { if (eff->ob != object) { @@ -387,7 +385,6 @@ void DepsgraphRelationBuilder::add_forcefield_relations( scene, object, NULL, - eff->ob->lay, true, "Force Absorption"); } @@ -419,7 +416,7 @@ void DepsgraphRelationBuilder::begin_build() nodetree->id.tag &= ~LIB_TAG_DOIT; } } - FOREACH_NODETREE_END; + FOREACH_NODETREE_END } void DepsgraphRelationBuilder::build_group(Object *object, Group *group) @@ -429,19 +426,26 @@ void DepsgraphRelationBuilder::build_group(Object *object, Group *group) OperationKey object_local_transform_key(&object->id, DEG_NODE_TYPE_TRANSFORM, DEG_OPCODE_TRANSFORM_LOCAL); - LINKLIST_FOREACH (GroupObject *, go, &group->gobject) { - if (!group_done) { - build_object(go->ob); + + if (!group_done) { + LINKLIST_FOREACH(Base *, base, &group->view_layer->object_bases) { + build_object(NULL, base->object); } - ComponentKey dupli_transform_key(&go->ob->id, DEG_NODE_TYPE_TRANSFORM); + group_id->tag |= LIB_TAG_DOIT; + } + + LINKLIST_FOREACH (Base *, base, &group->view_layer->object_bases) { + ComponentKey dupli_transform_key(&base->object->id, DEG_NODE_TYPE_TRANSFORM); add_relation(dupli_transform_key, object_local_transform_key, "Dupligroup"); } - group_id->tag |= LIB_TAG_DOIT; } -void DepsgraphRelationBuilder::build_object(Object *object) +void DepsgraphRelationBuilder::build_object(Base *base, Object *object) { if (object->id.tag & LIB_TAG_DOIT) { + if (base != NULL) { + build_object_flags(base, object); + } return; } object->id.tag |= LIB_TAG_DOIT; @@ -461,6 +465,8 @@ void DepsgraphRelationBuilder::build_object(Object *object) OperationKey ob_ubereval_key(&object->id, DEG_NODE_TYPE_TRANSFORM, DEG_OPCODE_TRANSFORM_OBJECT_UBEREVAL); + /* Various flags, flushing from bases/collections. */ + build_object_flags(base, object); /* Parenting. */ if (object->parent != NULL) { /* Parent relationship. */ @@ -528,7 +534,7 @@ void DepsgraphRelationBuilder::build_object(Object *object) /* Object that this is a proxy for. */ if (object->proxy != NULL) { object->proxy->proxy_from = object; - build_object(object->proxy); + build_object(NULL, object->proxy); /* TODO(sergey): This is an inverted relation, matches old depsgraph * behavior and need to be investigated if it still need to be inverted. */ @@ -542,6 +548,20 @@ void DepsgraphRelationBuilder::build_object(Object *object) } } +void DepsgraphRelationBuilder::build_object_flags(Base *base, Object *object) +{ + if (base == NULL) { + return; + } + OperationKey view_layer_done_key(&scene_->id, + DEG_NODE_TYPE_LAYER_COLLECTIONS, + DEG_OPCODE_VIEW_LAYER_DONE); + OperationKey object_flags_key(&object->id, + DEG_NODE_TYPE_LAYER_COLLECTIONS, + DEG_OPCODE_OBJECT_BASE_FLAGS); + add_relation(view_layer_done_key, object_flags_key, "Base flags flush"); +} + void DepsgraphRelationBuilder::build_object_data(Object *object) { if (object->data == NULL) { @@ -576,6 +596,9 @@ void DepsgraphRelationBuilder::build_object_data(Object *object) case OB_CAMERA: build_camera(object); break; + case OB_LIGHTPROBE: + build_lightprobe(object); + break; } Key *key = BKE_key_from_object(object); if (key != NULL) { @@ -1080,18 +1103,18 @@ void DepsgraphRelationBuilder::build_driver_data(ID *id, FCurve *fcu) */ IDDepsNode *arm_node = graph_->find_id_node(id); char *bone_name = BLI_str_quoted_substrN(rna_path, "bones["); - if (arm_node && bone_name) { + if (arm_node != NULL && bone_name != NULL) { /* Find objects which use this, and make their eval callbacks * depend on this. */ foreach (DepsRelation *rel, arm_node->outlinks) { IDDepsNode *to_node = (IDDepsNode *)rel->to; /* We only care about objects with pose data which use this. */ - if (GS(to_node->id->name) == ID_OB) { - Object *object = (Object *)to_node->id; - /* NOTE: object->pose may be NULL. */ - bPoseChannel *pchan = BKE_pose_channel_find_name( - object->pose, bone_name); + if (GS(to_node->id_orig->name) == ID_OB) { + Object *object = (Object *)to_node->id_orig; + // NOTE: object->pose may be NULL + bPoseChannel *pchan = BKE_pose_channel_find_name(object->pose, + bone_name); if (pchan != NULL) { OperationKey bone_key(&object->id, DEG_NODE_TYPE_BONE, @@ -1218,9 +1241,9 @@ void DepsgraphRelationBuilder::build_world(World *world) /* world's nodetree */ if (world->nodetree != NULL) { build_nodetree(world->nodetree); - ComponentKey ntree_key(&world->nodetree->id, DEG_NODE_TYPE_PARAMETERS); - ComponentKey world_key(world_id, DEG_NODE_TYPE_PARAMETERS); - add_relation(ntree_key, world_key, "NTree->World Parameters"); + ComponentKey ntree_key(&world->nodetree->id, DEG_NODE_TYPE_SHADING); + ComponentKey world_key(world_id, DEG_NODE_TYPE_SHADING); + add_relation(ntree_key, world_key, "NTree->World Shading Update"); } } @@ -1242,8 +1265,8 @@ void DepsgraphRelationBuilder::build_rigidbody(Scene *scene) /* objects - simulation participants */ if (rbw->group) { - LINKLIST_FOREACH (GroupObject *, go, &rbw->group->gobject) { - Object *object = go->ob; + LINKLIST_FOREACH (Base *, base, &rbw->group->view_layer->object_bases) { + Object *object = base->object; if (object == NULL || object->type != OB_MESH) { continue; } @@ -1296,8 +1319,8 @@ void DepsgraphRelationBuilder::build_rigidbody(Scene *scene) /* constraints */ if (rbw->constraints) { - LINKLIST_FOREACH (GroupObject *, go, &rbw->constraints->gobject) { - Object *object = go->ob; + LINKLIST_FOREACH (Base *, base, &rbw->constraints->view_layer->object_bases) { + Object *object = base->object; if (object == NULL || !object->rigidbody_constraint) { continue; } @@ -1335,11 +1358,35 @@ void DepsgraphRelationBuilder::build_particles(Object *object) LINKLIST_FOREACH (ParticleSystem *, psys, &object->particlesystem) { ParticleSettings *part = psys->part; - /* particle settings */ - build_animdata(&part->id); - - /* this particle system */ - OperationKey psys_key(&object->id, DEG_NODE_TYPE_EVAL_PARTICLES, DEG_OPCODE_PARTICLE_SYSTEM_EVAL, psys->name); + /* Build particle settings relations. + * + * NOTE: The call itself ensures settings are only build once. + */ + build_particle_settings(part); + + /* This particle system. */ + OperationKey psys_key(&object->id, + DEG_NODE_TYPE_EVAL_PARTICLES, + DEG_OPCODE_PARTICLE_SYSTEM_EVAL, + psys->name); + + /* Update particle system when settings changes. */ + OperationKey particle_settings_key(&part->id, + DEG_NODE_TYPE_PARAMETERS, + DEG_OPCODE_PARTICLE_SETTINGS_EVAL); + OperationKey particle_settings_recalc_clear_key( + &part->id, + DEG_NODE_TYPE_PARAMETERS, + DEG_OPCODE_PARTICLE_SETTINGS_RECALC_CLEAR); + OperationKey psys_settings_key(&object->id, + DEG_NODE_TYPE_EVAL_PARTICLES, + DEG_OPCODE_PARTICLE_SETTINGS_EVAL, + psys->name); + add_relation(particle_settings_key, psys_settings_key, "Particle Settings Change"); + add_relation(psys_settings_key, psys_key, "Particle Settings Update"); + add_relation(psys_key, + particle_settings_recalc_clear_key, + "Particle Settings Recalc Clear"); /* XXX: if particle system is later re-enabled, we must do full rebuild? */ if (!psys_check_enabled(object, psys, G.is_rendering)) @@ -1355,14 +1402,33 @@ void DepsgraphRelationBuilder::build_particles(Object *object) /* collisions */ if (part->type != PART_HAIR) { - add_collision_relations(psys_key, scene_, object, part->collision_group, object->lay, true, "Particle Collision"); + add_collision_relations(psys_key, + scene_, + object, + part->collision_group, + true, + "Particle Collision"); } - else if ((psys->flag & PSYS_HAIR_DYNAMICS) && psys->clmd && psys->clmd->coll_parms) { - add_collision_relations(psys_key, scene_, object, psys->clmd->coll_parms->group, object->lay | scene_->lay, true, "Hair Collision"); + else if ((psys->flag & PSYS_HAIR_DYNAMICS) && + psys->clmd != NULL && + psys->clmd->coll_parms != NULL) + { + add_collision_relations(psys_key, + scene_, + object, + psys->clmd->coll_parms->group, + true, + "Hair Collision"); } /* effectors */ - add_forcefield_relations(psys_key, scene_, object, psys, part->effector_weights, part->type == PART_HAIR, "Particle Field"); + add_forcefield_relations(psys_key, + scene_, + object, + psys, + part->effector_weights, + part->type == PART_HAIR, + "Particle Field"); /* boids */ if (part->boids) { @@ -1404,8 +1470,27 @@ void DepsgraphRelationBuilder::build_particles(Object *object) ComponentKey transform_key(&object->id, DEG_NODE_TYPE_TRANSFORM); add_relation(transform_key, obdata_ubereval_key, "Partcile Eval"); - /* pointcache */ - // TODO... + /* TODO(sergey): Do we need a point cache operations here? */ +} + +void DepsgraphRelationBuilder::build_particle_settings(ParticleSettings *part) +{ + ID *part_id = &part->id; + if (part_id->tag & LIB_TAG_DOIT) { + return; + } + part_id->tag |= LIB_TAG_DOIT; + + /* Animation data relations. */ + build_animdata(&part->id); + + OperationKey eval_key(part_id, + DEG_NODE_TYPE_PARAMETERS, + DEG_OPCODE_PARTICLE_SETTINGS_EVAL); + OperationKey recalc_clear_key(part_id, + DEG_NODE_TYPE_PARAMETERS, + DEG_OPCODE_PARTICLE_SETTINGS_RECALC_CLEAR); + add_relation(eval_key, recalc_clear_key, "Particle Settings Clear Recalc"); } void DepsgraphRelationBuilder::build_cloth(Object *object, @@ -1479,12 +1564,22 @@ void DepsgraphRelationBuilder::build_obdata_geom(Object *object) /* link components to each other */ add_relation(obdata_geom_key, geom_key, "Object Geometry Base Data"); + OperationKey obdata_ubereval_key(&object->id, + DEG_NODE_TYPE_GEOMETRY, + DEG_OPCODE_GEOMETRY_UBEREVAL); + + /* Special case: modifiers and DerivedMesh creation queries scene for various + * things like data mask to be used. We add relation here to ensure object is + * never evaluated prior to Scene's CoW is ready. + */ + OperationKey scene_key(&scene_->id, + DEG_NODE_TYPE_PARAMETERS, + DEG_OPCODE_PLACEHOLDER, + "Scene Eval"); + add_relation(scene_key, obdata_ubereval_key, "CoW Relation"); + /* Modifiers */ if (object->modifiers.first != NULL) { - OperationKey obdata_ubereval_key(&object->id, - DEG_NODE_TYPE_GEOMETRY, - DEG_OPCODE_GEOMETRY_UBEREVAL); - LINKLIST_FOREACH (ModifierData *, md, &object->modifiers) { const ModifierTypeInfo *mti = modifierType_getInfo((ModifierType)md->type); if (mti->updateDepsgraph) { @@ -1512,6 +1607,14 @@ void DepsgraphRelationBuilder::build_obdata_geom(Object *object) Material *ma = give_current_material(object, a); if (ma != NULL) { build_material(ma); + + if (object->type == OB_MESH) { + OperationKey material_key(&ma->id, + DEG_NODE_TYPE_SHADING, + DEG_OPCODE_MATERIAL_UPDATE); + OperationKey shading_key(&object->id, DEG_NODE_TYPE_SHADING, DEG_OPCODE_SHADING); + add_relation(material_key, shading_key, "Material Update"); + } } } } @@ -1590,18 +1693,18 @@ void DepsgraphRelationBuilder::build_obdata_geom(Object *object) // XXX: these needs geom data, but where is geom stored? if (cu->bevobj) { ComponentKey bevob_key(&cu->bevobj->id, DEG_NODE_TYPE_GEOMETRY); - build_object(cu->bevobj); + build_object(NULL, cu->bevobj); add_relation(bevob_key, geom_key, "Curve Bevel"); } if (cu->taperobj) { ComponentKey taperob_key(&cu->taperobj->id, DEG_NODE_TYPE_GEOMETRY); - build_object(cu->taperobj); + build_object(NULL, cu->taperobj); add_relation(taperob_key, geom_key, "Curve Taper"); } if (object->type == OB_FONT) { if (cu->textoncurve) { ComponentKey textoncurve_key(&cu->textoncurve->id, DEG_NODE_TYPE_GEOMETRY); - build_object(cu->textoncurve); + build_object(NULL, cu->textoncurve); add_relation(textoncurve_key, geom_key, "Text on Curve"); } } @@ -1636,11 +1739,17 @@ void DepsgraphRelationBuilder::build_camera(Object *object) return; } camera_id->tag |= LIB_TAG_DOIT; + + ComponentKey object_parameters_key(&object->id, DEG_NODE_TYPE_PARAMETERS); + ComponentKey camera_parameters_key(camera_id, DEG_NODE_TYPE_PARAMETERS); + + add_relation(camera_parameters_key, object_parameters_key, + "Camera -> Object"); + /* DOF */ - if (cam->dof_ob) { - ComponentKey ob_param_key(&object->id, DEG_NODE_TYPE_PARAMETERS); + if (cam->dof_ob != NULL) { ComponentKey dof_ob_key(&cam->dof_ob->id, DEG_NODE_TYPE_TRANSFORM); - add_relation(dof_ob_key, ob_param_key, "Camera DOF"); + add_relation(dof_ob_key, object_parameters_key, "Camera DOF"); } } @@ -1653,30 +1762,44 @@ void DepsgraphRelationBuilder::build_lamp(Object *object) return; } lamp_id->tag |= LIB_TAG_DOIT; + + ComponentKey object_parameters_key(&object->id, DEG_NODE_TYPE_PARAMETERS); + ComponentKey lamp_parameters_key(lamp_id, DEG_NODE_TYPE_PARAMETERS); + + add_relation(lamp_parameters_key, object_parameters_key, + "Lamp -> Object"); + /* lamp's nodetree */ if (la->nodetree != NULL) { build_nodetree(la->nodetree); - ComponentKey parameters_key(lamp_id, DEG_NODE_TYPE_PARAMETERS); - ComponentKey nodetree_key(&la->nodetree->id, DEG_NODE_TYPE_PARAMETERS); - add_relation(nodetree_key, parameters_key, "NTree->Lamp Parameters"); + ComponentKey nodetree_key(&la->nodetree->id, DEG_NODE_TYPE_SHADING); + add_relation(nodetree_key, lamp_parameters_key, "NTree->Lamp Parameters"); } /* textures */ build_texture_stack(la->mtex); + + if (DEG_depsgraph_use_copy_on_write()) { + /* Make sure copy on write of lamp data is always properly updated for + * visible lamps. + */ + OperationKey ob_copy_on_write_key(&object->id, + DEG_NODE_TYPE_COPY_ON_WRITE, + DEG_OPCODE_COPY_ON_WRITE); + OperationKey lamp_copy_on_write_key(lamp_id, + DEG_NODE_TYPE_COPY_ON_WRITE, + DEG_OPCODE_COPY_ON_WRITE); + add_relation(lamp_copy_on_write_key, ob_copy_on_write_key, "Eval Order"); + } } void DepsgraphRelationBuilder::build_nodetree(bNodeTree *ntree) { - if (!ntree) + if (ntree == NULL) { return; - + } ID *ntree_id = &ntree->id; - build_animdata(ntree_id); - - OperationKey parameters_key(ntree_id, - DEG_NODE_TYPE_PARAMETERS, - DEG_OPCODE_PARAMETERS_EVAL); - + ComponentKey shading_key(ntree_id, DEG_NODE_TYPE_SHADING); /* nodetree's nodes... */ LINKLIST_FOREACH (bNode *, bnode, &ntree->nodes) { ID *id = bnode->id; @@ -1694,7 +1817,7 @@ void DepsgraphRelationBuilder::build_nodetree(bNodeTree *ntree) /* nothing for now. */ } else if (id_type == ID_OB) { - build_object((Object *)id); + build_object(NULL, (Object *)id); } else if (id_type == ID_SCE) { /* Scenes are used by compositor trees, and handled by render @@ -1710,15 +1833,22 @@ void DepsgraphRelationBuilder::build_nodetree(bNodeTree *ntree) build_nodetree(group_ntree); group_ntree->id.tag |= LIB_TAG_DOIT; } - OperationKey group_parameters_key(&group_ntree->id, - DEG_NODE_TYPE_PARAMETERS, - DEG_OPCODE_PARAMETERS_EVAL); - add_relation(group_parameters_key, parameters_key, "Group Node"); + ComponentKey group_shading_key(&group_ntree->id, + DEG_NODE_TYPE_SHADING); + add_relation(group_shading_key, shading_key, "Group Node"); } else { BLI_assert(!"Unknown ID type used for node"); } } + + OperationKey shading_update_key(ntree_id, + DEG_NODE_TYPE_SHADING, + DEG_OPCODE_MATERIAL_UPDATE); + OperationKey shading_parameters_key(ntree_id, + DEG_NODE_TYPE_SHADING_PARAMETERS, + DEG_OPCODE_MATERIAL_UPDATE); + add_relation(shading_parameters_key, shading_update_key, "NTree Shading Parameters"); } /* Recursively build graph for material */ @@ -1740,12 +1870,11 @@ void DepsgraphRelationBuilder::build_material(Material *ma) if (ma->nodetree != NULL) { build_nodetree(ma->nodetree); OperationKey ntree_key(&ma->nodetree->id, - DEG_NODE_TYPE_PARAMETERS, - DEG_OPCODE_PARAMETERS_EVAL); + DEG_NODE_TYPE_SHADING, + DEG_OPCODE_MATERIAL_UPDATE); OperationKey material_key(&ma->id, DEG_NODE_TYPE_SHADING, - DEG_OPCODE_PLACEHOLDER, - "Material Update"); + DEG_OPCODE_MATERIAL_UPDATE); add_relation(ntree_key, material_key, "Material's NTree"); } } @@ -1821,4 +1950,101 @@ void DepsgraphRelationBuilder::build_movieclip(MovieClip *clip) build_animdata(&clip->id); } +void DepsgraphRelationBuilder::build_lightprobe(Object *object) +{ + LightProbe *probe = (LightProbe *)object->data; + ID *probe_id = &probe->id; + if (probe_id->tag & LIB_TAG_DOIT) { + return; + } + probe_id->tag |= LIB_TAG_DOIT; + build_animdata(&probe->id); + + OperationKey probe_key(probe_id, + DEG_NODE_TYPE_PARAMETERS, + DEG_OPCODE_PLACEHOLDER, + "LightProbe Eval"); + OperationKey object_key(&object->id, + DEG_NODE_TYPE_PARAMETERS, + DEG_OPCODE_PLACEHOLDER, + "LightProbe Eval"); + add_relation(probe_key, object_key, "LightProbe Update"); +} + +void DepsgraphRelationBuilder::build_copy_on_write_relations() +{ + foreach (IDDepsNode *id_node, graph_->id_nodes) { + build_copy_on_write_relations(id_node); + } +} + +void DepsgraphRelationBuilder::build_copy_on_write_relations(IDDepsNode *id_node) +{ + ID *id_orig = id_node->id_orig; + + TimeSourceKey time_source_key; + OperationKey copy_on_write_key(id_orig, + DEG_NODE_TYPE_COPY_ON_WRITE, + DEG_OPCODE_COPY_ON_WRITE); + /* XXX: This is a quick hack to make Alt-A to work. */ + // add_relation(time_source_key, copy_on_write_key, "Fluxgate capacitor hack"); + /* Resat of code is using rather low level trickery, so need to get some + * explicit pointers. + */ + DepsNode *node_cow = find_node(copy_on_write_key); + OperationDepsNode *op_cow = node_cow->get_exit_operation(); + /* Plug any other components to this one. */ + GHASH_FOREACH_BEGIN(ComponentDepsNode *, comp_node, id_node->components) + { + if (comp_node->type == DEG_NODE_TYPE_COPY_ON_WRITE) { + /* Copy-on-write component never depends on itself. */ + continue; + } + if (!comp_node->depends_on_cow()) { + /* Component explicitly requests to not add relation. */ + continue; + } + /* All entry operations of each component should wait for a proper + * copy of ID. + */ + OperationDepsNode *op_entry = comp_node->get_entry_operation(); + if (op_entry != NULL) { + graph_->add_new_relation(op_cow, op_entry, "CoW Dependency"); + } + /* All dangling operations should also be executed after copy-on-write. */ + GHASH_FOREACH_BEGIN(OperationDepsNode *, op_node, comp_node->operations_map) + { + if (op_node->inlinks.size() == 0) { + graph_->add_new_relation(op_cow, op_node, "CoW Dependency"); + } + } + GHASH_FOREACH_END(); + /* NOTE: We currently ignore implicit relations to an external + * datablocks for copy-on-write operations. This means, for example, + * copy-on-write component of Object will not wait for copy-on-write + * component of it's Mesh. This is because pointers are all known + * already so remapping will happen all correct. And then If some object + * evaluation step needs geometry, it will have transitive dependency + * to Mesh copy-on-write already. + */ + } + GHASH_FOREACH_END(); + /* TODO(sergey): This solves crash for now, but causes too many + * updates potentially. + */ + if (GS(id_orig->name) == ID_OB) { + Object *object = (Object *)id_orig; + ID *object_data_id = (ID *)object->data; + if (object_data_id != NULL) { + OperationKey data_copy_on_write_key(object_data_id, + DEG_NODE_TYPE_COPY_ON_WRITE, + DEG_OPCODE_COPY_ON_WRITE); + add_relation(data_copy_on_write_key, copy_on_write_key, "Eval Order"); + } + else { + BLI_assert(object->type == OB_EMPTY); + } + } +} + } // namespace DEG diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.h b/source/blender/depsgraph/intern/builder/deg_builder_relations.h index 9558a726402..9227957adb4 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations.h +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.h @@ -56,6 +56,7 @@ struct ID; struct FCurve; struct Group; struct Key; +struct LayerCollection; struct Main; struct Mask; struct Material; @@ -67,10 +68,12 @@ struct Object; struct bPoseChannel; struct bConstraint; struct Scene; +struct ViewLayer; struct Tex; struct World; struct EffectorWeights; struct ParticleSystem; +struct ParticleSettings; struct PropertyRNA; @@ -189,9 +192,10 @@ struct DepsgraphRelationBuilder const char *description, bool check_unique = false); - void build_scene(Scene *scene); + void build_view_layer(Scene *scene, ViewLayer *view_layer); void build_group(Object *object, Group *group); - void build_object(Object *object); + void build_object(Base *base, Object *object); + void build_object_flags(Base *base, Object *object); void build_object_data(Object *object); void build_object_parent(Object *object); void build_constraints(ID *id, @@ -209,6 +213,7 @@ struct DepsgraphRelationBuilder void build_world(World *world); void build_rigidbody(Scene *scene); void build_particles(Object *object); + void build_particle_settings(ParticleSettings *part); void build_cloth(Object *object, ModifierData *md); void build_ik_pose(Object *object, bPoseChannel *pchan, @@ -233,12 +238,12 @@ struct DepsgraphRelationBuilder void build_cachefile(CacheFile *cache_file); void build_mask(Mask *mask); void build_movieclip(MovieClip *clip); + void build_lightprobe(Object *object); void add_collision_relations(const OperationKey &key, Scene *scene, Object *object, Group *group, - int layer, bool dupli, const char *name); void add_forcefield_relations(const OperationKey &key, @@ -246,8 +251,24 @@ struct DepsgraphRelationBuilder Object *object, ParticleSystem *psys, EffectorWeights *eff, - bool add_absorption, - const char *name); + bool add_absorption, const char *name); + + struct LayerCollectionState { + int index; + OperationKey init_key; + OperationKey done_key; + OperationKey prev_key; + }; + void build_layer_collection(ID *owner_id, + LayerCollection *layer_collection, + LayerCollectionState *state); + void build_layer_collections(ID *owner_id, + ListBase *layer_collections, + LayerCollectionState *state); + void build_view_layer_collections(struct ID *owner_id, ViewLayer *view_layer); + + void build_copy_on_write_relations(); + void build_copy_on_write_relations(IDDepsNode *id_node); template <typename KeyType> OperationDepsNode *find_operation_node(const KeyType &key); diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations_layer_collection.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations_layer_collection.cc new file mode 100644 index 00000000000..452bd7b19e7 --- /dev/null +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations_layer_collection.cc @@ -0,0 +1,124 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * 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. + * + * The Original Code is Copyright (C) 2013 Blender Foundation. + * All rights reserved. + * + * Original Author: Joshua Leung + * Contributor(s): Based on original depsgraph.c code - Blender Foundation (2005-2013) + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/depsgraph/intern/builder/deg_builder_relations_layer_collection.cc + * \ingroup depsgraph + * + * Methods for constructing depsgraph + */ + +#include "intern/builder/deg_builder_relations.h" + +#include <stdio.h> +#include <stdlib.h> +#include <cstring> /* required for STREQ later on. */ + +#include "MEM_guardedalloc.h" + +extern "C" { +#include "BLI_blenlib.h" +#include "BLI_utildefines.h" + +#include "DNA_node_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" + +#include "BKE_layer.h" +#include "BKE_main.h" +#include "BKE_node.h" + +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_build.h" +} /* extern "C" */ + +#include "intern/builder/deg_builder.h" +#include "intern/builder/deg_builder_pchanmap.h" + +#include "intern/nodes/deg_node.h" +#include "intern/nodes/deg_node_component.h" +#include "intern/nodes/deg_node_operation.h" + +#include "intern/depsgraph_intern.h" +#include "intern/depsgraph_types.h" + +#include "util/deg_util_foreach.h" + +namespace DEG { + +void DepsgraphRelationBuilder::build_layer_collection( + ID *owner_id, + LayerCollection *layer_collection, + LayerCollectionState *state) +{ + OperationKey layer_key(owner_id, + DEG_NODE_TYPE_LAYER_COLLECTIONS, + DEG_OPCODE_VIEW_LAYER_EVAL, + layer_collection->scene_collection->name, + state->index); + add_relation(state->prev_key, layer_key, "Layer collection order"); + + ++state->index; + state->prev_key = layer_key; + + /* Recurs into nested layer collections. */ + build_layer_collections(owner_id, &layer_collection->layer_collections, state); +} + +void DepsgraphRelationBuilder::build_layer_collections( + ID *owner_id, + ListBase *layer_collections, + LayerCollectionState *state) +{ + LINKLIST_FOREACH (LayerCollection *, layer_collection, layer_collections) { + /* Recurs into the layer. */ + build_layer_collection(owner_id, layer_collection, state); + } +} + +void DepsgraphRelationBuilder::build_view_layer_collections( + ID *owner_id, + ViewLayer *view_layer) +{ + LayerCollectionState state; + state.index = 0; + + OperationKey init_key(owner_id, + DEG_NODE_TYPE_LAYER_COLLECTIONS, + DEG_OPCODE_VIEW_LAYER_INIT); + OperationKey done_key(owner_id, + DEG_NODE_TYPE_LAYER_COLLECTIONS, + DEG_OPCODE_VIEW_LAYER_DONE); + + state.init_key = init_key; + state.done_key = done_key; + state.prev_key = init_key; + + build_layer_collections(owner_id, &view_layer->layer_collections, &state); + + add_relation(state.prev_key, done_key, "Layer collection order"); +} + +} // namespace DEG diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations_rig.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations_rig.cc index dc88d80fed9..2aff1ca33c7 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations_rig.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations_rig.cc @@ -387,16 +387,12 @@ void DepsgraphRelationBuilder::build_rig(Object *object) OperationKey bone_pose_key(&object->id, DEG_NODE_TYPE_BONE, pchan->name, DEG_OPCODE_BONE_POSE_PARENT); OperationKey bone_ready_key(&object->id, DEG_NODE_TYPE_BONE, pchan->name, DEG_OPCODE_BONE_READY); OperationKey bone_done_key(&object->id, DEG_NODE_TYPE_BONE, pchan->name, DEG_OPCODE_BONE_DONE); - pchan->flag &= ~POSE_DONE; - - /* pose init to bone local */ + /* Pose init to bone local. */ add_relation(init_key, bone_local_key, "PoseEval Source-Bone Link"); - - /* local to pose parenting operation */ + /* Local to pose parenting operation. */ add_relation(bone_local_key, bone_pose_key, "Bone Local - PoseSpace Link"); - - /* parent relation */ + /* Parent relation. */ if (pchan->parent != NULL) { eDepsOperation_Code parent_key_opcode; @@ -411,8 +407,7 @@ void DepsgraphRelationBuilder::build_rig(Object *object) OperationKey parent_key(&object->id, DEG_NODE_TYPE_BONE, pchan->parent->name, parent_key_opcode); add_relation(parent_key, bone_pose_key, "Parent Bone -> Child Bone"); } - - /* constraints */ + /* Buil constraints. */ if (pchan->constraints.first != NULL) { /* constraints stack and constraint dependencies */ build_constraints(&object->id, DEG_NODE_TYPE_BONE, pchan->name, &pchan->constraints, &root_map); @@ -439,6 +434,10 @@ void DepsgraphRelationBuilder::build_rig(Object *object) /* assume that all bones must be done for the pose to be ready (for deformers) */ add_relation(bone_done_key, flush_key, "PoseEval Result-Bone Link"); + /* Custom shape. */ + if (pchan->custom != NULL) { + build_object(NULL, pchan->custom); + } } } diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations_scene.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations_view_layer.cc index 29c739bf784..9b3e46df69a 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations_scene.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations_view_layer.cc @@ -24,7 +24,7 @@ * ***** END GPL LICENSE BLOCK ***** */ -/** \file blender/depsgraph/intern/builder/deg_builder_relations_scene.cc +/** \file blender/depsgraph/intern/builder/deg_builder_relations_view_layer.cc * \ingroup depsgraph * * Methods for constructing depsgraph @@ -46,6 +46,7 @@ extern "C" { #include "DNA_object_types.h" #include "DNA_scene_types.h" +#include "BKE_layer.h" #include "BKE_main.h" #include "BKE_node.h" } /* extern "C" */ @@ -68,17 +69,20 @@ extern "C" { namespace DEG { -void DepsgraphRelationBuilder::build_scene(Scene *scene) +void DepsgraphRelationBuilder::build_view_layer(Scene *scene, ViewLayer *view_layer) { - if (scene->set != NULL) { - build_scene(scene->set); - } /* Setup currently building context. */ scene_ = scene; /* Scene objects. */ - LINKLIST_FOREACH (Base *, base, &scene->base) { - Object *object = base->object; - build_object(object); + /* NOTE: Nodes builder requires us to pass CoW base because it's being + * passed to the evaluation functions. During relations builder we only + * do NULL-pointer check of the base, so it's fine to pass original one. + */ + LINKLIST_FOREACH(Base *, base, &view_layer->object_bases) { + build_object(base, base->object); + } + if (scene->camera != NULL) { + build_object(NULL, scene->camera); } /* Rigidbody. */ if (scene->rigidbody_world != NULL) { @@ -108,18 +112,25 @@ void DepsgraphRelationBuilder::build_scene(Scene *scene) LINKLIST_FOREACH (MovieClip *, clip, &bmain_->movieclip) { build_movieclip(clip); } - for (Depsgraph::OperationNodes::const_iterator it_op = graph_->operations.begin(); - it_op != graph_->operations.end(); - ++it_op) - { - OperationDepsNode *node = *it_op; + /* Collections. */ + build_view_layer_collections(&scene_->id, view_layer); + /* TODO(sergey): Do this flush on CoW object? */ + foreach (OperationDepsNode *node, graph_->operations) { IDDepsNode *id_node = node->owner->owner; - ID *id = id_node->id; + ID *id = id_node->id_orig; if (GS(id->name) == ID_OB) { Object *object = (Object *)id; object->customdata_mask |= node->customdata_mask; } } + /* Build all set scenes. */ + if (scene->set != NULL) { + ViewLayer *set_view_layer = BKE_view_layer_from_scene_get(scene->set); + build_view_layer(scene->set, set_view_layer); + } + + graph_->scene = scene; + graph_->view_layer = view_layer; } } // namespace DEG diff --git a/source/blender/depsgraph/intern/debug/deg_debug_relations_graphviz.cc b/source/blender/depsgraph/intern/debug/deg_debug_relations_graphviz.cc index ca7e9c5c40c..827bb4d10ad 100644 --- a/source/blender/depsgraph/intern/debug/deg_debug_relations_graphviz.cc +++ b/source/blender/depsgraph/intern/debug/deg_debug_relations_graphviz.cc @@ -81,18 +81,21 @@ static const char *deg_debug_colors_light[] = { #ifdef COLOR_SCHEME_NODE_TYPE static const int deg_debug_node_type_color_map[][2] = { {DEG_NODE_TYPE_TIMESOURCE, 0}, - {DEG_NODE_TYPE_ID_REF, 2}, + {DEG_NODE_TYPE_ID_REF, 1}, /* Outer Types */ - {DEG_NODE_TYPE_PARAMETERS, 2}, - {DEG_NODE_TYPE_PROXY, 3}, - {DEG_NODE_TYPE_ANIMATION, 4}, - {DEG_NODE_TYPE_TRANSFORM, 5}, - {DEG_NODE_TYPE_GEOMETRY, 6}, - {DEG_NODE_TYPE_SEQUENCER, 7}, - {DEG_NODE_TYPE_SHADING, 8}, - {DEG_NODE_TYPE_CACHE, 9}, - {-1, 0} + {DEG_NODE_TYPE_PARAMETERS, 2}, + {DEG_NODE_TYPE_PROXY, 3}, + {DEG_NODE_TYPE_ANIMATION, 4}, + {DEG_NODE_TYPE_TRANSFORM, 5}, + {DEG_NODE_TYPE_GEOMETRY, 6}, + {DEG_NODE_TYPE_SEQUENCER, 7}, + {DEG_NODE_TYPE_SHADING, 8}, + {DEG_NODE_TYPE_SHADING_PARAMETERS, 9}, + {DEG_NODE_TYPE_CACHE, 10}, + {DEG_NODE_TYPE_LAYER_COLLECTIONS, 11}, + {DEG_NODE_TYPE_COPY_ON_WRITE, 12}, + {-1, 0} }; #endif @@ -285,12 +288,6 @@ static void deg_debug_graphviz_node_single(const DebugContext &ctx, { const char *shape = "box"; string name = node->identifier(); - if (node->type == DEG_NODE_TYPE_ID_REF) { - IDDepsNode *id_node = (IDDepsNode *)node; - char buf[256]; - BLI_snprintf(buf, sizeof(buf), " (Layers: %u)", id_node->layers); - name += buf; - } deg_debug_fprintf(ctx, "// %s\n", name.c_str()); deg_debug_fprintf(ctx, "\"node_%p\"", node); deg_debug_fprintf(ctx, "["); @@ -311,12 +308,6 @@ static void deg_debug_graphviz_node_cluster_begin(const DebugContext &ctx, const DepsNode *node) { string name = node->identifier(); - if (node->type == DEG_NODE_TYPE_ID_REF) { - IDDepsNode *id_node = (IDDepsNode *)node; - char buf[256]; - BLI_snprintf(buf, sizeof(buf), " (Layers: %u)", id_node->layers); - name += buf; - } deg_debug_fprintf(ctx, "// %s\n", name.c_str()); deg_debug_fprintf(ctx, "subgraph \"cluster_%p\" {" NL, node); // deg_debug_fprintf(ctx, "label=<<B>%s</B>>;" NL, name); @@ -378,8 +369,12 @@ static void deg_debug_graphviz_node(const DebugContext &ctx, case DEG_NODE_TYPE_EVAL_POSE: case DEG_NODE_TYPE_BONE: case DEG_NODE_TYPE_SHADING: + case DEG_NODE_TYPE_SHADING_PARAMETERS: case DEG_NODE_TYPE_CACHE: + case DEG_NODE_TYPE_LAYER_COLLECTIONS: case DEG_NODE_TYPE_EVAL_PARTICLES: + case DEG_NODE_TYPE_COPY_ON_WRITE: + case DEG_NODE_TYPE_BATCH_CACHE: { ComponentDepsNode *comp_node = (ComponentDepsNode *)node; if (!comp_node->operations.empty()) { @@ -394,9 +389,13 @@ static void deg_debug_graphviz_node(const DebugContext &ctx, } break; } - default: + case DEG_NODE_TYPE_UNDEFINED: + case DEG_NODE_TYPE_TIMESOURCE: + case DEG_NODE_TYPE_OPERATION: deg_debug_graphviz_node_single(ctx, node); break; + case NUM_DEG_NODE_TYPES: + break; } } diff --git a/source/blender/depsgraph/intern/debug/deg_debug_stats_gnuplot.cc b/source/blender/depsgraph/intern/debug/deg_debug_stats_gnuplot.cc index e92c7730482..35888f8d5e3 100644 --- a/source/blender/depsgraph/intern/debug/deg_debug_stats_gnuplot.cc +++ b/source/blender/depsgraph/intern/debug/deg_debug_stats_gnuplot.cc @@ -110,7 +110,7 @@ void write_stats_data(const DebugContext& ctx) deg_debug_fprintf(ctx, "$data << EOD" NL); foreach (const StatsEntry& entry, stats) { deg_debug_fprintf(ctx, "\"%s\",%f" NL, - entry.id_node->id->name + 2, + entry.id_node->id_orig->name + 2, entry.time); } deg_debug_fprintf(ctx, "EOD" NL); diff --git a/source/blender/depsgraph/intern/depsgraph.cc b/source/blender/depsgraph/intern/depsgraph.cc index c4841d6789e..45013bb1bcd 100644 --- a/source/blender/depsgraph/intern/depsgraph.cc +++ b/source/blender/depsgraph/intern/depsgraph.cc @@ -46,8 +46,6 @@ extern "C" { #include "DNA_object_types.h" #include "DNA_sequence_types.h" -#include "BKE_depsgraph.h" - #include "RNA_access.h" } @@ -56,6 +54,8 @@ extern "C" { #include "DEG_depsgraph.h" +#include "intern/eval/deg_eval_copy_on_write.h" + #include "intern/nodes/deg_node.h" #include "intern/nodes/deg_node_component.h" #include "intern/nodes/deg_node_id.h" @@ -65,11 +65,22 @@ extern "C" { #include "intern/depsgraph_intern.h" #include "util/deg_util_foreach.h" +static bool use_copy_on_write = false; + +bool DEG_depsgraph_use_copy_on_write(void) +{ + return use_copy_on_write; +} + +void DEG_depsgraph_enable_copy_on_write(void) +{ + use_copy_on_write = true; +} + namespace DEG { static DEG_EditorUpdateIDCb deg_editor_update_id_cb = NULL; static DEG_EditorUpdateSceneCb deg_editor_update_scene_cb = NULL; -static DEG_EditorUpdateScenePreCb deg_editor_update_scene_pre_cb = NULL; /* TODO(sergey): Find a better place for this. */ template <typename T> @@ -81,8 +92,9 @@ static void remove_from_vector(vector<T> *vector, const T& value) Depsgraph::Depsgraph() : time_source(NULL), - need_update(false), - layers(0) + need_update(true), + scene(NULL), + view_layer(NULL) { BLI_spin_init(&lock); id_hash = BLI_ghash_ptr_new("Depsgraph id hash"); @@ -259,12 +271,6 @@ DepsNode *Depsgraph::find_node_from_pointer(const PointerRNA *ptr, /* Node Management ---------------------------- */ -static void id_node_deleter(void *value) -{ - IDDepsNode *id_node = reinterpret_cast<IDDepsNode *>(value); - OBJECT_GUARDED_DELETE(id_node, IDDepsNode); -} - TimeSourceDepsNode *Depsgraph::add_time_source() { if (time_source == NULL) { @@ -284,23 +290,57 @@ IDDepsNode *Depsgraph::find_id_node(const ID *id) const return reinterpret_cast<IDDepsNode *>(BLI_ghash_lookup(id_hash, id)); } -IDDepsNode *Depsgraph::add_id_node(ID *id, const char *name) +IDDepsNode *Depsgraph::add_id_node(ID *id, bool do_tag, ID *id_cow_hint) { + BLI_assert((id->tag & LIB_TAG_COPY_ON_WRITE) == 0); IDDepsNode *id_node = find_id_node(id); if (!id_node) { DepsNodeFactory *factory = deg_type_get_factory(DEG_NODE_TYPE_ID_REF); - id_node = (IDDepsNode *)factory->create_node(id, "", name); - id->tag |= LIB_TAG_DOIT; - /* register */ + id_node = (IDDepsNode *)factory->create_node(id, "", id->name); + id_node->init_copy_on_write(id_cow_hint); + if (do_tag) { + id->tag |= LIB_TAG_DOIT; + } + /* Register node in ID hash. + * + * NOTE: We address ID nodes by the original ID pointer they are + * referencing to. + */ BLI_ghash_insert(id_hash, id, id_node); id_nodes.push_back(id_node); } + else if (do_tag) { + id->tag |= LIB_TAG_DOIT; + } return id_node; } void Depsgraph::clear_id_nodes() { - BLI_ghash_clear(id_hash, NULL, id_node_deleter); + /* Free memory used by ID nodes. */ + if (use_copy_on_write) { + /* Stupid workaround to ensure we free IDs in a proper order. */ + foreach (IDDepsNode *id_node, id_nodes) { + if (id_node->id_cow == NULL) { + /* This means builder "stole" ownership of the copy-on-written + * datablock for her own dirty needs. + */ + continue; + } + if (!deg_copy_on_write_is_expanded(id_node->id_cow)) { + continue; + } + const ID_Type id_type = GS(id_node->id_cow->name); + if (id_type != ID_PA) { + id_node->destroy(); + } + } + } + foreach (IDDepsNode *id_node, id_nodes) { + OBJECT_GUARDED_DELETE(id_node, IDDepsNode); + } + /* Clear containers. */ + BLI_ghash_clear(id_hash, NULL, NULL); id_nodes.clear(); } @@ -325,7 +365,7 @@ DepsRelation *Depsgraph::add_new_relation(OperationDepsNode *from, if (comp_node->type == DEG_NODE_TYPE_GEOMETRY) { IDDepsNode *id_to = to->owner->owner; IDDepsNode *id_from = from->owner->owner; - if (id_to != id_from && (id_to->id->recalc & ID_RECALC_ALL)) { + if (id_to != id_from && (id_to->id_orig->recalc & ID_RECALC_ALL)) { if ((id_from->eval_flags & DAG_EVAL_NEED_CPU) == 0) { id_from->tag_update(this); id_from->eval_flags |= DAG_EVAL_NEED_CPU; @@ -419,9 +459,9 @@ void DepsRelation::unlink() void Depsgraph::add_entry_tag(OperationDepsNode *node) { /* Sanity check. */ - if (!node) + if (node == NULL) { return; - + } /* Add to graph-level set of directly modified nodes to start searching from. * NOTE: this is necessary since we have several thousand nodes to play with... */ @@ -437,17 +477,46 @@ void Depsgraph::clear_all_nodes() } } -void deg_editors_id_update(Main *bmain, ID *id) +ID *Depsgraph::get_cow_id(const ID *id_orig) const +{ + IDDepsNode *id_node = find_id_node(id_orig); + if (id_node == NULL) { + /* This function is used from places where we expect ID to be either + * already a copy-on-write version or have a corresponding copy-on-write + * version. + * + * We try to enforce that in debug builds, for for release we play a bit + * safer game here. + */ + if ((id_orig->tag & LIB_TAG_COPY_ON_WRITE) == 0) { + /* TODO(sergey): This is nice sanity check to have, but it fails + * in following situations: + * + * - Material has link to texture, which is not needed by new + * shading system and hence can be ignored at construction. + * - Object or mesh has material at a slot which is not used (for + * example, object has material slot by materials are set to + * object data). + */ + // BLI_assert(!"Request for non-existing copy-on-write ID"); + } + return (ID *)id_orig; + } + return id_node->id_cow; +} + +void deg_editors_id_update(const DEGEditorUpdateContext *update_ctx, ID *id) { if (deg_editor_update_id_cb != NULL) { - deg_editor_update_id_cb(bmain, id); + deg_editor_update_id_cb(update_ctx, id); } } -void deg_editors_scene_update(Main *bmain, Scene *scene, bool updated) +void deg_editors_scene_update(const DEGEditorUpdateContext *update_ctx, + bool updated) { if (deg_editor_update_scene_cb != NULL) { - deg_editor_update_scene_cb(bmain, scene, updated); + deg_editor_update_scene_cb(update_ctx, updated); } } @@ -473,17 +542,8 @@ void DEG_graph_free(Depsgraph *graph) /* Set callbacks which are being called when depsgraph changes. */ void DEG_editors_set_update_cb(DEG_EditorUpdateIDCb id_func, - DEG_EditorUpdateSceneCb scene_func, - DEG_EditorUpdateScenePreCb scene_pre_func) + DEG_EditorUpdateSceneCb scene_func) { DEG::deg_editor_update_id_cb = id_func; DEG::deg_editor_update_scene_cb = scene_func; - DEG::deg_editor_update_scene_pre_cb = scene_pre_func; -} - -void DEG_editors_update_pre(Main *bmain, Scene *scene, bool time) -{ - if (DEG::deg_editor_update_scene_pre_cb != NULL) { - DEG::deg_editor_update_scene_pre_cb(bmain, scene, time); - } } diff --git a/source/blender/depsgraph/intern/depsgraph.h b/source/blender/depsgraph/intern/depsgraph.h index ce56d67b6de..f18b93c9807 100644 --- a/source/blender/depsgraph/intern/depsgraph.h +++ b/source/blender/depsgraph/intern/depsgraph.h @@ -42,9 +42,12 @@ struct ID; struct GHash; +struct Main; struct GSet; struct PointerRNA; struct PropertyRNA; +struct Scene; +struct ViewLayer; namespace DEG { @@ -104,7 +107,8 @@ struct Depsgraph { * Convenience wrapper to find node given just pointer + property. * * \param ptr: pointer to the data that node will represent - * \param prop: optional property affected - providing this effectively results in inner nodes being returned + * \param prop: optional property affected - providing this effectively + * results in inner nodes being returned * * \return A node matching the required characteristics if it exists * or NULL if no such node exists in the graph @@ -115,7 +119,7 @@ struct Depsgraph { TimeSourceDepsNode *find_time_source() const; IDDepsNode *find_id_node(const ID *id) const; - IDDepsNode *add_id_node(ID *id, const char *name = ""); + IDDepsNode *add_id_node(ID *id, bool do_tag = true, ID *id_cow_hint = NULL); void clear_id_nodes(); /* Add new relationship between two nodes. */ @@ -143,6 +147,11 @@ struct Depsgraph { /* Clear storage used by all nodes. */ void clear_all_nodes(); + /* Copy-on-Write Functionality ........ */ + + /* For given original ID get ID which is created by CoW system. */ + ID *get_cow_id(const ID *id_orig) const; + /* Core Graph Functionality ........... */ /* <ID : IDDepsNode> mapping from ID blocks to nodes representing these @@ -178,12 +187,9 @@ struct Depsgraph { */ SpinLock lock; - /* Layers Visibility .................. */ - - /* Visible layers bitfield, used for skipping invisible objects updates. */ - unsigned int layers; - - // XXX: additional stuff like eval contexts, mempools for allocating nodes from, etc. + /* Scene and layer this dependency graph is built for. */ + Scene *scene; + ViewLayer *view_layer; }; } // namespace DEG diff --git a/source/blender/depsgraph/intern/depsgraph_build.cc b/source/blender/depsgraph/intern/depsgraph_build.cc index 3884cfe49e7..5b07630240d 100644 --- a/source/blender/depsgraph/intern/depsgraph_build.cc +++ b/source/blender/depsgraph/intern/depsgraph_build.cc @@ -36,6 +36,7 @@ #include "BLI_utildefines.h" #include "BLI_ghash.h" +#include "BLI_listbase.h" #ifdef DEBUG_TIME # include "PIL_time.h" @@ -52,6 +53,7 @@ extern "C" { #include "BKE_collision.h" #include "BKE_effect.h" #include "BKE_modifier.h" +#include "BKE_scene.h" } /* extern "C" */ #include "DEG_depsgraph.h" @@ -192,31 +194,46 @@ void DEG_add_special_eval_flag(Depsgraph *graph, ID *id, short flag) /* ******************** */ /* Graph Building API's */ -/* Build depsgraph for the given scene, and dump results in given +/* Build depsgraph for the given scene layer, and dump results in given * graph container. */ -/* XXX: assume that this is called from outside, given the current scene as - * the "main" scene. - */ -void DEG_graph_build_from_scene(Depsgraph *graph, Main *bmain, Scene *scene) +void DEG_graph_build_from_view_layer(Depsgraph *graph, + Main *bmain, + Scene *scene, + ViewLayer *view_layer) { #ifdef DEBUG_TIME - TIMEIT_START(DEG_graph_build_from_scene); + TIMEIT_START(DEG_graph_build_from_view_layer); #endif DEG::Depsgraph *deg_graph = reinterpret_cast<DEG::Depsgraph *>(graph); + BLI_assert(BLI_findindex(&scene->view_layers, view_layer) != -1); + + /* TODO(sergey): This is a bit tricky, but ensures that all the data + * is evaluated properly when depsgraph is becoming "visible". + * + * This now could happen for both visible scene is changed and extra + * dependency graph was created for render engine. + */ + const bool need_on_visible_update = (deg_graph->scene == NULL); /* 1) Generate all the nodes in the graph first */ DEG::DepsgraphNodeBuilder node_builder(bmain, deg_graph); node_builder.begin_build(); - node_builder.build_scene(scene); + node_builder.build_view_layer(scene, + view_layer, + DEG::DEG_ID_LINKED_DIRECTLY); + node_builder.end_build(); /* 2) Hook up relationships between operations - to determine evaluation * order. */ DEG::DepsgraphRelationBuilder relation_builder(bmain, deg_graph); relation_builder.begin_build(); - relation_builder.build_scene(scene); + relation_builder.build_view_layer(scene, view_layer); + if (DEG_depsgraph_use_copy_on_write()) { + relation_builder.build_copy_on_write_relations(); + } /* Detect and solve cycles. */ DEG::deg_graph_detect_cycles(deg_graph); @@ -231,7 +248,7 @@ void DEG_graph_build_from_scene(Depsgraph *graph, Main *bmain, Scene *scene) } /* 4) Flush visibility layer and re-schedule nodes for update. */ - DEG::deg_graph_build_finalize(deg_graph); + DEG::deg_graph_build_finalize(bmain, deg_graph); #if 0 if (!DEG_debug_consistency_check(deg_graph)) { @@ -241,8 +258,15 @@ void DEG_graph_build_from_scene(Depsgraph *graph, Main *bmain, Scene *scene) #endif #ifdef DEBUG_TIME - TIMEIT_END(DEG_graph_build_from_scene); + TIMEIT_END(DEG_graph_build_from_view_layer); #endif + + /* Relations are up to date. */ + deg_graph->need_update = false; + + if (need_on_visible_update) { + DEG_graph_on_visible_update(bmain, graph); + } } /* Tag graph relations for update. */ @@ -252,64 +276,34 @@ void DEG_graph_tag_relations_update(Depsgraph *graph) deg_graph->need_update = true; } -/* Tag all relations for update. */ -void DEG_relations_tag_update(Main *bmain) -{ - for (Scene *scene = (Scene *)bmain->scene.first; - scene != NULL; - scene = (Scene *)scene->id.next) - { - if (scene->depsgraph != NULL) { - DEG_graph_tag_relations_update(scene->depsgraph); - } - } -} - -/* Create new graph if didn't exist yet, - * or update relations if graph was tagged for update. - */ -void DEG_scene_relations_update(Main *bmain, Scene *scene) +/* Create or update relations in the specified graph. */ +void DEG_graph_relations_update(Depsgraph *graph, + Main *bmain, + Scene *scene, + ViewLayer *view_layer) { - if (scene->depsgraph == NULL) { - /* Rebuild graph from scratch and exit. */ - scene->depsgraph = DEG_graph_new(); - DEG_graph_build_from_scene(scene->depsgraph, bmain, scene); - return; - } - - DEG::Depsgraph *graph = reinterpret_cast<DEG::Depsgraph *>(scene->depsgraph); - if (!graph->need_update) { + DEG::Depsgraph *deg_graph = (DEG::Depsgraph *)graph; + if (!deg_graph->need_update) { /* Graph is up to date, nothing to do. */ return; } - - /* Clear all previous nodes and operations. */ - graph->clear_all_nodes(); - graph->operations.clear(); - BLI_gset_clear(graph->entry_tags, NULL); - - /* Build new nodes and relations. */ - DEG_graph_build_from_scene(reinterpret_cast< ::Depsgraph * >(graph), - bmain, - scene); - - graph->need_update = false; -} - -/* Rebuild dependency graph only for a given scene. */ -void DEG_scene_relations_rebuild(Main *bmain, Scene *scene) -{ - if (scene->depsgraph != NULL) { - DEG_graph_tag_relations_update(scene->depsgraph); - } - DEG_scene_relations_update(bmain, scene); + DEG_graph_build_from_view_layer(graph, bmain, scene, view_layer); } -void DEG_scene_graph_free(Scene *scene) +/* Tag all relations for update. */ +void DEG_relations_tag_update(Main *bmain) { - if (scene->depsgraph) { - DEG_graph_free(scene->depsgraph); - scene->depsgraph = NULL; + DEG_DEBUG_PRINTF("%s: Tagging relations for update.\n", __func__); + LINKLIST_FOREACH(Scene *, scene, &bmain->scene) { + LINKLIST_FOREACH(ViewLayer *, view_layer, &scene->view_layers) { + Depsgraph *depsgraph = + (Depsgraph *)BKE_scene_get_depsgraph(scene, + view_layer, + false); + if (depsgraph != NULL) { + DEG_graph_tag_relations_update(depsgraph); + } + } } } @@ -317,14 +311,13 @@ void DEG_add_collision_relations(DepsNodeHandle *handle, Scene *scene, Object *object, Group *group, - int layer, unsigned int modifier_type, DEG_CollobjFilterFunction fn, bool dupli, const char *name) { unsigned int numcollobj; - Object **collobjs = get_collisionobjects_ext(scene, object, group, layer, &numcollobj, modifier_type, dupli); + Object **collobjs = get_collisionobjects_ext(scene, object, group, &numcollobj, modifier_type, dupli); for (unsigned int i = 0; i < numcollobj; i++) { Object *ob1 = collobjs[i]; @@ -347,7 +340,7 @@ void DEG_add_forcefield_relations(DepsNodeHandle *handle, int skip_forcefield, const char *name) { - ListBase *effectors = pdInitEffectors(scene, object, NULL, effector_weights, false); + ListBase *effectors = pdInitEffectors(NULL, scene, object, NULL, effector_weights, false); if (effectors) { for (EffectorCache *eff = (EffectorCache*)effectors->first; eff; eff = eff->next) { @@ -379,7 +372,6 @@ void DEG_add_forcefield_relations(DepsNodeHandle *handle, scene, object, NULL, - eff->ob->lay, eModifierType_Collision, NULL, true, diff --git a/source/blender/depsgraph/intern/depsgraph_debug.cc b/source/blender/depsgraph/intern/depsgraph_debug.cc index b7bf63e0cb0..e8d166532ad 100644 --- a/source/blender/depsgraph/intern/depsgraph_debug.cc +++ b/source/blender/depsgraph/intern/depsgraph_debug.cc @@ -67,18 +67,20 @@ bool DEG_debug_compare(const struct Depsgraph *graph1, return true; } -bool DEG_debug_scene_relations_validate(Main *bmain, - Scene *scene) +bool DEG_debug_graph_relations_validate(Depsgraph *graph, + Main *bmain, + Scene *scene, + ViewLayer *view_layer) { - Depsgraph *depsgraph = DEG_graph_new(); + Depsgraph *temp_depsgraph = DEG_graph_new(); bool valid = true; - DEG_graph_build_from_scene(depsgraph, bmain, scene); - if (!DEG_debug_compare(depsgraph, scene->depsgraph)) { + DEG_graph_build_from_view_layer(temp_depsgraph, bmain, scene, view_layer); + if (!DEG_debug_compare(temp_depsgraph, graph)) { fprintf(stderr, "ERROR! Depsgraph wasn't tagged for update when it should have!\n"); BLI_assert(!"This should not happen!"); valid = false; } - DEG_graph_free(depsgraph); + DEG_graph_free(temp_depsgraph); return valid; } diff --git a/source/blender/depsgraph/intern/depsgraph_eval.cc b/source/blender/depsgraph/intern/depsgraph_eval.cc index 0448dbe2c8d..ad1a850a807 100644 --- a/source/blender/depsgraph/intern/depsgraph_eval.cc +++ b/source/blender/depsgraph/intern/depsgraph_eval.cc @@ -36,10 +36,9 @@ #include "BLI_ghash.h" extern "C" { -#include "DNA_scene_types.h" - -#include "BKE_depsgraph.h" #include "BKE_scene.h" + +#include "DNA_scene_types.h" } /* extern "C" */ #include "DEG_depsgraph.h" @@ -53,50 +52,16 @@ extern "C" { #include "intern/depsgraph.h" -#ifdef WITH_LEGACY_DEPSGRAPH -static bool use_legacy_depsgraph = true; -#endif - -bool DEG_depsgraph_use_legacy(void) -{ -#ifdef DISABLE_NEW_DEPSGRAPH - return true; -#elif defined(WITH_LEGACY_DEPSGRAPH) - return use_legacy_depsgraph; -#else - BLI_assert(!"Should not be used with new depsgraph"); - return false; -#endif -} - -void DEG_depsgraph_switch_to_legacy(void) -{ -#ifdef WITH_LEGACY_DEPSGRAPH - use_legacy_depsgraph = true; -#else - BLI_assert(!"Should not be used with new depsgraph"); -#endif -} - -void DEG_depsgraph_switch_to_new(void) -{ -#ifdef WITH_LEGACY_DEPSGRAPH - use_legacy_depsgraph = false; -#else - BLI_assert(!"Should not be used with new depsgraph"); -#endif -} - /* ****************** */ /* Evaluation Context */ /* Create new evaluation context. */ -EvaluationContext *DEG_evaluation_context_new(int mode) +EvaluationContext *DEG_evaluation_context_new(eEvaluationMode mode) { EvaluationContext *eval_ctx = (EvaluationContext *)MEM_callocN(sizeof(EvaluationContext), "EvaluationContext"); - eval_ctx->mode = mode; + DEG_evaluation_context_init(eval_ctx, mode); return eval_ctx; } @@ -105,11 +70,25 @@ EvaluationContext *DEG_evaluation_context_new(int mode) * Used by the areas which currently overrides the context or doesn't have * access to a proper one. */ -void DEG_evaluation_context_init(EvaluationContext *eval_ctx, int mode) +void DEG_evaluation_context_init(EvaluationContext *eval_ctx, + eEvaluationMode mode) { eval_ctx->mode = mode; } +void DEG_evaluation_context_init_from_scene(EvaluationContext *eval_ctx, + Scene *scene, + ViewLayer *view_layer, + RenderEngineType *engine_type, + eEvaluationMode mode) +{ + DEG_evaluation_context_init(eval_ctx, mode); + eval_ctx->depsgraph = BKE_scene_get_depsgraph(scene, view_layer, true); + eval_ctx->view_layer = view_layer; + eval_ctx->engine_type = engine_type; + eval_ctx->ctime = BKE_scene_frame_get(scene); +} + /* Free evaluation context. */ void DEG_evaluation_context_free(EvaluationContext *eval_ctx) { @@ -118,29 +97,20 @@ void DEG_evaluation_context_free(EvaluationContext *eval_ctx) /* Evaluate all nodes tagged for updating. */ void DEG_evaluate_on_refresh(EvaluationContext *eval_ctx, - Depsgraph *graph, - Scene *scene) + Depsgraph *graph) { DEG::Depsgraph *deg_graph = reinterpret_cast<DEG::Depsgraph *>(graph); /* Update time on primary timesource. */ DEG::TimeSourceDepsNode *tsrc = deg_graph->find_time_source(); - tsrc->cfra = BKE_scene_frame_get(scene); - unsigned int layers = deg_graph->layers; - /* XXX(sergey): This works around missing updates in temp scenes used - * by various scripts, but is weak and needs closer investigation. - */ - if (layers == 0) { - layers = scene->lay; - } - DEG::deg_evaluate_on_refresh(eval_ctx, deg_graph, layers); + tsrc->cfra = BKE_scene_frame_get(deg_graph->scene); + DEG::deg_evaluate_on_refresh(eval_ctx, deg_graph); } /* Frame-change happened for root scene that graph belongs to. */ void DEG_evaluate_on_framechange(EvaluationContext *eval_ctx, Main *bmain, Depsgraph *graph, - float ctime, - const unsigned int layers) + float ctime) { DEG::Depsgraph *deg_graph = reinterpret_cast<DEG::Depsgraph *>(graph); /* Update time on primary timesource. */ @@ -149,7 +119,7 @@ void DEG_evaluate_on_framechange(EvaluationContext *eval_ctx, tsrc->tag_update(deg_graph); DEG::deg_graph_flush_updates(bmain, deg_graph); /* Perform recalculation updates. */ - DEG::deg_evaluate_on_refresh(eval_ctx, deg_graph, layers); + DEG::deg_evaluate_on_refresh(eval_ctx, deg_graph); } bool DEG_needs_eval(Depsgraph *graph) diff --git a/source/blender/depsgraph/intern/depsgraph_intern.h b/source/blender/depsgraph/intern/depsgraph_intern.h index 40229ef8f37..df5e51a3910 100644 --- a/source/blender/depsgraph/intern/depsgraph_intern.h +++ b/source/blender/depsgraph/intern/depsgraph_intern.h @@ -46,8 +46,9 @@ extern "C" { #include "intern/nodes/deg_node_operation.h" #include "intern/depsgraph.h" -struct Main; +struct DEGEditorUpdateContext; struct Group; +struct Main; struct Scene; namespace DEG { @@ -105,15 +106,24 @@ DepsNodeFactory *deg_type_get_factory(const eDepsNode_Type type); /* Editors Integration -------------------------------------------------- */ -void deg_editors_id_update(struct Main *bmain, struct ID *id); +void deg_editors_id_update(const DEGEditorUpdateContext *update_ctx, + struct ID *id); -void deg_editors_scene_update(struct Main *bmain, struct Scene *scene, bool updated); +void deg_editors_scene_update(const DEGEditorUpdateContext *update_ctx, + bool updated); + +#define DEG_DEBUG_PRINTF(...) \ + do { \ + if (G.debug & G_DEBUG_DEPSGRAPH) { \ + fprintf(stderr, __VA_ARGS__); \ + fflush(stderr); \ + } \ + } while (0) -#define DEG_DEBUG_PRINTF(...) \ - do { \ - if (G.debug & G_DEBUG_DEPSGRAPH) { \ - fprintf(stderr, __VA_ARGS__); \ - } \ +#define DEG_ERROR_PRINTF(...) \ + do { \ + fprintf(stderr, __VA_ARGS__); \ + fflush(stderr); \ } while (0) } // namespace DEG diff --git a/source/blender/depsgraph/intern/depsgraph_query.cc b/source/blender/depsgraph/intern/depsgraph_query.cc index 9b1961baa48..98bf335f89e 100644 --- a/source/blender/depsgraph/intern/depsgraph_query.cc +++ b/source/blender/depsgraph/intern/depsgraph_query.cc @@ -33,18 +33,24 @@ #include "MEM_guardedalloc.h" extern "C" { +#include "BLI_utildefines.h" #include "BKE_idcode.h" #include "BKE_main.h" +#include "BLI_listbase.h" } /* extern "C" */ +#include "DNA_object_types.h" +#include "DNA_scene_types.h" + +#include "DEG_depsgraph.h" #include "DEG_depsgraph_query.h" #include "intern/depsgraph_intern.h" #include "intern/nodes/deg_node_id.h" -bool DEG_id_type_tagged(Main *bmain, short idtype) +bool DEG_id_type_tagged(Main *bmain, short id_type) { - return bmain->id_tag_update[BKE_idcode_to_index(idtype)] != 0; + return bmain->id_tag_update[BKE_idcode_to_index(id_type)] != 0; } short DEG_get_eval_flags_for_id(Depsgraph *graph, ID *id) @@ -69,3 +75,42 @@ short DEG_get_eval_flags_for_id(Depsgraph *graph, ID *id) return id_node->eval_flags; } + +Scene *DEG_get_evaluated_scene(Depsgraph *graph) +{ + DEG::Depsgraph *deg_graph = reinterpret_cast<DEG::Depsgraph *>(graph); + Scene *scene_orig = deg_graph->scene; + return reinterpret_cast<Scene *>(deg_graph->get_cow_id(&scene_orig->id)); +} + +ViewLayer *DEG_get_evaluated_view_layer(Depsgraph *graph) +{ + DEG::Depsgraph *deg_graph = reinterpret_cast<DEG::Depsgraph *>(graph); + Scene *scene_cow = DEG_get_evaluated_scene(graph); + ViewLayer *view_layer_orig = deg_graph->view_layer; + ViewLayer *view_layer_cow = + (ViewLayer *)BLI_findstring(&scene_cow->view_layers, + view_layer_orig->name, + offsetof(ViewLayer, name)); + return view_layer_cow; +} + +Object *DEG_get_evaluated_object(Depsgraph *depsgraph, Object *object) +{ + return (Object *)DEG_get_evaluated_id(depsgraph, &object->id); +} + +ID *DEG_get_evaluated_id(struct Depsgraph *depsgraph, ID *id) +{ + /* TODO(sergey): This is a duplicate of Depsgraph::get_cow_id(), + * but here we never do assert, since we don't know nature of the + * incoming ID datablock. + */ + DEG::Depsgraph *deg_graph = (DEG::Depsgraph *)depsgraph; + DEG::IDDepsNode *id_node = deg_graph->find_id_node(id); + if (id_node == NULL) { + return id; + } + return id_node->id_cow; +} + diff --git a/source/blender/depsgraph/intern/depsgraph_query_foreach.cc b/source/blender/depsgraph/intern/depsgraph_query_foreach.cc index 65d8f58b851..e6692cf49b3 100644 --- a/source/blender/depsgraph/intern/depsgraph_query_foreach.cc +++ b/source/blender/depsgraph/intern/depsgraph_query_foreach.cc @@ -108,7 +108,7 @@ static void deg_foreach_dependent_ID(const Depsgraph *graph, IDDepsNode *id_node = comp_node->owner; if (!id_node->done) { /* TODO(sergey): Is it orig or CoW? */ - callback(id_node->id, user_data); + callback(id_node->id_orig, user_data); id_node->done = true; } /* Schedule outgoing operation nodes. */ diff --git a/source/blender/depsgraph/intern/depsgraph_query_iter.cc b/source/blender/depsgraph/intern/depsgraph_query_iter.cc new file mode 100644 index 00000000000..42d512d473c --- /dev/null +++ b/source/blender/depsgraph/intern/depsgraph_query_iter.cc @@ -0,0 +1,242 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * 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. + * + * The Original Code is Copyright (C) 2017 Blender Foundation. + * All rights reserved. + * + * Original Author: Dalai Felinto + * Contributor(s): Sergey Sharybin + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/depsgraph/intern/depsgraph_query_iter.cc + * \ingroup depsgraph + * + * Implementation of Querying and Filtering API's + */ + +#include "MEM_guardedalloc.h" + +extern "C" { +#include "BLI_utildefines.h" +#include "BLI_math.h" +#include "BKE_anim.h" +#include "BKE_idprop.h" +#include "BKE_layer.h" +#include "BKE_object.h" +} /* extern "C" */ + +#include "DNA_object_types.h" +#include "DNA_scene_types.h" + +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_query.h" + +#include "intern/depsgraph_intern.h" +#include "util/deg_util_foreach.h" + +#include "intern/nodes/deg_node_id.h" + +#ifndef NDEBUG +# include "intern/eval/deg_eval_copy_on_write.h" +#endif + +/* ************************ DEG ITERATORS ********************* */ + +static bool deg_objects_dupli_iterator_next(BLI_Iterator *iter) +{ + DEGObjectIterData *data = (DEGObjectIterData *)iter->data; + while (data->dupli_object_next != NULL) { + DupliObject *dob = data->dupli_object_next; + Object *obd = dob->ob; + + data->dupli_object_next = data->dupli_object_next->next; + + /* Group duplis need to set ob matrices correct, for deform. so no_draw + * is part handled. + */ + if ((obd->transflag & OB_RENDER_DUPLI) == 0 && dob->no_draw) { + continue; + } + + if (obd->type == OB_MBALL) { + continue; + } + + data->dupli_object_current = dob; + + /* Temporary object to evaluate. */ + Object *dupli_parent = data->dupli_parent; + Object *temp_dupli_object = &data->temp_dupli_object; + *temp_dupli_object = *dob->ob; + temp_dupli_object->transflag &= ~OB_DUPLI; + temp_dupli_object->select_color = dupli_parent->select_color; + temp_dupli_object->base_flag = dupli_parent->base_flag | BASE_FROMDUPLI; + + if (dob->collection_properties != NULL) { + temp_dupli_object->base_collection_properties = dob->collection_properties; + IDP_MergeGroup(temp_dupli_object->base_collection_properties, dupli_parent->base_collection_properties, false); + } + else { + temp_dupli_object->base_collection_properties = dupli_parent->base_collection_properties; + } + + copy_m4_m4(data->temp_dupli_object.obmat, dob->mat); + iter->current = &data->temp_dupli_object; + BLI_assert( + DEG::deg_validate_copy_on_write_datablock( + &data->temp_dupli_object.id)); + return true; + } + + return false; +} + +static void DEG_iterator_objects_step(BLI_Iterator *iter, DEG::IDDepsNode *id_node) +{ + /* Set it early in case we need to exit and we are running from within a loop. */ + iter->skip = true; + + DEGObjectIterData *data = (DEGObjectIterData *)iter->data; + const ID_Type id_type = GS(id_node->id_orig->name); + + if (id_type != ID_OB) { + return; + } + + switch (id_node->linked_state) { + case DEG::DEG_ID_LINKED_DIRECTLY: + if ((data->flag & DEG_ITER_OBJECT_FLAG_LINKED_DIRECTLY) == 0) { + return; + } + break; + case DEG::DEG_ID_LINKED_VIA_SET: + if ((data->flag & DEG_ITER_OBJECT_FLAG_LINKED_VIA_SET) == 0) { + return; + } + break; + case DEG::DEG_ID_LINKED_INDIRECTLY: + if ((data->flag & DEG_ITER_OBJECT_FLAG_LINKED_INDIRECTLY) == 0) { + return; + } + break; + } + + Object *object = (Object *)id_node->id_cow; + BLI_assert(DEG::deg_validate_copy_on_write_datablock(&object->id)); + + if ((BKE_object_is_visible(object, OB_VISIBILITY_CHECK_UNKNOWN_RENDER_MODE) == false) && + ((data->flag & DEG_ITER_OBJECT_FLAG_VISIBLE) != 0)) + { + return; + } + + if ((data->flag & DEG_ITER_OBJECT_FLAG_DUPLI) && (object->transflag & OB_DUPLI)) { + data->dupli_parent = object; + data->dupli_list = object_duplilist(&data->eval_ctx, data->scene, object); + data->dupli_object_next = (DupliObject *)data->dupli_list->first; + + const eObjectVisibilityCheck mode = (data->mode == DEG_ITER_OBJECT_MODE_RENDER) ? + OB_VISIBILITY_CHECK_FOR_RENDER : + OB_VISIBILITY_CHECK_FOR_VIEWPORT; + + if (BKE_object_is_visible(object, mode) == false) { + return; + } + } + + iter->current = object; + iter->skip = false; +} + +void DEG_iterator_objects_begin(BLI_Iterator *iter, DEGObjectIterData *data) +{ + Depsgraph *depsgraph = data->graph; + DEG::Depsgraph *deg_graph = reinterpret_cast<DEG::Depsgraph *>(depsgraph); + const size_t num_id_nodes = deg_graph->id_nodes.size(); + + if (num_id_nodes == 0) { + iter->valid = false; + return; + } + + /* TODO(sergey): What evaluation type we want here? */ + /* TODO(dfelinto): Get rid of evaluation context here, it's only used to do + * direct dupli-objects update in group.c. Which is terribly bad, and all + * objects are expected to be evaluated already. */ + DEG_evaluation_context_init(&data->eval_ctx, DAG_EVAL_VIEWPORT); + data->eval_ctx.view_layer = DEG_get_evaluated_view_layer(depsgraph); + + iter->data = data; + data->dupli_parent = NULL; + data->dupli_list = NULL; + data->dupli_object_next = NULL; + data->dupli_object_current = NULL; + data->scene = DEG_get_evaluated_scene(depsgraph); + data->id_node_index = 0; + data->num_id_nodes = num_id_nodes; + + DEG::IDDepsNode *id_node = deg_graph->id_nodes[data->id_node_index]; + DEG_iterator_objects_step(iter, id_node); + + if (iter->skip) { + DEG_iterator_objects_next(iter); + } +} + +void DEG_iterator_objects_next(BLI_Iterator *iter) +{ + DEGObjectIterData *data = (DEGObjectIterData *)iter->data; + Depsgraph *depsgraph = data->graph; + DEG::Depsgraph *deg_graph = reinterpret_cast<DEG::Depsgraph *>(depsgraph); + do { + iter->skip = false; + if (data->dupli_list) { + if (deg_objects_dupli_iterator_next(iter)) { + return; + } + else { + free_object_duplilist(data->dupli_list); + data->dupli_parent = NULL; + data->dupli_list = NULL; + data->dupli_object_next = NULL; + data->dupli_object_current = NULL; + } + } + + ++data->id_node_index; + if (data->id_node_index == data->num_id_nodes) { + iter->valid = false; + return; + } + + DEG::IDDepsNode *id_node = deg_graph->id_nodes[data->id_node_index]; + DEG_iterator_objects_step(iter, id_node); + } while (iter->skip); +} + +void DEG_iterator_objects_end(BLI_Iterator *iter) +{ +#ifndef NDEBUG + DEGObjectIterData *data = (DEGObjectIterData *)iter->data; + /* Force crash in case the iterator data is referenced and accessed down the line. (T51718) */ + memset(&data->temp_dupli_object, 0xff, sizeof(data->temp_dupli_object)); +#else + (void) iter; +#endif +} diff --git a/source/blender/depsgraph/intern/depsgraph_tag.cc b/source/blender/depsgraph/intern/depsgraph_tag.cc index 7f08f3f8052..2255e1ce1b9 100644 --- a/source/blender/depsgraph/intern/depsgraph_tag.cc +++ b/source/blender/depsgraph/intern/depsgraph_tag.cc @@ -35,8 +35,9 @@ #include <queue> #include "BLI_utildefines.h" -#include "BLI_task.h" #include "BLI_listbase.h" +#include "BLI_math_bits.h" +#include "BLI_task.h" extern "C" { #include "DNA_object_types.h" @@ -49,6 +50,8 @@ extern "C" { #include "BKE_library.h" #include "BKE_main.h" #include "BKE_node.h" +#include "BKE_scene.h" +#include "BKE_workspace.h" #define new new_ #include "BKE_screen.h" @@ -70,162 +73,340 @@ extern "C" { /* *********************** */ /* Update Tagging/Flushing */ -/* Legacy depsgraph did some special trickery for things like particle systems - * when tagging ID for an update. Ideally that tagging needs to become obsolete - * in favor of havng dedicated node for that which gets tagged, but for until - * design of those areas is more clear we'll do the same legacy code here. - * - sergey - - */ -#define DEPSGRAPH_USE_LEGACY_TAGGING +namespace DEG { namespace { -/* Data-Based Tagging ------------------------------- */ +void deg_graph_id_tag_update(Main *bmain, Depsgraph *graph, ID *id, int flag); -void lib_id_recalc_tag(Main *bmain, ID *id) +void depsgraph_geometry_tag_to_component(const ID *id, + eDepsNode_Type *component_type) { - id->recalc |= ID_RECALC; - DEG_id_type_tag(bmain, GS(id->name)); + const ID_Type id_type = GS(id->name); + switch (id_type) { + case ID_OB: + { + const Object *object = (Object *)id; + switch (object->type) { + case OB_MESH: + case OB_CURVE: + case OB_SURF: + case OB_FONT: + case OB_MBALL: + *component_type = DEG_NODE_TYPE_GEOMETRY; + break; + case OB_ARMATURE: + *component_type = DEG_NODE_TYPE_EVAL_POSE; + break; + /* TODO(sergey): More cases here? */ + } + break; + } + case ID_ME: + *component_type = DEG_NODE_TYPE_GEOMETRY; + break; + case ID_PA: + return; + case ID_LP: + *component_type = DEG_NODE_TYPE_PARAMETERS; + break; + default: + break; + } } -void lib_id_recalc_data_tag(Main *bmain, ID *id) +void depsgraph_select_tag_to_component_opcode( + const ID *id, + eDepsNode_Type *component_type, + eDepsOperation_Code *operation_code) { - id->recalc |= ID_RECALC_DATA; - DEG_id_type_tag(bmain, GS(id->name)); + const ID_Type id_type = GS(id->name); + if (id_type == ID_SCE) { + /* We need to flush base flags to all objects in a scene since we + * don't know which ones changed. However, we don't want to update + * the whole scene, so pick up some operation which will do as less + * as possible. + * + * TODO(sergey): We can introduce explicit exit operation which + * does nothing and which is only used to cascade flush down the + * road. + */ + *component_type = DEG_NODE_TYPE_LAYER_COLLECTIONS; + *operation_code = DEG_OPCODE_VIEW_LAYER_DONE; + } + else if (id_type == ID_OB) { + *component_type = DEG_NODE_TYPE_LAYER_COLLECTIONS; + *operation_code = DEG_OPCODE_OBJECT_BASE_FLAGS; + } + else { + *component_type = DEG_NODE_TYPE_BATCH_CACHE; + *operation_code = DEG_OPCODE_GEOMETRY_SELECT_UPDATE; + } } -void lib_id_recalc_tag_flag(Main *bmain, ID *id, int flag) +void depsgraph_base_flags_tag_to_component_opcode( + const ID *id, + eDepsNode_Type *component_type, + eDepsOperation_Code *operation_code) { - if (flag) { - /* This bit of code ensures legacy object->recalc flags - * are still filled in the same way as it was expected - * with the old dependency graph. - * - * This is because some areas like motion paths and likely - * some other physics baking process are doing manual scene - * update on all the frames, trying to minimize number of - * updates. - * - * But this flag will also let us to re-construct entry - * nodes for update after relations update and after layer - * visibility changes. - */ - ID_Type idtype = GS(id->name); - if (idtype == ID_OB) { - Object *object = (Object *)id; - object->recalc |= (flag & OB_RECALC_ALL); - } + const ID_Type id_type = GS(id->name); + if (id_type == ID_SCE) { + *component_type = DEG_NODE_TYPE_LAYER_COLLECTIONS; + *operation_code = DEG_OPCODE_VIEW_LAYER_INIT; + } + else if (id_type == ID_OB) { + *component_type = DEG_NODE_TYPE_LAYER_COLLECTIONS; + *operation_code = DEG_OPCODE_OBJECT_BASE_FLAGS; + } +} - if (flag & OB_RECALC_OB) - lib_id_recalc_tag(bmain, id); - if (flag & (OB_RECALC_DATA | PSYS_RECALC)) - lib_id_recalc_data_tag(bmain, id); +void depsgraph_tag_to_component_opcode(const ID *id, + eDepsgraph_Tag tag, + eDepsNode_Type *component_type, + eDepsOperation_Code *operation_code) +{ + const ID_Type id_type = GS(id->name); + *component_type = DEG_NODE_TYPE_UNDEFINED; + *operation_code = DEG_OPCODE_OPERATION; + /* Special case for now, in the future we should get rid of this. */ + if (tag == 0) { + *component_type = DEG_NODE_TYPE_ID_REF; + *operation_code = DEG_OPCODE_OPERATION; + return; } - else { - lib_id_recalc_tag(bmain, id); + switch (tag) { + case DEG_TAG_TRANSFORM: + *component_type = DEG_NODE_TYPE_TRANSFORM; + break; + case DEG_TAG_GEOMETRY: + depsgraph_geometry_tag_to_component(id, component_type); + break; + case DEG_TAG_TIME: + *component_type = DEG_NODE_TYPE_ANIMATION; + break; + case DEG_TAG_PSYS_REDO: + case DEG_TAG_PSYS_RESET: + case DEG_TAG_PSYS_TYPE: + case DEG_TAG_PSYS_CHILD: + case DEG_TAG_PSYS_PHYS: + *component_type = DEG_NODE_TYPE_EVAL_PARTICLES; + break; + case DEG_TAG_COPY_ON_WRITE: + *component_type = DEG_NODE_TYPE_COPY_ON_WRITE; + break; + case DEG_TAG_SHADING_UPDATE: + if (id_type == ID_NT) { + *component_type = DEG_NODE_TYPE_SHADING_PARAMETERS; + } + else { + *component_type = DEG_NODE_TYPE_SHADING; + } + break; + case DEG_TAG_SELECT_UPDATE: + depsgraph_select_tag_to_component_opcode(id, + component_type, + operation_code); + break; + case DEG_TAG_BASE_FLAGS_UPDATE: + depsgraph_base_flags_tag_to_component_opcode(id, + component_type, + operation_code); + case DEG_TAG_EDITORS_UPDATE: + /* There is no such node in depsgraph, this tag is to be handled + * separately. + */ + break; + case DEG_TAG_PSYS_ALL: + BLI_assert(!"Should not happen"); + break; + } +} + +void id_tag_update_ntree_special(Main *bmain, Depsgraph *graph, ID *id, int flag) +{ + bNodeTree *ntree = ntreeFromID(id); + if (ntree == NULL) { + return; } + deg_graph_id_tag_update(bmain, graph, &ntree->id, flag); } -#ifdef DEPSGRAPH_USE_LEGACY_TAGGING -void depsgraph_legacy_handle_update_tag(Main *bmain, ID *id, short flag) +void depsgraph_update_editors_tag(Main *bmain, Depsgraph *graph, ID *id) { - if (flag) { - Object *object; - short idtype = GS(id->name); - if (idtype == ID_PA) { - ParticleSystem *psys; - for (object = (Object *)bmain->object.first; - object != NULL; - object = (Object *)object->id.next) - { - for (psys = (ParticleSystem *)object->particlesystem.first; - psys != NULL; - psys = (ParticleSystem *)psys->next) - { - if (&psys->part->id == id) { - DEG_id_tag_update_ex(bmain, &object->id, flag & OB_RECALC_ALL); - psys->recalc |= (flag & PSYS_RECALC); - } + /* NOTE: We handle this immediately, without delaying anything, to be + * sure we don't cause threading issues with OpenGL. + */ + /* TODO(sergey): Make sure this works for CoW-ed datablocks as well. */ + DEGEditorUpdateContext update_ctx = {NULL}; + update_ctx.bmain = bmain; + update_ctx.scene = graph->scene; + update_ctx.view_layer = graph->view_layer; + deg_editors_id_update(&update_ctx, id); +} + +void deg_graph_id_tag_update_single_flag(Main *bmain, + Depsgraph *graph, + ID *id, + IDDepsNode *id_node, + eDepsgraph_Tag tag) +{ + if (tag == DEG_TAG_EDITORS_UPDATE) { + if (graph != NULL) { + depsgraph_update_editors_tag(bmain, graph, id); + } + return; + } + /* Get description of what is to be tagged. */ + eDepsNode_Type component_type; + eDepsOperation_Code operation_code; + depsgraph_tag_to_component_opcode(id, + tag, + &component_type, + &operation_code); + /* Check whether we've got something to tag. */ + if (component_type == DEG_NODE_TYPE_UNDEFINED) { + /* Given ID does not support tag. */ + /* TODO(sergey): Shall we raise some panic here? */ + return; + } + /* Tag ID recalc flag. */ + DepsNodeFactory *factory = deg_type_get_factory(component_type); + BLI_assert(factory != NULL); + id->recalc |= factory->id_recalc_tag(); + /* Some sanity checks before moving forward. */ + if (id_node == NULL) { + /* Happens when object is tagged for update and not yet in the + * dependency graph (but will be after relations update). + */ + return; + } + /* Tag corresponding dependency graph operation for update. */ + if (component_type == DEG_NODE_TYPE_ID_REF) { + id_node->tag_update(graph); + } + else { + ComponentDepsNode *component_node = + id_node->find_component(component_type); + if (component_node != NULL) { + if (operation_code == DEG_OPCODE_OPERATION) { + component_node->tag_update(graph); + } + else { + OperationDepsNode *operation_node = + component_node->find_operation(operation_code); + if (operation_node != NULL) { + operation_node->tag_update(graph); } } } } } -#endif -} /* namespace */ +void deg_graph_id_tag_update(Main *bmain, Depsgraph *graph, ID *id, int flag) +{ + IDDepsNode *id_node = (graph != NULL) ? graph->find_id_node(id) + : NULL; + DEG_id_type_tag(bmain, GS(id->name)); + if (flag == 0) { + /* TODO(sergey): Which recalc flags to set here? */ + id->recalc |= ID_RECALC_ALL; + if (id_node != NULL) { + id_node->tag_update(graph); + } + } + int current_flag = flag; + while (current_flag != 0) { + eDepsgraph_Tag tag = + (eDepsgraph_Tag)(1 << bitscan_forward_clear_i(¤t_flag)); + deg_graph_id_tag_update_single_flag(bmain, + graph, + id, + id_node, + tag); + } + /* Special case for nested node tree datablocks. */ + id_tag_update_ntree_special(bmain, graph, id, flag); +} -/* Tag all nodes in ID-block for update. - * This is a crude measure, but is most convenient for old code. - */ -void DEG_graph_id_tag_update(Main *bmain, Depsgraph *graph, ID *id) +void deg_id_tag_update(Main *bmain, ID *id, int flag) +{ + deg_graph_id_tag_update(bmain, NULL, id, flag); + LINKLIST_FOREACH(Scene *, scene, &bmain->scene) { + LINKLIST_FOREACH(ViewLayer *, view_layer, &scene->view_layers) { + Depsgraph *depsgraph = + (Depsgraph *)BKE_scene_get_depsgraph(scene, + view_layer, + false); + if (depsgraph != NULL) { + deg_graph_id_tag_update(bmain, depsgraph, id, flag); + } + } + } +} + +void deg_graph_on_visible_update(Main *bmain, Depsgraph *graph) { - DEG::Depsgraph *deg_graph = reinterpret_cast<DEG::Depsgraph *>(graph); - DEG::IDDepsNode *node = deg_graph->find_id_node(id); - lib_id_recalc_tag(bmain, id); - if (node != NULL) { - node->tag_update(deg_graph); + /* Make sure objects are up to date. */ + foreach (DEG::IDDepsNode *id_node, graph->id_nodes) { + const ID_Type id_type = GS(id_node->id_orig->name); + int flag = 0; + /* We only tag components which needs an update. Tagging everything is + * not a good idea because that might reset particles cache (or any + * other type of cache). + * + * TODO(sergey): Need to generalize this somehow. + */ + if (id_type == ID_OB) { + flag |= OB_RECALC_OB | OB_RECALC_DATA | DEG_TAG_COPY_ON_WRITE; + } + deg_graph_id_tag_update(bmain, graph, id_node->id_orig, flag); + } + /* Make sure collection properties are up to date. */ + for (Scene *scene_iter = graph->scene; + scene_iter != NULL; + scene_iter = scene_iter->set) + { + IDDepsNode *scene_id_node = graph->find_id_node(&scene_iter->id); + BLI_assert(scene_id_node != NULL); + scene_id_node->tag_update(graph); } } +} /* namespace */ + +} // namespace DEG + +/* Data-Based Tagging */ + /* Tag given ID for an update in all the dependency graphs. */ -void DEG_id_tag_update(ID *id, short flag) +void DEG_id_tag_update(ID *id, int flag) { DEG_id_tag_update_ex(G.main, id, flag); } -void DEG_id_tag_update_ex(Main *bmain, ID *id, short flag) +void DEG_id_tag_update_ex(Main *bmain, ID *id, int flag) { if (id == NULL) { /* Ideally should not happen, but old depsgraph allowed this. */ return; } DEG_DEBUG_PRINTF("%s: id=%s flag=%d\n", __func__, id->name, flag); - lib_id_recalc_tag_flag(bmain, id, flag); - for (Scene *scene = (Scene *)bmain->scene.first; - scene != NULL; - scene = (Scene *)scene->id.next) - { - if (scene->depsgraph) { - Depsgraph *graph = scene->depsgraph; - if (flag == 0) { - /* TODO(sergey): Currently blender is still tagging IDs - * for recalc just using flag=0. This isn't totally correct - * but we'd better deal with such cases and don't fail. - */ - DEG_graph_id_tag_update(bmain, graph, id); - continue; - } - if (flag & OB_RECALC_DATA && GS(id->name) == ID_OB) { - Object *object = (Object *)id; - if (object->data != NULL) { - DEG_graph_id_tag_update(bmain, - graph, - (ID *)object->data); - } - } - if (flag & (OB_RECALC_OB | OB_RECALC_DATA)) { - DEG_graph_id_tag_update(bmain, graph, id); - } - else if (flag & OB_RECALC_TIME) { - DEG_graph_id_tag_update(bmain, graph, id); - } - } - } + DEG::deg_id_tag_update(bmain, id, flag); +} -#ifdef DEPSGRAPH_USE_LEGACY_TAGGING - /* Special handling from the legacy depsgraph. - * TODO(sergey): Need to get rid of those once all the areas - * are re-formulated in terms of franular nodes. - */ - depsgraph_legacy_handle_update_tag(bmain, id, flag); -#endif +void DEG_graph_id_tag_update(struct Main *bmain, + struct Depsgraph *depsgraph, + struct ID *id, + int flag) +{ + DEG::Depsgraph *graph = (DEG::Depsgraph *)depsgraph; + DEG::deg_graph_id_tag_update(bmain, graph, id, flag); } /* Mark a particular datablock type as having changing. */ -void DEG_id_type_tag(Main *bmain, short idtype) +void DEG_id_type_tag(Main *bmain, short id_type) { - if (idtype == ID_NT) { + if (id_type == ID_NT) { /* Stupid workaround so parent datablocks of nested nodetree get looped * over when we loop over tagged datablock types. */ @@ -236,132 +417,35 @@ void DEG_id_type_tag(Main *bmain, short idtype) DEG_id_type_tag(bmain, ID_SCE); } - bmain->id_tag_update[BKE_idcode_to_index(idtype)] = 1; + bmain->id_tag_update[BKE_idcode_to_index(id_type)] = 1; } -/* Recursively push updates out to all nodes dependent on this, - * until all affected are tagged and/or scheduled up for eval - */ -void DEG_ids_flush_tagged(Main *bmain) -{ - for (Scene *scene = (Scene *)bmain->scene.first; - scene != NULL; - scene = (Scene *)scene->id.next) - { - DEG_scene_flush_update(bmain, scene); - } -} - -void DEG_scene_flush_update(Main *bmain, Scene *scene) +void DEG_graph_flush_update(Main *bmain, Depsgraph *depsgraph) { - if (scene->depsgraph == NULL) { + if (depsgraph == NULL) { return; } - DEG::deg_graph_flush_updates( - bmain, - reinterpret_cast<DEG::Depsgraph *>(scene->depsgraph)); + DEG::deg_graph_flush_updates(bmain, (DEG::Depsgraph *)depsgraph); } /* Update dependency graph when visible scenes/layers changes. */ -void DEG_graph_on_visible_update(Main *bmain, Scene *scene) +void DEG_graph_on_visible_update(Main *bmain, Depsgraph *depsgraph) { - DEG::Depsgraph *graph = reinterpret_cast<DEG::Depsgraph *>(scene->depsgraph); - wmWindowManager *wm = (wmWindowManager *)bmain->wm.first; - int old_layers = graph->layers; - if (wm != NULL) { - BKE_main_id_tag_listbase(&bmain->scene, LIB_TAG_DOIT, true); - graph->layers = 0; - for (wmWindow *win = (wmWindow *)wm->windows.first; - win != NULL; - win = (wmWindow *)win->next) - { - Scene *scene = win->screen->scene; - if (scene->id.tag & LIB_TAG_DOIT) { - graph->layers |= BKE_screen_visible_layers(win->screen, scene); - scene->id.tag &= ~LIB_TAG_DOIT; - } - } - } - else { - /* All the layers for background render for now. */ - graph->layers = (1 << 20) - 1; - } - if (old_layers != graph->layers) { - /* Tag all objects which becomes visible (or which becomes needed for dependencies) - * for recalc. - * - * This is mainly needed on file load only, after that updates of invisible objects - * will be stored in the pending list. - */ - foreach (DEG::IDDepsNode *id_node, graph->id_nodes) { - ID *id = id_node->id; - if ((id->recalc & ID_RECALC_ALL) != 0 || - (id_node->layers & scene->lay_updated) == 0) - { - id_node->tag_update(graph); - } - /* A bit of magic: if object->recalc is set it means somebody tagged - * it for update. If corresponding ID recalc flags are zero it means - * graph has been evaluated after that and the recalc was skipped - * because of visibility check. - */ - if (GS(id->name) == ID_OB) { - Object *object = (Object *)id; - if ((id->recalc & ID_RECALC_ALL) == 0 && - (object->recalc & OB_RECALC_ALL) != 0) - { - id_node->tag_update(graph); - DEG::ComponentDepsNode *anim_comp = - id_node->find_component(DEG::DEG_NODE_TYPE_ANIMATION); - if (anim_comp != NULL && object->recalc & OB_RECALC_TIME) { - anim_comp->tag_update(graph); - } - } - } - } - } - scene->lay_updated |= graph->layers; - /* If graph is tagged for update, we don't need to bother with updates here, - * nodes will be re-created. - */ - if (graph->need_update) { - return; - } - /* Special trick to get local view to work. */ - LINKLIST_FOREACH (Base *, base, &scene->base) { - Object *object = base->object; - DEG::IDDepsNode *id_node = graph->find_id_node(&object->id); - id_node->layers = 0; - } - LINKLIST_FOREACH (Base *, base, &scene->base) { - Object *object = base->object; - DEG::IDDepsNode *id_node = graph->find_id_node(&object->id); - id_node->layers |= base->lay; - if (object == scene->camera) { - /* Camera should always be updated, it used directly by viewport. */ - id_node->layers |= (unsigned int)(-1); - } - } - DEG::deg_graph_build_flush_layers(graph); - LINKLIST_FOREACH (Base *, base, &scene->base) { - Object *object = base->object; - DEG::IDDepsNode *id_node = graph->find_id_node(&object->id); - GHASH_FOREACH_BEGIN(DEG::ComponentDepsNode *, comp, id_node->components) - { - id_node->layers |= comp->layers; - } - GHASH_FOREACH_END(); - } + DEG::Depsgraph *graph = (DEG::Depsgraph *)depsgraph; + DEG::deg_graph_on_visible_update(bmain, graph); } void DEG_on_visible_update(Main *bmain, const bool UNUSED(do_time)) { - for (Scene *scene = (Scene *)bmain->scene.first; - scene != NULL; - scene = (Scene *)scene->id.next) - { - if (scene->depsgraph != NULL) { - DEG_graph_on_visible_update(bmain, scene); + LINKLIST_FOREACH(Scene *, scene, &bmain->scene) { + LINKLIST_FOREACH(ViewLayer *, view_layer, &scene->view_layers) { + Depsgraph *depsgraph = + (Depsgraph *)BKE_scene_get_depsgraph(scene, + view_layer, + false); + if (depsgraph != NULL) { + DEG_graph_on_visible_update(bmain, depsgraph); + } } } } @@ -369,7 +453,10 @@ void DEG_on_visible_update(Main *bmain, const bool UNUSED(do_time)) /* Check if something was changed in the database and inform * editors about this. */ -void DEG_ids_check_recalc(Main *bmain, Scene *scene, bool time) +void DEG_ids_check_recalc(Main *bmain, + Scene *scene, + ViewLayer *view_layer, + bool time) { ListBase *lbarray[MAX_LIBARRAY]; int a; @@ -387,7 +474,11 @@ void DEG_ids_check_recalc(Main *bmain, Scene *scene, bool time) } } - DEG::deg_editors_scene_update(bmain, scene, (updated || time)); + DEGEditorUpdateContext update_ctx = {NULL}; + update_ctx.bmain = bmain; + update_ctx.scene = scene; + update_ctx.view_layer = view_layer; + DEG::deg_editors_scene_update(&update_ctx, (updated || time)); } void DEG_ids_clear_recalc(Main *bmain) diff --git a/source/blender/depsgraph/intern/depsgraph_type_defines.cc b/source/blender/depsgraph/intern/depsgraph_type_defines.cc index 675002d6b90..22df3161428 100644 --- a/source/blender/depsgraph/intern/depsgraph_type_defines.cc +++ b/source/blender/depsgraph/intern/depsgraph_type_defines.cc @@ -86,6 +86,8 @@ static const char *stringify_opcode(eDepsOperation_Code opcode) /* Animation, Drivers, etc. */ STRINGIFY_OPCODE(ANIMATION); STRINGIFY_OPCODE(DRIVER); + /* Object related. */ + STRINGIFY_OPCODE(OBJECT_BASE_FLAGS); /* Transform. */ STRINGIFY_OPCODE(TRANSFORM_LOCAL); STRINGIFY_OPCODE(TRANSFORM_PARENT); @@ -115,11 +117,23 @@ static const char *stringify_opcode(eDepsOperation_Code opcode) /* Particles. */ STRINGIFY_OPCODE(PARTICLE_SYSTEM_EVAL_INIT); STRINGIFY_OPCODE(PARTICLE_SYSTEM_EVAL); + STRINGIFY_OPCODE(PARTICLE_SETTINGS_EVAL); + STRINGIFY_OPCODE(PARTICLE_SETTINGS_RECALC_CLEAR); + /* Batch cache. */ + STRINGIFY_OPCODE(GEOMETRY_SELECT_UPDATE); /* Masks. */ STRINGIFY_OPCODE(MASK_ANIMATION); STRINGIFY_OPCODE(MASK_EVAL); + /* Collections. */ + STRINGIFY_OPCODE(VIEW_LAYER_INIT); + STRINGIFY_OPCODE(VIEW_LAYER_EVAL); + STRINGIFY_OPCODE(VIEW_LAYER_DONE); + /* Copy on write. */ + STRINGIFY_OPCODE(COPY_ON_WRITE); /* Shading. */ STRINGIFY_OPCODE(SHADING); + STRINGIFY_OPCODE(MATERIAL_UPDATE); + STRINGIFY_OPCODE(WORLD_UPDATE); /* Movie clip. */ STRINGIFY_OPCODE(MOVIECLIP_EVAL); diff --git a/source/blender/depsgraph/intern/depsgraph_types.h b/source/blender/depsgraph/intern/depsgraph_types.h index b2dbe5d2a62..d091f42bf80 100644 --- a/source/blender/depsgraph/intern/depsgraph_types.h +++ b/source/blender/depsgraph/intern/depsgraph_types.h @@ -53,8 +53,10 @@ struct FCurve; namespace DEG { +/* TODO(sergey): Find a better place for this. */ using std::string; using std::vector; +using std::max; /* Evaluation Operation for atomic operation */ // XXX: move this to another header that can be exposed? @@ -79,6 +81,18 @@ typedef enum eDepsNode_Class { DEG_NODE_CLASS_OPERATION = 2, } eDepsNode_Class; +/* Note: We use max comparison to mark an id node that is linked more than once + * So keep this enum ordered accordingly. + */ +typedef enum eDepsNode_LinkedState_Type { + /* Generic indirectly linked id node. */ + DEG_ID_LINKED_INDIRECTLY = 0, + /* Id node present in the set (background) only. */ + DEG_ID_LINKED_VIA_SET = 1, + /* Id node directly linked via the ScenLayer. */ + DEG_ID_LINKED_DIRECTLY = 2, +} eDepsNode_LinkedState_Type; + /* Types of Nodes */ typedef enum eDepsNode_Type { /* Fallback type for invalid return value */ @@ -111,6 +125,14 @@ typedef enum eDepsNode_Type { DEG_NODE_TYPE_GEOMETRY, /* Sequencer Component (Scene Only) */ DEG_NODE_TYPE_SEQUENCER, + /* Component which contains all operations needed for layer collections + * evaluation. + */ + DEG_NODE_TYPE_LAYER_COLLECTIONS, + /* Entry component of majority of ID nodes: prepares CoW pointers for + * execution. + */ + DEG_NODE_TYPE_COPY_ON_WRITE, /* **** Evaluation-Related Outer Types (with Subdata) **** */ @@ -122,8 +144,11 @@ typedef enum eDepsNode_Type { DEG_NODE_TYPE_EVAL_PARTICLES, /* Material Shading Component */ DEG_NODE_TYPE_SHADING, + DEG_NODE_TYPE_SHADING_PARAMETERS, /* Cache Component */ DEG_NODE_TYPE_CACHE, + /* Batch Cache Component */ + DEG_NODE_TYPE_BATCH_CACHE, /* Total number of meaningful node types. */ NUM_DEG_NODE_TYPES, @@ -148,6 +173,9 @@ typedef enum eDepsOperation_Code { /* Driver */ DEG_OPCODE_DRIVER, + /* Object related. --------------------------------- */ + DEG_OPCODE_OBJECT_BASE_FLAGS, + /* Transform. -------------------------------------- */ /* Transform entry point - local transforms only */ DEG_OPCODE_TRANSFORM_LOCAL, @@ -209,9 +237,24 @@ typedef enum eDepsOperation_Code { /* Particle System evaluation. */ DEG_OPCODE_PARTICLE_SYSTEM_EVAL_INIT, DEG_OPCODE_PARTICLE_SYSTEM_EVAL, + DEG_OPCODE_PARTICLE_SETTINGS_EVAL, + DEG_OPCODE_PARTICLE_SETTINGS_RECALC_CLEAR, + + /* Collections. ------------------------------------- */ + DEG_OPCODE_VIEW_LAYER_INIT, + DEG_OPCODE_VIEW_LAYER_EVAL, + DEG_OPCODE_VIEW_LAYER_DONE, + + /* Copy on Write. ------------------------------------ */ + DEG_OPCODE_COPY_ON_WRITE, /* Shading. ------------------------------------------- */ DEG_OPCODE_SHADING, + DEG_OPCODE_MATERIAL_UPDATE, + DEG_OPCODE_WORLD_UPDATE, + + /* Batch caches. -------------------------------------- */ + DEG_OPCODE_GEOMETRY_SELECT_UPDATE, /* Masks. ------------------------------------------ */ DEG_OPCODE_MASK_ANIMATION, diff --git a/source/blender/depsgraph/intern/eval/deg_eval.cc b/source/blender/depsgraph/intern/eval/deg_eval.cc index c29a0708cef..a6c6a16a528 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval.cc +++ b/source/blender/depsgraph/intern/eval/deg_eval.cc @@ -38,12 +38,10 @@ #include "BLI_task.h" #include "BLI_ghash.h" -extern "C" { -#include "BKE_depsgraph.h" -#include "BKE_global.h" -} /* extern "C" */ +#include "DNA_object_types.h" #include "DEG_depsgraph.h" +#include "DEG_depsgraph_query.h" #include "atomic_ops.h" @@ -68,13 +66,11 @@ namespace DEG { static void schedule_children(TaskPool *pool, Depsgraph *graph, OperationDepsNode *node, - const unsigned int layers, const int thread_id); struct DepsgraphEvalState { EvaluationContext *eval_ctx; Depsgraph *graph; - unsigned int layers; bool do_stats; }; @@ -98,13 +94,12 @@ static void deg_task_run_func(TaskPool *pool, } /* Schedule children. */ BLI_task_pool_delayed_push_begin(pool, thread_id); - schedule_children(pool, state->graph, node, state->layers, thread_id); + schedule_children(pool, state->graph, node, thread_id); BLI_task_pool_delayed_push_end(pool, thread_id); } typedef struct CalculatePengindData { Depsgraph *graph; - unsigned int layers; } CalculatePengindData; static void calculate_pending_func( @@ -114,26 +109,19 @@ static void calculate_pending_func( { CalculatePengindData *data = (CalculatePengindData *)data_v; Depsgraph *graph = data->graph; - unsigned int layers = data->layers; OperationDepsNode *node = graph->operations[i]; - IDDepsNode *id_node = node->owner->owner; node->num_links_pending = 0; node->scheduled = false; /* count number of inputs that need updates */ - if ((id_node->layers & layers) != 0 && - (node->flag & DEPSOP_FLAG_NEEDS_UPDATE) != 0) - { + if ((node->flag & DEPSOP_FLAG_NEEDS_UPDATE) != 0) { foreach (DepsRelation *rel, node->inlinks) { if (rel->from->type == DEG_NODE_TYPE_OPERATION && (rel->flag & DEPSREL_FLAG_CYCLIC) == 0) { OperationDepsNode *from = (OperationDepsNode *)rel->from; - IDDepsNode *id_from_node = from->owner->owner; - if ((id_from_node->layers & layers) != 0 && - (from->flag & DEPSOP_FLAG_NEEDS_UPDATE) != 0) - { + if ((from->flag & DEPSOP_FLAG_NEEDS_UPDATE) != 0) { ++node->num_links_pending; } } @@ -141,12 +129,11 @@ static void calculate_pending_func( } } -static void calculate_pending_parents(Depsgraph *graph, unsigned int layers) +static void calculate_pending_parents(Depsgraph *graph) { const int num_operations = graph->operations.size(); CalculatePengindData data; data.graph = graph; - data.layers = layers; ParallelRangeSettings settings; BLI_parallel_range_settings_defaults(&settings); settings.min_iter_per_thread = 1024; @@ -160,7 +147,7 @@ static void calculate_pending_parents(Depsgraph *graph, unsigned int layers) static void initialize_execution(DepsgraphEvalState *state, Depsgraph *graph) { const bool do_stats = state->do_stats; - calculate_pending_parents(graph, state->layers); + calculate_pending_parents(graph); /* Clear tags and other things which needs to be clear. */ foreach (OperationDepsNode *node, graph->operations) { node->done = 0; @@ -174,15 +161,11 @@ static void initialize_execution(DepsgraphEvalState *state, Depsgraph *graph) * dec_parents: Decrement pending parents count, true when child nodes are * scheduled after a task has been completed. */ -static void schedule_node(TaskPool *pool, Depsgraph *graph, unsigned int layers, +static void schedule_node(TaskPool *pool, Depsgraph *graph, OperationDepsNode *node, bool dec_parents, const int thread_id) { - unsigned int id_layers = node->owner->owner->layers; - - if ((node->flag & DEPSOP_FLAG_NEEDS_UPDATE) != 0 && - (id_layers & layers) != 0) - { + if ((node->flag & DEPSOP_FLAG_NEEDS_UPDATE) != 0) { if (dec_parents) { BLI_assert(node->num_links_pending > 0); atomic_sub_and_fetch_uint32(&node->num_links_pending, 1); @@ -194,7 +177,7 @@ static void schedule_node(TaskPool *pool, Depsgraph *graph, unsigned int layers, if (!is_scheduled) { if (node->is_noop()) { /* skip NOOP node, schedule children right away */ - schedule_children(pool, graph, node, layers, thread_id); + schedule_children(pool, graph, node, thread_id); } else { /* children are scheduled once this task is completed */ @@ -210,19 +193,16 @@ static void schedule_node(TaskPool *pool, Depsgraph *graph, unsigned int layers, } } -static void schedule_graph(TaskPool *pool, - Depsgraph *graph, - const unsigned int layers) +static void schedule_graph(TaskPool *pool, Depsgraph *graph) { foreach (OperationDepsNode *node, graph->operations) { - schedule_node(pool, graph, layers, node, false, 0); + schedule_node(pool, graph, node, false, 0); } } static void schedule_children(TaskPool *pool, Depsgraph *graph, OperationDepsNode *node, - const unsigned int layers, const int thread_id) { foreach (DepsRelation *rel, node->outlinks) { @@ -234,7 +214,6 @@ static void schedule_children(TaskPool *pool, } schedule_node(pool, graph, - layers, child, (rel->flag & DEPSREL_FLAG_CYCLIC) == 0, thread_id); @@ -249,25 +228,21 @@ static void schedule_children(TaskPool *pool, * \note Time sources should be all valid! */ void deg_evaluate_on_refresh(EvaluationContext *eval_ctx, - Depsgraph *graph, - const unsigned int layers) + Depsgraph *graph) { /* Nothing to update, early out. */ if (BLI_gset_size(graph->entry_tags) == 0) { return; } - DEG_DEBUG_PRINTF("%s: layers:%u, graph->layers:%u\n", - __func__, - layers, - graph->layers); /* Set time for the current graph evaluation context. */ TimeSourceDepsNode *time_src = graph->find_time_source(); + eval_ctx->depsgraph = (::Depsgraph *)graph; + eval_ctx->view_layer = DEG_get_evaluated_view_layer((::Depsgraph *)graph); eval_ctx->ctime = time_src->cfra; /* Set up evaluation context for depsgraph itself. */ DepsgraphEvalState state; state.eval_ctx = eval_ctx; state.graph = graph; - state.layers = layers; state.do_stats = (G.debug_value != 0); /* Set up task scheduler and pull for threaded evaluation. */ TaskScheduler *task_scheduler; @@ -284,7 +259,7 @@ void deg_evaluate_on_refresh(EvaluationContext *eval_ctx, /* Prepare all nodes for evaluation. */ initialize_execution(&state, graph); /* Do actual evaluation now. */ - schedule_graph(task_pool, graph, layers); + schedule_graph(task_pool, graph); BLI_task_pool_work_and_wait(task_pool); BLI_task_pool_free(task_pool); /* Finalize statistics gathering. This is because we only gather single diff --git a/source/blender/depsgraph/intern/eval/deg_eval.h b/source/blender/depsgraph/intern/eval/deg_eval.h index 49c0379939e..576ab89dec1 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval.h +++ b/source/blender/depsgraph/intern/eval/deg_eval.h @@ -46,7 +46,6 @@ struct Depsgraph; * \note Time sources should be all valid! */ void deg_evaluate_on_refresh(EvaluationContext *eval_ctx, - Depsgraph *graph, - const unsigned int layers); + Depsgraph *graph); } // namespace DEG diff --git a/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc b/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc new file mode 100644 index 00000000000..abd17616584 --- /dev/null +++ b/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc @@ -0,0 +1,939 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * 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. + * + * The Original Code is Copyright (C) 20137Blender Foundation. + * All rights reserved. + * + * Original Author: Sergey Sharybin + * Contributor(s): None Yet + * + * ***** END GPL LICENSE BLOCK ***** + */ + + +/** \file blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc + * \ingroup depsgraph + */ + +/* Enable special; trickery to treat nested owned IDs (such as nodetree of + * material) to be handled in same way as "real" datablocks, even tho some + * internal BKE routines doesn't treat them like that. + * + * TODO(sergey): Re-evaluate that after new ID handling is in place. + */ +#define NESTED_ID_NASTY_WORKAROUND + +#include "intern/eval/deg_eval_copy_on_write.h" + +#include <cstring> + +#include "BLI_utildefines.h" +#include "BLI_threads.h" +#include "BLI_string.h" + +#include "BKE_global.h" +#include "BKE_layer.h" +#include "BKE_library.h" +#include "BKE_main.h" +#include "BKE_scene.h" + +#include "DEG_depsgraph.h" + +#include "MEM_guardedalloc.h" + +extern "C" { +#include "DNA_ID.h" +#include "DNA_mesh_types.h" +#include "DNA_scene_types.h" +#include "DNA_object_types.h" + +#ifdef NESTED_ID_NASTY_WORKAROUND +# include "DNA_key_types.h" +# include "DNA_lamp_types.h" +# include "DNA_linestyle_types.h" +# include "DNA_material_types.h" +# include "DNA_node_types.h" +# include "DNA_texture_types.h" +# include "DNA_world_types.h" +#endif + +#include "BKE_action.h" +#include "BKE_editmesh.h" +#include "BKE_library_query.h" +#include "BKE_object.h" +} + +#include "intern/depsgraph.h" +#include "intern/builder/deg_builder_nodes.h" +#include "intern/nodes/deg_node.h" +#include "intern/nodes/deg_node_id.h" + +namespace DEG { + +#define DEBUG_PRINT if (G.debug & G_DEBUG_DEPSGRAPH) printf + +namespace { + +#ifdef NESTED_ID_NASTY_WORKAROUND +union NestedIDHackTempStorage { + FreestyleLineStyle linestyle; + Lamp lamp; + Material material; + Mesh mesh; + Scene scene; + Tex tex; + World world; +}; + +/* Set nested owned ID pointers to NULL. */ +void nested_id_hack_discard_pointers(ID *id_cow) +{ + switch (GS(id_cow->name)) { +# define SPECIAL_CASE(id_type, dna_type, field) \ + case id_type: \ + { \ + ((dna_type *)id_cow)->field = NULL; \ + break; \ + } + + SPECIAL_CASE(ID_LS, FreestyleLineStyle, nodetree) + SPECIAL_CASE(ID_LA, Lamp, nodetree) + SPECIAL_CASE(ID_MA, Material, nodetree) + SPECIAL_CASE(ID_SCE, Scene, nodetree) + SPECIAL_CASE(ID_TE, Tex, nodetree) + SPECIAL_CASE(ID_WO, World, nodetree) + + SPECIAL_CASE(ID_ME, Mesh, key) + +# undef SPECIAL_CASE + + default: + break; + } +} + +/* Set ID pointer of nested owned IDs (nodetree, key) to NULL. + * + * Return pointer to a new ID to be used. + */ +const ID *nested_id_hack_get_discarded_pointers(NestedIDHackTempStorage *storage, + const ID *id) +{ + switch (GS(id->name)) { +# define SPECIAL_CASE(id_type, dna_type, field, variable) \ + case id_type: \ + { \ + storage->variable = *(dna_type *)id; \ + storage->variable.field = NULL; \ + return &storage->variable.id; \ + } + + SPECIAL_CASE(ID_LS, FreestyleLineStyle, nodetree, linestyle) + SPECIAL_CASE(ID_LA, Lamp, nodetree, lamp) + SPECIAL_CASE(ID_MA, Material, nodetree, material) + SPECIAL_CASE(ID_SCE, Scene, nodetree, scene) + SPECIAL_CASE(ID_TE, Tex, nodetree, tex) + SPECIAL_CASE(ID_WO, World, nodetree, world) + + SPECIAL_CASE(ID_ME, Mesh, key, mesh) + +# undef SPECIAL_CASE + + default: + break; + } + return id; +} + +/* Set ID pointer of nested owned IDs (nodetree, key) to the original value. */ +void nested_id_hack_restore_pointers(const ID *old_id, ID *new_id) +{ + if (new_id == NULL) { + return; + } + switch (GS(old_id->name)) { +# define SPECIAL_CASE(id_type, dna_type, field) \ + case id_type: \ + { \ + ((dna_type *)(new_id))->field = \ + ((dna_type *)(old_id))->field; \ + break; \ + } + + SPECIAL_CASE(ID_LS, FreestyleLineStyle, nodetree) + SPECIAL_CASE(ID_LA, Lamp, nodetree) + SPECIAL_CASE(ID_MA, Material, nodetree) + SPECIAL_CASE(ID_SCE, Scene, nodetree) + SPECIAL_CASE(ID_TE, Tex, nodetree) + SPECIAL_CASE(ID_WO, World, nodetree) + + SPECIAL_CASE(ID_ME, Mesh, key) + +#undef SPECIAL_CASE + default: + break; + } +} + +/* Remap pointer of nested owned IDs (nodetree. key) to the new ID values. */ +void ntree_hack_remap_pointers(const Depsgraph *depsgraph, ID *id_cow) +{ + switch (GS(id_cow->name)) { +# define SPECIAL_CASE(id_type, dna_type, field, field_type) \ + case id_type: \ + { \ + dna_type *data = (dna_type *)id_cow; \ + if (data->field != NULL) { \ + ID *ntree_id_cow = depsgraph->get_cow_id(&data->field->id); \ + if (ntree_id_cow != NULL) { \ + DEG_COW_PRINT(" Remapping datablock for %s: id_orig=%p id_cow=%p\n", \ + data->field->id.name, \ + data->field, \ + ntree_id_cow); \ + data->field = (field_type *)ntree_id_cow; \ + } \ + } \ + break; \ + } + + SPECIAL_CASE(ID_LS, FreestyleLineStyle, nodetree, bNodeTree) + SPECIAL_CASE(ID_LA, Lamp, nodetree, bNodeTree) + SPECIAL_CASE(ID_MA, Material, nodetree, bNodeTree) + SPECIAL_CASE(ID_SCE, Scene, nodetree, bNodeTree) + SPECIAL_CASE(ID_TE, Tex, nodetree, bNodeTree) + SPECIAL_CASE(ID_WO, World, nodetree, bNodeTree) + + SPECIAL_CASE(ID_ME, Mesh, key, Key) + +#undef SPECIAL_CASE + default: + break; + } +} +#endif /* NODETREE_NASTY_WORKAROUND */ + +struct ValidateData { + bool is_valid; +}; + +/* Similar to generic id_copy() but does not require main and assumes pointer + * is already allocated, + */ +bool id_copy_inplace_no_main(const ID *id, ID *newid) +{ + const ID *id_for_copy = id; + +#ifdef NESTED_ID_NASTY_WORKAROUND + NestedIDHackTempStorage id_hack_storage; + id_for_copy = nested_id_hack_get_discarded_pointers(&id_hack_storage, id); +#endif + + bool result = BKE_id_copy_ex(NULL, + (ID *)id_for_copy, + &newid, + LIB_ID_CREATE_NO_MAIN | + LIB_ID_CREATE_NO_USER_REFCOUNT | + LIB_ID_CREATE_NO_ALLOCATE | + LIB_ID_CREATE_NO_DEG_TAG, + false); + +#ifdef NESTED_ID_NASTY_WORKAROUND + if (result) { + nested_id_hack_restore_pointers(id, newid); + } +#endif + + return result; +} + +/* Similar to BKE_scene_copy() but does not require main and assumes pointer + * is already allocated. + */ +bool scene_copy_inplace_no_main(const Scene *scene, Scene *new_scene) +{ + const ID *id_for_copy = &scene->id; + +#ifdef NESTED_ID_NASTY_WORKAROUND + NestedIDHackTempStorage id_hack_storage; + id_for_copy = nested_id_hack_get_discarded_pointers(&id_hack_storage, + &scene->id); +#endif + + bool result = BKE_id_copy_ex(NULL, + id_for_copy, + (ID **)&new_scene, + LIB_ID_COPY_ACTIONS | + LIB_ID_CREATE_NO_MAIN | + LIB_ID_CREATE_NO_USER_REFCOUNT | + LIB_ID_CREATE_NO_ALLOCATE | + LIB_ID_CREATE_NO_DEG_TAG, + false); + +#ifdef NESTED_ID_NASTY_WORKAROUND + if (result) { + nested_id_hack_restore_pointers(&scene->id, &new_scene->id); + } +#endif + + return result; +} + +/* Check whether given ID is expanded or still a shallow copy. */ +BLI_INLINE bool check_datablock_expanded(const ID *id_cow) +{ + return (id_cow->name[0] != '\0'); +} + +/* Check whether datablock was already expanded during depsgraph + * construction. + */ +static bool check_datablock_expanded_at_construction(const ID *id_orig) +{ + const ID_Type id_type = GS(id_orig->name); + return (id_type == ID_SCE) || + (id_type == ID_OB && ((Object *)id_orig)->type == OB_ARMATURE) || + (id_type == ID_AR); +} + +/* Those are datablocks which are not covered by dependency graph and hence + * does not need any remapping or anything. + * + * TODO(sergey): How to make it more robust for the future, so we don't have + * to maintain exception lists all over the code? + */ +static bool check_datablocks_copy_on_writable(const ID *id_orig) +{ + const ID_Type id_type = GS(id_orig->name); + /* We shouldn't bother if copied ID is same as original one. */ + if (!deg_copy_on_write_is_needed(id_orig)) { + return false; + } + return !ELEM(id_type, ID_BR, + ID_LS, + ID_AC, + ID_PAL); +} + +/* Callback for BKE_library_foreach_ID_link which remaps original ID pointer + * with the one created by CoW system. + */ + +struct RemapCallbackUserData { + /* Dependency graph for which remapping is happening. */ + const Depsgraph *depsgraph; + /* Temporarily allocated memory for copying purposes. This ID will + * be discarded after expanding is done, so need to make sure temp_id + * is replaced with proper real_id. + * + * NOTE: This is due to our logic of "inplace" duplication, where we + * use generic duplication routines (which gives us new ID) which then + * is followed with copying data to a placeholder we prepared before and + * discarding pointer returned by duplication routines. + */ + const ID *temp_id; + ID *real_id; + /* Create placeholder for ID nodes for cases when we need to remap original + * ID to it[s CoW version but we don't have required ID node yet. + * + * This happens when expansion happens a ta construction time. + */ + DepsgraphNodeBuilder *node_builder; + bool create_placeholders; +}; + +int foreach_libblock_remap_callback(void *user_data_v, + ID *id_self, + ID **id_p, + int /*cb_flag*/) +{ + RemapCallbackUserData *user_data = (RemapCallbackUserData *)user_data_v; + const Depsgraph *depsgraph = user_data->depsgraph; + if (*id_p != NULL) { + ID *id_orig = *id_p; + if (id_orig == user_data->temp_id) { + DEG_COW_PRINT(" Remapping datablock for %s: id_temp=%p id_cow=%p\n", + id_orig->name, id_orig, user_data->real_id); + *id_p = user_data->real_id; + } + else if (check_datablocks_copy_on_writable(id_orig)) { + ID *id_cow; + if (user_data->create_placeholders) { + /* Special workaround to stop creating temp datablocks for + * objects which are coming from scene's collection and which + * are never linked to any of layers. + * + * TODO(sergey): Ideally we need to tell ID looper to ignore + * those or at least make it more reliable check where the + * pointer is coming from. + */ + const ID_Type id_type = GS(id_orig->name); + const ID_Type id_type_self = GS(id_self->name); + if (id_type == ID_OB && id_type_self == ID_SCE) { + IDDepsNode *id_node = depsgraph->find_id_node(id_orig); + if (id_node == NULL) { + id_cow = id_orig; + } + else { + id_cow = id_node->id_cow; + } + } + else { + id_cow = user_data->node_builder->ensure_cow_id(id_orig); + } + } + else { + id_cow = depsgraph->get_cow_id(id_orig); + } + BLI_assert(id_cow != NULL); + DEG_COW_PRINT(" Remapping datablock for %s: id_orig=%p id_cow=%p\n", + id_orig->name, id_orig, id_cow); + *id_p = id_cow; + } + } + return IDWALK_RET_NOP; +} + +/* Do some special treatment of data transfer from original ID to it's + * CoW complementary part. + * + * Only use for the newly created CoW datablocks. + */ +void update_special_pointers(const Depsgraph *depsgraph, + const ID *id_orig, ID *id_cow) +{ + const ID_Type type = GS(id_orig->name); + switch (type) { + case ID_OB: + { + /* Ensure we don't drag someone's else derived mesh to the + * new copy of the object. + */ + Object *object_cow = (Object *)id_cow; + const Object *object_orig = (const Object *)id_orig; + (void) object_cow; /* Ignored for release builds. */ + BLI_assert(object_cow->derivedFinal == NULL); + BLI_assert(object_cow->derivedDeform == NULL); + object_cow->mode = object_orig->mode; + break; + } + case ID_ME: + { + /* For meshes we need to update edit_btmesh to make it to point + * to the CoW version of object. + * + * This is kind of confusing, because actual bmesh is not owned by + * the CoW object, so need to be accurate about using link from + * edit_btmesh to object. + */ + const Mesh *mesh_orig = (const Mesh *)id_orig; + Mesh *mesh_cow = (Mesh *)id_cow; + if (mesh_orig->edit_btmesh != NULL) { + mesh_cow->edit_btmesh = (BMEditMesh *)MEM_dupallocN(mesh_orig->edit_btmesh); + mesh_cow->edit_btmesh->ob = + (Object *)depsgraph->get_cow_id(&mesh_orig->edit_btmesh->ob->id); + mesh_cow->edit_btmesh->derivedFinal = NULL; + mesh_cow->edit_btmesh->derivedCage = NULL; + } + break; + } + case ID_SCE: + { + const Scene *scene_orig = (const Scene *)id_orig; + Scene *scene_cow = (Scene *)id_cow; + if (scene_orig->obedit != NULL) { + scene_cow->obedit = (Object *)depsgraph->get_cow_id(&scene_orig->obedit->id); + } + else { + scene_cow->obedit = NULL; + } + break; + } + default: + break; + } +} + +/* Update copy-on-write version of scene from original scene. */ +void update_copy_on_write_scene(const Depsgraph *depsgraph, + Scene *scene_cow, + const Scene *scene_orig) +{ + // Some non-pointer data sync, current frame for now. + // TODO(sergey): Are we missing something here? + scene_cow->r.cfra = scene_orig->r.cfra; + scene_cow->r.subframe = scene_orig->r.subframe; + // Update bases. + const ViewLayer *view_layer_orig = (ViewLayer *)scene_orig->view_layers.first; + ViewLayer *view_layer_cow = (ViewLayer *)scene_cow->view_layers.first; + while (view_layer_orig != NULL) { + // Update pointers to active base. + if (view_layer_orig->basact == NULL) { + view_layer_cow->basact = NULL; + } + else { + const Object *obact_orig = view_layer_orig->basact->object; + Object *obact_cow = (Object *)depsgraph->get_cow_id(&obact_orig->id); + view_layer_cow->basact = BKE_view_layer_base_find(view_layer_cow, obact_cow); + } + // Update base flags. + // + // TODO(sergey): We should probably check visibled/selectabled + // flag here? + const Base *base_orig = (Base *)view_layer_orig->object_bases.first; + Base *base_cow = (Base *)view_layer_cow->object_bases.first;; + while (base_orig != NULL) { + base_cow->flag = base_orig->flag; + base_orig = base_orig->next; + base_cow = base_cow->next; + } + view_layer_orig = view_layer_orig->next; + view_layer_cow = view_layer_cow->next; + } + // Update edit object pointer. + if (scene_orig->obedit != NULL) { + scene_cow->obedit = (Object *)depsgraph->get_cow_id(&scene_orig->obedit->id); + } + else { + scene_cow->obedit = NULL; + } + /* Synchronize active render engine. */ + BLI_strncpy(scene_cow->view_render.engine_id, + scene_orig->view_render.engine_id, + sizeof(scene_cow->view_render.engine_id)); + /* TODO(sergey): What else do we need here? */ +} + +/* Update copy-on-write version of armature object from original scene. */ +void update_copy_on_write_object(const Depsgraph * /*depsgraph*/, + Object *object_cow, + const Object *object_orig) +{ + /* TODO(sergey): This function might be split into a smaller ones, + * reused for different updates. And maybe even moved to BKE. + */ + /* Update armature/pose related flags. */ + bPose *pose_cow = object_cow->pose; + const bPose *pose_orig = object_orig->pose; + extract_pose_from_pose(pose_cow, pose_orig); + /* Update object itself. */ + BKE_object_transform_copy(object_cow, object_orig); + object_cow->mode = object_orig->mode; +} + +/* Update copy-on-write version of datablock from it's original ID without re-building + * the whole datablock from scratch. + * + * Used for such special cases as scene collections and armatures, which can not use full + * re-alloc due to pointers used as function bindings. + */ +void update_copy_on_write_datablock(const Depsgraph *depsgraph, + const ID *id_orig, ID *id_cow) +{ + bool ok = false; + const ID_Type id_type = GS(id_orig->name); + switch (id_type) { + case ID_SCE: { + const Scene *scene_orig = (const Scene *)id_orig; + Scene *scene_cow = (Scene *)id_cow; + update_copy_on_write_scene(depsgraph, scene_cow, scene_orig); + ok = true; + break; + } + case ID_OB: { + const Object *object_orig = (const Object *)id_orig; + Object *object_cow = (Object *)id_cow; + if (object_orig->type == OB_ARMATURE) { + update_copy_on_write_object(depsgraph, + object_cow, + object_orig); + ok = true; + } + break; + } + case ID_AR: + /* Nothing to do currently. */ + ok = true; + break; + default: + break; + } + // TODO(sergey): Other ID types here. + if (!ok) { + BLI_assert(!"Missing update logic of expanded datablock"); + } +} + +/* This callback is used to validate that all nested ID datablocks are + * properly expanded. + */ +int foreach_libblock_validate_callback(void *user_data, + ID * /*id_self*/, + ID **id_p, + int /*cb_flag*/) +{ + ValidateData *data = (ValidateData *)user_data; + if (*id_p != NULL) { + if (!check_datablock_expanded(*id_p)) { + data->is_valid = false; + /* TODO(sergey): Store which is is not valid? */ + } + } + return IDWALK_RET_NOP; +} + +} // namespace + +/* Actual implementation of logic which "expands" all the data which was not + * yet copied-on-write. + * + * NOTE: Expects that CoW datablock is empty. + */ +ID *deg_expand_copy_on_write_datablock(const Depsgraph *depsgraph, + const IDDepsNode *id_node, + DepsgraphNodeBuilder *node_builder, + bool create_placeholders) +{ + BLI_assert(!create_placeholders || + check_datablock_expanded_at_construction(id_node->id_orig)); + const ID *id_orig = id_node->id_orig; + ID *id_cow = id_node->id_cow; + /* No need to expand such datablocks, their copied ID is same as original + * one already. + */ + if (!deg_copy_on_write_is_needed(id_orig)) { + return id_cow; + } + DEG_COW_PRINT("Expanding datablock for %s: id_orig=%p id_cow=%p\n", + id_orig->name, id_orig, id_cow); + /* Sanity checks. */ + /* NOTE: Disabled for now, conflicts when re-using evaluated datablock when + * rebuilding dependencies. + */ + if (check_datablock_expanded(id_cow) && create_placeholders) { + deg_free_copy_on_write_datablock(id_cow); + } + // BLI_assert(check_datablock_expanded(id_cow) == false); + /* Copy data from original ID to a copied version. */ + /* TODO(sergey): Avoid doing full ID copy somehow, make Mesh to reference + * original geometry arrays for until those are modified. + */ + /* TODO(sergey): We do some trickery with temp bmain and extra ID pointer + * just to be able to use existing API. Ideally we need to replace this with + * in-place copy from existing datablock to a prepared memory. + * + * NOTE: We don't use BKE_main_{new,free} because: + * - We don't want heap-allocations here. + * - We don't want bmain's content to be freed when main is freed. + */ + bool done = false; + /* Need to make sure the possibly temporary allocated memory is correct for + * until we are fully done with remapping original pointers with copied on + * write ones. + */ + ID *newid = NULL; + /* First we handle special cases which are not covered by id_copy() yet. + * or cases where we want to do something smarter than simple datablock + * copy. + */ + const ID_Type id_type = GS(id_orig->name); + switch (id_type) { + case ID_SCE: + { + done = scene_copy_inplace_no_main((Scene *)id_orig, (Scene *)id_cow); + break; + } + case ID_ME: + { + /* TODO(sergey): Ideally we want to handle meshes in a special + * manner here to avoid initial copy of all the geometry arrays. + */ + break; + } + default: + break; + } + if (!done) { + done = id_copy_inplace_no_main(id_orig, id_cow); + } + if (!done) { + BLI_assert(!"No idea how to perform CoW on datablock"); + } + /* Update pointers to nested ID datablocks. */ + DEG_COW_PRINT(" Remapping ID links for %s: id_orig=%p id_cow=%p\n", + id_orig->name, id_orig, id_cow); + +#ifdef NESTED_ID_NASTY_WORKAROUND + ntree_hack_remap_pointers(depsgraph, id_cow); +#endif + /* Do it now, so remapping will understand that possibly remapped self ID + * is not to be remapped again. + */ + deg_tag_copy_on_write_id(id_cow, id_orig); + /* Perform remapping of the nodes. */ + RemapCallbackUserData user_data; + user_data.depsgraph = depsgraph; + user_data.temp_id = newid; + user_data.real_id = id_cow; + user_data.node_builder = node_builder; + user_data.create_placeholders = create_placeholders; + BKE_library_foreach_ID_link(NULL, + id_cow, + foreach_libblock_remap_callback, + (void *)&user_data, + IDWALK_NOP); + /* Correct or tweak some pointers which are not taken care by foreach + * from above. + */ + update_special_pointers(depsgraph, id_orig, id_cow); + /* Now we can safely discard temporary memory used for copying. */ + if (newid != NULL) { + MEM_freeN(newid); + } + return id_cow; +} + +/* NOTE: Depsgraph is supposed to have ID node already. */ +ID *deg_expand_copy_on_write_datablock(const Depsgraph *depsgraph, + ID *id_orig, + DepsgraphNodeBuilder *node_builder, + bool create_placeholders) +{ + DEG::IDDepsNode *id_node = depsgraph->find_id_node(id_orig); + BLI_assert(id_node != NULL); + return deg_expand_copy_on_write_datablock(depsgraph, + id_node, + node_builder, + create_placeholders); +} + +ID *deg_update_copy_on_write_datablock(const Depsgraph *depsgraph, + const IDDepsNode *id_node) +{ + const ID *id_orig = id_node->id_orig; + const ID_Type id_type = GS(id_orig->name); + ID *id_cow = id_node->id_cow; + /* Similar to expansion, no need to do anything here. */ + if (!deg_copy_on_write_is_needed(id_orig)) { + return id_cow; + } + /* Special case for datablocks which are expanded at the dependency graph + * construction time. This datablocks must never change pointers of their + * nested data since it is used for function bindings. + */ + if (check_datablock_expanded_at_construction(id_orig)) { + BLI_assert(check_datablock_expanded(id_cow) == true); + update_copy_on_write_datablock(depsgraph, id_orig, id_cow); + return id_cow; + } + /* For the rest if datablock types we use simple logic: + * - Free previously expanded data, if any. + * - Perform full datablock copy. + * + * Note that we never free GPU materials from here since that's not + * safe for threading and GPU materials are likely to be re-used. + */ + /* TODO(sergey): Either move this to an utility function or redesign + * Copy-on-Write components in a way that only needed parts are being + * copied over. + */ + ListBase gpumaterial_backup; + ListBase *gpumaterial_ptr = NULL; + Mesh *mesh_evaluated = NULL; + IDProperty *base_collection_properties = NULL; + short base_flag = 0; + if (check_datablock_expanded(id_cow)) { + switch (id_type) { + case ID_MA: + { + Material *material = (Material *)id_cow; + gpumaterial_ptr = &material->gpumaterial; + break; + } + case ID_WO: + { + World *world = (World *)id_cow; + gpumaterial_ptr = &world->gpumaterial; + break; + } + case ID_OB: + { + Object *object = (Object *)id_cow; + /* Store evaluated mesh, make sure we don't free it. */ + mesh_evaluated = object->mesh_evaluated; + object->mesh_evaluated = NULL; + /* Currently object update will override actual object->data + * to an evaluated version. Need to make sure we don't have + * data set to evaluated one before free anything. + */ + if (mesh_evaluated != NULL) { + if (object->data == mesh_evaluated) { + object->data = mesh_evaluated->id.newid; + } + } + /* Make a backup of base flags. */ + base_collection_properties = object->base_collection_properties; + base_flag = object->base_flag; + break; + } + default: + break; + } + if (gpumaterial_ptr != NULL) { + gpumaterial_backup = *gpumaterial_ptr; + gpumaterial_ptr->first = gpumaterial_ptr->last = NULL; + } + } + deg_free_copy_on_write_datablock(id_cow); + deg_expand_copy_on_write_datablock(depsgraph, id_node); + /* Restore GPU materials. */ + if (gpumaterial_ptr != NULL) { + *gpumaterial_ptr = gpumaterial_backup; + } + if (id_type == ID_OB) { + Object *object = (Object *)id_cow; + if (mesh_evaluated != NULL) { + object->mesh_evaluated = mesh_evaluated; + /* Do same thing as object update: override actual object data + * pointer with evaluated datablock. + */ + if (object->type == OB_MESH) { + object->data = mesh_evaluated; + /* Evaluated mesh simply copied edit_btmesh pointer from + * original mesh during update, need to make sure no dead + * pointers are left behind. + */ + mesh_evaluated->edit_btmesh = + ((Mesh *)mesh_evaluated->id.newid)->edit_btmesh; + } + } + if (base_collection_properties != NULL) { + object->base_collection_properties = base_collection_properties; + object->base_flag = base_flag; + } + } + return id_cow; +} + +/* NOTE: Depsgraph is supposed to have ID node already. */ +ID *deg_update_copy_on_write_datablock(const Depsgraph *depsgraph, + ID *id_orig) +{ + DEG::IDDepsNode *id_node = depsgraph->find_id_node(id_orig); + BLI_assert(id_node != NULL); + return deg_update_copy_on_write_datablock(depsgraph, id_node); +} + +/* Free content of the CoW datablock + * Notes: + * - Does not recurs into nested ID datablocks. + * - Does not free datablock itself. + */ +void deg_free_copy_on_write_datablock(ID *id_cow) +{ + if (!check_datablock_expanded(id_cow)) { + /* Actual content was never copied on top of CoW block, we have + * nothing to free. + */ + return; + } + const ID_Type type = GS(id_cow->name); +#ifdef NESTED_ID_NASTY_WORKAROUND + nested_id_hack_discard_pointers(id_cow); +#endif + switch (type) { + case ID_OB: + { + /* TODO(sergey): This workaround is only to prevent free derived + * caches from modifying object->data. This is currently happening + * due to mesh/curve datablock boundbox tagging dirty. + */ + Object *ob_cow = (Object *)id_cow; + ob_cow->data = NULL; + break; + } + case ID_ME: + { + Mesh *mesh_cow = (Mesh *)id_cow; + if (mesh_cow->edit_btmesh != NULL) { + BKE_editmesh_free_derivedmesh(mesh_cow->edit_btmesh); + MEM_freeN(mesh_cow->edit_btmesh); + mesh_cow->edit_btmesh = NULL; + } + break; + } + case ID_SCE: + { + /* Special case for scene: we use explicit function call which + * ensures no access to other datablocks is done. + */ + Scene *scene = (Scene *)id_cow; + BKE_scene_free_ex(scene, false); + BKE_libblock_free_data(id_cow, false); + id_cow->name[0] = '\0'; + return; + } + default: + break; + } + BKE_libblock_free_datablock(id_cow, 0); + BKE_libblock_free_data(id_cow, false); + /* Signal datablock as not being expanded. */ + id_cow->name[0] = '\0'; +} + +void deg_evaluate_copy_on_write(const EvaluationContext * /*eval_ctx*/, + const Depsgraph *depsgraph, + const IDDepsNode *id_node) +{ + DEBUG_PRINT("%s on %s\n", __func__, id_node->id_orig->name); + deg_update_copy_on_write_datablock(depsgraph, id_node); +} + +bool deg_validate_copy_on_write_datablock(ID *id_cow) +{ + if (id_cow == NULL) { + return false; + } + ValidateData data; + data.is_valid = true; + BKE_library_foreach_ID_link(NULL, + id_cow, + foreach_libblock_validate_callback, + &data, + IDWALK_NOP); + return data.is_valid; +} + +void deg_tag_copy_on_write_id(ID *id_cow, const ID *id_orig) +{ + id_cow->tag |= LIB_TAG_COPY_ON_WRITE; + /* TODO(sergey): Is it safe to re-use newid for original ID link? */ + id_cow->newid = (ID *)id_orig; +} + +bool deg_copy_on_write_is_expanded(const ID *id_cow) +{ + return check_datablock_expanded(id_cow); +} + +bool deg_copy_on_write_is_needed(const ID *id_orig) +{ + const ID_Type id_type = GS(id_orig->name); + return !ELEM(id_type, ID_IM); +} + +} // namespace DEG diff --git a/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.h b/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.h new file mode 100644 index 00000000000..a2b57cb7198 --- /dev/null +++ b/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.h @@ -0,0 +1,109 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * 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. + * + * The Original Code is Copyright (C) 20137Blender Foundation. + * All rights reserved. + * + * Original Author: Sergey Sharybin + * Contributor(s): None Yet + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/depsgraph/intern/eval/deg_eval_copy_on_write.h + * \ingroup depsgraph + */ + +#pragma once + +#include <stddef.h> + +struct EvaluationContext; +struct ID; + +/* Unkomment this to have verbose log about original and CoW pointers + * logged, with detailed information when they are allocated, expanded + * and remapped. + */ +// #define DEG_DEBUG_COW_POINTERS + +#ifdef DEG_DEBUG_COW_POINTERS +# define DEG_COW_PRINT(format, ...) printf(format, __VA_ARGS__); +#else +# define DEG_COW_PRINT(format, ...) +#endif + +namespace DEG { + +struct Depsgraph; +struct DepsgraphNodeBuilder; +struct IDDepsNode; + +/* Get fully expanded (ready for use) copy-on-write datablock for the given + * original datablock. + */ +ID *deg_expand_copy_on_write_datablock(const struct Depsgraph *depsgraph, + const IDDepsNode *id_node, + DepsgraphNodeBuilder *node_builder = NULL, + bool create_placeholders = false); +ID *deg_expand_copy_on_write_datablock(const struct Depsgraph *depsgraph, + struct ID *id_orig, + DepsgraphNodeBuilder *node_builder = NULL, + bool create_placeholders = false); + +/* Makes sure given CoW datablock is brought back to state of the original + * datablock. + */ +ID *deg_update_copy_on_write_datablock(const struct Depsgraph *depsgraph, + const IDDepsNode *id_node); +ID *deg_update_copy_on_write_datablock(const struct Depsgraph *depsgraph, + struct ID *id_orig); + +/* Helper function which frees memory used by copy-on-written databnlock. */ +void deg_free_copy_on_write_datablock(struct ID *id_cow); + +/* Callback function for depsgraph operation node which ensures copy-on-write + * datablock is ready for use by further evaluation routines. + */ +void deg_evaluate_copy_on_write(const struct EvaluationContext *eval_ctx, + const struct Depsgraph *depsgraph, + const struct IDDepsNode *id_node); + +/* Check that given ID is propely expanded and does not have any shallow + * copies inside. + */ +bool deg_validate_copy_on_write_datablock(ID *id_cow); + +/* Tag given ID block as being copy-on-wtritten. */ +void deg_tag_copy_on_write_id(struct ID *id_cow, const struct ID *id_orig); + +/* Check whether ID datablock is expanded. + * + * TODO(sergey): Make it an inline function or a macro. + */ +bool deg_copy_on_write_is_expanded(const struct ID *id_cow); + +/* Check whether copy-on-write datablock is needed for given ID. + * + * There are some exceptions on datablocks which are covered by dependency graph + * but which we don't want to start duplicating. + * + * This includes images. + */ +bool deg_copy_on_write_is_needed(const ID *id_orig); + +} // namespace DEG diff --git a/source/blender/depsgraph/intern/eval/deg_eval_flush.cc b/source/blender/depsgraph/intern/eval/deg_eval_flush.cc index daf008ddb7d..74c3cd28455 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval_flush.cc +++ b/source/blender/depsgraph/intern/eval/deg_eval_flush.cc @@ -51,6 +51,7 @@ extern "C" { #include "intern/nodes/deg_node_operation.h" #include "intern/depsgraph_intern.h" +#include "intern/eval/deg_eval_copy_on_write.h" #include "util/deg_util_foreach.h" namespace DEG { @@ -70,19 +71,6 @@ typedef std::deque<OperationDepsNode *> FlushQueue; namespace { -// TODO(sergey): De-duplicate with depsgraph_tag,cc -void lib_id_recalc_tag(Main *bmain, ID *id) -{ - id->recalc |= ID_RECALC; - DEG_id_type_tag(bmain, GS(id->name)); -} - -void lib_id_recalc_data_tag(Main *bmain, ID *id) -{ - id->recalc |= ID_RECALC_DATA; - DEG_id_type_tag(bmain, GS(id->name)); -} - void flush_init_operation_node_func( void *__restrict data_v, const int i, @@ -146,9 +134,10 @@ BLI_INLINE void flush_handle_id_node(IDDepsNode *id_node) } /* TODO(sergey): We can reduce number of arguments here. */ -BLI_INLINE void flush_handle_component_node(Depsgraph * /*graph*/, +BLI_INLINE void flush_handle_component_node(Depsgraph *graph, IDDepsNode *id_node, ComponentDepsNode *comp_node, + bool use_copy_on_write, FlushQueue *queue) { /* We only handle component once. */ @@ -156,46 +145,27 @@ BLI_INLINE void flush_handle_component_node(Depsgraph * /*graph*/, return; } comp_node->done = COMPONENT_STATE_DONE; + /* Currently this is needed to get object->mesh to be replaced with + * original mesh (rather than being evaluated_mesh). + * + * TODO(sergey): This is something we need to avoid. + */ + if (use_copy_on_write && comp_node->depends_on_cow()) { + ComponentDepsNode *cow_comp = + id_node->find_component(DEG_NODE_TYPE_COPY_ON_WRITE); + cow_comp->tag_update(graph); + } /* 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, + /* We don't want to flush tags in "upstream" direction for + * certain types of operations. * - * Plus it ensures visibility changes and relations and - * layers visibility update has proper flags to work with. + * TODO(sergey): Need a more generic solution for this. */ - 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; + if (op->opcode == DEG_OPCODE_PARTICLE_SETTINGS_EVAL) { + continue; } + op->flag |= DEPSOP_FLAG_NEEDS_UPDATE; } /* When some target changes bone, we might need to re-run the * whole IK solver, otherwise result might be unpredictable. @@ -237,19 +207,41 @@ BLI_INLINE OperationDepsNode *flush_schedule_children( return result; } +/* NOTE: It will also accumulate flags from changed components. */ BLI_INLINE void flush_editors_id_update(Main *bmain, - Depsgraph *graph) + Depsgraph *graph, + const DEGEditorUpdateContext *update_ctx) { foreach (IDDepsNode *id_node, graph->id_nodes) { if (id_node->done != ID_STATE_MODIFIED) { continue; } + DEG_id_type_tag(bmain, GS(id_node->id_orig->name)); /* 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); + ID *id_orig = id_node->id_orig; + ID *id_cow = id_node->id_cow; + /* Copy tag from original data to CoW storage. + * This is because DEG_id_tag_update() sets tags on original + * data. + */ + id_cow->recalc |= (id_orig->recalc & ID_RECALC_ALL); + /* Gather recalc flags from all changed components. */ + GHASH_FOREACH_BEGIN(ComponentDepsNode *, comp_node, id_node->components) + { + if (comp_node->done != COMPONENT_STATE_DONE) { + continue; + } + DepsNodeFactory *factory = deg_type_get_factory(comp_node->type); + BLI_assert(factory != NULL); + id_cow->recalc |= factory->id_recalc_tag(); + } + GHASH_FOREACH_END(); + DEG_DEBUG_PRINTF("Accumulated recalc bits for %s: %u\n", + id_orig->name, (unsigned int)id_cow->recalc); + /* Inform editors. */ + if (deg_copy_on_write_is_expanded(id_cow)) { + deg_editors_id_update(update_ctx, id_cow); + } } } @@ -260,6 +252,7 @@ BLI_INLINE void flush_editors_id_update(Main *bmain, */ void deg_graph_flush_updates(Main *bmain, Depsgraph *graph) { + const bool use_copy_on_write = DEG_depsgraph_use_copy_on_write(); /* Sanity checks. */ BLI_assert(bmain != NULL); BLI_assert(graph != NULL); @@ -272,6 +265,11 @@ void deg_graph_flush_updates(Main *bmain, Depsgraph *graph) /* Starting from the tagged "entry" nodes, flush outwards. */ FlushQueue queue; flush_schedule_entrypoints(graph, &queue); + /* Prepare update context for editors. */ + DEGEditorUpdateContext update_ctx; + update_ctx.bmain = bmain; + update_ctx.scene = graph->scene; + update_ctx.view_layer = graph->view_layer; /* Do actual flush. */ while (!queue.empty()) { OperationDepsNode *op_node = queue.front(); @@ -286,13 +284,14 @@ void deg_graph_flush_updates(Main *bmain, Depsgraph *graph) flush_handle_component_node(graph, id_node, comp_node, + use_copy_on_write, &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); + flush_editors_id_update(bmain, graph, &update_ctx); } static void graph_clear_func( diff --git a/source/blender/depsgraph/intern/nodes/deg_node.cc b/source/blender/depsgraph/intern/nodes/deg_node.cc index d72ca384044..fdcfc129073 100644 --- a/source/blender/depsgraph/intern/nodes/deg_node.cc +++ b/source/blender/depsgraph/intern/nodes/deg_node.cc @@ -34,6 +34,7 @@ #include "BLI_utildefines.h" +#include "intern/eval/deg_eval_copy_on_write.h" #include "intern/nodes/deg_node_component.h" #include "intern/nodes/deg_node_id.h" #include "intern/nodes/deg_node_operation.h" @@ -41,6 +42,7 @@ #include "intern/depsgraph_intern.h" #include "util/deg_util_foreach.h" +#include "util/deg_util_function.h" namespace DEG { diff --git a/source/blender/depsgraph/intern/nodes/deg_node_component.cc b/source/blender/depsgraph/intern/nodes/deg_node_component.cc index 9450ed7f17d..1f56edd1f87 100644 --- a/source/blender/depsgraph/intern/nodes/deg_node_component.cc +++ b/source/blender/depsgraph/intern/nodes/deg_node_component.cc @@ -124,8 +124,7 @@ static void comp_node_hash_value_free(void *value_v) ComponentDepsNode::ComponentDepsNode() : entry_operation(NULL), - exit_operation(NULL), - layers(0) + exit_operation(NULL) { operations_map = BLI_ghash_new(comp_node_hash_key, comp_node_hash_key_cmp, @@ -158,15 +157,12 @@ string ComponentDepsNode::identifier() const char typebuf[16]; sprintf(typebuf, "(%d)", type); - char layers[16]; - sprintf(layers, "%u", this->layers); - - return string(typebuf) + name + " : " + idname + " (Layers: " + layers + ")"; + return string(typebuf) + name + " : " + idname; } OperationDepsNode *ComponentDepsNode::find_operation(OperationIDKey key) const { - OperationDepsNode *node; + OperationDepsNode *node = NULL; if (operations_map != NULL) { node = (OperationDepsNode *)BLI_ghash_lookup(operations_map, &key); } @@ -233,7 +229,7 @@ OperationDepsNode *ComponentDepsNode::add_operation(const DepsEvalOperationCb& o OperationDepsNode *op_node = find_operation(opcode, name, name_tag); if (!op_node) { DepsNodeFactory *factory = deg_type_get_factory(DEG_NODE_TYPE_OPERATION); - op_node = (OperationDepsNode *)factory->create_node(this->owner->id, "", name); + op_node = (OperationDepsNode *)factory->create_node(this->owner->id_orig, "", name); /* register opnode in this component's operation set */ OperationIDKey *key = OBJECT_GUARDED_NEW(OperationIDKey, opcode, name, name_tag); @@ -346,7 +342,7 @@ OperationDepsNode *ComponentDepsNode::get_exit_operation() return NULL; } -void ComponentDepsNode::finalize_build() +void ComponentDepsNode::finalize_build(Depsgraph * /*graph*/) { operations.reserve(BLI_ghash_size(operations_map)); GHASH_FOREACH_BEGIN(OperationDepsNode *, op_node, operations_map) @@ -381,36 +377,41 @@ void BoneComponentDepsNode::init(const ID *id, const char *subdata) /* Register all components. =============================== */ -DEG_COMPONENT_NODE_DEFINE(Animation, ANIMATION, ID_RECALC_NONE); -DEG_COMPONENT_NODE_DEFINE(Bone, BONE, ID_RECALC_NONE); -DEG_COMPONENT_NODE_DEFINE(Cache, CACHE, ID_RECALC_NONE); -DEG_COMPONENT_NODE_DEFINE(Geometry, GEOMETRY, ID_RECALC_NONE); -DEG_COMPONENT_NODE_DEFINE(Parameters, PARAMETERS, ID_RECALC_NONE); -DEG_COMPONENT_NODE_DEFINE(Particles, EVAL_PARTICLES, ID_RECALC_NONE); -DEG_COMPONENT_NODE_DEFINE(Proxy, PROXY, ID_RECALC_NONE); -DEG_COMPONENT_NODE_DEFINE(Pose, EVAL_POSE, ID_RECALC_NONE); -DEG_COMPONENT_NODE_DEFINE(Sequencer, SEQUENCER, ID_RECALC_NONE); -DEG_COMPONENT_NODE_DEFINE(Shading, SHADING, ID_RECALC_NONE); -DEG_COMPONENT_NODE_DEFINE(Transform, TRANSFORM, ID_RECALC_NONE); +DEG_COMPONENT_NODE_DEFINE(Animation, ANIMATION, ID_RECALC_ANIMATION); +DEG_COMPONENT_NODE_DEFINE(BatchCache, BATCH_CACHE, ID_RECALC_DRAW_CACHE); +DEG_COMPONENT_NODE_DEFINE(Bone, BONE, ID_RECALC_GEOMETRY); +DEG_COMPONENT_NODE_DEFINE(Cache, CACHE, ID_RECALC); +DEG_COMPONENT_NODE_DEFINE(CopyOnWrite, COPY_ON_WRITE, ID_RECALC); +DEG_COMPONENT_NODE_DEFINE(Geometry, GEOMETRY, ID_RECALC_GEOMETRY); +DEG_COMPONENT_NODE_DEFINE(LayerCollections, LAYER_COLLECTIONS, ID_RECALC_COLLECTIONS); +DEG_COMPONENT_NODE_DEFINE(Parameters, PARAMETERS, ID_RECALC); +DEG_COMPONENT_NODE_DEFINE(Particles, EVAL_PARTICLES, ID_RECALC_GEOMETRY); +DEG_COMPONENT_NODE_DEFINE(Proxy, PROXY, ID_RECALC_GEOMETRY); +DEG_COMPONENT_NODE_DEFINE(Pose, EVAL_POSE, ID_RECALC_GEOMETRY); +DEG_COMPONENT_NODE_DEFINE(Sequencer, SEQUENCER, ID_RECALC); +DEG_COMPONENT_NODE_DEFINE(Shading, SHADING, ID_RECALC_DRAW); +DEG_COMPONENT_NODE_DEFINE(ShadingParameters, SHADING_PARAMETERS, ID_RECALC_DRAW); +DEG_COMPONENT_NODE_DEFINE(Transform, TRANSFORM, ID_RECALC_TRANSFORM); /* Node Types Register =================================== */ void deg_register_component_depsnodes() { - deg_register_node_typeinfo(&DNTI_PARAMETERS); - deg_register_node_typeinfo(&DNTI_PROXY); deg_register_node_typeinfo(&DNTI_ANIMATION); - deg_register_node_typeinfo(&DNTI_TRANSFORM); - deg_register_node_typeinfo(&DNTI_GEOMETRY); - deg_register_node_typeinfo(&DNTI_SEQUENCER); - - deg_register_node_typeinfo(&DNTI_EVAL_POSE); deg_register_node_typeinfo(&DNTI_BONE); - + deg_register_node_typeinfo(&DNTI_CACHE); + deg_register_node_typeinfo(&DNTI_BATCH_CACHE); + deg_register_node_typeinfo(&DNTI_COPY_ON_WRITE); + deg_register_node_typeinfo(&DNTI_GEOMETRY); + deg_register_node_typeinfo(&DNTI_LAYER_COLLECTIONS); + deg_register_node_typeinfo(&DNTI_PARAMETERS); deg_register_node_typeinfo(&DNTI_EVAL_PARTICLES); + deg_register_node_typeinfo(&DNTI_PROXY); + deg_register_node_typeinfo(&DNTI_EVAL_POSE); + deg_register_node_typeinfo(&DNTI_SEQUENCER); deg_register_node_typeinfo(&DNTI_SHADING); - - deg_register_node_typeinfo(&DNTI_CACHE); + deg_register_node_typeinfo(&DNTI_SHADING_PARAMETERS); + deg_register_node_typeinfo(&DNTI_TRANSFORM); } } // namespace DEG diff --git a/source/blender/depsgraph/intern/nodes/deg_node_component.h b/source/blender/depsgraph/intern/nodes/deg_node_component.h index 985716deaac..b8009cc0a7f 100644 --- a/source/blender/depsgraph/intern/nodes/deg_node_component.h +++ b/source/blender/depsgraph/intern/nodes/deg_node_component.h @@ -127,7 +127,7 @@ struct ComponentDepsNode : public DepsNode { OperationDepsNode *get_entry_operation(); OperationDepsNode *get_exit_operation(); - void finalize_build(); + void finalize_build(Depsgraph *graph); IDDepsNode *owner; @@ -147,9 +147,7 @@ struct ComponentDepsNode : public DepsNode { OperationDepsNode *exit_operation; // XXX: a poll() callback to check if component's first node can be started? - - /* Temporary bitmask, used during graph construction. */ - unsigned int layers; + virtual bool depends_on_cow() { return true; } }; /* ---------------------------------------- */ @@ -173,14 +171,18 @@ struct ComponentDepsNode : public DepsNode { } DEG_COMPONENT_NODE_DECLARE_GENERIC(Animation); +DEG_COMPONENT_NODE_DECLARE_GENERIC(BatchCache); DEG_COMPONENT_NODE_DECLARE_GENERIC(Cache); +DEG_COMPONENT_NODE_DECLARE_GENERIC(CopyOnWrite); DEG_COMPONENT_NODE_DECLARE_GENERIC(Geometry); +DEG_COMPONENT_NODE_DECLARE_GENERIC(LayerCollections); DEG_COMPONENT_NODE_DECLARE_GENERIC(Parameters); DEG_COMPONENT_NODE_DECLARE_GENERIC(Particles); DEG_COMPONENT_NODE_DECLARE_GENERIC(Proxy); DEG_COMPONENT_NODE_DECLARE_GENERIC(Pose); DEG_COMPONENT_NODE_DECLARE_GENERIC(Sequencer); DEG_COMPONENT_NODE_DECLARE_GENERIC(Shading); +DEG_COMPONENT_NODE_DECLARE_GENERIC(ShadingParameters); DEG_COMPONENT_NODE_DECLARE_GENERIC(Transform); /* Bone Component */ diff --git a/source/blender/depsgraph/intern/nodes/deg_node_id.cc b/source/blender/depsgraph/intern/nodes/deg_node_id.cc index 478cc2863b0..edc5c0114f9 100644 --- a/source/blender/depsgraph/intern/nodes/deg_node_id.cc +++ b/source/blender/depsgraph/intern/nodes/deg_node_id.cc @@ -41,8 +41,12 @@ extern "C" { #include "DNA_anim_types.h" #include "BKE_animsys.h" +#include "BKE_library.h" } +#include "DEG_depsgraph.h" + +#include "intern/eval/deg_eval_copy_on_write.h" #include "intern/nodes/deg_node_time.h" #include "intern/depsgraph_intern.h" @@ -58,8 +62,8 @@ IDDepsNode::ComponentIDKey::ComponentIDKey(eDepsNode_Type type, bool IDDepsNode::ComponentIDKey::operator== (const ComponentIDKey &other) const { - return type == other.type && - STREQ(name, other.name); + return type == other.type && + STREQ(name, other.name); } static unsigned int id_deps_node_hash_key(const void *key_v) @@ -95,33 +99,77 @@ static void id_deps_node_hash_value_free(void *value_v) /* Initialize 'id' node - from pointer data given. */ void IDDepsNode::init(const ID *id, const char *UNUSED(subdata)) { - /* Store ID-pointer. */ BLI_assert(id != NULL); - this->id = (ID *)id; - this->layers = (1 << 20) - 1; - this->eval_flags = 0; - - /* For object we initialize layers to layer from base. */ - if (GS(id->name) == ID_OB) { - this->layers = 0; - } + /* Store ID-pointer. */ + id_orig = (ID *)id; + eval_flags = 0; + linked_state = DEG_ID_LINKED_INDIRECTLY; components = BLI_ghash_new(id_deps_node_hash_key, id_deps_node_hash_key_cmp, "Depsgraph id components hash"); +} - /* NOTE: components themselves are created if/when needed. - * This prevents problems with components getting added - * twice if an ID-Ref needs to be created to house it... +void IDDepsNode::init_copy_on_write(ID *id_cow_hint) +{ + /* Early output for non-copy-on-write case: we keep CoW pointer same as + * an original one. + */ + if (!DEG_depsgraph_use_copy_on_write()) { + UNUSED_VARS(id_cow_hint); + id_cow = id_orig; + return; + } + /* Create pointer as early as possible, so we can use it for function + * bindings. Rest of data we'll be copying to the new datablock when + * it is actually needed. */ + if (id_cow_hint != NULL) { + // BLI_assert(deg_copy_on_write_is_needed(id_orig)); + if (deg_copy_on_write_is_needed(id_orig)) { + id_cow = id_cow_hint; + } + else { + id_cow = id_orig; + } + } + else if (deg_copy_on_write_is_needed(id_orig)) { + id_cow = (ID *)BKE_libblock_alloc_notest(GS(id_orig->name)); + DEG_COW_PRINT("Create shallow copy for %s: id_orig=%p id_cow=%p\n", + id_orig->name, id_orig, id_cow); + deg_tag_copy_on_write_id(id_cow, id_orig); + } + else { + id_cow = id_orig; + } } /* Free 'id' node. */ IDDepsNode::~IDDepsNode() { + destroy(); +} + +void IDDepsNode::destroy() +{ + if (id_orig == NULL) { + return; + } + BLI_ghash_free(components, id_deps_node_hash_key_free, id_deps_node_hash_value_free); + + /* Free memory used by this CoW ID. */ + if (id_cow != id_orig && id_cow != NULL) { + deg_free_copy_on_write_datablock(id_cow); + MEM_freeN(id_cow); + DEG_COW_PRINT("Destroy CoW for %s: id_orig=%p id_cow=%p\n", + id_orig->name, id_orig, id_cow); + } + + /* Tag that the node is freed. */ + id_orig = NULL; } ComponentDepsNode *IDDepsNode::find_component(eDepsNode_Type type, @@ -137,7 +185,7 @@ ComponentDepsNode *IDDepsNode::add_component(eDepsNode_Type type, ComponentDepsNode *comp_node = find_component(type, name); if (!comp_node) { DepsNodeFactory *factory = deg_type_get_factory(type); - comp_node = (ComponentDepsNode *)factory->create_node(this->id, "", name); + comp_node = (ComponentDepsNode *)factory->create_node(this->id_orig, "", name); /* Register. */ ComponentIDKey *key = OBJECT_GUARDED_NEW(ComponentIDKey, type, name); @@ -151,27 +199,17 @@ void IDDepsNode::tag_update(Depsgraph *graph) { GHASH_FOREACH_BEGIN(ComponentDepsNode *, comp_node, components) { - /* TODO(sergey): What about drievrs? */ - bool do_component_tag = comp_node->type != DEG_NODE_TYPE_ANIMATION; - if (comp_node->type == DEG_NODE_TYPE_ANIMATION) { - AnimData *adt = BKE_animdata_from_id(id); - /* Animation data might be null if relations are tagged for update. */ - if (adt != NULL && (adt->recalc & ADT_RECALC_ANIM)) { - do_component_tag = true; - } - } - if (do_component_tag) { - comp_node->tag_update(graph); - } + comp_node->tag_update(graph); } GHASH_FOREACH_END(); } -void IDDepsNode::finalize_build() +void IDDepsNode::finalize_build(Depsgraph *graph) { + /* Finalize build of all components. */ GHASH_FOREACH_BEGIN(ComponentDepsNode *, comp_node, components) { - comp_node->finalize_build(); + comp_node->finalize_build(graph); } GHASH_FOREACH_END(); } diff --git a/source/blender/depsgraph/intern/nodes/deg_node_id.h b/source/blender/depsgraph/intern/nodes/deg_node_id.h index 55022916c4d..505a1129192 100644 --- a/source/blender/depsgraph/intern/nodes/deg_node_id.h +++ b/source/blender/depsgraph/intern/nodes/deg_node_id.h @@ -47,7 +47,9 @@ struct IDDepsNode : public DepsNode { }; void init(const ID *id, const char *subdata); + void init_copy_on_write(ID *id_cow_hint = NULL); ~IDDepsNode(); + void destroy(); ComponentDepsNode *find_component(eDepsNode_Type type, const char *name = "") const; @@ -56,23 +58,23 @@ struct IDDepsNode : public DepsNode { void tag_update(Depsgraph *graph); - void finalize_build(); + void finalize_build(Depsgraph *graph); /* ID Block referenced. */ - ID *id; + ID *id_orig; + ID *id_cow; /* Hash to make it faster to look up components. */ GHash *components; - /* Layers of this node with accumulated layers of it's output relations. */ - unsigned int layers; - /* Additional flags needed for scene evaluation. * TODO(sergey): Only needed for until really granular updates * of all the entities. */ int eval_flags; + eDepsNode_LinkedState_Type linked_state; + DEG_DEPSNODE_DECLARE; }; |