Depsgraph: Initial groundwork for copy-on-write support

< Dependency graph Copy-on-Write >
 --------------------------------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||

This is an initial commit of Copy-on-write support added to dependency graph.
Main priority for now: get playback (Alt-A) and all operators (selection,
transform etc) to work with the new concept of clear separation between
evaluated data coming from dependency graph and original data coming from
.blend file (and stored in bmain).

= How does this work? =

The idea is to support Copy-on-Write on the ID level. This means, we duplicate
the whole ID before we cann it's evaluaiton function. This is currently done
in the following way:

- At the depsgraph construction time we create "shallow" copy of the ID
  datablock, just so we know it's pointer in memory and can use for function
  bindings.

- At the evaluaiton time, the copy of ID get's "expanded" (needs a better
  name internally, so it does not conflict with expanding datablocks during
  library linking), which means the content of the datablock is being
  copied over and all IDs are getting remapped to the copied ones.

  Currently we do the whole copy, in the future we will support some tricks
  here to prevent duplicating geometry arrays (verts, edges, loops, faces
  and polys) when we don't need that.

- Evaluation functions are operating on copied datablocks and never touching
  original datablock.

- There are some cases when we need to know non-ID pointers for function
  bindings. This mainly applies to scene collections and armatures. The
  idea of dealing with this is to "expand" copy-on-write datablock at
  the dependency graph build time. This might introduce some slowdown to the
  dependency graph construction time, but allows us to have minimal changes
  in the code and avoid any hash look-up from evaluation function (one of
  the ideas to avoid using pointers as function bindings is to pass name
  of layer or a bone to the evaluation function and look up actual data based
  on that name).

  Currently there is a special function in depsgraph which does such a
  synchronization, in the future we might want to make it more generic.

At some point we need to synchronize copy-on-write version of datablock with
the original version. This happens, i.e., when we change active object or
change selection. We don't want any actual evaluation of update flush happening
for such thins, so now we have a special update tag:

  DEG_id_tag_update((id, DEG_TAG_COPY_ON_WRITE)

- For the render engines we now have special call for the dependency graph to
  give evaluated datablock for the given original one. This isn't fully ideal
  but allows to have Cycles viewport render.

  This is definitely a subject for further investigation / improvement.

This call will tag copy-on-write component tagged for update without causing
updates to be flushed to any other objects, causing chain reaction of updates.
This tag is handy when selection in the scene changes.

This basically summarizes ideas underneath this commit. The code should be
reasonably documented.

Here is a demo of dependency graph with all copy-on-write stuff in it:

  https://developer.blender.org/F635468

= What to expect to (not) work? =

- Only meshes are properly-ish aware of copy-on-write currently, Non-mesh
  geometry will probably crash or will not work at all.

- Armatures will need similar depsgraph built-time expansion of the copied
  datablock.

- There are some extra tags / relations added, to keep things demo-able but
  which are slowing things down for evaluation.

- Edit mode works for until click selection is used (due to the selection
  code using EditDerivedMesh created ad-hoc).

- Lots of tools will lack tagging synchronization of copied datablock for
  sync with original ID.

= How to move forward? =

There is some tedious work related on going over all the tools, checking
whether they need to work with original or final evaluated object and make
the required changes.

Additionally, there need synchronization tag done in fair amount of tools
and operators as well. For example, currently it's not possible to change
render engine without re-opening the file or forcing dependency graph for
re-build via python console.

There is also now some thoughts required about copying evaluated properties
between objects or from collection to a new object. Perhaps easiest way
would be to move base flag flush to Object ID node and tag new objects for
update instead of doing manual copy.

here is some WIP patch which moves such evaluaiton / flush:

  https://developer.blender.org/F635479

Lots of TODOs in the code, with possible optimization.

= How to test? =

This is a feature under heavy development, so obviously it is disabled by
default. The only reason it goes to 2.8 branch is to avoid possible merge
hell.

In order to enable this feature use WITH_DEPSGRAPH_COPY_ON_WRITE CMake
configuration option.
This commit is contained in:
Sergey Sharybin 2017-06-14 10:26:24 +02:00
parent b4d053efc7
commit 802027f3f8
Notes: blender-bot 2023-02-14 10:43:47 +01:00
Referenced by commit aa6f0f3d1f, Depsgraph: remove mesh edit-mode pointer duplication
Referenced by commit 45720922f7, Revert "Temporarily disable material preview (T51796 workaround)"
Referenced by issue #51796, Workspace: Memleak in particles and customdata
Referenced by issue #51782, Workspace: Cycles material preview crash
37 changed files with 1186 additions and 95 deletions

View File

@ -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)

View File

@ -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 */

View File

@ -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}")

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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}")

View File

@ -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.
*

View File

@ -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 {

View File

@ -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();
}

View File

@ -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();

View File

@ -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();

View File

@ -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,

View File

@ -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

View File

@ -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

View File

@ -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);

View File

@ -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;

View File

@ -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()) {

View File

@ -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) {

View File

@ -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

View File

@ -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);

View File

@ -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(). */

View File

@ -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
}
}
}

View File

@ -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
}

View File

@ -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,
/* Rigidbody Sim - Perform Sim */
DEG_OPCODE_RIGIDBODY_REBUILD,
DEG_OPCODE_RIGIDBODY_SIM,
/* Rigidbody Sim - Copy Results to Object */
DEG_OPCODE_TRANSFORM_RIGIDBODY,
/* Transform exitpoint */
/* Transform exit point */
DEG_OPCODE_TRANSFORM_FINAL,
/* XXX: ubereval is for temporary porting purposes only */
/* Handle object-level updates, mainly proxies hacks and recalc flags. */
DEG_OPCODE_OBJECT_UBEREVAL,
/* Rigid body -------------------------------------- */
/* Perform Simulation */
DEG_OPCODE_RIGIDBODY_REBUILD,
DEG_OPCODE_RIGIDBODY_SIM,
/* Copy results to object */
DEG_OPCODE_TRANSFORM_RIGIDBODY,
/* 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;

View File

@ -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

View File

@ -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

View File

@ -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);

View File

@ -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:

View File

@ -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();
}

View File

@ -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;

View File

@ -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

View File

@ -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();

View File

@ -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;
}

View File

@ -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 */

View File

@ -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 */

View File

@ -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 */

View File

@ -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");