Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt4
-rw-r--r--intern/cycles/blender/blender_shader.cpp24
-rw-r--r--source/blender/blenkernel/CMakeLists.txt4
-rw-r--r--source/blender/blenkernel/intern/object.c11
-rw-r--r--source/blender/blenkernel/intern/object_update.c48
-rw-r--r--source/blender/depsgraph/CMakeLists.txt6
-rw-r--r--source/blender/depsgraph/DEG_depsgraph.h25
-rw-r--r--source/blender/depsgraph/DEG_depsgraph_query.h3
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder.cc9
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_nodes.cc29
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_nodes.h2
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_nodes_layer.cc14
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_nodes_scene.cc7
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_relations.cc80
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_relations.h12
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_relations_scene.cc3
-rw-r--r--source/blender/depsgraph/intern/debug/deg_debug_graphviz.cc2
-rw-r--r--source/blender/depsgraph/intern/depsgraph.cc17
-rw-r--r--source/blender/depsgraph/intern/depsgraph.h5
-rw-r--r--source/blender/depsgraph/intern/depsgraph_build.cc3
-rw-r--r--source/blender/depsgraph/intern/depsgraph_query.cc27
-rw-r--r--source/blender/depsgraph/intern/depsgraph_tag.cc25
-rw-r--r--source/blender/depsgraph/intern/depsgraph_type_defines.cc9
-rw-r--r--source/blender/depsgraph/intern/depsgraph_types.h55
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc650
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.h84
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval_debug.cc4
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval_flush.cc23
-rw-r--r--source/blender/depsgraph/intern/nodes/deg_node.cc38
-rw-r--r--source/blender/depsgraph/intern/nodes/deg_node.h5
-rw-r--r--source/blender/depsgraph/intern/nodes/deg_node_component.cc11
-rw-r--r--source/blender/depsgraph/intern/nodes/deg_node_component.h6
-rw-r--r--source/blender/depsgraph/intern/nodes/deg_node_operation.cc3
-rw-r--r--source/blender/depsgraph/intern/nodes/deg_node_operation.h7
-rw-r--r--source/blender/editors/object/object_edit.c5
-rw-r--r--source/blender/makesdna/DNA_object_types.h6
-rw-r--r--source/blender/makesrna/intern/rna_depsgraph.c11
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");