Depsgraph: Rework tagging and flushing routines

The goal is: have id->recalc flags set to components which got changed.
To make it possible for render engines to check on a more granular basis
what changed in the object. For example, is it a transform which changed
or is it just some ID property changed which has nothing to do with rendering.

The tricky part is: we don't want duplicated logic in tagging and flushing.
In order to avoid this duplication, we store ID recalc flag in the component
node type information. That type information could easily be accessed by both
tagging and flushing routines.

Remaining part of the changes are related on changing the way how tagging
works. The new idea here is to have utility function which maps update tag to
a component. This way we can easily set ID recalc flags right away. Without
any duplication of ID recalc flags set in multiple flag handler functions.

With all this being said, there should be no user measurable difference for
now, it's a gigantic basement for some upcoming work and fixes.
This commit is contained in:
Sergey Sharybin
2017-12-19 11:24:34 +01:00
parent e4849ad3a6
commit 4045a51a11
3 changed files with 190 additions and 286 deletions

View File

@@ -115,10 +115,6 @@ void deg_editors_id_update(const DEGEditorUpdateContext *update_ctx,
void deg_editors_scene_update(const DEGEditorUpdateContext *update_ctx,
bool updated);
/* Tagging helpers ------------------------------------------------------ */
void lib_id_recalc_tag(struct Main *bmain, struct ID *id);
#define DEG_DEBUG_PRINTF(...) \
do { \
if (G.debug & G_DEBUG_DEPSGRAPH) { \

View File

@@ -35,8 +35,9 @@
#include <queue>
#include "BLI_utildefines.h"
#include "BLI_task.h"
#include "BLI_listbase.h"
#include "BLI_math_bits.h"
#include "BLI_task.h"
extern "C" {
#include "DNA_object_types.h"
@@ -68,227 +69,57 @@ extern "C" {
#include "intern/depsgraph_intern.h"
#include "util/deg_util_foreach.h"
/* Define this in order to have more strict sanitization of what tagging flags
* are used for ID databnlocks. Ideally, we would always want this, but there
* are cases in generic modules (like IR remapping) where we don't want to spent
* lots of time trying to guess which components are to be updated.
*/
// #define STRICT_COMPONENT_TAGGING
/* *********************** */
/* Update Tagging/Flushing */
namespace DEG {
/* Data-Based Tagging ------------------------------- */
void lib_id_recalc_tag(Main *bmain, ID *id)
{
id->recalc |= ID_RECALC;
DEG_id_type_tag(bmain, GS(id->name));
}
namespace {
void deg_graph_id_tag_update(Main *bmain, Depsgraph *graph, ID *id, int flag);
void lib_id_recalc_tag_flag(Main *bmain, ID *id, int flag)
void depsgraph_geometry_tag_to_component(const ID *id,
eDepsNode_Type *component_type)
{
/* This bit of code ensures legacy object->recalc flags are still filled in
* the same way as it was expected with the old dependency graph.
*
* This is because some areas like motion paths and likely some other
* physics baking process are doing manual scene update on all the frames,
* trying to minimize number of updates.
*
* But this flag will also let us to re-construct entry nodes for update
* after relations update and after layer visibility changes.
*/
if (flag) {
if (flag & OB_RECALC_OB) {
lib_id_recalc_tag(bmain, id);
}
if (flag & (OB_RECALC_DATA)) {
if (GS(id->name) == ID_OB) {
Object *object = (Object *)id;
ID *object_data = (ID *)object->data;
if (object_data != NULL) {
lib_id_recalc_tag(bmain, object_data);
}
}
else {
// BLI_assert(!"Tagging non-object as object data update");
lib_id_recalc_tag(bmain, id);
}
}
if (flag & PSYS_RECALC) {
lib_id_recalc_tag(bmain, id);
}
}
else {
lib_id_recalc_tag(bmain, id);
}
}
/* Special tagging */
void id_tag_update_special_zero_flag(Depsgraph *graph, IDDepsNode *id_node)
{
/* NOTE: Full ID node update for now, need to minimize that in the future. */
id_node->tag_update(graph);
}
/* Tag corresponding to OB_RECALC_OB. */
void id_tag_update_object_transform(Depsgraph *graph, IDDepsNode *id_node)
{
ComponentDepsNode *transform_comp =
id_node->find_component(DEG_NODE_TYPE_TRANSFORM);
if (transform_comp == NULL) {
#ifdef STRICT_COMPONENT_TAGGING
DEG_ERROR_PRINTF("ERROR: Unable to find transform component for %s\n",
id_node->id_orig->name);
BLI_assert(!"This is not supposed to happen!");
#endif
return;
}
transform_comp->tag_update(graph);
}
/* Tag corresponding to OB_RECALC_DATA. */
void id_tag_update_object_data(Depsgraph *graph, IDDepsNode *id_node)
{
const ID_Type id_type = GS(id_node->id_orig->name);
ComponentDepsNode *data_comp = NULL;
const ID_Type id_type = GS(id->name);
switch (id_type) {
case ID_OB:
{
const Object *object = (Object *)id_node->id_orig;
const Object *object = (Object *)id;
switch (object->type) {
case OB_MESH:
case OB_CURVE:
case OB_SURF:
case OB_FONT:
case OB_MBALL:
data_comp = id_node->find_component(DEG_NODE_TYPE_GEOMETRY);
*component_type = DEG_NODE_TYPE_GEOMETRY;
break;
case OB_ARMATURE:
data_comp = id_node->find_component(DEG_NODE_TYPE_EVAL_POSE);
*component_type = DEG_NODE_TYPE_EVAL_POSE;
break;
/* TODO(sergey): More cases here? */
/* TODO(sergey): More cases here? */
}
break;
}
case ID_ME:
data_comp = id_node->find_component(DEG_NODE_TYPE_GEOMETRY);
*component_type = DEG_NODE_TYPE_GEOMETRY;
break;
case ID_PA:
return;
case ID_LP:
data_comp = id_node->find_component(DEG_NODE_TYPE_PARAMETERS);
*component_type = DEG_NODE_TYPE_PARAMETERS;
break;
default:
break;
}
if (data_comp == NULL) {
#ifdef STRICT_COMPONENT_TAGGING
DEG_ERROR_PRINTF("ERROR: Unable to find data component for %s\n",
id_node->id_orig->name);
BLI_assert(!"This is not supposed to happen!");
#endif
return;
}
data_comp->tag_update(graph);
/* Special legacy compatibility code, tag data ID for update when object
* is tagged for data update.
*/
if (id_type == ID_OB) {
Object *object = (Object *)id_node->id_orig;
ID *data_id = (ID *)object->data;
if (data_id != NULL) {
IDDepsNode *data_id_node = graph->find_id_node(data_id);
// BLI_assert(data_id_node != NULL);
/* TODO(sergey): Do we want more granular tags here? */
/* TODO(sergey): Hrm, during some operations it's possible to have
* object node existing but not it's data. For example, when making
* objects local. This is valid situation, but how can we distinguish
* that from someone trying to do stupid things with dependency
* graph?
*/
if (data_id_node != NULL) {
data_id_node->tag_update(graph);
}
}
}
}
/* Tag corresponding to OB_RECALC_TIME. */
void id_tag_update_object_time(Depsgraph *graph, IDDepsNode *id_node)
void depsgraph_select_tag_to_component_opcode(
const ID *id,
eDepsNode_Type *component_type,
eDepsOperation_Code *operation_code)
{
ComponentDepsNode *animation_comp =
id_node->find_component(DEG_NODE_TYPE_ANIMATION);
if (animation_comp == NULL) {
/* It's not necessarily we've got animation component in cases when
* we are tagging for time updates.
*/
return;
}
animation_comp->tag_update(graph);
/* TODO(sergey): More components to tag here? */
}
void id_tag_update_particle(Depsgraph *graph, IDDepsNode *id_node, int tag)
{
ComponentDepsNode *particle_comp =
id_node->find_component(DEG_NODE_TYPE_PARAMETERS);
ParticleSettings *particle_settings = (ParticleSettings *)id_node->id_orig;
particle_settings->recalc |= (tag & PSYS_RECALC);
if (particle_comp == NULL) {
#ifdef STRICT_COMPONENT_TAGGING
DEG_ERROR_PRINTF("ERROR: Unable to find particle component for %s\n",
id_node->id_orig->name);
BLI_assert(!"This is not supposed to happen!");
#endif
return;
}
particle_comp->tag_update(graph);
}
void id_tag_update_shading(Depsgraph *graph, IDDepsNode *id_node)
{
ComponentDepsNode *shading_comp;
if (GS(id_node->id_orig->name) == ID_NT) {
shading_comp = id_node->find_component(DEG_NODE_TYPE_SHADING_PARAMETERS);
}
else {
shading_comp = id_node->find_component(DEG_NODE_TYPE_SHADING);
}
if (shading_comp == NULL) {
#ifdef STRICT_COMPONENT_TAGGING
DEG_ERROR_PRINTF("ERROR: Unable to find shading component for %s\n",
id_node->id_orig->name);
BLI_assert(!"This is not supposed to happen!");
#endif
return;
}
shading_comp->tag_update(graph);
}
/* Tag corresponding to DEG_TAG_COPY_ON_WRITE. */
void id_tag_update_copy_on_write(Depsgraph *graph, IDDepsNode *id_node)
{
if (!DEG_depsgraph_use_copy_on_write()) {
return;
}
ComponentDepsNode *cow_comp =
id_node->find_component(DEG_NODE_TYPE_COPY_ON_WRITE);
OperationDepsNode *cow_node = cow_comp->get_entry_operation();
cow_node->tag_update(graph);
}
void id_tag_update_select_update(Depsgraph *graph, IDDepsNode *id_node)
{
ComponentDepsNode *component;
OperationDepsNode *node = NULL;
const ID_Type id_type = GS(id_node->id_orig->name);
const ID_Type id_type = GS(id->name);
if (id_type == ID_SCE) {
/* We need to flush base flags to all objects in a scene since we
* don't know which ones changed. However, we don't want to update
@@ -299,62 +130,107 @@ void id_tag_update_select_update(Depsgraph *graph, IDDepsNode *id_node)
* does nothing and which is only used to cascade flush down the
* road.
*/
component = id_node->find_component(DEG_NODE_TYPE_LAYER_COLLECTIONS);
BLI_assert(component != NULL);
if (component != NULL) {
node = component->find_operation(DEG_OPCODE_VIEW_LAYER_DONE);
}
*component_type = DEG_NODE_TYPE_LAYER_COLLECTIONS;
*operation_code = DEG_OPCODE_VIEW_LAYER_DONE;
}
else if (id_type == ID_OB) {
component = id_node->find_component(DEG_NODE_TYPE_LAYER_COLLECTIONS);
/* NOTE: This component might be missing for indirectly linked
* objects.
*/
if (component != NULL) {
node = component->find_operation(DEG_OPCODE_OBJECT_BASE_FLAGS);
}
*component_type = DEG_NODE_TYPE_LAYER_COLLECTIONS;
*operation_code = DEG_OPCODE_OBJECT_BASE_FLAGS;
}
else {
component = id_node->find_component(DEG_NODE_TYPE_BATCH_CACHE);
BLI_assert(component != NULL);
if (component != NULL) {
node = component->find_operation(DEG_OPCODE_GEOMETRY_SELECT_UPDATE,
"", -1);
}
}
if (node != NULL) {
node->tag_update(graph);
*component_type = DEG_NODE_TYPE_BATCH_CACHE;
*operation_code = DEG_OPCODE_GEOMETRY_SELECT_UPDATE;
}
}
void id_tag_update_base_flags(Depsgraph *graph, IDDepsNode *id_node)
void depsgraph_base_flags_tag_to_component_opcode(
const ID *id,
eDepsNode_Type *component_type,
eDepsOperation_Code *operation_code)
{
ComponentDepsNode *component;
OperationDepsNode *node = NULL;
const ID_Type id_type = GS(id_node->id_orig->name);
const ID_Type id_type = GS(id->name);
if (id_type == ID_SCE) {
component = id_node->find_component(DEG_NODE_TYPE_LAYER_COLLECTIONS);
if (component == NULL) {
return;
}
node = component->find_operation(DEG_OPCODE_VIEW_LAYER_INIT);
*component_type = DEG_NODE_TYPE_LAYER_COLLECTIONS;
*operation_code = DEG_OPCODE_VIEW_LAYER_INIT;
}
else if (id_type == ID_OB) {
component = id_node->find_component(DEG_NODE_TYPE_LAYER_COLLECTIONS);
if (component == NULL) {
return;
}
node = component->find_operation(DEG_OPCODE_OBJECT_BASE_FLAGS);
if (node == NULL) {
return;
}
}
if (node != NULL) {
node->tag_update(graph);
*component_type = DEG_NODE_TYPE_LAYER_COLLECTIONS;
*operation_code = DEG_OPCODE_OBJECT_BASE_FLAGS;
}
}
void id_tag_update_editors_update(Main *bmain, Depsgraph *graph, ID *id)
void depsgraph_tag_to_component_opcode(const ID *id,
eDepsgraph_Tag tag,
eDepsNode_Type *component_type,
eDepsOperation_Code *operation_code)
{
const ID_Type id_type = GS(id->name);
*component_type = DEG_NODE_TYPE_UNDEFINED;
*operation_code = DEG_OPCODE_OPERATION;
/* Special case for now, in the future we should get rid of this. */
if (tag == 0) {
*component_type = DEG_NODE_TYPE_ID_REF;
*operation_code = DEG_OPCODE_OPERATION;
return;
}
switch (tag) {
case DEG_TAG_TRANSFORM:
*component_type = DEG_NODE_TYPE_TRANSFORM;
break;
case DEG_TAG_GEOMETRY:
depsgraph_geometry_tag_to_component(id, component_type);
break;
case DEG_TAG_TIME:
*component_type = DEG_NODE_TYPE_ANIMATION;
break;
case DEG_TAG_PSYS_REDO:
case DEG_TAG_PSYS_RESET:
case DEG_TAG_PSYS_TYPE:
case DEG_TAG_PSYS_CHILD:
case DEG_TAG_PSYS_PHYS:
*component_type = DEG_NODE_TYPE_EVAL_PARTICLES;
break;
case DEG_TAG_COPY_ON_WRITE:
*component_type = DEG_NODE_TYPE_COPY_ON_WRITE;
break;
case DEG_TAG_SHADING_UPDATE:
if (id_type == ID_NT) {
*component_type = DEG_NODE_TYPE_SHADING_PARAMETERS;
}
else {
*component_type = DEG_NODE_TYPE_SHADING;
}
break;
case DEG_TAG_SELECT_UPDATE:
depsgraph_select_tag_to_component_opcode(id,
component_type,
operation_code);
break;
case DEG_TAG_BASE_FLAGS_UPDATE:
depsgraph_base_flags_tag_to_component_opcode(id,
component_type,
operation_code);
case DEG_TAG_EDITORS_UPDATE:
/* There is no such node in depsgraph, this tag is to be handled
* separately.
*/
break;
case DEG_TAG_PSYS_ALL:
BLI_assert(!"Should not happen");
break;
}
}
void id_tag_update_ntree_special(Main *bmain, Depsgraph *graph, ID *id, int flag)
{
bNodeTree *ntree = ntreeFromID(id);
if (ntree == NULL) {
return;
}
deg_graph_id_tag_update(bmain, graph, &ntree->id, flag);
}
void depsgraph_update_editors_tag(Main *bmain, Depsgraph *graph, ID *id)
{
/* NOTE: We handle this immediately, without delaying anything, to be
* sure we don't cause threading issues with OpenGL.
@@ -367,77 +243,92 @@ void id_tag_update_editors_update(Main *bmain, Depsgraph *graph, ID *id)
deg_editors_id_update(&update_ctx, id);
}
void id_tag_update_ntree_special(Main *bmain, Depsgraph *graph, ID *id, int flag)
void deg_graph_id_tag_update_single_flag(Main *bmain,
Depsgraph *graph,
ID *id,
IDDepsNode *id_node,
eDepsgraph_Tag tag)
{
bNodeTree *ntree = ntreeFromID(id);
if (ntree == NULL) {
if (tag == DEG_TAG_EDITORS_UPDATE) {
if (graph != NULL) {
depsgraph_update_editors_tag(bmain, graph, id);
}
return;
}
IDDepsNode *id_node = graph->find_id_node(&ntree->id);
if (id_node != NULL) {
deg_graph_id_tag_update(bmain, graph, id_node->id_orig, flag);
/* Get description of what is to be tagged. */
eDepsNode_Type component_type;
eDepsOperation_Code operation_code;
depsgraph_tag_to_component_opcode(id,
tag,
&component_type,
&operation_code);
/* Check whether we've got something to tag. */
if (component_type == DEG_NODE_TYPE_UNDEFINED) {
/* Given ID does not support tag. */
/* TODO(sergey): Shall we raise some panic here? */
return;
}
/* Tag ID recalc flag. */
DepsNodeFactory *factory = deg_type_get_factory(component_type);
BLI_assert(factory != NULL);
id->recalc |= factory->id_recalc_tag();
/* Some sanity checks before moving forward. */
if (id_node == NULL) {
/* Happens when object is tagged for update and not yet in the
* dependency graph (but will be after relations update).
*/
return;
}
/* Tag corresponding dependency graph operation for update. */
if (component_type == DEG_NODE_TYPE_ID_REF) {
id_node->tag_update(graph);
}
else {
ComponentDepsNode *component_node =
id_node->find_component(component_type);
if (component_node != NULL) {
if (operation_code == DEG_OPCODE_OPERATION) {
component_node->tag_update(graph);
}
else {
OperationDepsNode *operation_node =
component_node->find_operation(operation_code);
if (operation_node != NULL) {
operation_node->tag_update(graph);
}
}
}
}
}
void deg_graph_id_tag_update(Main *bmain, Depsgraph *graph, ID *id, int flag)
{
Depsgraph *deg_graph = reinterpret_cast<DEG::Depsgraph *>(graph);
IDDepsNode *id_node = deg_graph->find_id_node(id);
/* Make sure legacy flags are all nicely update. */
lib_id_recalc_tag_flag(bmain, id, flag);
if (id_node == NULL) {
/* Shouldn't happen, but better be sure here. */
return;
}
/* Tag components based on flags. */
IDDepsNode *id_node = (graph != NULL) ? graph->find_id_node(id)
: NULL;
DEG_id_type_tag(bmain, GS(id->name));
if (flag == 0) {
id_tag_update_special_zero_flag(graph, id_node);
id_tag_update_ntree_special(bmain, graph, id, flag);
return;
}
if (flag & OB_RECALC_OB) {
id_tag_update_object_transform(graph, id_node);
}
if (flag & OB_RECALC_DATA) {
id_tag_update_object_data(graph, id_node);
if (DEG_depsgraph_use_copy_on_write()) {
if (flag & DEG_TAG_COPY_ON_WRITE) {
const ID_Type id_type = GS(id_node->id_orig->name);
if (id_type == ID_OB) {
Object *object = (Object *)id_node->id_orig;
ID *ob_data = (ID *)object->data;
DEG_id_tag_update_ex(bmain, ob_data, flag);
}
}
/* TODO(sergey): Which recalc flags to set here? */
id->recalc |= ID_RECALC_ALL;
if (id_node != NULL) {
id_node->tag_update(graph);
}
}
if (flag & OB_RECALC_TIME) {
id_tag_update_object_time(graph, id_node);
}
if (flag & PSYS_RECALC) {
id_tag_update_particle(graph, id_node, flag);
}
if (flag & DEG_TAG_SHADING_UPDATE) {
id_tag_update_shading(graph, id_node);
}
if (flag & DEG_TAG_COPY_ON_WRITE) {
id_tag_update_copy_on_write(graph, id_node);
}
if (flag & DEG_TAG_SELECT_UPDATE) {
id_tag_update_select_update(graph, id_node);
}
if (flag & DEG_TAG_BASE_FLAGS_UPDATE) {
id_tag_update_base_flags(graph, id_node);
}
if (flag & DEG_TAG_EDITORS_UPDATE) {
id_tag_update_editors_update(bmain, graph, id);
while (flag != 0) {
eDepsgraph_Tag tag =
(eDepsgraph_Tag)(1 << bitscan_forward_clear_i(&flag));
deg_graph_id_tag_update_single_flag(bmain,
graph,
id,
id_node,
tag);
}
/* Special case for nested node tree datablocks. */
id_tag_update_ntree_special(bmain, graph, id, flag);
}
void deg_id_tag_update(Main *bmain, ID *id, int flag)
{
lib_id_recalc_tag_flag(bmain, id, flag);
deg_graph_id_tag_update(bmain, NULL, id, flag);
LINKLIST_FOREACH(Scene *, scene, &bmain->scene) {
LINKLIST_FOREACH(ViewLayer *, view_layer, &scene->view_layers) {
Depsgraph *depsgraph =
@@ -483,6 +374,8 @@ void deg_graph_on_visible_update(Main *bmain, Depsgraph *graph)
} // namespace DEG
/* Data-Based Tagging */
/* Tag given ID for an update in all the dependency graphs. */
void DEG_id_tag_update(ID *id, int flag)
{

View File

@@ -190,6 +190,7 @@ BLI_INLINE OperationDepsNode *flush_schedule_children(
return result;
}
/* NOTE: It will also accumulate flags from changed components. */
BLI_INLINE void flush_editors_id_update(Main *bmain,
Depsgraph *graph,
const DEGEditorUpdateContext *update_ctx)
@@ -198,6 +199,7 @@ BLI_INLINE void flush_editors_id_update(Main *bmain,
if (id_node->done != ID_STATE_MODIFIED) {
continue;
}
DEG_id_type_tag(bmain, GS(id_node->id_orig->name));
/* TODO(sergey): Do we need to pass original or evaluated ID here? */
ID *id_orig = id_node->id_orig;
ID *id_cow = id_node->id_cow;
@@ -206,10 +208,23 @@ BLI_INLINE void flush_editors_id_update(Main *bmain,
* data.
*/
id_cow->recalc |= (id_orig->recalc & ID_RECALC_ALL);
/* Gather recalc flags from all changed components. */
GHASH_FOREACH_BEGIN(ComponentDepsNode *, comp_node, id_node->components)
{
if (comp_node->done != COMPONENT_STATE_DONE) {
continue;
}
DepsNodeFactory *factory = deg_type_get_factory(comp_node->type);
BLI_assert(factory != NULL);
id_cow->recalc |= factory->id_recalc_tag();
}
GHASH_FOREACH_END();
DEG_DEBUG_PRINTF("Accumulated recalc bits for %s: %u\n",
id_orig->name, (unsigned int)id_cow->recalc);
/* Inform editors. */
if (deg_copy_on_write_is_expanded(id_cow)) {
deg_editors_id_update(update_ctx, id_cow);
}
lib_id_recalc_tag(bmain, id_orig);
}
}