diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc index 5910a7d8338..81bb8454976 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc @@ -144,6 +144,12 @@ void constraint_walk(bConstraint * /*con*/, } } +void free_copy_on_write_datablock(void *id_v) +{ + ID *id = (ID *)id_v; + deg_free_copy_on_write_datablock(id); +} + } /* namespace */ /* ************ */ @@ -153,18 +159,30 @@ void constraint_walk(bConstraint * /*con*/, DepsgraphNodeBuilder::DepsgraphNodeBuilder(Main *bmain, Depsgraph *graph) : m_bmain(bmain), - m_graph(graph) + m_graph(graph), + m_cow_id_hash(NULL) { } DepsgraphNodeBuilder::~DepsgraphNodeBuilder() { + if (m_cow_id_hash != NULL) { + BLI_ghash_free(m_cow_id_hash, NULL, free_copy_on_write_datablock); + } } -IDDepsNode *DepsgraphNodeBuilder::add_id_node(ID *id) +IDDepsNode *DepsgraphNodeBuilder::add_id_node(ID *id, bool do_tag) { - IDDepsNode *id_node = m_graph->add_id_node(id); #ifdef WITH_COPY_ON_WRITE + IDDepsNode *id_node = NULL; + ID *id_cow = (ID *)BLI_ghash_lookup(m_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(m_cow_id_hash, id, NULL, NULL); + } + id_node = m_graph->add_id_node(id, do_tag, id_cow); /* Currently all ID nodes are supposed to have copy-on-write logic. * * NOTE: Zero number of components indicates that ID node was just created. @@ -178,6 +196,8 @@ IDDepsNode *DepsgraphNodeBuilder::add_id_node(ID *id) "", -1); m_graph->operations.push_back(op_cow); } +#else + IDDepsNode *id_node = m_graph->add_id_node(id); #endif return id_node; } @@ -301,7 +321,7 @@ ID *DepsgraphNodeBuilder::ensure_cow_id(ID *id_orig) /* ID is already remapped to copy-on-write. */ return id_orig; } - IDDepsNode *id_node = m_graph->add_id_node(id_orig, false); + IDDepsNode *id_node = add_id_node(id_orig, false); return id_node->id_cow; } @@ -328,11 +348,36 @@ void DepsgraphNodeBuilder::begin_build(Main *bmain) { /* XXX nested node trees are not included in tag-clearing above, * so we need to do this manually. */ - FOREACH_NODETREE(bmain, nodetree, id) { + FOREACH_NODETREE(bmain, nodetree, id) + { if (id != (ID *)nodetree) { nodetree->id.tag &= ~LIB_TAG_DOIT; } - } FOREACH_NODETREE_END + } + FOREACH_NODETREE_END; + +#ifdef WITH_COPY_ON_WRITE + /* Store existing copy-on-write versions of datablock, so we can re-use + * them for new ID nodes. + */ + m_cow_id_hash = BLI_ghash_ptr_new("Depsgraph id hash"); + GHASH_FOREACH_BEGIN(IDDepsNode *, id_node, m_graph->id_hash) + { + if (GS(id_node->id_orig->name) != ID_SCE) { + continue; + } + if (deg_copy_on_write_is_expanded(id_node->id_cow)) { + BLI_ghash_insert(m_cow_id_hash, id_node->id_orig, id_node->id_cow); + id_node->id_cow = NULL; + } + } + GHASH_FOREACH_END(); +#endif + + /* Make sure graph has no nodes left from previous state. */ + m_graph->clear_all_nodes(); + m_graph->operations.clear(); + BLI_gset_clear(m_graph->entry_tags, NULL); } void DepsgraphNodeBuilder::build_group(Scene *scene, Group *group) diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.h b/source/blender/depsgraph/intern/builder/deg_builder_nodes.h index 279cc92397b..7e28df1276d 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.h +++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.h @@ -107,7 +107,7 @@ struct DepsgraphNodeBuilder { void begin_build(Main *bmain); - IDDepsNode *add_id_node(ID *id); + IDDepsNode *add_id_node(ID *id, bool do_tag = true); TimeSourceDepsNode *add_time_source(); ComponentDepsNode *add_component_node(ID *id, @@ -206,6 +206,7 @@ struct DepsgraphNodeBuilder { protected: Main *m_bmain; Depsgraph *m_graph; + GHash *m_cow_id_hash; }; } // 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 70fbcd3d58a..861aa9521c0 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc @@ -1917,7 +1917,7 @@ void DepsgraphRelationBuilder::build_copy_on_write_relations(IDDepsNode *id_node 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"); + // 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. */ diff --git a/source/blender/depsgraph/intern/depsgraph.cc b/source/blender/depsgraph/intern/depsgraph.cc index 868e7c0e3e5..9664d389794 100644 --- a/source/blender/depsgraph/intern/depsgraph.cc +++ b/source/blender/depsgraph/intern/depsgraph.cc @@ -279,14 +279,14 @@ IDDepsNode *Depsgraph::find_id_node(const ID *id) const return reinterpret_cast(BLI_ghash_lookup(id_hash, id)); } -IDDepsNode *Depsgraph::add_id_node(ID *id, bool do_tag) +IDDepsNode *Depsgraph::add_id_node(ID *id, bool do_tag, ID *id_cow_hint) { BLI_assert((id->tag & LIB_TAG_COPY_ON_WRITE) == 0); IDDepsNode *id_node = find_id_node(id); if (!id_node) { DepsNodeFactory *factory = deg_get_node_factory(DEG_NODE_TYPE_ID_REF); id_node = (IDDepsNode *)factory->create_node(id, "", id->name); - id_node->init_copy_on_write(); + id_node->init_copy_on_write(id_cow_hint); if (do_tag) { id->tag |= LIB_TAG_DOIT; } @@ -310,6 +310,12 @@ void Depsgraph::clear_id_nodes() /* Stupid workaround to ensure we free IDs in a proper order. */ GHASH_FOREACH_BEGIN(IDDepsNode *, id_node, id_hash) { + 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; } diff --git a/source/blender/depsgraph/intern/depsgraph.h b/source/blender/depsgraph/intern/depsgraph.h index 6290dbe4ba7..823a1935e18 100644 --- a/source/blender/depsgraph/intern/depsgraph.h +++ b/source/blender/depsgraph/intern/depsgraph.h @@ -114,7 +114,7 @@ struct Depsgraph { TimeSourceDepsNode *find_time_source() const; IDDepsNode *find_id_node(const ID *id) const; - IDDepsNode *add_id_node(ID *id, bool do_tag = true); + IDDepsNode *add_id_node(ID *id, bool do_tag = true, ID *id_cow_hint = NULL); void clear_id_nodes(); /* Add new relationship between two nodes. */ diff --git a/source/blender/depsgraph/intern/depsgraph_build.cc b/source/blender/depsgraph/intern/depsgraph_build.cc index 68f14790d87..e21c3d29aa5 100644 --- a/source/blender/depsgraph/intern/depsgraph_build.cc +++ b/source/blender/depsgraph/intern/depsgraph_build.cc @@ -257,6 +257,7 @@ void DEG_graph_tag_relations_update(Depsgraph *graph) /* Tag all relations for update. */ void DEG_relations_tag_update(Main *bmain) { + DEG_DEBUG_PRINTF("%s: Tagging relations for update.\n", __func__); for (Scene *scene = (Scene *)bmain->scene.first; scene != NULL; scene = (Scene *)scene->id.next) @@ -285,11 +286,6 @@ void DEG_scene_relations_update(Main *bmain, Scene *scene) 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, diff --git a/source/blender/depsgraph/intern/depsgraph_tag.cc b/source/blender/depsgraph/intern/depsgraph_tag.cc index 0c4bc45eb3a..38919fb890e 100644 --- a/source/blender/depsgraph/intern/depsgraph_tag.cc +++ b/source/blender/depsgraph/intern/depsgraph_tag.cc @@ -288,6 +288,16 @@ void deg_graph_id_tag_update(Main *bmain, Depsgraph *graph, ID *id, int flag) } if (flag & OB_RECALC_DATA) { id_tag_update_object_data(graph, id_node); +#ifdef WITH_COPY_ON_WRITE + if (flag & DEG_TAG_COPY_ON_WRITE) { + const short id_type = GS(id_node->id_orig->name); + if (id_type == ID_OB) { + Object *object = (Object *)id_node->id_orig; + ID *ob_data = (ID *)object->data; + DEG_id_tag_update_ex(bmain, ob_data, flag); + } + } +#endif } if (flag & OB_RECALC_TIME) { id_tag_update_object_time(graph, id_node); @@ -337,16 +347,7 @@ void deg_graph_on_visible_update(Main *bmain, Scene *scene, Depsgraph *graph) * TODO(sergey): Need to generalize this somehow. */ if (id_type == ID_OB) { - Object *object = (Object *)id_node->id_orig; - flag |= OB_RECALC_OB; - if (ELEM(object->type, OB_MESH, - OB_CURVE, - OB_SURF, - OB_FONT, - OB_MBALL)) - { - flag |= OB_RECALC_DATA; - } + flag |= OB_RECALC_OB | OB_RECALC_DATA | DEG_TAG_COPY_ON_WRITE; } deg_graph_id_tag_update(bmain, graph, id_node->id_orig, flag); } 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 index a0642210664..3ed93ea5677 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc +++ b/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc @@ -661,7 +661,13 @@ ID *deg_expand_copy_on_write_datablock(const Depsgraph *depsgraph, 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); + /* 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. diff --git a/source/blender/depsgraph/intern/nodes/deg_node.cc b/source/blender/depsgraph/intern/nodes/deg_node.cc index 8706ec94d52..cc305665594 100644 --- a/source/blender/depsgraph/intern/nodes/deg_node.cc +++ b/source/blender/depsgraph/intern/nodes/deg_node.cc @@ -183,8 +183,13 @@ void IDDepsNode::init_copy_on_write(ID *id_cow_hint) * it is actually needed. */ if (id_cow_hint != NULL) { - BLI_assert(deg_copy_on_write_is_needed(id_orig)); - id_cow = id_cow_hint; + // 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));