diff options
37 files changed, 1184 insertions, 93 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index a4a668fcdee..3da27fb52d5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -510,6 +510,10 @@ if(CMAKE_COMPILER_IS_GNUCC) mark_as_advanced(WITH_LINKER_GOLD) endif() +# Dependency graph +option(WITH_DEPSGRAPH_COPY_ON_WRITE "Build Blender with copy-on-write support for dependency graph" OFF) +mark_as_advanced(WITH_DEPSGRAPH_COPY_ON_WRITE) + if(WIN32) # Use hardcoded paths or find_package to find externals option(WITH_WINDOWS_FIND_MODULES "Use find_package to locate libraries" OFF) diff --git a/intern/cycles/blender/blender_shader.cpp b/intern/cycles/blender/blender_shader.cpp index bdbab1006c0..c96727d5f64 100644 --- a/intern/cycles/blender/blender_shader.cpp +++ b/intern/cycles/blender/blender_shader.cpp @@ -1177,13 +1177,18 @@ void BlenderSync::sync_materials(bool update_all) { shader_map.set_default(scene->default_surface); - /* material loop */ - BL::BlendData::materials_iterator b_mat; - TaskPool pool; set<Shader*> updated_shaders; - for(b_data.materials.begin(b_mat); b_mat != b_data.materials.end(); ++b_mat) { + /* material loop */ + BL::BlendData::materials_iterator b_mat_orig; + for(b_data.materials.begin(b_mat_orig); + b_mat_orig != b_data.materials.end(); + ++b_mat_orig) + { + /* TODO(sergey): Iterate over evaluated data rather than using mapping. */ + BL::Material b_mat_(b_depsgraph.evaluated_id_get(*b_mat_orig)); + BL::Material *b_mat = &b_mat_; Shader *shader; /* test if we need to sync */ @@ -1343,9 +1348,14 @@ void BlenderSync::sync_lamps(bool update_all) shader_map.set_default(scene->default_light); /* lamp loop */ - BL::BlendData::lamps_iterator b_lamp; - - for(b_data.lamps.begin(b_lamp); b_lamp != b_data.lamps.end(); ++b_lamp) { + BL::BlendData::lamps_iterator b_lamp_orig; + for(b_data.lamps.begin(b_lamp_orig); + b_lamp_orig != b_data.lamps.end(); + ++b_lamp_orig) + { + /* TODO(sergey): Iterate over evaluated data rather than using mapping. */ + BL::Lamp b_lamp_(b_depsgraph.evaluated_id_get(*b_lamp_orig)); + BL::Lamp *b_lamp = &b_lamp_; Shader *shader; /* test if we need to sync */ diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index 66c71dcfab1..1d4565f1ec6 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -547,4 +547,8 @@ endif() # set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /WX") #endif() +if(WITH_DEPSGRAPH_COPY_ON_WRITE) + add_definitions(-DWITH_COPY_ON_WRITE) +endif() + blender_add_lib(bf_blenkernel "${SRC}" "${INC}" "${INC_SYS}") diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c index 14d93d75440..68e5ddf8583 100644 --- a/source/blender/blenkernel/intern/object.c +++ b/source/blender/blenkernel/intern/object.c @@ -363,7 +363,16 @@ void BKE_object_free_derived_caches(Object *ob) ob->derivedDeform->release(ob->derivedDeform); ob->derivedDeform = NULL; } - + + if (ob->mesh_evaluated != NULL) { + /* Evaluated mesh points to edit mesh, but does not own it. */ + ob->mesh_evaluated->edit_btmesh = NULL; + BKE_mesh_free(ob->mesh_evaluated); + BKE_libblock_free_data(&ob->mesh_evaluated->id, false); + MEM_freeN(ob->mesh_evaluated); + ob->mesh_evaluated = NULL; + } + BKE_object_free_curve_cache(ob); } diff --git a/source/blender/blenkernel/intern/object_update.c b/source/blender/blenkernel/intern/object_update.c index 0c53ee8818a..70e1f434388 100644 --- a/source/blender/blenkernel/intern/object_update.c +++ b/source/blender/blenkernel/intern/object_update.c @@ -32,6 +32,7 @@ #include "DNA_group_types.h" #include "DNA_key_types.h" #include "DNA_material_types.h" +#include "DNA_mesh_types.h" #include "DNA_scene_types.h" #include "BLI_blenlib.h" @@ -51,6 +52,7 @@ #include "BKE_key.h" #include "BKE_lamp.h" #include "BKE_lattice.h" +#include "BKE_library.h" #include "BKE_editmesh.h" #include "BKE_object.h" #include "BKE_particle.h" @@ -60,6 +62,7 @@ #include "BKE_mesh.h" #include "BKE_image.h" +#include "MEM_guardedalloc.h" #include "DEG_depsgraph.h" #define DEBUG_PRINT if (G.debug & G_DEBUG_DEPSGRAPH) printf @@ -345,6 +348,51 @@ void BKE_object_eval_uber_data(EvaluationContext *eval_ctx, break; } +#ifdef WITH_COPY_ON_WRITE + if (ob->type == OB_MESH) { + /* Quick hack to convert evaluated derivedMesh to Mesh. */ + DerivedMesh *dm = ob->derivedFinal; + if (dm != NULL) { + Mesh *mesh = (Mesh *)ob->data; + Mesh *new_mesh = BKE_libblock_alloc_notest(ID_ME); + BKE_mesh_init(new_mesh); + /* Copy ID name so GS(new_mesh->id) works correct later on. */ + BLI_strncpy(new_mesh->id.name, mesh->id.name, sizeof(new_mesh->id.name)); + /* Copy materials so render engines can access them. */ + new_mesh->mat = MEM_dupallocN(mesh->mat); + new_mesh->totcol = mesh->totcol; + DM_to_mesh(dm, new_mesh, ob, ob->lastDataMask, true); + new_mesh->edit_btmesh = mesh->edit_btmesh; + /* Store result mesh as derived_mesh of object. This way we have + * explicit way to query final object evaluated data and know for sure + * who owns the newly created mesh datablock. + */ + ob->mesh_evaluated = new_mesh; + /* TODO(sergey): This is kind of compatibility thing, so all render + * engines can use object->data for mesh data for display. This is + * something what we might want to change in the future. + */ + ob->data = new_mesh; + /* Save some memory by throwing DerivedMesh away. */ + /* NOTE: Watch out, some tools might need it! + * So keep around for now.. + */ + } +#if 0 + if (ob->derivedFinal != NULL) { + ob->derivedFinal->needsFree = 1; + ob->derivedFinal->release(ob->derivedFinal); + ob->derivedFinal = NULL; + } + if (ob->derivedDeform != NULL) { + ob->derivedDeform->needsFree = 1; + ob->derivedDeform->release(ob->derivedDeform); + ob->derivedDeform = NULL; + } +#endif + } +#endif + ob->recalc &= ~(OB_RECALC_DATA | OB_RECALC_TIME); } diff --git a/source/blender/depsgraph/CMakeLists.txt b/source/blender/depsgraph/CMakeLists.txt index 5eb5d69d62e..17a13b66aaa 100644 --- a/source/blender/depsgraph/CMakeLists.txt +++ b/source/blender/depsgraph/CMakeLists.txt @@ -55,6 +55,7 @@ set(SRC intern/builder/deg_builder_transitive.cc intern/debug/deg_debug_graphviz.cc intern/eval/deg_eval.cc + intern/eval/deg_eval_copy_on_write.cc intern/eval/deg_eval_debug.cc intern/eval/deg_eval_flush.cc intern/nodes/deg_node.cc @@ -80,6 +81,7 @@ set(SRC intern/builder/deg_builder_relations.h intern/builder/deg_builder_transitive.h intern/eval/deg_eval.h + intern/eval/deg_eval_copy_on_write.h intern/eval/deg_eval_debug.h intern/eval/deg_eval_flush.h intern/nodes/deg_node.h @@ -126,4 +128,8 @@ if(WITH_OPENSUBDIV) add_definitions(-DWITH_OPENSUBDIV) endif() +if(WITH_DEPSGRAPH_COPY_ON_WRITE) + add_definitions(-DWITH_COPY_ON_WRITE) +endif() + blender_add_lib(bf_depsgraph "${SRC}" "${INC}" "${INC_SYS}") diff --git a/source/blender/depsgraph/DEG_depsgraph.h b/source/blender/depsgraph/DEG_depsgraph.h index 63c02d007dc..9caf7635ccf 100644 --- a/source/blender/depsgraph/DEG_depsgraph.h +++ b/source/blender/depsgraph/DEG_depsgraph.h @@ -144,10 +144,31 @@ void DEG_graph_data_tag_update(Depsgraph *graph, const struct PointerRNA *ptr); void DEG_graph_property_tag_update(Depsgraph *graph, const struct PointerRNA *ptr, const struct PropertyRNA *prop); /* Tag given ID for an update in all the dependency graphs. */ -void DEG_id_tag_update(struct ID *id, short flag); +enum { + /* Object transformation changed, corresponds to OB_RECALC_OB. */ + DEG_TAG_TRANSFORM = (1 << 0), + + /* Object geoemtry changed, corresponds to OB_RECALC_DATA. */ + DEG_TAG_GEOMETRY = (1 << 1), + + /* Time changed and animation is to be re-evaluated, OB_RECALC_TIME. */ + DEG_TAG_TIME = (1 << 2), + + /* Particle system changed. */ + DEG_TAG_PSYSC_REDO = (1 << 3), + DEG_TAG_PSYS_RESET = (1 << 4), + DEG_TAG_PSYS_TYPE = (1 << 5), + DEG_TAG_PSYS_CHILD = (1 << 6), + DEG_TAG_PSYS_PHYS = (1 << 7), + DEG_TAG_PSYS = ((1 << 3) | (1 << 4) | (1 << 5) | (1 << 6) | (1 << 7)), + + /* Update copy on write component without flushing down the road. */ + DEG_TAG_COPY_ON_WRITE = (1 << 8), +}; +void DEG_id_tag_update(struct ID *id, int flag); void DEG_id_tag_update_ex(struct Main *bmain, struct ID *id, - short flag); + int flag); /* Tag given ID type for update. * diff --git a/source/blender/depsgraph/DEG_depsgraph_query.h b/source/blender/depsgraph/DEG_depsgraph_query.h index 649e788a0ef..674389ddd6c 100644 --- a/source/blender/depsgraph/DEG_depsgraph_query.h +++ b/source/blender/depsgraph/DEG_depsgraph_query.h @@ -64,6 +64,9 @@ struct SceneLayer *DEG_get_scene_layer(struct Depsgraph *graph); /* Get the object as properly evaluated by depsgraph. */ struct Object *DEG_get_object(struct Depsgraph *depsgraph, struct Object *ob); +/* Get evaluated version of given ID datablock. */ +struct ID *DEG_get_evaluated_id(struct Depsgraph *depsgraph, struct ID *id); + /* ************************ DAG iterators ********************* */ enum { diff --git a/source/blender/depsgraph/intern/builder/deg_builder.cc b/source/blender/depsgraph/intern/builder/deg_builder.cc index e7f54f9b0cb..92c79388657 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder.cc @@ -50,7 +50,8 @@ void deg_graph_build_finalize(Depsgraph *graph) */ GHASH_FOREACH_BEGIN(IDDepsNode *, id_node, graph->id_hash) { - ID *id = id_node->id; + ID *id = id_node->id_orig; + id_node->finalize_build(graph); if ((id->tag & LIB_TAG_ID_RECALC_ALL)) { id_node->tag_update(graph); } @@ -60,7 +61,11 @@ void deg_graph_build_finalize(Depsgraph *graph) id_node->tag_update(graph); } } - id_node->finalize_build(); + /* XXX: This is only so we've got proper COW IDs after rebuild. */ + /* TODO(sergey): Ideally we'll need to copy evaluated CoW from previous + * depsgraph, so we don't need to re-tag anything what we already have. + */ + id_node->tag_update(graph); } GHASH_FOREACH_END(); } diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc index 5ec541612f5..1fc107f0437 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc @@ -102,6 +102,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" @@ -162,7 +163,23 @@ DepsgraphNodeBuilder::~DepsgraphNodeBuilder() IDDepsNode *DepsgraphNodeBuilder::add_id_node(ID *id) { - return m_graph->add_id_node(id, id->name); + IDDepsNode *id_node = m_graph->add_id_node(id, id->name); +#ifdef WITH_COPY_ON_WRITE + /* Currently all ID nodes are supposed to have copy-on-write logic. + * + * NOTE: Zero number of components indicates that ID node was just created. + */ + if (BLI_ghash_size(id_node->components) == 0) { + ComponentDepsNode *comp_cow = + id_node->add_component(DEG_NODE_TYPE_COPY_ON_WRITE); + OperationDepsNode *op_cow = comp_cow->add_operation( + function_bind(deg_evaluate_copy_on_write, _1, m_graph, id_node), + DEG_OPCODE_COPY_ON_WRITE, + "", -1); + m_graph->operations.push_back(op_cow); + } +#endif + return id_node; } TimeSourceDepsNode *DepsgraphNodeBuilder::add_time_source() @@ -273,6 +290,11 @@ 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 m_graph->get_cow_id(id_orig); +} + /* **** Build functions for entity nodes **** */ void DepsgraphNodeBuilder::begin_build(Main *bmain) { @@ -735,7 +757,10 @@ void DepsgraphNodeBuilder::build_obdata_geom(Scene *scene, Object *ob) */ op_node = add_operation_node(&ob->id, DEG_NODE_TYPE_GEOMETRY, - function_bind(BKE_object_eval_uber_data, _1, scene, ob), + function_bind(BKE_object_eval_uber_data, + _1, + (Scene *)get_cow_id(&scene->id), + (Object *)get_cow_id(&ob->id)), DEG_OPCODE_GEOMETRY_UBEREVAL); op_node->set_as_exit(); diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.h b/source/blender/depsgraph/intern/builder/deg_builder_nodes.h index 9aa5d68f1dd..54ad1bd5d8a 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.h +++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.h @@ -73,6 +73,8 @@ struct DepsgraphNodeBuilder { void begin_build(Main *bmain); + ID *get_cow_id(const ID *id_orig) const; + IDDepsNode *add_id_node(ID *id); TimeSourceDepsNode *add_time_source(); diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes_layer.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes_layer.cc index e419b72fee8..b6df176545e 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_nodes_layer.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes_layer.cc @@ -49,6 +49,7 @@ extern "C" { } /* extern "C" */ #include "intern/builder/deg_builder.h" +#include "intern/eval/deg_eval_copy_on_write.h" #include "intern/nodes/deg_node.h" #include "intern/nodes/deg_node_component.h" #include "intern/nodes/deg_node_operation.h" @@ -95,13 +96,22 @@ void DepsgraphNodeBuilder::build_layer_collections(Scene *scene, void DepsgraphNodeBuilder::build_scene_layer_collections(Scene *scene) { +#ifdef WITH_COPY_ON_WRITE + /* Make sure we've got ID node, so we can get pointer to CoW datablock. */ + IDDepsNode *id_node = add_id_node(&scene->id); + Scene *scene_cow = (Scene *)deg_expand_copy_on_write_datablock(m_graph, + id_node); +#else + Scene *scene_cow = scene; +#endif + LayerCollectionState state; state.index = 0; - LINKLIST_FOREACH (SceneLayer *, scene_layer, &scene->render_layers) { + LINKLIST_FOREACH (SceneLayer *, scene_layer, &scene_cow->render_layers) { ComponentDepsNode *comp = add_component_node(&scene->id, DEG_NODE_TYPE_LAYER_COLLECTIONS); add_operation_node(comp, - function_bind(BKE_layer_eval_layer_collection_pre, _1, scene, scene_layer), + function_bind(BKE_layer_eval_layer_collection_pre, _1, scene_cow, scene_layer), DEG_OPCODE_SCENE_LAYER_INIT, scene_layer->name); add_operation_node(comp, diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes_scene.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes_scene.cc index 71c74aad966..f24d4e8d3f2 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_nodes_scene.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes_scene.cc @@ -135,6 +135,13 @@ void DepsgraphNodeBuilder::build_scene(Main *bmain, Scene *scene) /* Collections. */ build_scene_layer_collections(scene); + + /* Parameters evaluation for scene relations mainly. */ + add_operation_node(&scene->id, + DEG_NODE_TYPE_PARAMETERS, + NULL, + DEG_OPCODE_PLACEHOLDER, + "Scene Eval"); } } // 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 2c703441c8c..006358ea9e1 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc @@ -976,8 +976,8 @@ void DepsgraphRelationBuilder::build_driver(ID *id, FCurve *fcu) 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 *ob = (Object *)to_node->id; + if (GS(to_node->id_orig->name) == ID_OB) { + Object *ob = (Object *)to_node->id_orig; bPoseChannel *pchan = BKE_pose_channel_find_name(ob->pose, bone_name); // NOTE: ob->pose may be NULL if (pchan) { @@ -1391,12 +1391,22 @@ void DepsgraphRelationBuilder::build_obdata_geom(Main *bmain, Scene *scene, Obje /* link components to each other */ add_relation(obdata_geom_key, geom_key, "Object Geometry Base Data"); + OperationKey obdata_ubereval_key(&ob->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 (ob->modifiers.first != NULL) { - OperationKey obdata_ubereval_key(&ob->id, - DEG_NODE_TYPE_GEOMETRY, - DEG_OPCODE_GEOMETRY_UBEREVAL); - LINKLIST_FOREACH (ModifierData *, md, &ob->modifiers) { const ModifierTypeInfo *mti = modifierType_getInfo((ModifierType)md->type); @@ -1760,4 +1770,62 @@ void DepsgraphRelationBuilder::build_lightprobe(Object *object) add_relation(probe_key, object_key, "LightProbe Update"); } +void DepsgraphRelationBuilder::build_copy_on_write_relations() +{ + GHASH_FOREACH_BEGIN(IDDepsNode *, id_node, m_graph->id_hash) + { + build_copy_on_write_relations(id_node); + } + GHASH_FOREACH_END(); +} + +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; + } + /* 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) { + m_graph->add_new_relation(op_cow, op_entry, "CoW Dependency"); + } + /* All dangling operations should also be executed after copy-on-write. */ + GHASH_FOREACH_BEGIN(OperationDepsNode *, op_node, comp_node->operations_map) + { + if (op_node->inlinks.size() == 0) { + m_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(); +} + } // namespace DEG diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.h b/source/blender/depsgraph/intern/builder/deg_builder_relations.h index acab5da66b4..8a53bf4a6bf 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations.h +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.h @@ -225,8 +225,13 @@ struct DepsgraphRelationBuilder void build_movieclip(MovieClip *clip); void build_lightprobe(Object *object); - void add_collision_relations(const OperationKey &key, Scene *scene, Object *ob, Group *group, bool dupli, const char *name); - void add_forcefield_relations(const OperationKey &key, Scene *scene, Object *ob, ParticleSystem *psys, EffectorWeights *eff, bool add_absorption, const char *name); + void add_collision_relations(const OperationKey &key, + Scene *scene, Object *ob, Group *group, + bool dupli, const char *name); + void add_forcefield_relations(const OperationKey &key, + Scene *scene, Object *ob, ParticleSystem *psys, + EffectorWeights *eff, + bool add_absorption, const char *name); struct LayerCollectionState { int index; @@ -242,6 +247,9 @@ struct DepsgraphRelationBuilder LayerCollectionState *state); void build_scene_layer_collections(Scene *scene); + 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_scene.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations_scene.cc index f157a4b58d0..43204d5fe98 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations_scene.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations_scene.cc @@ -122,13 +122,14 @@ void DepsgraphRelationBuilder::build_scene(Main *bmain, Scene *scene) /* Collections. */ build_scene_layer_collections(scene); + /* TODO(sergey): Do this flush on CoW object? */ for (Depsgraph::OperationNodes::const_iterator it_op = m_graph->operations.begin(); it_op != m_graph->operations.end(); ++it_op) { OperationDepsNode *node = *it_op; 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; diff --git a/source/blender/depsgraph/intern/debug/deg_debug_graphviz.cc b/source/blender/depsgraph/intern/debug/deg_debug_graphviz.cc index 4a79f9c54b2..66b63f861ee 100644 --- a/source/blender/depsgraph/intern/debug/deg_debug_graphviz.cc +++ b/source/blender/depsgraph/intern/debug/deg_debug_graphviz.cc @@ -90,6 +90,7 @@ static const int deg_debug_node_type_color_map[][2] = { {DEG_NODE_TYPE_SHADING, 8}, {DEG_NODE_TYPE_CACHE, 9}, {DEG_NODE_TYPE_LAYER_COLLECTIONS, 10}, + {DEG_NODE_TYPE_COPY_ON_WRITE, 11}, {-1, 0} }; #endif @@ -379,6 +380,7 @@ static void deg_debug_graphviz_node(const DebugContext &ctx, case DEG_NODE_TYPE_CACHE: case DEG_NODE_TYPE_LAYER_COLLECTIONS: case DEG_NODE_TYPE_EVAL_PARTICLES: + case DEG_NODE_TYPE_COPY_ON_WRITE: { ComponentDepsNode *comp_node = (ComponentDepsNode *)node; if (!comp_node->operations.empty()) { diff --git a/source/blender/depsgraph/intern/depsgraph.cc b/source/blender/depsgraph/intern/depsgraph.cc index e5c5b197481..dc3174751bd 100644 --- a/source/blender/depsgraph/intern/depsgraph.cc +++ b/source/blender/depsgraph/intern/depsgraph.cc @@ -282,7 +282,11 @@ IDDepsNode *Depsgraph::add_id_node(ID *id, const char *name) DepsNodeFactory *factory = deg_get_node_factory(DEG_NODE_TYPE_ID_REF); id_node = (IDDepsNode *)factory->create_node(id, "", name); id->tag |= LIB_TAG_DOIT; - /* register */ + /* 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); } return id_node; @@ -306,7 +310,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->tag & LIB_TAG_ID_RECALC_ALL)) { + if (id_to != id_from && (id_to->id_orig->tag & LIB_TAG_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; @@ -404,6 +408,15 @@ void Depsgraph::clear_all_nodes() } } +ID *Depsgraph::get_cow_id(const ID *id_orig) const +{ + IDDepsNode *id_node = find_id_node(id_orig); + if (id_node == NULL) { + return (ID *)id_orig; + } + return id_node->id_cow; +} + void deg_editors_id_update(Main *bmain, ID *id) { if (deg_editor_update_id_cb != NULL) { diff --git a/source/blender/depsgraph/intern/depsgraph.h b/source/blender/depsgraph/intern/depsgraph.h index 86928f10d12..9785d2f90d0 100644 --- a/source/blender/depsgraph/intern/depsgraph.h +++ b/source/blender/depsgraph/intern/depsgraph.h @@ -130,6 +130,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 blocks diff --git a/source/blender/depsgraph/intern/depsgraph_build.cc b/source/blender/depsgraph/intern/depsgraph_build.cc index cd15882e306..88ff1ac0a9c 100644 --- a/source/blender/depsgraph/intern/depsgraph_build.cc +++ b/source/blender/depsgraph/intern/depsgraph_build.cc @@ -216,6 +216,9 @@ void DEG_graph_build_from_scene(Depsgraph *graph, Main *bmain, Scene *scene) DEG::DepsgraphRelationBuilder relation_builder(deg_graph); relation_builder.begin_build(bmain); relation_builder.build_scene(bmain, scene); +#ifdef WITH_COPY_ON_WRITE + relation_builder.build_copy_on_write_relations(); +#endif /* Detect and solve cycles. */ DEG::deg_graph_detect_cycles(deg_graph); diff --git a/source/blender/depsgraph/intern/depsgraph_query.cc b/source/blender/depsgraph/intern/depsgraph_query.cc index c8d8e4ee37f..3dfa6bf5e9d 100644 --- a/source/blender/depsgraph/intern/depsgraph_query.cc +++ b/source/blender/depsgraph/intern/depsgraph_query.cc @@ -49,6 +49,10 @@ extern "C" { #include "intern/depsgraph_intern.h" #include "util/deg_util_foreach.h" +#ifndef NDEBUG +# include "intern/eval/deg_eval_copy_on_write.h" +#endif + bool DEG_id_type_tagged(Main *bmain, short idtype) { return bmain->id_tag_update[BKE_idcode_to_index(idtype)] != 0; @@ -80,22 +84,29 @@ short DEG_get_eval_flags_for_id(Depsgraph *graph, ID *id) Scene *DEG_get_scene(Depsgraph *graph) { DEG::Depsgraph *deg_graph = reinterpret_cast<DEG::Depsgraph *>(graph); - return deg_graph->scene; + Scene *scene_orig = deg_graph->scene; + return reinterpret_cast<Scene *>(deg_graph->get_cow_id(&scene_orig->id)); } SceneLayer *DEG_get_scene_layer(Depsgraph *graph) { Scene *scene = DEG_get_scene(graph); if (scene) { - return BKE_scene_layer_context_active(scene); + return BKE_scene_layer_render_active(scene); } return NULL; } -Object *DEG_get_object(Depsgraph * /*depsgraph*/, Object *ob) +Object *DEG_get_object(Depsgraph *depsgraph, Object *ob) { - /* XXX TODO */ - return ob; + DEG::Depsgraph *deg_graph = reinterpret_cast<DEG::Depsgraph *>(depsgraph); + return (Object *)deg_graph->get_cow_id(&ob->id); +} + +ID *DEG_get_evaluated_id(struct Depsgraph *depsgraph, ID *id) +{ + DEG::Depsgraph *deg_graph = reinterpret_cast<DEG::Depsgraph *>(depsgraph); + return deg_graph->get_cow_id(id); } /* ************************ DAG ITERATORS ********************* */ @@ -167,6 +178,7 @@ static bool deg_objects_dupli_iterator_next(BLI_Iterator *iter) data->base, data->base_flag | BASE_FROMDUPLI); iter->current = &data->temp_dupli_object; + BLI_assert(DEG::deg_validate_copy_on_write_datablock(&data->temp_dupli_object.id)); return true; } @@ -194,10 +206,13 @@ void DEG_objects_iterator_next(BLI_Iterator *iter) base = data->base->next; while (base != NULL) { if ((base->flag & BASE_VISIBLED) != 0) { - Object *ob = DEG_get_object(data->graph, base->object); + // Object *ob = DEG_get_object(data->graph, base->object); + Object *ob = base->object; iter->current = ob; data->base = base; + BLI_assert(DEG::deg_validate_copy_on_write_datablock(&ob->id)); + /* Make sure we have the base collection settings is already populated. * This will fail when BKE_layer_eval_layer_collection_pre hasn't run yet * Which usually means a missing call to DAG_id_tag_update(). */ diff --git a/source/blender/depsgraph/intern/depsgraph_tag.cc b/source/blender/depsgraph/intern/depsgraph_tag.cc index a2b8a93ecda..80fa89bbb97 100644 --- a/source/blender/depsgraph/intern/depsgraph_tag.cc +++ b/source/blender/depsgraph/intern/depsgraph_tag.cc @@ -127,7 +127,7 @@ void lib_id_recalc_tag_flag(Main *bmain, ID *id, int flag) } #ifdef DEPSGRAPH_USE_LEGACY_TAGGING -void depsgraph_legacy_handle_update_tag(Main *bmain, ID *id, short flag) +void depsgraph_legacy_handle_update_tag(Main *bmain, ID *id, int flag) { if (flag) { Object *object; @@ -153,6 +153,20 @@ void depsgraph_legacy_handle_update_tag(Main *bmain, ID *id, short flag) } #endif +#ifdef WITH_COPY_ON_WRITE +void id_tag_copy_on_write_update(Main *bmain, Depsgraph *graph, ID *id) +{ + lib_id_recalc_tag(bmain, id); + DEG::Depsgraph *deg_graph = reinterpret_cast<DEG::Depsgraph *>(graph); + DEG::IDDepsNode *id_node = deg_graph->find_id_node(id); + DEG::ComponentDepsNode *cow_comp = + id_node->find_component(DEG::DEG_NODE_TYPE_COPY_ON_WRITE); + DEG::OperationDepsNode *cow_node = cow_comp->get_entry_operation(); + cow_node->tag_update(deg_graph); + cow_node->flag |= DEG::DEPSOP_FLAG_SKIP_FLUSH; +} +#endif + } /* namespace */ /* Tag all nodes in ID-block for update. @@ -199,12 +213,12 @@ void DEG_graph_property_tag_update(Depsgraph *graph, } /* 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. */ @@ -240,6 +254,11 @@ void DEG_id_tag_update_ex(Main *bmain, ID *id, short flag) else if (flag & OB_RECALC_TIME) { DEG_graph_id_tag_update(bmain, graph, id); } + else if (flag & DEG_TAG_COPY_ON_WRITE) { +#ifdef WITH_COPY_ON_WRITE + id_tag_copy_on_write_update(bmain, graph, id); +#endif + } } } diff --git a/source/blender/depsgraph/intern/depsgraph_type_defines.cc b/source/blender/depsgraph/intern/depsgraph_type_defines.cc index 612944dab6e..bb75a85eea4 100644 --- a/source/blender/depsgraph/intern/depsgraph_type_defines.cc +++ b/source/blender/depsgraph/intern/depsgraph_type_defines.cc @@ -66,7 +66,9 @@ static GHash *_depsnode_typeinfo_registry = NULL; void deg_register_node_typeinfo(DepsNodeFactory *factory) { BLI_assert(factory != NULL); - BLI_ghash_insert(_depsnode_typeinfo_registry, SET_INT_IN_POINTER(factory->type()), factory); + BLI_ghash_insert(_depsnode_typeinfo_registry, + SET_INT_IN_POINTER(factory->type()), + factory); } /* Getters ------------------------------------------------- */ @@ -75,7 +77,8 @@ void deg_register_node_typeinfo(DepsNodeFactory *factory) DepsNodeFactory *deg_get_node_factory(const eDepsNode_Type type) { /* look up type - at worst, it doesn't exist in table yet, and we fail */ - return (DepsNodeFactory *)BLI_ghash_lookup(_depsnode_typeinfo_registry, SET_INT_IN_POINTER(type)); + return (DepsNodeFactory *)BLI_ghash_lookup(_depsnode_typeinfo_registry, + SET_INT_IN_POINTER(type)); } /* Get typeinfo for provided node */ @@ -124,6 +127,8 @@ static const char *stringify_opcode(eDepsOperation_Code opcode) STRINGIFY_OPCODE(SCENE_LAYER_EVAL); STRINGIFY_OPCODE(SCENE_LAYER_DONE); + STRINGIFY_OPCODE(COPY_ON_WRITE); + case DEG_NUM_OPCODES: return "SpecialCase"; #undef STRINGIFY_OPCODE } diff --git a/source/blender/depsgraph/intern/depsgraph_types.h b/source/blender/depsgraph/intern/depsgraph_types.h index 467aedcd9c7..737452549ec 100644 --- a/source/blender/depsgraph/intern/depsgraph_types.h +++ b/source/blender/depsgraph/intern/depsgraph_types.h @@ -103,10 +103,7 @@ typedef enum eDepsNode_Type { DEG_NODE_TYPE_PARAMETERS, /* Generic "Proxy-Inherit" Component. */ DEG_NODE_TYPE_PROXY, - /* Animation Component - * - * XXX: merge in with parameters? - */ + /* Animation Component */ DEG_NODE_TYPE_ANIMATION, /* Transform Component (Parenting/Constraints) */ DEG_NODE_TYPE_TRANSFORM, @@ -114,6 +111,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) **** */ @@ -127,8 +132,6 @@ typedef enum eDepsNode_Type { DEG_NODE_TYPE_SHADING, /* Cache Component */ DEG_NODE_TYPE_CACHE, - /* Component which contains all operations needed for layer collections evaluation. */ - DEG_NODE_TYPE_LAYER_COLLECTIONS, } eDepsNode_Type; /* Identifiers for common operations (as an enum). */ @@ -142,68 +145,52 @@ typedef enum eDepsOperation_Code { DEG_OPCODE_PLACEHOLDER, /* Animation, Drivers, etc. ------------------------ */ - /* NLA + Action */ DEG_OPCODE_ANIMATION, - /* Driver */ DEG_OPCODE_DRIVER, /* Transform --------------------------------------- */ - /* Transform entry point - local transforms only */ DEG_OPCODE_TRANSFORM_LOCAL, - /* Parenting */ DEG_OPCODE_TRANSFORM_PARENT, - /* Constraints */ DEG_OPCODE_TRANSFORM_CONSTRAINTS, + /* Transform exit point */ + DEG_OPCODE_TRANSFORM_FINAL, + /* Handle object-level updates, mainly proxies hacks and recalc flags. */ + DEG_OPCODE_OBJECT_UBEREVAL, - /* Rigidbody Sim - Perform Sim */ + /* Rigid body -------------------------------------- */ + /* Perform Simulation */ DEG_OPCODE_RIGIDBODY_REBUILD, DEG_OPCODE_RIGIDBODY_SIM, - - /* Rigidbody Sim - Copy Results to Object */ + /* Copy results to object */ DEG_OPCODE_TRANSFORM_RIGIDBODY, - /* Transform exitpoint */ - DEG_OPCODE_TRANSFORM_FINAL, - - /* XXX: ubereval is for temporary porting purposes only */ - DEG_OPCODE_OBJECT_UBEREVAL, - /* Geometry ---------------------------------------- */ - - /* XXX: Placeholder - UberEval */ + /* Evaluate the whole geometry, including modifiers. */ DEG_OPCODE_GEOMETRY_UBEREVAL, - /* Curve Objects - Path Calculation (used for path-following tools, */ DEG_OPCODE_GEOMETRY_PATH, /* Pose -------------------------------------------- */ - /* Init IK Trees, etc. */ DEG_OPCODE_POSE_INIT, - /* Free IK Trees + Compute Deform Matrices */ DEG_OPCODE_POSE_DONE, - /* IK/Spline Solvers */ DEG_OPCODE_POSE_IK_SOLVER, DEG_OPCODE_POSE_SPLINE_IK_SOLVER, /* Bone -------------------------------------------- */ - - /* Bone local transforms - Entrypoint */ + /* Bone local transforms - entry point */ DEG_OPCODE_BONE_LOCAL, - /* Pose-space conversion (includes parent + restpose, */ DEG_OPCODE_BONE_POSE_PARENT, - /* Constraints */ DEG_OPCODE_BONE_CONSTRAINTS, - /* Bone transforms are ready * * - "READY" This (internal, noop is used to signal that all pre-IK @@ -219,8 +206,7 @@ typedef enum eDepsOperation_Code { DEG_OPCODE_BONE_DONE, /* Particles --------------------------------------- */ - - /* XXX: placeholder - Particle System eval */ + /* Particle System evaluation. */ DEG_OPCODE_PSYS_EVAL, /* Collections ------------------------------------- */ @@ -228,6 +214,9 @@ typedef enum eDepsOperation_Code { DEG_OPCODE_SCENE_LAYER_EVAL, DEG_OPCODE_SCENE_LAYER_DONE, + /* Copy on Write ------------------------- */ + DEG_OPCODE_COPY_ON_WRITE, + DEG_NUM_OPCODES, } eDepsOperation_Code; 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..ade822473e8 --- /dev/null +++ b/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc @@ -0,0 +1,650 @@ +/* + * ***** 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 + */ + +/* 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 "BKE_global.h" +#include "BKE_layer.h" +#include "BKE_library.h" +#include "BKE_main.h" +#include "BKE_scene.h" + +#include "DEG_depsgraph.h" + +#include "MEM_guardedalloc.h" + +extern "C" { +#include "DNA_ID.h" +#include "DNA_mesh_types.h" +#include "DNA_scene_types.h" +#include "DNA_object_types.h" + +#ifdef NESTED_ID_NASTY_WORKAROUND +# include "DNA_key_types.h" +# include "DNA_lamp_types.h" +# include "DNA_linestyle_types.h" +# include "DNA_material_types.h" +# include "DNA_mesh_types.h" +# include "DNA_node_types.h" +# include "DNA_scene_types.h" +# include "DNA_texture_types.h" +# include "DNA_world_types.h" +#endif + +#include "BKE_editmesh.h" +#include "BKE_library_query.h" +} + +#include "intern/depsgraph.h" +#include "intern/nodes/deg_node.h" + +namespace DEG { + +#define DEBUG_PRINT if (G.debug & G_DEBUG_DEPSGRAPH) printf + +namespace { + +#ifdef NESTED_ID_NASTY_WORKAROUND +union NestedIDHackTempStorage { + FreestyleLineStyle linestyle; + Lamp lamp; + Material material; + Mesh mesh; + Scene scene; + Tex tex; + World world; +}; + +/* Set nested owned ID pointers to NULL. */ +void nested_id_hack_discard_pointers(ID *id_cow) +{ + switch (GS(id_cow->name)) { +# define SPECIAL_CASE(id_type, dna_type, field) \ + case id_type: \ + { \ + ((dna_type *)id_cow)->field = NULL; \ + break; \ + } + + SPECIAL_CASE(ID_LS, FreestyleLineStyle, nodetree) + SPECIAL_CASE(ID_LA, Lamp, nodetree) + SPECIAL_CASE(ID_MA, Material, nodetree) + SPECIAL_CASE(ID_SCE, Scene, nodetree) + SPECIAL_CASE(ID_TE, Tex, nodetree) + SPECIAL_CASE(ID_WO, World, nodetree) + + SPECIAL_CASE(ID_ME, Mesh, key) + +# undef SPECIAL_CASE + + default: + break; + } +} + +/* Set ID pointer of nested owned IDs (nodetree, key) to NULL. + * + * Return pointer to a new ID to be used. + */ +const ID *nested_id_hack_get_discarded_pointers(NestedIDHackTempStorage *storage, + const ID *id) +{ + switch (GS(id->name)) { +# define SPECIAL_CASE(id_type, dna_type, field, variable) \ + case id_type: \ + { \ + storage->variable = *(dna_type *)id; \ + storage->variable.field = NULL; \ + return &storage->variable.id; \ + } + + SPECIAL_CASE(ID_LS, FreestyleLineStyle, nodetree, linestyle) + SPECIAL_CASE(ID_LA, Lamp, nodetree, lamp) + SPECIAL_CASE(ID_MA, Material, nodetree, material) + SPECIAL_CASE(ID_SCE, Scene, nodetree, scene) + SPECIAL_CASE(ID_TE, Tex, nodetree, tex) + SPECIAL_CASE(ID_WO, World, nodetree, world) + + SPECIAL_CASE(ID_ME, Mesh, key, mesh) + +# undef SPECIAL_CASE + + default: + break; + } + return id; +} + +/* Set ID pointer of nested owned IDs (nodetree, key) to the original value. */ +void nested_id_hack_restore_pointers(const ID *old_id, ID *new_id) +{ + if (new_id == NULL) { + return; + } + switch (GS(old_id->name)) { +# define SPECIAL_CASE(id_type, dna_type, field) \ + case id_type: \ + { \ + ((dna_type *)(new_id))->field = \ + ((dna_type *)(old_id))->field; \ + break; \ + } + + SPECIAL_CASE(ID_LS, FreestyleLineStyle, nodetree) + SPECIAL_CASE(ID_LA, Lamp, nodetree) + SPECIAL_CASE(ID_MA, Material, nodetree) + SPECIAL_CASE(ID_SCE, Scene, nodetree) + SPECIAL_CASE(ID_TE, Tex, nodetree) + SPECIAL_CASE(ID_WO, World, nodetree) + + SPECIAL_CASE(ID_ME, Mesh, key) + +#undef SPECIAL_CASE + default: + break; + } +} + +/* Remap pointer of nested owned IDs (nodetree. key) to the new ID values. */ +void ntree_hack_remap_pointers(const Depsgraph *depsgraph, ID *id_cow) +{ + switch (GS(id_cow->name)) { +# define SPECIAL_CASE(id_type, dna_type, field, field_type) \ + case id_type: \ + { \ + dna_type *data = (dna_type *)id_cow; \ + if (data->field != NULL) { \ + ID *ntree_id_cow = depsgraph->get_cow_id(&data->field->id); \ + if (ntree_id_cow != NULL) { \ + DEG_COW_PRINT(" Remapping datablock for %s: id_orig=%p id_cow=%p\n", \ + data->field->id.name, \ + data->field, \ + ntree_id_cow); \ + data->field = (field_type *)ntree_id_cow; \ + } \ + } \ + break; \ + } + + SPECIAL_CASE(ID_LS, FreestyleLineStyle, nodetree, bNodeTree) + SPECIAL_CASE(ID_LA, Lamp, nodetree, bNodeTree) + SPECIAL_CASE(ID_MA, Material, nodetree, bNodeTree) + SPECIAL_CASE(ID_SCE, Scene, nodetree, bNodeTree) + SPECIAL_CASE(ID_TE, Tex, nodetree, bNodeTree) + SPECIAL_CASE(ID_WO, World, nodetree, bNodeTree) + + SPECIAL_CASE(ID_ME, Mesh, key, Key) + +#undef SPECIAL_CASE + default: + break; + } +} +#endif /* NODETREE_NASTY_WORKAROUND */ + +struct ValidateData { + bool is_valid; +}; + +/* Similar to generic id_copy() but does not require main. + * + * TODO(sergey): Get rid of this once T51804 is handled. + */ +bool id_copy_no_main(const ID *id, ID **newid) +{ + const ID *id_for_copy = id; + Main temp_bmain = {0}; + SpinLock lock; + temp_bmain.lock = (MainLock *)&lock; + BLI_spin_init(&lock); + +#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 = id_copy(&temp_bmain, (ID *)id_for_copy, newid, false); + +#ifdef NESTED_ID_NASTY_WORKAROUND + if (result) { + nested_id_hack_restore_pointers(id, *newid); + } +#endif + + BLI_spin_end(&lock); + return result; +} + +/* Similar to BKE_scene_copy() but does not require main. + * + * TODO(sergey): Get rid of this once T51804 is handled. + */ +Scene *scene_copy_no_main(Scene *scene) +{ + const ID *id_for_copy = &scene->id; + Main temp_bmain = {0}; + SpinLock lock; + temp_bmain.lock = (MainLock *)&lock; + BLI_spin_init(&lock); + +#ifdef NESTED_ID_NASTY_WORKAROUND + NestedIDHackTempStorage id_hack_storage; + id_for_copy = nested_id_hack_get_discarded_pointers(&id_hack_storage, + &scene->id); +#endif + + Scene *new_scene = BKE_scene_copy(&temp_bmain, + (Scene *)id_for_copy, + SCE_COPY_LINK_OB); + +#ifdef NESTED_ID_NASTY_WORKAROUND + nested_id_hack_restore_pointers(&scene->id, &new_scene->id); +#endif + + BLI_spin_end(&lock); + return new_scene; +} + +/* Callback for BKE_library_foreach_ID_link which remaps original ID pointer + * with the one created by CoW system. + */ +int foreach_libblock_remap_callback(void *user_data, + ID * /*id_self*/, + ID **id_p, + int /*cb_flag*/) +{ + Depsgraph *depsgraph = (Depsgraph *)user_data; + if (*id_p != NULL) { + const ID *id_orig = *id_p; + ID *id_cow = depsgraph->get_cow_id(id_orig); + if (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; +} + +/* Check whether given ID is expanded or still a shallow copy. */ +BLI_INLINE bool check_datablock_expanded(ID *id_cow) +{ + return (id_cow->name[0] != '\0'); +} + +/* 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 short 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; + (void) object_cow; /* Ignored for release builds. */ + BLI_assert(object_cow->derivedFinal == NULL); + BLI_assert(object_cow->derivedDeform == NULL); + break; + } + case ID_ME: + { + /* For meshes we need to update edit_brtmesh 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); + } + break; + } + case ID_SCE: + { + const Scene *scene_orig = (const Scene *)id_orig; + Scene *scene_cow = (Scene *)id_cow; + if (scene_orig->obedit != NULL) { + scene_cow->obedit = (Object *)depsgraph->get_cow_id(&scene_orig->obedit->id); + } + else { + scene_cow->obedit = NULL; + } + break; + } + } +} + +/* Update copy-on-write version of datablock from it's original ID without re-building + * the whole datablock from scratch. + * + * Used for such special cases as scene collections and armatures, which can not use full + * re-alloc due to pointers used as function bindings. + */ +void update_copy_on_write_datablock(const Depsgraph *depsgraph, + const ID *id_orig, ID *id_cow) +{ + if (GS(id_orig->name) == ID_SCE) { + const Scene *scene_orig = (const Scene *)id_orig; + Scene *scene_cow = (Scene *)id_cow; + // Some non-pointer data sync, current frame for now. + // TODO(sergey): Are we missing something here? + scene_cow->r.cfra = scene_orig->r.cfra; + scene_cow->r.subframe = scene_orig->r.subframe; + // Update bases. + const SceneLayer *sl_orig = (SceneLayer *)scene_orig->render_layers.first; + SceneLayer *sl_cow = (SceneLayer *)scene_cow->render_layers.first; + while (sl_orig != NULL) { + // Update pointers to active base. + if (sl_orig->basact == NULL) { + sl_cow->basact = NULL; + } + else { + const Object *obact_orig = sl_orig->basact->object; + Object *obact_cow = (Object *)depsgraph->get_cow_id(&obact_orig->id); + sl_cow->basact = BKE_scene_layer_base_find(sl_cow, obact_cow); + } + // Update base flags. + // + // TODO(sergey): We should probably check visibled/selectabled + // flag here? + const Base *base_orig = (Base *)sl_orig->object_bases.first; + Base *base_cow = (Base *)sl_cow->object_bases.first;; + while (base_orig != NULL) { + base_cow->flag = base_orig->flag; + base_orig = base_orig->next; + base_cow = base_cow->next; + } + sl_orig = sl_orig->next; + sl_cow = sl_cow->next; + } + // Update edit object pointer. + if (scene_orig->obedit != NULL) { + scene_cow->obedit = (Object *)depsgraph->get_cow_id(&scene_orig->obedit->id); + } + else { + scene_cow->obedit = NULL; + } + // TODO(sergey): Things which are still missing here: + // - Active render engine. + // - Something else? + } +} + +/* 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) +{ + const ID *id_orig = id_node->id_orig; + ID *id_cow = id_node->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. */ + 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; + /* 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 short type = GS(id_orig->name); + switch (type) { + case ID_SCE: + { + Scene *new_scene = scene_copy_no_main((Scene *)id_orig); + *(Scene *)id_cow = *new_scene; + MEM_freeN(new_scene); + done = true; + 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; + } + } + if (!done) { + ID *newid; + if (id_copy_no_main(id_orig, &newid)) { + /* We copy contents of new ID to our CoW placeholder and free ID memory + * returned by id_copy(). + * + * TODO(sergey): We can avoid having extra ID allocation here if we'll + * have some smarter id_copy() which can use externally allocated memory. + */ + const size_t size = BKE_libblock_get_alloc_info(GS(newid->name), NULL); + memcpy(id_cow, newid, size); + MEM_freeN(newid); + done = true; + } + } + 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 + + BKE_library_foreach_ID_link(NULL, + id_cow, + foreach_libblock_remap_callback, + (void *)depsgraph, + IDWALK_NOP); + /* Correct or tweak some pointers which are not taken care by foreach + * from above. + */ + update_special_pointers(depsgraph, id_orig, id_cow); + 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) +{ + 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); +} + +ID *deg_update_copy_on_write_datablock(const Depsgraph *depsgraph, + const IDDepsNode *id_node) +{ + const ID *id_orig = id_node->id_orig; + ID *id_cow = id_node->id_cow; + /* Special case for datablocks which are expanded at the dependency graph + * construction time. This datablocks must never change pointers of their + * nested data since it is used for function bindings. + */ + if (GS(id_orig->name) == ID_SCE) { + BLI_assert(check_datablock_expanded(id_cow) == true); + update_copy_on_write_datablock(depsgraph, id_orig, id_cow); + return id_cow; + } + /* For the rest if datablock types we use simple logic: + * - Free previously expanded data, if any. + * - Perform full datablock copy. + */ + deg_free_copy_on_write_datablock(id_cow); + deg_expand_copy_on_write_datablock(depsgraph, id_node); + 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 short type = GS(id_cow->name); + 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. + */ + BKE_scene_free_ex((Scene *)id_cow, false); + BKE_libblock_free_data(id_cow, false); + id_cow->name[0] = '\0'; + return; + } + } +#ifdef NESTED_ID_NASTY_WORKAROUND + nested_id_hack_discard_pointers(id_cow); +#endif + BKE_libblock_free_datablock(id_cow); + BKE_libblock_free_data(id_cow, false); + /* Signal datablock as not being expanded. */ + id_cow->name[0] = '\0'; +} + +void deg_evaluate_copy_on_write(EvaluationContext * /*eval_ctx*/, + const Depsgraph *depsgraph, + const IDDepsNode *id_node) +{ + DEBUG_PRINT("%s on %s\n", __func__, id_node->id_orig->name); + deg_update_copy_on_write_datablock(depsgraph, id_node); +} + +bool deg_validate_copy_on_write_datablock(ID *id_cow) +{ + if (id_cow == NULL) { + return false; + } + ValidateData data; + data.is_valid = true; + BKE_library_foreach_ID_link(NULL, + id_cow, + foreach_libblock_validate_callback, + &data, + IDWALK_NOP); + return data.is_valid; +} + +} // 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..0e1b6642002 --- /dev/null +++ b/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.h @@ -0,0 +1,84 @@ +/* + * ***** 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 + +struct EvaluationContext; +struct ID; + +/* Unkomment this to have verbose log about original and CoW pointers + * logged, with detailed information when they are allocated, expanded + * and remapped. + */ +// #define DEG_DEBUG_COW_POINTERS + +#ifdef DEG_DEBUG_COW_POINTERS +# define DEG_COW_PRINT(format, ...) printf(format, __VA_ARGS__); +#else +# define DEG_COW_PRINT(format, ...) +#endif + +namespace DEG { + +struct Depsgraph; +struct IDDepsNode; + +/* Get fully expanded (ready for use) copy-on-write datablock for the given + * original datablock. + */ +ID *deg_expand_copy_on_write_datablock(const Depsgraph *depsgraph, + const IDDepsNode *id_node); +ID *deg_expand_copy_on_write_datablock(const struct Depsgraph *depsgraph, + struct ID *id_orig); + +/* Makes sure given CoW datablock is brought back to state of the original + * datablock. + */ +ID *deg_update_copy_on_write_datablock(const 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 EvaluationContext *eval_ctx, + const struct Depsgraph *depsgraph, + const struct IDDepsNode *id_node); + +/* Check that given ID is propely expanded and does not have any shallow + * copies inside. + */ +bool deg_validate_copy_on_write_datablock(ID *id_cow); + +} // namespace DEG diff --git a/source/blender/depsgraph/intern/eval/deg_eval_debug.cc b/source/blender/depsgraph/intern/eval/deg_eval_debug.cc index 23f4adbaacd..e195fdd8ec8 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval_debug.cc +++ b/source/blender/depsgraph/intern/eval/deg_eval_debug.cc @@ -104,7 +104,7 @@ void DepsgraphDebug::task_started(Depsgraph *graph, BLI_spin_lock(&graph->lock); ComponentDepsNode *comp = node->owner; - ID *id = comp->owner->id; + ID *id = comp->owner->id_orig; DepsgraphStatsID *id_stats = get_id_stats(id, true); times_clear(id_stats->times); @@ -133,7 +133,7 @@ void DepsgraphDebug::task_completed(Depsgraph *graph, BLI_spin_lock(&graph->lock); ComponentDepsNode *comp = node->owner; - ID *id = comp->owner->id; + ID *id = comp->owner->id_orig; DepsgraphStatsID *id_stats = get_id_stats(id, true); times_add(id_stats->times, time); diff --git a/source/blender/depsgraph/intern/eval/deg_eval_flush.cc b/source/blender/depsgraph/intern/eval/deg_eval_flush.cc index 8894b2732a1..605ca990e07 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval_flush.cc +++ b/source/blender/depsgraph/intern/eval/deg_eval_flush.cc @@ -120,10 +120,12 @@ void deg_graph_flush_updates(Main *bmain, Depsgraph *graph) * NOTE: Count how many nodes we need to handle - entry nodes may be * component nodes which don't count for this purpose! */ - GSET_FOREACH_BEGIN(OperationDepsNode *, node, graph->entry_tags) + GSET_FOREACH_BEGIN(OperationDepsNode *, op_node, graph->entry_tags) { - queue.push_back(node); - node->scheduled = true; + if ((op_node->flag & DEPSOP_FLAG_SKIP_FLUSH) == 0) { + queue.push_back(op_node); + op_node->scheduled = true; + } } GSET_FOREACH_END(); @@ -138,12 +140,24 @@ void deg_graph_flush_updates(Main *bmain, Depsgraph *graph) ComponentDepsNode *comp_node = node->owner; IDDepsNode *id_node = comp_node->owner; - ID *id = id_node->id; + /* TODO(sergey): Do we need to pass original or evaluated ID here? */ + ID *id = id_node->id_orig; if (id_node->done == 0) { deg_editors_id_update(bmain, id); lib_id_recalc_tag(bmain, id); /* TODO(sergey): For until we've got proper data nodes in the graph. */ lib_id_recalc_data_tag(bmain, id); + +#ifdef WITH_COPY_ON_WRITE + /* Currently this is needed to get ob->mesh to be replaced with + * original mesh (rather than being evaluated_mesh). + * + * TODO(sergey): This is something we need to avoid. + */ + ComponentDepsNode *cow_comp = + id_node->find_component(DEG_NODE_TYPE_COPY_ON_WRITE); + cow_comp->tag_update(graph); +#endif } if (comp_node->done == 0) { @@ -172,6 +186,7 @@ void deg_graph_flush_updates(Main *bmain, Depsgraph *graph) case DEG_NODE_TYPE_PARAMETERS: case DEG_NODE_TYPE_SEQUENCER: case DEG_NODE_TYPE_LAYER_COLLECTIONS: + case DEG_NODE_TYPE_COPY_ON_WRITE: /* Ignore, does not translate to object component. */ break; case DEG_NODE_TYPE_ANIMATION: diff --git a/source/blender/depsgraph/intern/nodes/deg_node.cc b/source/blender/depsgraph/intern/nodes/deg_node.cc index 401de7fe048..548911dcfa9 100644 --- a/source/blender/depsgraph/intern/nodes/deg_node.cc +++ b/source/blender/depsgraph/intern/nodes/deg_node.cc @@ -39,16 +39,20 @@ extern "C" { #include "DNA_ID.h" #include "DNA_anim_types.h" +#include "DNA_object_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_component.h" #include "intern/nodes/deg_node_operation.h" #include "intern/depsgraph_intern.h" #include "util/deg_util_foreach.h" +#include "util/deg_util_function.h" namespace DEG { @@ -163,17 +167,24 @@ void IDDepsNode::init(const ID *id, const char *UNUSED(subdata)) { /* Store ID-pointer. */ BLI_assert(id != NULL); - this->id = (ID *)id; + this->id_orig = (ID *)id; this->eval_flags = 0; 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... +#ifdef WITH_COPY_ON_WRITE + /* 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. */ + id_cow = (ID *)BKE_libblock_alloc_notest(GS(id->name)); + DEG_COW_PRINT("Create shallow copy for %s: id_orig=%p id_cow=%p\n", + id_orig->name, id_orig, id_cow); +#else + id_cow = id_orig; +#endif } /* Free 'id' node. */ @@ -182,6 +193,14 @@ IDDepsNode::~IDDepsNode() BLI_ghash_free(components, id_deps_node_hash_key_free, id_deps_node_hash_value_free); + +#ifdef WITH_COPY_ON_WRITE + /* Free memory used by this CoW ID. */ + 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); +#endif } ComponentDepsNode *IDDepsNode::find_component(eDepsNode_Type type, @@ -197,7 +216,7 @@ ComponentDepsNode *IDDepsNode::add_component(eDepsNode_Type type, ComponentDepsNode *comp_node = find_component(type, name); if (!comp_node) { DepsNodeFactory *factory = deg_get_node_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); @@ -211,10 +230,10 @@ void IDDepsNode::tag_update(Depsgraph *graph) { GHASH_FOREACH_BEGIN(ComponentDepsNode *, comp_node, components) { - /* TODO(sergey): What about drievrs? */ + /* TODO(sergey): What about drivers? */ 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); + AnimData *adt = BKE_animdata_from_id(id_orig); /* Animation data might be null if relations are tagged for update. */ if (adt != NULL && (adt->recalc & ADT_RECALC_ANIM)) { do_component_tag = true; @@ -227,11 +246,12 @@ void IDDepsNode::tag_update(Depsgraph *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.h b/source/blender/depsgraph/intern/nodes/deg_node.h index 7b5bd0137a6..4e03072d486 100644 --- a/source/blender/depsgraph/intern/nodes/deg_node.h +++ b/source/blender/depsgraph/intern/nodes/deg_node.h @@ -147,10 +147,11 @@ 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; diff --git a/source/blender/depsgraph/intern/nodes/deg_node_component.cc b/source/blender/depsgraph/intern/nodes/deg_node_component.cc index e1afb4d767c..bd9583a7b67 100644 --- a/source/blender/depsgraph/intern/nodes/deg_node_component.cc +++ b/source/blender/depsgraph/intern/nodes/deg_node_component.cc @@ -202,7 +202,7 @@ OperationDepsNode *ComponentDepsNode::add_operation(const DepsEvalOperationCb& o OperationDepsNode *op_node = has_operation(opcode, name, name_tag); if (!op_node) { DepsNodeFactory *factory = deg_get_node_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); @@ -315,7 +315,7 @@ OperationDepsNode *ComponentDepsNode::get_exit_operation() return NULL; } -void ComponentDepsNode::finalize_build() +void ComponentDepsNode::finalize_build(Depsgraph * /*graph*/) { operations.reserve(BLI_ghash_size(operations_map)); GHASH_FOREACH_BEGIN(OperationDepsNode *, op_node, operations_map) @@ -406,6 +406,11 @@ static DepsNodeFactoryImpl<CacheComponentDepsNode> DNTI_CACHE; DEG_DEPSNODE_DEFINE(LayerCollectionsDepsNode, DEG_NODE_TYPE_LAYER_COLLECTIONS, "Layer Collections Component"); static DepsNodeFactoryImpl<LayerCollectionsDepsNode> DNTI_LAYER_COLLECTIONS; +/* Copy-on-write Defines ============================ */ + +DEG_DEPSNODE_DEFINE(CopyOnWriteDepsNode, DEG_NODE_TYPE_COPY_ON_WRITE, "Copy-on-Write Component"); +static DepsNodeFactoryImpl<CopyOnWriteDepsNode> DNTI_COPY_ON_WRITE; + /* Node Types Register =================================== */ void deg_register_component_depsnodes() @@ -426,6 +431,8 @@ void deg_register_component_depsnodes() deg_register_node_typeinfo(&DNTI_CACHE); deg_register_node_typeinfo(&DNTI_LAYER_COLLECTIONS); + + deg_register_node_typeinfo(&DNTI_COPY_ON_WRITE); } } // 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 0d68f103c69..955d197b33a 100644 --- a/source/blender/depsgraph/intern/nodes/deg_node_component.h +++ b/source/blender/depsgraph/intern/nodes/deg_node_component.h @@ -131,7 +131,7 @@ struct ComponentDepsNode : public DepsNode { OperationDepsNode *get_entry_operation(); OperationDepsNode *get_exit_operation(); - void finalize_build(); + void finalize_build(Depsgraph *graph); IDDepsNode *owner; @@ -208,6 +208,10 @@ struct LayerCollectionsDepsNode : public ComponentDepsNode { DEG_DEPSNODE_DECLARE; }; +struct CopyOnWriteDepsNode : public ComponentDepsNode { + DEG_DEPSNODE_DECLARE; +}; + void deg_register_component_depsnodes(); diff --git a/source/blender/depsgraph/intern/nodes/deg_node_operation.cc b/source/blender/depsgraph/intern/nodes/deg_node_operation.cc index 7467264f612..84b3d33f494 100644 --- a/source/blender/depsgraph/intern/nodes/deg_node_operation.cc +++ b/source/blender/depsgraph/intern/nodes/deg_node_operation.cc @@ -76,6 +76,9 @@ string OperationDepsNode::full_identifier() const void OperationDepsNode::tag_update(Depsgraph *graph) { + if (flag & DEPSOP_FLAG_SKIP_FLUSH) { + flag &= ~DEPSOP_FLAG_SKIP_FLUSH; + } if (flag & DEPSOP_FLAG_NEEDS_UPDATE) { return; } diff --git a/source/blender/depsgraph/intern/nodes/deg_node_operation.h b/source/blender/depsgraph/intern/nodes/deg_node_operation.h index 1e5c3832d03..8a1fadd9c6c 100644 --- a/source/blender/depsgraph/intern/nodes/deg_node_operation.h +++ b/source/blender/depsgraph/intern/nodes/deg_node_operation.h @@ -49,7 +49,12 @@ typedef enum eDepsOperation_Flag { /* Operation is evaluated using CPython; has GIL and security * implications... */ - DEPSOP_FLAG_USES_PYTHON = (1 << 2), + DEPSOP_FLAG_USES_PYTHON = (1 << 2), + + /* Special flag which indicates that update tag sohuld not be flushed + * up to the dependent nodes. + */ + DEPSOP_FLAG_SKIP_FLUSH = (1 << 3), } eDepsOperation_Flag; /* Atomic Operation - Base type for all operations */ diff --git a/source/blender/editors/object/object_edit.c b/source/blender/editors/object/object_edit.c index b7012810fd1..2579b7db419 100644 --- a/source/blender/editors/object/object_edit.c +++ b/source/blender/editors/object/object_edit.c @@ -281,6 +281,9 @@ void ED_object_editmode_exit(bContext *C, int flag) } if (flag & EM_WAITCURSOR) waitcursor(0); + + /* This way we ensure scene's obedit is copied into all CoW scenes. */ + DEG_id_tag_update(&scene->id, 0); } @@ -396,6 +399,8 @@ void ED_object_editmode_enter(bContext *C, int flag) if (ok) { DEG_id_tag_update(&ob->id, OB_RECALC_DATA); + /* This way we ensure scene's obedit is copied into all CoW scenes. */ + DEG_id_tag_update(&scene->id, 0); } else { scene->obedit = NULL; /* XXX for context */ diff --git a/source/blender/makesdna/DNA_object_types.h b/source/blender/makesdna/DNA_object_types.h index 8ec5e4d763d..160f7f7feff 100644 --- a/source/blender/makesdna/DNA_object_types.h +++ b/source/blender/makesdna/DNA_object_types.h @@ -326,7 +326,11 @@ typedef struct Object { ListBase drawdata; /* runtime, ObjectEngineData */ int deg_update_flag; /* what has been updated in this object */ int select_color; - int pad3[2]; + + /* Mesh structure createrd during object evaluaiton. + * It has all modifiers applied. + */ + struct Mesh *mesh_evaluated; } Object; /* Warning, this is not used anymore because hooks are now modifiers */ diff --git a/source/blender/makesrna/intern/rna_depsgraph.c b/source/blender/makesrna/intern/rna_depsgraph.c index 5047bee6b45..9ea12e3befa 100644 --- a/source/blender/makesrna/intern/rna_depsgraph.c +++ b/source/blender/makesrna/intern/rna_depsgraph.c @@ -224,6 +224,11 @@ static PointerRNA rna_Depsgraph_duplis_get(CollectionPropertyIterator *iter) return rna_pointer_inherit_refine(&iter->parent, &RNA_DepsgraphIter, iterator); } +static ID *rna_Depsgraph_evaluated_id_get(Depsgraph *depsgraph, ID *id_orig) +{ + return DEG_get_evaluated_id(depsgraph, id_orig); +} + #else static void rna_def_depsgraph_iter(BlenderRNA *brna) @@ -317,6 +322,12 @@ static void rna_def_depsgraph(BlenderRNA *brna) "rna_Depsgraph_objects_get", NULL, NULL, NULL, NULL); + func = RNA_def_function(srna, "evaluated_id_get", "rna_Depsgraph_evaluated_id_get"); + parm = RNA_def_pointer(func, "id", "ID", "", "Original ID to get evaluated complementary part for"); + RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + parm = RNA_def_pointer(func, "evaluated_id", "ID", "", "Evaluated ID for the given original one"); + RNA_def_function_return(func, parm); + /* TODO(sergey): Find a better name. */ prop = RNA_def_property(srna, "duplis", PROP_COLLECTION, PROP_NONE); RNA_def_property_struct_type(prop, "DepsgraphIter"); |