diff options
Diffstat (limited to 'source/blender/depsgraph/intern')
35 files changed, 3309 insertions, 1115 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 3a4ca7515a3..cbe11f8d7fa 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" @@ -90,6 +90,7 @@ extern "C" { #include "BKE_node.h" #include "BKE_object.h" #include "BKE_particle.h" +#include "BKE_pointcache.h" #include "BKE_rigidbody.h" #include "BKE_sound.h" #include "BKE_tracking.h" @@ -103,6 +104,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" @@ -114,6 +116,17 @@ extern "C" { namespace DEG { +namespace { + +void free_copy_on_write_datablock(void *id_v) +{ + ID *id = (ID *)id_v; + deg_free_copy_on_write_datablock(id); + MEM_freeN(id); +} + +} /* namespace */ + /* ************ */ /* Node Builder */ @@ -122,17 +135,52 @@ namespace DEG { DepsgraphNodeBuilder::DepsgraphNodeBuilder(Main *bmain, Depsgraph *graph) : bmain_(bmain), graph_(graph), - scene_(NULL) + scene_(NULL), + view_layer_(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) { - return graph_->add_id_node(id, id->name); + if (!DEG_depsgraph_use_copy_on_write()) { + return graph_->add_id_node(id); + } + 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, 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_len(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, 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() @@ -259,9 +307,79 @@ 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); + return id_node->id_cow; +} + /* **** Build functions for entity nodes **** */ void DepsgraphNodeBuilder::begin_build() { + 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)) { + if (id_node->id_orig == id_node->id_cow) { + continue; + } + 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_id(ID* id) { @@ -269,14 +387,11 @@ void DepsgraphNodeBuilder::build_id(ID* id) { return; } switch (GS(id->name)) { - case ID_SCE: - build_scene((Scene *)id); - break; case ID_GR: - build_group(NULL, (Group *)id); + build_group((Group *)id); break; case ID_OB: - build_object(NULL, (Object *)id); + build_object(-1, (Object *)id, DEG_ID_LINKED_INDIRECTLY); break; case ID_NT: build_nodetree((bNodeTree *)id); @@ -300,51 +415,63 @@ void DepsgraphNodeBuilder::build_id(ID* id) { build_movieclip((MovieClip *)id); break; default: - /* fprintf(stderr, "Unhandled ID %s\n", id->name); */ - break; + fprintf(stderr, "Unhandled ID %s\n", id->name); } } -void DepsgraphNodeBuilder::build_group(Base *base, Group *group) +void DepsgraphNodeBuilder::build_group(Group *group) { if (built_map_.checkIsBuiltAndTag(group)) { return; } - LISTBASE_FOREACH (GroupObject *, go, &group->gobject) { - build_object(base, go->ob); + /* Build group objects. */ + LISTBASE_FOREACH (Base *, base, &group->view_layer->object_bases) { + build_object(-1, 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_EVAL); } -void DepsgraphNodeBuilder::build_object(Base *base, Object *object) +void DepsgraphNodeBuilder::build_object(int base_index, + Object *object, + eDepsNode_LinkedState_Type linked_state) { const bool has_object = built_map_.checkIsBuiltAndTag(object); - 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) { + 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_index, object, linked_state); + } + id_node->linked_state = max(id_node->linked_state, linked_state); return; } + /* 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_index, object, linked_state); /* Transform. */ build_object_transform(object); /* Parent. */ if (object->parent != NULL) { - build_object(NULL, object->parent); + build_object(-1, object->parent, DEG_ID_LINKED_INDIRECTLY); } /* Modifiers. */ if (object->modifiers.first != NULL) { @@ -383,12 +510,35 @@ 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(-1, 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( + int base_index, + Object *object, + eDepsNode_LinkedState_Type linked_state) +{ + if (base_index == -1) { + return; } + Scene *scene_cow = get_cow_datablock(scene_); + Object *object_cow = get_cow_datablock(object); + const bool is_from_set = (linked_state == DEG_ID_LINKED_VIA_SET); + /* TODO(sergey): Is this really best component to be used? */ + add_operation_node(&object->id, + DEG_NODE_TYPE_LAYER_COLLECTIONS, + function_bind(BKE_object_eval_flush_base_flags, + _1, + scene_cow, + view_layer_index_, + object_cow, base_index, + is_from_set), + DEG_OPCODE_OBJECT_BASE_FLAGS); } void DepsgraphNodeBuilder::build_object_data(Object *object) @@ -397,9 +547,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: @@ -430,6 +580,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; @@ -444,39 +597,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(); } @@ -502,7 +660,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); } @@ -513,23 +674,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); + + // 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 */ + /* 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 */ @@ -547,10 +719,13 @@ void DepsgraphNodeBuilder::build_animdata(ID *id) */ void DepsgraphNodeBuilder::build_driver(ID *id, FCurve *fcurve) { + ID *id_cow = get_cow_id(id); + /* Create data node for this driver */ + /* TODO(sergey): Shall we use COW of fcu itself here? */ ensure_operation_node(id, DEG_NODE_TYPE_PARAMETERS, - function_bind(BKE_animsys_eval_driver, _1, id, fcurve), + function_bind(BKE_animsys_eval_driver, _1, id_cow, fcurve), DEG_OPCODE_DRIVER, fcurve->rna_path ? fcurve->rna_path : "", fcurve->array_index); @@ -602,17 +777,17 @@ void DepsgraphNodeBuilder::build_world(World *world) if (built_map_.checkIsBuiltAndTag(world)) { return; } - ID *world_id = &world->id; - build_animdata(world_id); + /* Animation. */ + build_animdata(&world->id); /* world itself */ - add_operation_node(world_id, - DEG_NODE_TYPE_PARAMETERS, - NULL, - DEG_OPCODE_PARAMETERS_EVAL); - /* textures */ - build_texture_stack(world->mtex); + add_operation_node(&world->id, + DEG_NODE_TYPE_SHADING, + function_bind(BKE_world_eval, + _1, + get_cow_datablock(world)), + DEG_OPCODE_WORLD_UPDATE); /* world's nodetree */ - if (world->nodetree) { + if (world->nodetree != NULL) { build_nodetree(world->nodetree); } } @@ -621,55 +796,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) { - LISTBASE_FOREACH (GroupObject *, go, &rbw->group->gobject) { - Object *object = go->ob; + LISTBASE_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); } } @@ -694,20 +880,26 @@ void DepsgraphNodeBuilder::build_particles(Object *object) /* Component for all particle systems. */ 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); /* Build all particle systems. */ LISTBASE_FOREACH (ParticleSystem *, psys, &object->particlesystem) { ParticleSettings *part = psys->part; - /* Particle settings. */ - // XXX: what if this is used more than once! - build_animdata(&part->id); - /* This particle system evaluation. */ - // TODO: for now, this will just be a placeholder "ubereval" node + /* Build particle settings operations. + * + * NOTE: The call itself ensures settings are only build once. + */ + build_particle_settings(part); + /* Particle system evaluation. */ add_operation_node(psys_comp, NULL, DEG_OPCODE_PARTICLE_SYSTEM_EVAL, @@ -716,29 +908,52 @@ void DepsgraphNodeBuilder::build_particles(Object *object) switch (part->ren_as) { case PART_DRAW_OB: if (part->dup_ob != NULL) { - build_object(NULL, part->dup_ob); + build_object(-1, + part->dup_ob, + DEG_ID_LINKED_INDIRECTLY); } break; case PART_DRAW_GR: if (part->dup_group != NULL) { - build_group(NULL, part->dup_group); + build_group(part->dup_group); } break; } } - /* pointcache */ - // TODO... + /* TODO(sergey): Do we need a point cache operations here? */ + add_operation_node(&object->id, + DEG_NODE_TYPE_CACHE, + function_bind(BKE_ptcache_object_reset, + scene_cow, + ob_cow, + PTCACHE_RESET_DEPSGRAPH), + DEG_OPCODE_POINT_CACHE_RESET); +} + +void DepsgraphNodeBuilder::build_particle_settings(ParticleSettings *part) { + if (built_map_.checkIsBuiltAndTag(part)) { + return; + } + /* Animation data. */ + build_animdata(&part->id); + /* Parameters change. */ + add_operation_node(&part->id, + DEG_NODE_TYPE_PARAMETERS, + NULL, + DEG_OPCODE_PARTICLE_SETTINGS_EVAL); } 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); } @@ -756,8 +971,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); /* Temporary uber-update node, which does everything. * It is for the being we're porting old dependencies into the new system. @@ -770,8 +986,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(); @@ -792,10 +1008,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); + } } } @@ -804,9 +1031,13 @@ void DepsgraphNodeBuilder::build_obdata_geom(Object *object) // add geometry collider relations } + ID *obdata = (ID *)object->data; if (built_map_.checkIsBuiltAndTag(obdata)) { return; } + /* 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); @@ -829,7 +1060,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(); @@ -846,9 +1077,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"); } @@ -873,23 +1105,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(-1, cu->bevobj, DEG_ID_LINKED_INDIRECTLY); } if (cu->taperobj != NULL) { - build_object(NULL, cu->taperobj); + build_object(-1, cu->taperobj, DEG_ID_LINKED_INDIRECTLY); } if (object->type == OB_FONT && cu->textoncurve != NULL) { - build_object(NULL, cu->textoncurve); + build_object(-1, cu->textoncurve, DEG_ID_LINKED_INDIRECTLY); } break; } @@ -901,7 +1132,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(); @@ -918,11 +1149,20 @@ 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) { + /* Object data. */ /* TODO: Link scene-camera links in somehow... */ Camera *camera = (Camera *)object->data; if (built_map_.checkIsBuiltAndTag(camera)) { @@ -933,30 +1173,23 @@ void DepsgraphNodeBuilder::build_camera(Object *object) DEG_NODE_TYPE_PARAMETERS, NULL, DEG_OPCODE_PARAMETERS_EVAL); - if (camera->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 data. */ Lamp *lamp = (Lamp *)object->data; if (built_map_.checkIsBuiltAndTag(lamp)) { return; } build_animdata(&lamp->id); - /* TODO(sergey): Is it really how we're supposed to work with drivers? */ add_operation_node(&lamp->id, DEG_NODE_TYPE_PARAMETERS, NULL, DEG_OPCODE_PARAMETERS_EVAL); /* lamp's nodetree */ build_nodetree(lamp->nodetree); - /* textures */ - build_texture_stack(lamp->mtex); } void DepsgraphNodeBuilder::build_nodetree(bNodeTree *ntree) @@ -967,16 +1200,26 @@ void DepsgraphNodeBuilder::build_nodetree(bNodeTree *ntree) if (built_map_.checkIsBuiltAndTag(ntree)) { return; } - /* nodetree itself */ - 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... */ LISTBASE_FOREACH (bNode *, bnode, &ntree->nodes) { ID *id = bnode->id; @@ -994,7 +1237,7 @@ void DepsgraphNodeBuilder::build_nodetree(bNodeTree *ntree) build_image((Image *)id); } else if (id_type == ID_OB) { - build_object(NULL, (Object *)id); + build_object(-1, (Object *)id, DEG_ID_LINKED_INDIRECTLY); } else if (id_type == ID_SCE) { /* Scenes are used by compositor trees, and handled by render @@ -1022,30 +1265,22 @@ void DepsgraphNodeBuilder::build_material(Material *material) if (built_map_.checkIsBuiltAndTag(material)) { return; } - add_operation_node(&material->id, DEG_NODE_TYPE_SHADING, NULL, - DEG_OPCODE_PLACEHOLDER, "Material Update"); - - /* material animation */ + /* 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 */ + /* 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++) { - MTex *mtex = texture_stack[i]; - if (mtex && mtex->tex) - build_texture(mtex->tex); - } -} - /* Recursively build graph for texture */ void DepsgraphNodeBuilder::build_texture(Tex *texture) { @@ -1088,7 +1323,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); } @@ -1120,31 +1357,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; + if (built_map_.checkIsBuiltAndTag(probe)) { + return; + } + /* 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); +} + /* **** ID traversal callbacks functions **** */ void DepsgraphNodeBuilder::modifier_walk(void *user_data, @@ -1159,7 +1420,9 @@ void DepsgraphNodeBuilder::modifier_walk(void *user_data, } switch (GS(id->name)) { case ID_OB: - data->builder->build_object(NULL, (Object *)id); + data->builder->build_object(-1, + (Object *)id, + DEG_ID_LINKED_INDIRECTLY); break; case ID_TE: data->builder->build_texture((Tex *)id); @@ -1182,7 +1445,9 @@ void DepsgraphNodeBuilder::constraint_walk(bConstraint * /*con*/, } switch (GS(id->name)) { case ID_OB: - data->builder->build_object(NULL, (Object *)id); + data->builder->build_object(-1, + (Object *)id, + DEG_ID_LINKED_INDIRECTLY); break; default: /* pass */ @@ -1190,5 +1455,4 @@ void DepsgraphNodeBuilder::constraint_walk(bConstraint * /*con*/, } } - } // 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 f2154e58703..0ed3f5e334f 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.h +++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.h @@ -33,6 +33,8 @@ #include "intern/builder/deg_builder_map.h" #include "intern/depsgraph_types.h" +#include "DEG_depsgraph.h" /* used for DEG_depsgraph_use_copy_on_write() */ + struct Base; struct CacheFile; struct bGPdata; @@ -43,6 +45,7 @@ struct Image; struct FCurve; struct Group; struct Key; +struct LayerCollection; struct Main; struct Material; struct Mask; @@ -50,6 +53,8 @@ struct MTex; struct MovieClip; struct bNodeTree; struct Object; +struct ParticleSettings; +struct Probe; struct bPoseChannel; struct bConstraint; struct Scene; @@ -71,9 +76,35 @@ 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); + } + + /* 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.orig_id; + } + else { + return (T *)cow; + } + } + void begin_build(); + void end_build(); IDDepsNode *add_id_node(ID *id); + IDDepsNode *find_id_node(ID *id); TimeSourceDepsNode *add_time_source(); ComponentDepsNode *add_component_node(ID *id, @@ -127,15 +158,23 @@ struct DepsgraphNodeBuilder { int name_tag = -1); void build_id(ID* id); - 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(int base_index, + Object *object, + eDepsNode_LinkedState_Type linked_state); + void build_object_flags(int base_index, + 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, int pchan_index); 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); void build_driver(ID *id, FCurve *fcurve); @@ -156,7 +195,6 @@ struct DepsgraphNodeBuilder { void build_nodetree(bNodeTree *ntree); void build_material(Material *ma); void build_texture(Tex *tex); - void build_texture_stack(MTex **texture_stack); void build_image(Image *image); void build_world(World *world); void build_compositor(Scene *scene); @@ -164,8 +202,16 @@ struct DepsgraphNodeBuilder { void build_cachefile(CacheFile *cache_file); void build_mask(Mask *mask); void build_movieclip(MovieClip *clip); + void build_lightprobe(Object *object); protected: + struct SavedEntryTag { + ID *id; + eDepsNode_Type component_type; + eDepsOperation_Code opcode; + }; + vector<SavedEntryTag> saved_entry_tags_; + struct BuilderWalkUserData { DepsgraphNodeBuilder *builder; }; @@ -186,7 +232,10 @@ protected: /* State which demotes currently built entities. */ Scene *scene_; + ViewLayer *view_layer_; + int view_layer_index_; + GHash *cow_id_hash_; BuilderMap built_map_; }; 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 9cfe83e0087..208462713a5 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" @@ -56,6 +57,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" @@ -77,8 +79,8 @@ void DepsgraphNodeBuilder::build_pose_constraints(Object *object, add_operation_node(&object->id, DEG_NODE_TYPE_BONE, pchan->name, function_bind(BKE_pose_constraints_evaluate, _1, - scene_, - object, + get_cow_datablock(scene_), + get_cow_datablock(object), pchan_index), DEG_OPCODE_BONE_CONSTRAINTS); } @@ -108,8 +110,8 @@ void DepsgraphNodeBuilder::build_ik_pose(Object *object, add_operation_node(&object->id, DEG_NODE_TYPE_EVAL_POSE, rootchan->name, function_bind(BKE_pose_iktree_evaluate, _1, - scene_, - object, + get_cow_datablock(scene_), + get_cow_datablock(object), rootchan_index), DEG_OPCODE_POSE_IK_SOLVER); } @@ -125,15 +127,16 @@ void DepsgraphNodeBuilder::build_splineik_pose(Object *object, 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. */ int rootchan_index = BLI_findindex(&object->pose->chanbase, rootchan); BLI_assert(rootchan_index != -1); add_operation_node(&object->id, DEG_NODE_TYPE_EVAL_POSE, rootchan->name, function_bind(BKE_pose_splineik_evaluate, _1, - scene_, - object, + get_cow_datablock(scene_), + get_cow_datablock(object), rootchan_index), DEG_OPCODE_POSE_SPLINE_IK_SOLVER); } @@ -141,19 +144,32 @@ void DepsgraphNodeBuilder::build_splineik_pose(Object *object, /* Pose/Armature Bones Graph */ void DepsgraphNodeBuilder::build_rig(Object *object) { - bArmature *arm = (bArmature *)object->data; + bArmature *armature = (bArmature *)object->data; + Scene *scene_cow; + Object *object_cow; + if (DEG_depsgraph_use_copy_on_write()) { + scene_cow = get_cow_datablock(scene_); + object_cow = get_cow_datablock(object); + } + else { + scene_cow = scene_; + object_cow = object; + } 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 (!built_map_.checkIsBuilt(arm)) { - build_animdata(&arm->id); + if (!built_map_.checkIsBuilt(armature)) { + 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, @@ -162,7 +178,7 @@ 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); + BKE_pose_rebuild(object, armature); /* XXX: Without this animation gets lost in certain circumstances * after loading file. Need to investigate further since it does * not happen with simple scenes.. @@ -173,7 +189,7 @@ void DepsgraphNodeBuilder::build_rig(Object *object) } /* speed optimization for animation lookups */ - if (object->pose) { + if (object->pose != NULL) { BKE_pose_channels_hash_make(object->pose); if (object->pose->flag & POSE_CONSTRAINTS_NEED_UPDATE_FLAGS) { BKE_pose_update_constraint_flags(object->pose); @@ -197,8 +213,8 @@ 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. */ @@ -207,8 +223,8 @@ void DepsgraphNodeBuilder::build_rig(Object *object) DEG_NODE_TYPE_EVAL_POSE, function_bind(BKE_pose_eval_init, _1, - scene_, - object), + scene_cow, + object_cow), DEG_OPCODE_POSE_INIT); op_node->set_as_entry(); @@ -216,16 +232,16 @@ void DepsgraphNodeBuilder::build_rig(Object *object) DEG_NODE_TYPE_EVAL_POSE, function_bind(BKE_pose_eval_init_ik, _1, - scene_, - object), + scene_cow, + object_cow), 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), + scene_cow, + object_cow), DEG_OPCODE_POSE_DONE); op_node->set_as_exit(); @@ -238,17 +254,21 @@ void DepsgraphNodeBuilder::build_rig(Object *object) 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_index), + function_bind(BKE_pose_eval_bone, _1, + scene_cow, + object_cow, + pchan_index), 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, function_bind(BKE_pose_bone_done, _1, - object, + object_cow, pchan_index), DEG_OPCODE_BONE_DONE); op_node->set_as_exit(); @@ -260,7 +280,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, pchan_index); } @@ -273,8 +293,8 @@ void DepsgraphNodeBuilder::build_rig(Object *object) * * 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... + * as in ik-tree building + * - Animated chain-lengths are a problem. */ LISTBASE_FOREACH (bConstraint *, con, &pchan->constraints) { switch (con->type) { @@ -293,7 +313,7 @@ void DepsgraphNodeBuilder::build_rig(Object *object) /* Custom shape. */ if (pchan->custom != NULL) { - build_object(NULL, pchan->custom); + build_object(-1, pchan->custom, DEG_ID_LINKED_INDIRECTLY); } pchan_index++; @@ -304,34 +324,49 @@ 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()) { + object_cow = get_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); } - 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(); - LISTBASE_FOREACH (bPoseChannel *, pchan, &object->pose->chanbase) { - op_node = add_operation_node(&object->id, DEG_NODE_TYPE_BONE, pchan->name, - NULL, DEG_OPCODE_BONE_LOCAL); + 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. */ @@ -343,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 4bb15350f3a..750b0054ee5 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,43 @@ 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) { + view_layer_index_ = BLI_findindex(&scene->view_layers, view_layer); + BLI_assert(view_layer_index_ != -1); /* 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 */ - LISTBASE_FOREACH (Base *, base, &scene->base) { - Object *object = base->object; - build_object(base, object); + view_layer_ = view_layer; + /* Get pointer to a CoW version of scene ID. */ + Scene *scene_cow; + if (DEG_depsgraph_use_copy_on_write()) { + scene_cow = get_cow_datablock(scene); + } + else { + scene_cow = scene; + } + /* 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. + */ + int base_index = 0; + LISTBASE_FOREACH(Base *, base, &view_layer->object_bases) { + /* object itself */ + build_object(base_index, base->object, linked_state); + base->object->select_color = select_color++; + ++base_index; + } + if (scene->camera != NULL) { + build_object(-1, scene->camera, DEG_ID_LINKED_INDIRECTLY); } /* Rigidbody. */ if (scene->rigidbody_world != NULL) { @@ -94,7 +115,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 +135,25 @@ void DepsgraphNodeBuilder::build_scene(Scene *scene) LISTBASE_FOREACH (MovieClip *, clip, &bmain_->movieclip) { build_movieclip(clip); } + /* Collections. */ + add_operation_node(&scene->id, + DEG_NODE_TYPE_LAYER_COLLECTIONS, + function_bind(BKE_layer_eval_view_layer_indexed, + _1, + &scene_cow->id, + view_layer_index_), + DEG_OPCODE_VIEW_LAYER_EVAL); /* 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 066c9868876..f1869fdd916 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" @@ -284,19 +285,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]; @@ -320,9 +318,9 @@ 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) { - LISTBASE_FOREACH(EffectorCache *, eff, effectors) { + LISTBASE_FOREACH (EffectorCache *, eff, effectors) { if (eff->ob != object) { ComponentKey eff_key(&eff->ob->id, DEG_NODE_TYPE_TRANSFORM); add_relation(eff_key, key, name); @@ -360,7 +358,6 @@ void DepsgraphRelationBuilder::add_forcefield_relations( scene, object, NULL, - eff->ob->lay, true, "Force Absorption"); } @@ -387,20 +384,25 @@ void DepsgraphRelationBuilder::build_group(Object *object, Group *group) OperationKey object_local_transform_key(object != NULL ? &object->id : NULL, DEG_NODE_TYPE_TRANSFORM, DEG_OPCODE_TRANSFORM_LOCAL); - LISTBASE_FOREACH (GroupObject *, go, &group->gobject) { - if (!group_done) { - build_object(go->ob); + if (!group_done) { + LISTBASE_FOREACH (Base *, base, &group->view_layer->object_bases) { + build_object(NULL, base->object); } - if (object != NULL) { - ComponentKey dupli_transform_key(&go->ob->id, DEG_NODE_TYPE_TRANSFORM); + } + if (object != NULL) { + LISTBASE_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"); } } } -void DepsgraphRelationBuilder::build_object(Object *object) +void DepsgraphRelationBuilder::build_object(Base *base, Object *object) { if (built_map_.checkIsBuiltAndTag(object)) { + if (base != NULL) { + build_object_flags(base, object); + } return; } /* Object Transforms */ @@ -419,10 +421,12 @@ 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) { /* Make sure parent object's relations are built. */ - build_object(object->parent); + build_object(NULL, object->parent); /* Parent relationship. */ build_object_parent(object); /* Local -> parent. */ @@ -488,7 +492,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. */ @@ -502,6 +506,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_EVAL); + 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) { @@ -536,6 +554,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) { @@ -933,6 +954,18 @@ void DepsgraphRelationBuilder::build_animdata_curves_targets( graph_->add_new_relation(operation_from, operation_to, "Animation -> Prop", true); + /* It is possible that animation is writing to a nested ID datablock, + * need to make sure animation is evaluated after target ID is copied. + */ + if (DEG_depsgraph_use_copy_on_write()) { + const IDDepsNode *id_node_from = operation_from->owner->owner; + const IDDepsNode *id_node_to = operation_to->owner->owner; + if (id_node_from != id_node_to) { + ComponentKey cow_key(id_node_to->id_orig, + DEG_NODE_TYPE_COPY_ON_WRITE); + add_relation(cow_key, adt_key, "Target CoW -> Animation", true); + } + } } } @@ -971,7 +1004,6 @@ void DepsgraphRelationBuilder::build_animdata_drivers(ID *id) /* create the driver's relations to targets */ build_driver(id, fcu); - /* Special case for array drivers: we can not multithread them because * of the way how they work internally: animation system will write the * whole array back to RNA even when changing individual array value. @@ -1066,18 +1098,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, @@ -1101,6 +1133,26 @@ void DepsgraphRelationBuilder::build_driver_data(ID *id, FCurve *fcu) } else { RNAPathKey target_key(id, rna_path); + add_relation(driver_key, target_key, "Driver -> Target"); + /* Similar to the case with f-curves, driver might drive a nested + * datablock, which means driver execution should wait for that + * datablock to be copied. + */ + if (DEG_depsgraph_use_copy_on_write()) { + PointerRNA id_ptr; + PointerRNA ptr; + RNA_id_pointer_create(id, &id_ptr); + if (RNA_path_resolve_full(&id_ptr, fcu->rna_path, &ptr, NULL, NULL)) { + if (id_ptr.id.data != ptr.id.data) { + ComponentKey cow_key((ID *)ptr.id.data, + DEG_NODE_TYPE_COPY_ON_WRITE); + add_relation(cow_key, + driver_key, + "Target CoW -> Driver", + true); + } + } + } if (RNA_pointer_is_null(&target_key.ptr)) { /* TODO(sergey): This would only mean that driver is broken. * so we can't create relation anyway. However, we need to avoid @@ -1215,14 +1267,12 @@ void DepsgraphRelationBuilder::build_world(World *world) } build_animdata(&world->id); /* TODO: other settings? */ - /* textures */ - build_texture_stack(world->mtex); /* 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"); } } @@ -1244,8 +1294,8 @@ void DepsgraphRelationBuilder::build_rigidbody(Scene *scene) /* objects - simulation participants */ if (rbw->group) { - LISTBASE_FOREACH (GroupObject *, go, &rbw->group->gobject) { - Object *object = go->ob; + LISTBASE_FOREACH (Base *, base, &rbw->group->view_layer->object_bases) { + Object *object = base->object; if (object == NULL || object->type != OB_MESH) { continue; } @@ -1298,8 +1348,8 @@ void DepsgraphRelationBuilder::build_rigidbody(Scene *scene) /* constraints */ if (rbw->constraints) { - LISTBASE_FOREACH (GroupObject *, go, &rbw->constraints->gobject) { - Object *object = go->ob; + LISTBASE_FOREACH (Base *, base, &rbw->constraints->view_layer->object_bases) { + Object *object = base->object; if (object == NULL || !object->rigidbody_constraint) { continue; } @@ -1336,13 +1386,24 @@ void DepsgraphRelationBuilder::build_particles(Object *object) /* Particle systems. */ LISTBASE_FOREACH (ParticleSystem *, psys, &object->particlesystem) { ParticleSettings *part = psys->part; - /* Animation of particle settings, */ - build_animdata(&part->id); + + /* 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); + add_relation(particle_settings_key, eval_init_key, "Particle Settings Change"); add_relation(eval_init_key, psys_key, "Init -> PSys"); /* TODO(sergey): Currently particle update is just a placeholder, * hook it to the ubereval node so particle system is getting updated @@ -1355,18 +1416,17 @@ void DepsgraphRelationBuilder::build_particles(Object *object) scene_, object, part->collision_group, - object->lay, true, "Particle Collision"); } else if ((psys->flag & PSYS_HAIR_DYNAMICS) && - psys->clmd && psys->clmd->coll_parms) + psys->clmd != NULL && + psys->clmd->coll_parms != NULL) { add_collision_relations(psys_key, scene_, object, psys->clmd->coll_parms->group, - object->lay | scene_->lay, true, "Hair Collision"); } @@ -1378,7 +1438,7 @@ void DepsgraphRelationBuilder::build_particles(Object *object) part->effector_weights, part->type == PART_HAIR, "Particle Field"); - /* Boids. */ + /* Boids .*/ if (part->boids) { LISTBASE_FOREACH (BoidState *, state, &part->boids->states) { LISTBASE_FOREACH (BoidRule *, rule, &state->rules) { @@ -1397,12 +1457,11 @@ void DepsgraphRelationBuilder::build_particles(Object *object) } } } - switch (part->ren_as) { case PART_DRAW_OB: if (part->dup_ob != NULL) { /* Make sure object's relations are all built. */ - build_object(part->dup_ob); + build_object(NULL, part->dup_ob); /* Build relation for the particle visualization. */ build_particles_visualization_object(object, psys, @@ -1431,8 +1490,20 @@ 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... + OperationKey point_cache_reset_key(&object->id, + DEG_NODE_TYPE_CACHE, + DEG_OPCODE_POINT_CACHE_RESET); + add_relation(transform_key, point_cache_reset_key, "Object Transform -> Point Cache Reset"); + add_relation(point_cache_reset_key, obdata_ubereval_key, "Point Cache Reset -> UberEval"); +} + +void DepsgraphRelationBuilder::build_particle_settings(ParticleSettings *part) +{ + if (built_map_.checkIsBuiltAndTag(part)) { + return; + } + /* Animation data relations. */ + build_animdata(&part->id); } void DepsgraphRelationBuilder::build_particles_visualization_object( @@ -1528,11 +1599,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); ModifierUpdateDepsgraphContext ctx = {}; ctx.scene = scene_; ctx.object = object; @@ -1560,6 +1642,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"); + } } } } @@ -1637,18 +1727,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"); } } @@ -1681,11 +1771,17 @@ void DepsgraphRelationBuilder::build_camera(Object *object) if (built_map_.checkIsBuiltAndTag(camera)) { return; } + + 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 (camera->dof_ob) { - ComponentKey ob_param_key(&object->id, DEG_NODE_TYPE_PARAMETERS); + if (camera->dof_ob != NULL) { ComponentKey dof_ob_key(&camera->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"); } } @@ -1696,15 +1792,32 @@ void DepsgraphRelationBuilder::build_lamp(Object *object) if (built_map_.checkIsBuiltAndTag(lamp)) { return; } + + 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 (lamp->nodetree != NULL) { build_nodetree(lamp->nodetree); - ComponentKey parameters_key(&lamp->id, DEG_NODE_TYPE_PARAMETERS); - ComponentKey nodetree_key(&lamp->nodetree->id, DEG_NODE_TYPE_PARAMETERS); - add_relation(nodetree_key, parameters_key, "NTree->Lamp Parameters"); + ComponentKey nodetree_key(&lamp->nodetree->id, DEG_NODE_TYPE_SHADING); + add_relation(nodetree_key, lamp_parameters_key, "NTree->Lamp Parameters"); + } + + 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"); } - /* textures */ - build_texture_stack(lamp->mtex); } void DepsgraphRelationBuilder::build_nodetree(bNodeTree *ntree) @@ -1716,9 +1829,7 @@ void DepsgraphRelationBuilder::build_nodetree(bNodeTree *ntree) return; } 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... */ LISTBASE_FOREACH (bNode *, bnode, &ntree->nodes) { ID *id = bnode->id; @@ -1736,7 +1847,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 @@ -1749,15 +1860,22 @@ void DepsgraphRelationBuilder::build_nodetree(bNodeTree *ntree) else if (bnode->type == NODE_GROUP) { bNodeTree *group_ntree = (bNodeTree *)id; build_nodetree(group_ntree); - 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 */ @@ -1768,18 +1886,15 @@ void DepsgraphRelationBuilder::build_material(Material *material) } /* animation */ build_animdata(&material->id); - /* textures */ - build_texture_stack(material->mtex); /* material's nodetree */ if (material->nodetree != NULL) { build_nodetree(material->nodetree); OperationKey ntree_key(&material->nodetree->id, - DEG_NODE_TYPE_PARAMETERS, - DEG_OPCODE_PARAMETERS_EVAL); + DEG_NODE_TYPE_SHADING, + DEG_OPCODE_MATERIAL_UPDATE); OperationKey material_key(&material->id, DEG_NODE_TYPE_SHADING, - DEG_OPCODE_PLACEHOLDER, - "Material Update"); + DEG_OPCODE_MATERIAL_UPDATE); add_relation(ntree_key, material_key, "Material's NTree"); } } @@ -1796,17 +1911,6 @@ void DepsgraphRelationBuilder::build_texture(Tex *texture) build_nodetree(texture->nodetree); } -/* Texture-stack attached to some shading datablock */ -void DepsgraphRelationBuilder::build_texture_stack(MTex **texture_stack) -{ - /* for now assume that all texture-stacks have same number of max items */ - for (int i = 0; i < MAX_MTEX; i++) { - MTex *mtex = texture_stack[i]; - if (mtex && mtex->tex) - build_texture(mtex->tex); - } -} - void DepsgraphRelationBuilder::build_compositor(Scene *scene) { /* For now, just a plain wrapper? */ @@ -1849,6 +1953,120 @@ void DepsgraphRelationBuilder::build_movieclip(MovieClip *clip) build_animdata(&clip->id); } +void DepsgraphRelationBuilder::build_lightprobe(Object *object) +{ + LightProbe *probe = (LightProbe *)object->data; + if (built_map_.checkIsBuiltAndTag(probe)) { + return; + } + 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 == op_entry) { + continue; + } + if (op_node->inlinks.size() == 0) { + graph_->add_new_relation(op_cow, op_node, "CoW Dependency"); + } + else { + bool has_same_comp_dependency = false; + foreach (DepsRelation *rel, op_node->inlinks) { + if (rel->from->type != DEG_NODE_TYPE_OPERATION) { + continue; + } + OperationDepsNode *op_node_from = (OperationDepsNode *)rel->from; + if (op_node_from->owner == op_node->owner) { + has_same_comp_dependency = true; + break; + } + } + if (!has_same_comp_dependency) { + 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); + } + } +} + /* **** ID traversal callbacks functions **** */ void DepsgraphRelationBuilder::modifier_walk(void *user_data, @@ -1863,7 +2081,7 @@ void DepsgraphRelationBuilder::modifier_walk(void *user_data, } switch (GS(id->name)) { case ID_OB: - data->builder->build_object((Object *)id); + data->builder->build_object(NULL, (Object *)id); break; case ID_TE: data->builder->build_texture((Tex *)id); @@ -1883,7 +2101,7 @@ void DepsgraphRelationBuilder::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); } } } diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.h b/source/blender/depsgraph/intern/builder/deg_builder_relations.h index 4a8e91f18db..7469540521b 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations.h +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.h @@ -57,6 +57,7 @@ struct ID; struct FCurve; struct Group; struct Key; +struct LayerCollection; struct Main; struct Mask; struct Material; @@ -68,7 +69,9 @@ struct Object; struct bPoseChannel; struct bConstraint; struct ParticleSystem; +struct ParticleSettings; struct Scene; +struct ViewLayer; struct Tex; struct World; struct EffectorWeights; @@ -191,9 +194,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, @@ -218,6 +222,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_particles_visualization_object(Object *object, ParticleSystem *psys, Object *draw_object); @@ -239,18 +244,17 @@ struct DepsgraphRelationBuilder void build_nodetree(bNodeTree *ntree); void build_material(Material *ma); void build_texture(Tex *tex); - void build_texture_stack(MTex **texture_stack); void build_compositor(Scene *scene); void build_gpencil(bGPdata *gpd); 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, @@ -258,8 +262,10 @@ struct DepsgraphRelationBuilder Object *object, ParticleSystem *psys, EffectorWeights *eff, - bool add_absorption, - const char *name); + bool add_absorption, const char *name); + + 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_impl.h b/source/blender/depsgraph/intern/builder/deg_builder_relations_impl.h index 570227601db..894d4172c00 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations_impl.h +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations_impl.h @@ -184,7 +184,7 @@ bool DepsgraphRelationBuilder::is_same_nodetree_node_dependency( return false; } /* Check if this is actually a node tree. */ - if (GS(op_from->owner->owner->id->name) != ID_NT) { + if (GS(op_from->owner->owner->id_orig->name) != ID_NT) { return false; } /* Different node trees. */ @@ -217,7 +217,7 @@ bool DepsgraphRelationBuilder::is_same_shapekey_dependency( return false; } /* Check if this is actually a shape key datablock. */ - if (GS(op_from->owner->owner->id->name) != ID_KE) { + if (GS(op_from->owner->owner->id_orig->name) != ID_KE) { return false; } /* Different key data blocks. */ 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 2eee1671795..c80e0d568f3 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations_rig.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations_rig.cc @@ -385,16 +385,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; @@ -409,8 +405,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) { /* Build relations for indirectly linked objects. */ BuilderWalkUserData data; @@ -445,7 +440,7 @@ void DepsgraphRelationBuilder::build_rig(Object *object) /* Custom shape. */ if (pchan->custom != NULL) { - build_object(pchan->custom); + 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 c51addbd206..c97fa3b3a5a 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. */ - LISTBASE_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. + */ + LISTBASE_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,20 @@ void DepsgraphRelationBuilder::build_scene(Scene *scene) LISTBASE_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; + /* 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); + } } } // 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 80772f9595f..d64ab79d918 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 @@ -297,12 +300,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, "["); @@ -323,12 +320,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); @@ -390,8 +381,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()) { @@ -406,9 +401,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 34fb20ee2ba..c90279b0e90 100644 --- a/source/blender/depsgraph/intern/depsgraph.cc +++ b/source/blender/depsgraph/intern/depsgraph.cc @@ -48,9 +48,9 @@ extern "C" { #include "DNA_object_types.h" #include "DNA_sequence_types.h" -#include "BKE_depsgraph.h" - #include "RNA_access.h" + +#include "BKE_scene.h" } #include <algorithm> @@ -58,6 +58,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" @@ -67,11 +69,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,10 +94,15 @@ static void remove_from_vector(vector<T> *vector, const T& value) vector->end()); } -Depsgraph::Depsgraph() +Depsgraph::Depsgraph(Scene *scene, + ViewLayer *view_layer, + eEvaluationMode mode) : time_source(NULL), - need_update(false), - layers(0) + need_update(true), + scene(scene), + view_layer(view_layer), + mode(mode), + ctime(BKE_scene_frame_get(scene)) { BLI_spin_init(&lock); id_hash = BLI_ghash_ptr_new("Depsgraph id hash"); @@ -214,6 +232,10 @@ static bool pointer_to_component_node_criteria( *subdata = seq->name; // xxx? return true; } + else if (RNA_struct_is_a(ptr->type, &RNA_NodeSocket)) { + *type = DEG_NODE_TYPE_SHADING; + return true; + } else if (ptr->type == &RNA_Curve) { *id = (ID *)ptr->id.data; *type = DEG_NODE_TYPE_GEOMETRY; @@ -274,12 +296,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) { @@ -299,13 +315,19 @@ 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, 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); - /* register */ + id_node = (IDDepsNode *)factory->create_node(id, "", id->name); + id_node->init_copy_on_write(id_cow_hint); + /* 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); } @@ -314,7 +336,30 @@ IDDepsNode *Depsgraph::add_id_node(ID *id, const char *name) 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(); } @@ -339,7 +384,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; @@ -433,9 +478,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... */ @@ -451,17 +496,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); } } @@ -496,9 +570,14 @@ string deg_color_end(void) /* Public Graph API */ /* Initialize a new Depsgraph */ -Depsgraph *DEG_graph_new() -{ - DEG::Depsgraph *deg_depsgraph = OBJECT_GUARDED_NEW(DEG::Depsgraph); +Depsgraph *DEG_graph_new(Scene *scene, + ViewLayer *view_layer, + eEvaluationMode mode) +{ + DEG::Depsgraph *deg_depsgraph = OBJECT_GUARDED_NEW(DEG::Depsgraph, + scene, + view_layer, + mode); return reinterpret_cast<Depsgraph *>(deg_depsgraph); } @@ -512,19 +591,10 @@ 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); - } } /* Evaluation and debug */ diff --git a/source/blender/depsgraph/intern/depsgraph.h b/source/blender/depsgraph/intern/depsgraph.h index dda4da7bc13..e10f7d5b115 100644 --- a/source/blender/depsgraph/intern/depsgraph.h +++ b/source/blender/depsgraph/intern/depsgraph.h @@ -38,13 +38,18 @@ #include "BLI_threads.h" /* for SpinLock */ +#include "DEG_depsgraph.h" + #include "intern/depsgraph_types.h" struct ID; struct GHash; +struct Main; struct GSet; struct PointerRNA; struct PropertyRNA; +struct Scene; +struct ViewLayer; namespace DEG { @@ -96,14 +101,17 @@ struct Depsgraph { typedef vector<OperationDepsNode *> OperationNodes; typedef vector<IDDepsNode *> IDDepsNodes; - Depsgraph(); + Depsgraph(Scene *scene, + ViewLayer *view_layer, + eEvaluationMode mode); ~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 @@ -114,7 +122,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, ID *id_cow_hint = NULL); void clear_id_nodes(); /* Add new relationship between two nodes. */ @@ -142,6 +150,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 @@ -177,12 +190,13 @@ struct Depsgraph { */ SpinLock lock; - /* Layers Visibility .................. */ - - /* Visible layers bitfield, used for skipping invisible objects updates. */ - unsigned int layers; + /* Scene, layer, mode this dependency graph is built for. */ + Scene *scene; + ViewLayer *view_layer; + eEvaluationMode mode; - // XXX: additional stuff like eval contexts, mempools for allocating nodes from, etc. + /* Time at which dependency graph is being or was last evaluated. */ + float ctime; }; } // namespace DEG diff --git a/source/blender/depsgraph/intern/depsgraph_build.cc b/source/blender/depsgraph/intern/depsgraph_build.cc index 1b270e71d64..ecd3d5361f8 100644 --- a/source/blender/depsgraph/intern/depsgraph_build.cc +++ b/source/blender/depsgraph/intern/depsgraph_build.cc @@ -34,6 +34,7 @@ #include "BLI_utildefines.h" #include "BLI_ghash.h" +#include "BLI_listbase.h" #include "PIL_time.h" #include "PIL_time_utildefines.h" @@ -48,6 +49,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" @@ -188,13 +190,13 @@ 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) { double start_time; if (G.debug & G_DEBUG_DEPSGRAPH_BUILD) { @@ -202,18 +204,36 @@ void DEG_graph_build_from_scene(Depsgraph *graph, Main *bmain, Scene *scene) } DEG::Depsgraph *deg_graph = reinterpret_cast<DEG::Depsgraph *>(graph); + BLI_assert(BLI_findindex(&scene->view_layers, view_layer) != -1); + + BLI_assert(deg_graph->scene == scene); + BLI_assert(deg_graph->view_layer == view_layer); + + /* 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->id_nodes.size() == 0); /* 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); @@ -228,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)) { @@ -237,6 +257,13 @@ void DEG_graph_build_from_scene(Depsgraph *graph, Main *bmain, Scene *scene) } #endif + /* Relations are up to date. */ + deg_graph->need_update = false; + + if (need_on_visible_update) { + DEG_graph_on_visible_update(bmain, graph); + } + if (G.debug & G_DEBUG_DEPSGRAPH_BUILD) { printf("Depsgraph built in %f seconds.\n", PIL_check_seconds_timer() - start_time); @@ -248,66 +275,49 @@ void DEG_graph_tag_relations_update(Depsgraph *graph) { DEG::Depsgraph *deg_graph = reinterpret_cast<DEG::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) + /* NOTE: When relations are updated, it's quite possible that + * we've got new bases in the scene. This means, we need to + * re-create flat array of bases in view layer. + * + * TODO(sergey): Try to make it so we don't flush updates + * to the whole depsgraph. + */ { - if (scene->depsgraph != NULL) { - DEG_graph_tag_relations_update(scene->depsgraph); + DEG::IDDepsNode *id_node = deg_graph->find_id_node(°_graph->scene->id); + if (id_node != NULL) { + id_node->tag_update(deg_graph); } } } -/* 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(TAG, "%s: Tagging relations for update.\n", __func__); + LISTBASE_FOREACH (Scene *, scene, &bmain->scene) { + LISTBASE_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); + } + } } } @@ -315,14 +325,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]; @@ -345,7 +354,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 == NULL) { return; } @@ -374,7 +383,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..78b1c6f88c5 100644 --- a/source/blender/depsgraph/intern/depsgraph_debug.cc +++ b/source/blender/depsgraph/intern/depsgraph_debug.cc @@ -37,9 +37,12 @@ extern "C" { #include "DNA_scene_types.h" } /* extern "C" */ +#include "DNA_object_types.h" + #include "DEG_depsgraph.h" #include "DEG_depsgraph_debug.h" #include "DEG_depsgraph_build.h" +#include "DEG_depsgraph_query.h" #include "intern/depsgraph_intern.h" #include "intern/nodes/deg_node_id.h" @@ -67,18 +70,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(scene, view_layer, DEG_get_mode(graph)); 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 a6bdcdca19d..edb6600f3e0 100644 --- a/source/blender/depsgraph/intern/depsgraph_eval.cc +++ b/source/blender/depsgraph/intern/depsgraph_eval.cc @@ -32,17 +32,19 @@ #include "MEM_guardedalloc.h" +#include "BLI_listbase.h" #include "BLI_utildefines.h" #include "BLI_ghash.h" extern "C" { -#include "DNA_scene_types.h" - -#include "BKE_depsgraph.h" #include "BKE_scene.h" + +#include "DNA_object_types.h" +#include "DNA_scene_types.h" } /* extern "C" */ #include "DEG_depsgraph.h" +#include "DEG_depsgraph_query.h" #include "intern/eval/deg_eval.h" #include "intern/eval/deg_eval_flush.h" @@ -53,103 +55,31 @@ 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 *eval_ctx = - (EvaluationContext *)MEM_callocN(sizeof(EvaluationContext), - "EvaluationContext"); - eval_ctx->mode = mode; - return eval_ctx; -} - -/** - * Initialize evaluation context. - * 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) -{ - eval_ctx->mode = mode; -} - -/* Free evaluation context. */ -void DEG_evaluation_context_free(EvaluationContext *eval_ctx) -{ - MEM_freeN(eval_ctx); -} - /* Evaluate all nodes tagged for updating. */ -void DEG_evaluate_on_refresh(EvaluationContext *eval_ctx, - Depsgraph *graph, - Scene *scene) +void DEG_evaluate_on_refresh(Depsgraph *graph) { DEG::Depsgraph *deg_graph = reinterpret_cast<DEG::Depsgraph *>(graph); + deg_graph->ctime = BKE_scene_frame_get(deg_graph->scene); /* 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 = deg_graph->ctime; + DEG::deg_evaluate_on_refresh(deg_graph); } /* Frame-change happened for root scene that graph belongs to. */ -void DEG_evaluate_on_framechange(EvaluationContext *eval_ctx, - Main *bmain, +void DEG_evaluate_on_framechange(Main *bmain, Depsgraph *graph, - float ctime, - const unsigned int layers) + float ctime) { DEG::Depsgraph *deg_graph = reinterpret_cast<DEG::Depsgraph *>(graph); + deg_graph->ctime = ctime; /* Update time on primary timesource. */ DEG::TimeSourceDepsNode *tsrc = deg_graph->find_time_source(); tsrc->cfra = ctime; 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(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 89432e17f87..9961723ed17 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,9 +106,11 @@ 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(type, ...) \ do { \ @@ -116,6 +119,12 @@ void deg_editors_scene_update(struct Main *bmain, struct Scene *scene, bool upda } \ } while (0) +#define DEG_ERROR_PRINTF(...) \ + do { \ + fprintf(stderr, __VA_ARGS__); \ + fflush(stderr); \ + } while (0) + bool deg_terminal_do_color(void); string deg_color_for_pointer(const void *pointer); string deg_color_end(void); diff --git a/source/blender/depsgraph/intern/depsgraph_query.cc b/source/blender/depsgraph/intern/depsgraph_query.cc index 9b1961baa48..63eb1f18281 100644 --- a/source/blender/depsgraph/intern/depsgraph_query.cc +++ b/source/blender/depsgraph/intern/depsgraph_query.cc @@ -33,21 +33,53 @@ #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/eval/deg_eval_copy_on_write.h" #include "intern/depsgraph_intern.h" #include "intern/nodes/deg_node_id.h" -bool DEG_id_type_tagged(Main *bmain, short idtype) +struct Scene *DEG_get_input_scene(const Depsgraph *graph) +{ + const DEG::Depsgraph *deg_graph = reinterpret_cast<const DEG::Depsgraph *>(graph); + return deg_graph->scene; +} + +struct ViewLayer *DEG_get_input_view_layer(const Depsgraph *graph) +{ + const DEG::Depsgraph *deg_graph = reinterpret_cast<const DEG::Depsgraph *>(graph); + return deg_graph->view_layer; +} + +eEvaluationMode DEG_get_mode(const Depsgraph *graph) +{ + const DEG::Depsgraph *deg_graph = reinterpret_cast<const DEG::Depsgraph *>(graph); + return deg_graph->mode; +} + +float DEG_get_ctime(const Depsgraph *graph) { - return bmain->id_tag_update[BKE_idcode_to_index(idtype)] != 0; + const DEG::Depsgraph *deg_graph = reinterpret_cast<const DEG::Depsgraph *>(graph); + return deg_graph->ctime; } -short DEG_get_eval_flags_for_id(Depsgraph *graph, ID *id) + +bool DEG_id_type_tagged(Main *bmain, short id_type) +{ + return bmain->id_tag_update[BKE_idcode_to_index(id_type)] != 0; +} + +short DEG_get_eval_flags_for_id(const Depsgraph *graph, ID *id) { if (graph == NULL) { /* Happens when converting objects to mesh from a python script @@ -59,9 +91,8 @@ short DEG_get_eval_flags_for_id(Depsgraph *graph, ID *id) return 0; } - DEG::Depsgraph *deg_graph = reinterpret_cast<DEG::Depsgraph *>(graph); - - DEG::IDDepsNode *id_node = deg_graph->find_id_node(id); + const DEG::Depsgraph *deg_graph = reinterpret_cast<const DEG::Depsgraph *>(graph); + const DEG::IDDepsNode *id_node = deg_graph->find_id_node(id); if (id_node == NULL) { /* TODO(sergey): Does it mean we need to check set scene? */ return 0; @@ -69,3 +100,85 @@ short DEG_get_eval_flags_for_id(Depsgraph *graph, ID *id) return id_node->eval_flags; } + +Scene *DEG_get_evaluated_scene(const Depsgraph *graph) +{ + const DEG::Depsgraph *deg_graph = + reinterpret_cast<const DEG::Depsgraph *>(graph); + Scene *scene_orig = deg_graph->scene; + Scene *scene_cow = + reinterpret_cast<Scene *>(deg_graph->get_cow_id(&scene_orig->id)); + /* TODO(sergey): Shall we expand datablock here? Or is it OK to assume + * that calleer is OK with just a pointer in case scene is not up[dated + * yet? + */ + return scene_cow; +} + +ViewLayer *DEG_get_evaluated_view_layer(const Depsgraph *graph) +{ + const DEG::Depsgraph *deg_graph = + reinterpret_cast<const DEG::Depsgraph *>(graph); + Scene *scene_cow = DEG_get_evaluated_scene(graph); + /* We update copy-on-write scene in the following cases: + * - It was not expanded yet. + * - It was tagged for update of CoW component. + * This allows us to have proper view layer pointer. + */ + if (DEG_depsgraph_use_copy_on_write() && + (!DEG::deg_copy_on_write_is_expanded(&scene_cow->id) || + scene_cow->id.recalc & ID_RECALC_COPY_ON_WRITE)) + { + const DEG::IDDepsNode *id_node = + deg_graph->find_id_node(°_graph->scene->id); + DEG::deg_update_copy_on_write_datablock(deg_graph, id_node); + } + /* Do name-based lookup. */ + /* TODO(sergey): Can this be optimized? */ + 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)); + BLI_assert(view_layer_cow != NULL); + return view_layer_cow; +} + +Object *DEG_get_evaluated_object(const Depsgraph *depsgraph, Object *object) +{ + return (Object *)DEG_get_evaluated_id(depsgraph, &object->id); +} + +ID *DEG_get_evaluated_id(const Depsgraph *depsgraph, ID *id) +{ + if (id == NULL) { + return NULL; + } + /* 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. + */ + const DEG::Depsgraph *deg_graph = (const DEG::Depsgraph *)depsgraph; + const DEG::IDDepsNode *id_node = deg_graph->find_id_node(id); + if (id_node == NULL) { + return id; + } + return id_node->id_cow; +} + +Object *DEG_get_original_object(Object *object) +{ + return (Object *)DEG_get_original_id(&object->id); +} + +ID *DEG_get_original_id(ID *id) +{ + if (id == NULL) { + return NULL; + } + if (id->orig_id == NULL) { + return id; + } + BLI_assert((id->tag & LIB_TAG_COPY_ON_WRITE) != 0); + return (ID *)id->orig_id; +} 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..c610e7fc500 --- /dev/null +++ b/source/blender/depsgraph/intern/depsgraph_query_iter.cc @@ -0,0 +1,280 @@ +/* + * ***** 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 void verify_id_proeprties_freed(DEGObjectIterData *data) +{ + if (data->dupli_object_current == NULL) { + // We didn't enter duplication yet, so we can't have any dangling + // pointers. + return; + } + const Object *dupli_object = data->dupli_object_current->ob; + Object *temp_dupli_object = &data->temp_dupli_object; + if (temp_dupli_object->id.properties == NULL) { + // No ID proeprties in temp datablock -- no leak is possible. + return; + } + if (temp_dupli_object->id.properties == dupli_object->id.properties) { + // Temp copy of object did not modify ID properties. + return; + } + // Free memory which is owned by temporary storage which is about to + // get overwritten. + IDP_FreeProperty(temp_dupli_object->id.properties); + MEM_freeN(temp_dupli_object->id.properties); + temp_dupli_object->id.properties = NULL; +} + +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; + } + + verify_id_proeprties_freed(data); + + 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->select_color = dupli_parent->select_color; + temp_dupli_object->base_flag = dupli_parent->base_flag | BASE_FROMDUPLI; + + /* Duplicated elements shouldn't care whether their original collection is visible or not. */ + temp_dupli_object->base_flag |= BASE_VISIBLED; + + if (BKE_object_is_visible(temp_dupli_object, (eObjectVisibilityCheck)data->visibility_check) == false) { + continue; + } + + temp_dupli_object->transflag &= ~OB_DUPLI; + + 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->graph, data->scene, object); + data->dupli_object_next = (DupliObject *)data->dupli_list->first; + if (BKE_object_is_visible(object, (eObjectVisibilityCheck)data->visibility_check) == 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: Calling this forces the scene datablock to be expanded, + * otherwise we get crashes on load with copy-on-write. There may + * be a better solution for this. */ + 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; + data->visibility_check = (data->mode == DEG_ITER_OBJECT_MODE_RENDER) + ? OB_VISIBILITY_CHECK_FOR_RENDER + : OB_VISIBILITY_CHECK_FOR_VIEWPORT; + + 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 { + verify_id_proeprties_freed(data); + 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 948e4fc1f1b..826194c09a3 100644 --- a/source/blender/depsgraph/intern/depsgraph_tag.cc +++ b/source/blender/depsgraph/intern/depsgraph_tag.cc @@ -35,20 +35,26 @@ #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_curve_types.h" +#include "DNA_key_types.h" +#include "DNA_lattice_types.h" +#include "DNA_mesh_types.h" #include "DNA_object_types.h" #include "DNA_particle_types.h" #include "DNA_screen_types.h" #include "DNA_windowmanager_types.h" - #include "BKE_idcode.h" #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 +76,497 @@ 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_LATTICE: + 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_EVAL; + } + 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_EVAL; + } + 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; + } + 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: + if (id_type == ID_PA) { + /* NOTES: + * - For particle settings node we need to use different + * component. Will be nice to get this unified with object, + * but we can survive for now with single exception here. + * Particles needs reconsideration anyway, + */ + *component_type = DEG_NODE_TYPE_PARAMETERS; + } + else { + *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); +} + +void depsgraph_update_editors_tag(Main *bmain, Depsgraph *graph, ID *id) +{ + /* 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.depsgraph = (::Depsgraph *)graph; + update_ctx.scene = graph->scene; + update_ctx.view_layer = graph->view_layer; + deg_editors_id_update(&update_ctx, id); +} + +void depsgraph_tag_component(Depsgraph *graph, + IDDepsNode *id_node, + eDepsNode_Type component_type, + eDepsOperation_Code operation_code) +{ + ComponentDepsNode *component_node = + id_node->find_component(component_type); + if (component_node == NULL) { + return; + } + if (operation_code == DEG_OPCODE_OPERATION) { + component_node->tag_update(graph); } else { - lib_id_recalc_tag(bmain, id); + OperationDepsNode *operation_node = + component_node->find_operation(operation_code); + if (operation_node != NULL) { + operation_node->tag_update(graph); + } + } + /* If component depends on copy-on-write, tag it as well. */ + if (DEG_depsgraph_use_copy_on_write() && component_node->depends_on_cow()) { + ComponentDepsNode *cow_comp = + id_node->find_component(DEG_NODE_TYPE_COPY_ON_WRITE); + cow_comp->tag_update(graph); + id_node->id_orig->recalc |= ID_RECALC_COPY_ON_WRITE; } } -#ifdef DEPSGRAPH_USE_LEGACY_TAGGING -void depsgraph_legacy_handle_update_tag(Main *bmain, ID *id, short flag) +/* This is a tag compatibility with legacy code. + * + * Mainly, old code was tagging object with OB_RECALC_DATA tag to inform + * that object's data datablock changed. Now API expects that ID is given + * explicitly, but not all areas are aware of this yet. + */ +void deg_graph_id_tag_legacy_compat(Main *bmain, + ID *id, + eDepsgraph_Tag tag) { - 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) + if (tag == DEG_TAG_GEOMETRY || tag == 0) { + switch (GS(id->name)) { + case ID_OB: { - 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); - } + Object *object = (Object *)id; + ID *data_id = (ID *)object->data; + if (data_id != NULL) { + DEG_id_tag_update_ex(bmain, data_id, 0); } + break; } + /* TODO(sergey): Shape keys are annoying, maybe we should find a + * way to chain geometry evaluation to them, so we don't need extra + * tagging here. + */ + case ID_ME: + { + Mesh *mesh = (Mesh *)id; + ID *key_id = &mesh->key->id; + if (key_id != NULL) { + DEG_id_tag_update_ex(bmain, key_id, 0); + } + break; + } + case ID_LT: + { + Lattice *lattice = (Lattice *)id; + ID *key_id = &lattice->key->id; + if (key_id != NULL) { + DEG_id_tag_update_ex(bmain, key_id, 0); + } + break; + } + case ID_CU: + { + Curve *curve = (Curve *)id; + ID *key_id = &curve->key->id; + if (key_id != NULL) { + DEG_id_tag_update_ex(bmain, key_id, 0); + } + break; + } + default: + break; } } } -#endif + +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 { + depsgraph_tag_component(graph, id_node, component_type, operation_code); + } + /* TODO(sergey): Get rid of this once all areas are using proper data ID + * for tagging. + */ + deg_graph_id_tag_legacy_compat(bmain, id, tag); + +} + +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 & ~DEG_TAG_PSYS_ALL; + if (id_node != NULL) { + id_node->tag_update(graph); + } + deg_graph_id_tag_legacy_compat(bmain, id, (eDepsgraph_Tag)0); + } + id->recalc |= (flag & PSYS_RECALC); + 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); +} + +void deg_id_tag_update(Main *bmain, ID *id, int flag) +{ + deg_graph_id_tag_update(bmain, NULL, id, flag); + LISTBASE_FOREACH (Scene *, scene, &bmain->scene) { + LISTBASE_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) +{ + /* 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); + if (scene_id_node != NULL) { + scene_id_node->tag_update(graph); + } + else { + BLI_assert(graph->need_update); + } + } +} + +string stringify_append_bit(const string& str, eDepsgraph_Tag tag) +{ + string result = str; + if (!result.empty()) { + result += ", "; + } + result += DEG_update_tag_as_string(tag); + return result; +} + +string stringify_update_bitfield(int flag) +{ + if (flag == 0) { + return "LEGACY_0"; + } + string result = ""; + int current_flag = flag; + /* Special cases to avoid ALL flags form being split into + * individual bits. + */ + if ((current_flag & DEG_TAG_PSYS_ALL) == DEG_TAG_PSYS_ALL) { + result = stringify_append_bit(result, DEG_TAG_PSYS_ALL); + } + /* Handle all the rest of the flags. */ + while (current_flag != 0) { + eDepsgraph_Tag tag = + (eDepsgraph_Tag)(1 << bitscan_forward_clear_i(¤t_flag)); + result = stringify_append_bit(result, tag); + } + return result; +} } /* namespace */ -/* 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) +} // namespace DEG + +const char *DEG_update_tag_as_string(eDepsgraph_Tag flag) { - 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); + switch (flag) { + case DEG_TAG_TRANSFORM: return "TRANSFORM"; + case DEG_TAG_GEOMETRY: return "GEOMETRY"; + case DEG_TAG_TIME: return "TIME"; + case DEG_TAG_PSYS_REDO: return "PSYS_REDO"; + case DEG_TAG_PSYS_RESET: return "PSYS_RESET"; + case DEG_TAG_PSYS_TYPE: return "PSYS_TYPE"; + case DEG_TAG_PSYS_CHILD: return "PSYS_CHILD"; + case DEG_TAG_PSYS_PHYS: return "PSYS_PHYS"; + case DEG_TAG_PSYS_ALL: return "PSYS_ALL"; + case DEG_TAG_COPY_ON_WRITE: return "COPY_ON_WRITE"; + case DEG_TAG_SHADING_UPDATE: return "SHADING_UPDATE"; + case DEG_TAG_SELECT_UPDATE: return "SELECT_UPDATE"; + case DEG_TAG_BASE_FLAGS_UPDATE: return "BASE_FLAGS_UPDATE"; + case DEG_TAG_EDITORS_UPDATE: return "EDITORS_UPDATE"; } + BLI_assert(!"Unhandled update flag, should never happen!"); + return "UNKNOWN"; } +/* 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(TAG, "%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); - } - } + if (G.debug & G_DEBUG_DEPSGRAPH_TAG) { + printf("%s: id=%s flags=%s\n", + __func__, + id->name, + DEG::stringify_update_bitfield(flag).c_str()); } + 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 +577,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. */ - LISTBASE_FOREACH (Base *, base, &scene->base) { - Object *object = base->object; - DEG::IDDepsNode *id_node = graph->find_id_node(&object->id); - id_node->layers = 0; - } - LISTBASE_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 || object->type == OB_CAMERA) { - /* Camera should always be updated, it used directly by viewport. */ - id_node->layers |= (unsigned int)(-1); - } - } - DEG::deg_graph_build_flush_layers(graph); - LISTBASE_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); + LISTBASE_FOREACH (Scene *, scene, &bmain->scene) { + LISTBASE_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 +613,11 @@ 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, + Depsgraph *depsgraph, + Scene *scene, + ViewLayer *view_layer, + bool time) { ListBase *lbarray[MAX_LIBARRAY]; int a; @@ -387,7 +635,12 @@ 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.depsgraph = depsgraph; + 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 41c72d11eac..c4fc9591611 100644 --- a/source/blender/depsgraph/intern/depsgraph_type_defines.cc +++ b/source/blender/depsgraph/intern/depsgraph_type_defines.cc @@ -87,6 +87,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); @@ -116,11 +118,22 @@ 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); + /* Point Cache. */ + STRINGIFY_OPCODE(POINT_CACHE_RESET); + /* Batch cache. */ + STRINGIFY_OPCODE(GEOMETRY_SELECT_UPDATE); /* Masks. */ STRINGIFY_OPCODE(MASK_ANIMATION); STRINGIFY_OPCODE(MASK_EVAL); + /* Collections. */ + STRINGIFY_OPCODE(VIEW_LAYER_EVAL); + /* 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 0e48be6b1e2..6d328b399e0 100644 --- a/source/blender/depsgraph/intern/depsgraph_types.h +++ b/source/blender/depsgraph/intern/depsgraph_types.h @@ -49,17 +49,19 @@ struct bAction; struct ChannelDriver; struct ModifierData; struct PointerRNA; -struct EvaluationContext; struct FCurve; +struct Depsgraph; 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? -typedef function<void(struct EvaluationContext *)> DepsEvalOperationCb; +typedef function<void(struct ::Depsgraph *)> DepsEvalOperationCb; /* Metatype of Nodes - The general "level" in the graph structure * the node serves. @@ -80,6 +82,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 */ @@ -112,6 +126,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) **** */ @@ -123,8 +145,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, @@ -150,6 +175,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, @@ -211,9 +239,24 @@ typedef enum eDepsOperation_Code { /* Particle System evaluation. */ DEG_OPCODE_PARTICLE_SYSTEM_EVAL_INIT, DEG_OPCODE_PARTICLE_SYSTEM_EVAL, + DEG_OPCODE_PARTICLE_SETTINGS_EVAL, + + /* Point Cache. ------------------------------------- */ + DEG_OPCODE_POINT_CACHE_RESET, + + /* Collections. ------------------------------------- */ + DEG_OPCODE_VIEW_LAYER_EVAL, + + /* 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 fc71b5ccb7b..4dd3842070e 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,10 @@ 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; }; @@ -90,21 +85,20 @@ static void deg_task_run_func(TaskPool *pool, /* Perform operation. */ if (state->do_stats) { const double start_time = PIL_check_seconds_timer(); - node->evaluate(state->eval_ctx); + node->evaluate((::Depsgraph*)state->graph); node->stats.current_time += PIL_check_seconds_timer() - start_time; } else { - node->evaluate(state->eval_ctx); + node->evaluate((::Depsgraph*)state->graph); } /* 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 +108,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 +128,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 +146,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 +160,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 +176,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 +192,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 +213,6 @@ static void schedule_children(TaskPool *pool, } schedule_node(pool, graph, - layers, child, (rel->flag & DEPSREL_FLAG_CYCLIC) == 0, thread_id); @@ -248,28 +226,23 @@ 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) +void deg_evaluate_on_refresh(Depsgraph *graph) { - /* Set time for the current graph evaluation context. */ - TimeSourceDepsNode *time_src = graph->find_time_source(); - eval_ctx->ctime = time_src->cfra; /* Nothing to update, early out. */ if (BLI_gset_len(graph->entry_tags) == 0) { return; } - DEG_DEBUG_PRINTF(EVAL, "%s: layers:%u, graph->layers:%u\n", - __func__, - layers, - graph->layers); const bool do_time_debug = ((G.debug & G_DEBUG_DEPSGRAPH_TIME) != 0); const double start_time = do_time_debug ? PIL_check_seconds_timer() : 0; - /* Set up evaluation context for depsgraph itself. */ + + /* TODO: Calling this forces the scene datablock to be expanded, + * otherwise we get crashes on load with copy-on-write. There may + * be a better solution for this. */ + DEG_get_evaluated_view_layer((const ::Depsgraph*)graph); + + /* Set up evaluation state. */ DepsgraphEvalState state; - state.eval_ctx = eval_ctx; state.graph = graph; - state.layers = layers; state.do_stats = do_time_debug; /* Set up task scheduler and pull for threaded evaluation. */ TaskScheduler *task_scheduler; @@ -286,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..a8f5f7c145f 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval.h +++ b/source/blender/depsgraph/intern/eval/deg_eval.h @@ -32,8 +32,6 @@ #pragma once -struct EvaluationContext; - namespace DEG { struct Depsgraph; @@ -45,8 +43,6 @@ struct Depsgraph; * * \note Time sources should be all valid! */ -void deg_evaluate_on_refresh(EvaluationContext *eval_ctx, - Depsgraph *graph, - const unsigned int layers); +void deg_evaluate_on_refresh(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..f6a37c49d32 --- /dev/null +++ b/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc @@ -0,0 +1,832 @@ +/* + * ***** 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_idprop.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_curve_types.h" +# include "DNA_key_types.h" +# include "DNA_lamp_types.h" +# include "DNA_lattice_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_EVAL) printf + +namespace { + +#ifdef NESTED_ID_NASTY_WORKAROUND +union NestedIDHackTempStorage { + Curve curve; + FreestyleLineStyle linestyle; + Lamp lamp; + Lattice lattice; + 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_CU, Curve, key) + SPECIAL_CASE(ID_LT, Lattice, key) + 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_CU, Curve, key, curve) + SPECIAL_CASE(ID_LT, Lattice, key, lattice) + 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_CU, Curve, key) + SPECIAL_CASE(ID_LT, Lattice, key) + 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_CU, Curve, key, Key) + SPECIAL_CASE(ID_LT, Lattice, key, Key) + 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'); +} + +/* 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*/) +{ + if (*id_p == NULL) { + return IDWALK_RET_NOP; + } + RemapCallbackUserData *user_data = (RemapCallbackUserData *)user_data_v; + const Depsgraph *depsgraph = user_data->depsgraph; + 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; + } + default: + break; + } +} + +/* 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) +{ + 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; + } + /* 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_NT: + { + /* Node trees should try to preserve their socket pointers + * as much as possible. This is due to UBOs code in GPU, + * which references sockets from trees. + * + * These flags CURRENTLY don't need full datablock update, + * everything is done by node tree update function which + * only copies socket values. + */ + const int ignore_flag = (ID_RECALC_DRAW | + ID_RECALC_ANIMATION | + ID_RECALC_COPY_ON_WRITE); + if ((id_cow->recalc & ~ignore_flag) == 0) { + return id_cow; + } + 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.orig_id; + } + } + /* 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.orig_id)->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(struct ::Depsgraph *graph, + const IDDepsNode *id_node) +{ + const DEG::Depsgraph *depsgraph = reinterpret_cast<const DEG::Depsgraph *>(graph); + DEBUG_PRINT("%s on %s\n", __func__, id_node->id_orig->name); + if (id_node->id_orig == &depsgraph->scene->id) { + /* NOTE: This is handled by eval_ctx setup routines, which + * ensures scene and view layer pointers are valid. + */ + return; + } + 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; + id_cow->orig_id = (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..8fca90e9f5b --- /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 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 + +struct Depsgraph; + +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(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 e7764cf5a27..864d9d9e1ff 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval_flush.cc +++ b/source/blender/depsgraph/intern/eval/deg_eval_flush.cc @@ -36,6 +36,7 @@ #include <deque> #include "BLI_utildefines.h" +#include "BLI_listbase.h" #include "BLI_task.h" #include "BLI_ghash.h" @@ -51,6 +52,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 +72,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,8 +135,7 @@ 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*/, - IDDepsNode *id_node, +BLI_INLINE void flush_handle_component_node(IDDepsNode *id_node, ComponentDepsNode *comp_node, FlushQueue *queue) { @@ -158,46 +146,17 @@ BLI_INLINE void flush_handle_component_node(Depsgraph * /*graph*/, comp_node->done = COMPONENT_STATE_DONE; /* Tag all required operations in component for update. */ foreach (OperationDepsNode *op, comp_node->operations) { - op->flag |= DEPSOP_FLAG_NEEDS_UPDATE; - } - if (GS(id_node->id->name) == ID_OB) { - Object *object = (Object *)id_node->id; - /* This code is used to preserve those areas which does - * direct object update, + /* 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 + /* when some target changes bone, we might need to re-run the * whole IK solver, otherwise result might be unpredictable. */ if (comp_node->type == DEG_NODE_TYPE_BONE) { @@ -240,19 +199,58 @@ BLI_INLINE OperationDepsNode *flush_schedule_children( return result; } -BLI_INLINE void flush_editors_id_update(Main *bmain, - Depsgraph *graph) +void flush_engine_data_update(ID *id) +{ + if (GS(id->name) != ID_OB) { + return; + } + Object *object = (Object *)id; + LISTBASE_FOREACH(ObjectEngineData *, engine_data, &object->drawdata) { + engine_data->recalc |= id->recalc; + } +} + +/* NOTE: It will also accumulate flags from changed components. */ +void flush_editors_id_update(Main *bmain, + 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(EVAL, "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); + /* ID may need to get its auto-override operations refreshed. */ + if (ID_IS_STATIC_OVERRIDE_AUTO(id_orig)) { + id_orig->tag |= LIB_TAG_OVERRIDESTATIC_AUTOREFRESH; + } + /* Inform draw engines that something was changed. */ + flush_engine_data_update(id_cow); + } } } @@ -275,6 +273,12 @@ 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.depsgraph = (::Depsgraph *)graph; + update_ctx.scene = graph->scene; + update_ctx.view_layer = graph->view_layer; /* Do actual flush. */ while (!queue.empty()) { OperationDepsNode *op_node = queue.front(); @@ -286,8 +290,7 @@ void deg_graph_flush_updates(Main *bmain, Depsgraph *graph) ComponentDepsNode *comp_node = op_node->owner; IDDepsNode *id_node = comp_node->owner; flush_handle_id_node(id_node); - flush_handle_component_node(graph, - id_node, + flush_handle_component_node(id_node, comp_node, &queue); /* Flush to nodes along links. */ @@ -295,7 +298,7 @@ void deg_graph_flush_updates(Main *bmain, Depsgraph *graph) } } /* 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 755b10c3f1d..786faa6e86c 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,10 +157,7 @@ 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 @@ -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_len(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_COPY_ON_WRITE); +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..5b6461f950e 100644 --- a/source/blender/depsgraph/intern/nodes/deg_node_component.h +++ b/source/blender/depsgraph/intern/nodes/deg_node_component.h @@ -39,8 +39,6 @@ struct ID; struct bPoseChannel; struct GHash; -struct EvaluationContext; - namespace DEG { struct Depsgraph; @@ -127,7 +125,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 +145,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 +169,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; }; |