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:
authorSergey Sharybin <sergey@blender.org>2022-07-20 11:04:02 +0300
committerSergey Sharybin <sergey@blender.org>2022-07-21 10:49:16 +0300
commit0dcee6a386645bb1e976d11aa2d1ae45b01f968a (patch)
tree4597d5a758b74dcc88fcde60f545821665c0d7c8 /source/blender/depsgraph
parent4089b7b80ba291dc04266a0dc58820ceeb48eb7b (diff)
Fix T99733: Objects with driven visibility are evaluated when not needed
The issue was caused by the fact that objects with driven or animated visibility were considered visible by the dependency graph evaluation. This change makes it so the dependency graph evaluation is aware of visibility which might be changing. This is achieved by evaluating the path of the graph which affects objects visibility and adjusts to it before evaluating the rest of the graph. There is some time penalty to this, but there does not seem to be a way to fully avoid this penalty. With the production shot from the heist project the FPS drops by a tenth of a frame (~9.4 vs ~9.3 fps) when adding a driver to an object which keeps it visible. Note that this is a bit hard to measure since the FPS fluctuates quite a bit throughout the playback. On the other hand, having a driver on a visibility of a heavy object from character and setting visibility to false gives big speedup. Also worth noting that there is no penalty at all when there are no animated visibilities in the scene. Differential Revision: https://developer.blender.org/D15498
Diffstat (limited to 'source/blender/depsgraph')
-rw-r--r--source/blender/depsgraph/CMakeLists.txt2
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder.cc85
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder.h2
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_nodes.cc33
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_nodes_view_layer.cc22
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_relations.cc7
-rw-r--r--source/blender/depsgraph/intern/depsgraph.cc2
-rw-r--r--source/blender/depsgraph/intern/depsgraph.h6
-rw-r--r--source/blender/depsgraph/intern/depsgraph_query_iter.cc9
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval.cc82
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval_visibility.cc147
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval_visibility.h26
-rw-r--r--source/blender/depsgraph/intern/node/deg_node_component.cc7
-rw-r--r--source/blender/depsgraph/intern/node/deg_node_component.h18
-rw-r--r--source/blender/depsgraph/intern/node/deg_node_id.cc9
-rw-r--r--source/blender/depsgraph/intern/node/deg_node_id.h17
-rw-r--r--source/blender/depsgraph/intern/node/deg_node_operation.h3
17 files changed, 348 insertions, 129 deletions
diff --git a/source/blender/depsgraph/CMakeLists.txt b/source/blender/depsgraph/CMakeLists.txt
index 3d539018cef..e799c5ce32a 100644
--- a/source/blender/depsgraph/CMakeLists.txt
+++ b/source/blender/depsgraph/CMakeLists.txt
@@ -67,6 +67,8 @@ set(SRC
intern/eval/deg_eval_runtime_backup_sound.cc
intern/eval/deg_eval_runtime_backup_volume.cc
intern/eval/deg_eval_stats.cc
+ intern/eval/deg_eval_visibility.cc
+ intern/eval/deg_eval_visibility.h
intern/node/deg_node.cc
intern/node/deg_node_component.cc
intern/node/deg_node_factory.cc
diff --git a/source/blender/depsgraph/intern/builder/deg_builder.cc b/source/blender/depsgraph/intern/builder/deg_builder.cc
index 1fec1fb3219..5353f71685c 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder.cc
+++ b/source/blender/depsgraph/intern/builder/deg_builder.cc
@@ -29,6 +29,7 @@
#include "intern/depsgraph_tag.h"
#include "intern/depsgraph_type.h"
#include "intern/eval/deg_eval_copy_on_write.h"
+#include "intern/eval/deg_eval_visibility.h"
#include "intern/node/deg_node.h"
#include "intern/node/deg_node_component.h"
#include "intern/node/deg_node_id.h"
@@ -71,10 +72,15 @@ bool DepsgraphBuilder::need_pull_base_into_graph(const Base *base)
if (base->flag & base_flag) {
return true;
}
+
/* More involved check: since we don't support dynamic changes in dependency graph topology and
* all visible objects are to be part of dependency graph, we pull all objects which has animated
* visibility. */
- const Object *object = base->object;
+ return is_object_visibility_animated(base->object);
+}
+
+bool DepsgraphBuilder::is_object_visibility_animated(const Object *object)
+{
AnimatedPropertyID property_id;
if (graph_->mode == DAG_EVAL_VIEWPORT) {
property_id = AnimatedPropertyID(&object->id, &RNA_Object, "hide_viewport");
@@ -127,84 +133,9 @@ bool DepsgraphBuilder::check_pchan_has_bbone_segments(const Object *object, cons
/** \name Builder Finalizer.
* \{ */
-namespace {
-
-void deg_graph_build_flush_visibility(Depsgraph *graph)
-{
- enum {
- DEG_NODE_VISITED = (1 << 0),
- };
-
- BLI_Stack *stack = BLI_stack_new(sizeof(OperationNode *), "DEG flush layers stack");
- for (IDNode *id_node : graph->id_nodes) {
- for (ComponentNode *comp_node : id_node->components.values()) {
- comp_node->affects_directly_visible |= id_node->is_directly_visible;
- }
- }
-
- for (OperationNode *op_node : graph->operations) {
- op_node->custom_flags = 0;
- op_node->num_links_pending = 0;
- for (Relation *rel : op_node->outlinks) {
- if ((rel->from->type == NodeType::OPERATION) && (rel->flag & RELATION_FLAG_CYCLIC) == 0) {
- ++op_node->num_links_pending;
- }
- }
- if (op_node->num_links_pending == 0) {
- BLI_stack_push(stack, &op_node);
- op_node->custom_flags |= DEG_NODE_VISITED;
- }
- }
-
- while (!BLI_stack_is_empty(stack)) {
- OperationNode *op_node;
- BLI_stack_pop(stack, &op_node);
- /* Flush layers to parents. */
- for (Relation *rel : op_node->inlinks) {
- if (rel->from->type == NodeType::OPERATION) {
- OperationNode *op_from = (OperationNode *)rel->from;
- ComponentNode *comp_from = op_from->owner;
- const bool target_directly_visible = op_node->owner->affects_directly_visible;
-
- /* Visibility component forces all components of the current ID to be considered as
- * affecting directly visible. */
- if (comp_from->type == NodeType::VISIBILITY) {
- if (target_directly_visible) {
- IDNode *id_node_from = comp_from->owner;
- for (ComponentNode *comp_node : id_node_from->components.values()) {
- comp_node->affects_directly_visible |= target_directly_visible;
- }
- }
- }
- else {
- comp_from->affects_directly_visible |= target_directly_visible;
- }
- }
- }
- /* Schedule parent nodes. */
- for (Relation *rel : op_node->inlinks) {
- if (rel->from->type == NodeType::OPERATION) {
- OperationNode *op_from = (OperationNode *)rel->from;
- if ((rel->flag & RELATION_FLAG_CYCLIC) == 0) {
- BLI_assert(op_from->num_links_pending > 0);
- --op_from->num_links_pending;
- }
- if ((op_from->num_links_pending == 0) && (op_from->custom_flags & DEG_NODE_VISITED) == 0) {
- BLI_stack_push(stack, &op_from);
- op_from->custom_flags |= DEG_NODE_VISITED;
- }
- }
- }
- }
- BLI_stack_free(stack);
-}
-
-} // namespace
-
void deg_graph_build_finalize(Main *bmain, Depsgraph *graph)
{
- /* Make sure dependencies of visible ID data-blocks are visible. */
- deg_graph_build_flush_visibility(graph);
+ deg_graph_flush_visibility_flags(graph);
deg_graph_remove_unused_noops(graph);
/* Re-tag IDs for update if it was tagged before the relations
diff --git a/source/blender/depsgraph/intern/builder/deg_builder.h b/source/blender/depsgraph/intern/builder/deg_builder.h
index 950ebfb35ba..c44e5fd5f4d 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder.h
+++ b/source/blender/depsgraph/intern/builder/deg_builder.h
@@ -24,6 +24,8 @@ class DepsgraphBuilder {
virtual bool need_pull_base_into_graph(const Base *base);
+ virtual bool is_object_visibility_animated(const Object *object);
+
virtual bool check_pchan_has_bbone(const Object *object, const bPoseChannel *pchan);
virtual bool check_pchan_has_bbone_segments(const Object *object, const bPoseChannel *pchan);
virtual bool check_pchan_has_bbone_segments(const Object *object, const char *bone_name);
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc
index 473dda2290c..c65c4beeed6 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc
+++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc
@@ -107,6 +107,7 @@
#include "intern/depsgraph_tag.h"
#include "intern/depsgraph_type.h"
#include "intern/eval/deg_eval_copy_on_write.h"
+#include "intern/eval/deg_eval_visibility.h"
#include "intern/node/deg_node.h"
#include "intern/node/deg_node_component.h"
#include "intern/node/deg_node_id.h"
@@ -179,11 +180,27 @@ IDNode *DepsgraphNodeBuilder::add_id_node(ID *id)
}
ComponentNode *visibility_component = id_node->add_component(NodeType::VISIBILITY);
- OperationNode *visibility_operation = visibility_component->add_operation(
- nullptr, OperationCode::VISIBILITY);
+ OperationNode *visibility_operation;
+
+ /* Optimization: currently only objects need a special visibility evaluation. For the rest ID
+ * types keep the node as a NO-OP so that relations can still be routed, but without penalty
+ * during the graph evaluation. */
+ if (id_type == ID_OB) {
+ visibility_operation = visibility_component->add_operation(
+ [id_node](::Depsgraph *depsgraph) {
+ deg_evaluate_object_node_visibility(depsgraph, id_node);
+ },
+ OperationCode::VISIBILITY);
+ }
+ else {
+ visibility_operation = visibility_component->add_operation(nullptr,
+ OperationCode::VISIBILITY);
+ }
+
/* Pin the node so that it and its relations are preserved by the unused nodes/relations
* deletion. This is mainly to make it easier to debug visibility. */
- visibility_operation->flag |= OperationFlag::DEPSOP_FLAG_PINNED;
+ visibility_operation->flag |= (OperationFlag::DEPSOP_FLAG_PINNED |
+ OperationFlag::DEPSOP_FLAG_AFFECTS_VISIBILITY);
graph_->operations.append(visibility_operation);
}
return id_node;
@@ -652,7 +669,7 @@ void DepsgraphNodeBuilder::build_collection(LayerCollection *from_layer_collecti
IDNode *id_node;
if (built_map_.checkIsBuiltAndTag(collection)) {
id_node = find_id_node(&collection->id);
- if (is_collection_visible && id_node->is_directly_visible == false &&
+ if (is_collection_visible && id_node->is_visible_on_build == false &&
id_node->is_collection_fully_expanded == true) {
/* Collection became visible, make sure nested collections and
* objects are poked with the new visibility flag, since they
@@ -669,7 +686,7 @@ void DepsgraphNodeBuilder::build_collection(LayerCollection *from_layer_collecti
else {
/* Collection itself. */
id_node = add_id_node(&collection->id);
- id_node->is_directly_visible = is_collection_visible;
+ id_node->is_visible_on_build = is_collection_visible;
build_idproperties(collection->id.properties);
add_operation_node(&collection->id, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL_DONE);
@@ -716,7 +733,7 @@ void DepsgraphNodeBuilder::build_object(int base_index,
build_object_flags(base_index, object, linked_state);
}
id_node->linked_state = max(id_node->linked_state, linked_state);
- id_node->is_directly_visible |= is_visible;
+ id_node->is_visible_on_build |= is_visible;
id_node->has_base |= (base_index != -1);
/* There is no relation path which will connect current object with all the ones which come
@@ -735,10 +752,10 @@ void DepsgraphNodeBuilder::build_object(int base_index,
* Probably need to assign that to something non-nullptr, but then the logic here will still be
* somewhat weird. */
if (scene_ != nullptr && object == scene_->camera) {
- id_node->is_directly_visible = true;
+ id_node->is_visible_on_build = true;
}
else {
- id_node->is_directly_visible = is_visible;
+ id_node->is_visible_on_build = is_visible;
}
id_node->has_base |= (base_index != -1);
/* Various flags, flushing from bases/collections. */
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes_view_layer.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes_view_layer.cc
index 32ec94211a0..5af9e7d4fe9 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_nodes_view_layer.cc
+++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes_view_layer.cc
@@ -92,14 +92,20 @@ void DepsgraphNodeBuilder::build_view_layer(Scene *scene,
int base_index = 0;
LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) {
/* object itself */
- if (need_pull_base_into_graph(base)) {
- /* NOTE: We consider object visible even if it's currently
- * restricted by the base/restriction flags. Otherwise its drivers
- * will never be evaluated.
- *
- * TODO(sergey): Need to go more granular on visibility checks. */
- build_object(base_index, base->object, linked_state, true);
- base_index++;
+ if (!need_pull_base_into_graph(base)) {
+ continue;
+ }
+
+ /* NOTE: We consider object visible even if it's currently
+ * restricted by the base/restriction flags. Otherwise its drivers
+ * will never be evaluated.
+ *
+ * TODO(sergey): Need to go more granular on visibility checks. */
+ build_object(base_index, base->object, linked_state, true);
+ base_index++;
+
+ if (!graph_->has_animated_visibility) {
+ graph_->has_animated_visibility |= is_object_visibility_animated(base->object);
}
}
build_layer_collections(&view_layer->layer_collections);
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc
index 4fe8a626af5..f2646ebc1f1 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc
+++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc
@@ -810,6 +810,13 @@ void DepsgraphRelationBuilder::build_object(Object *object)
/* Parameters. */
build_parameters(&object->id);
+
+ /* Visibility.
+ * Evaluate visibility node after the object's base_flags has been updated to the current state
+ * of collections restrict and object's restrict flags. */
+ const ComponentKey object_from_layer_entry_key(&object->id, NodeType::OBJECT_FROM_LAYER);
+ const ComponentKey visibility_key(&object->id, NodeType::VISIBILITY);
+ add_relation(object_from_layer_entry_key, visibility_key, "Object Visibility");
}
/* NOTE: Implies that the object has base in the current view layer. */
diff --git a/source/blender/depsgraph/intern/depsgraph.cc b/source/blender/depsgraph/intern/depsgraph.cc
index d460a68747d..316d0b615c6 100644
--- a/source/blender/depsgraph/intern/depsgraph.cc
+++ b/source/blender/depsgraph/intern/depsgraph.cc
@@ -45,7 +45,9 @@ namespace blender::deg {
Depsgraph::Depsgraph(Main *bmain, Scene *scene, ViewLayer *view_layer, eEvaluationMode mode)
: time_source(nullptr),
+ has_animated_visibility(false),
need_update_relations(true),
+ need_update_nodes_visibility(true),
need_tag_id_on_graph_visibility_update(true),
need_tag_id_on_graph_visibility_time_update(false),
bmain(bmain),
diff --git a/source/blender/depsgraph/intern/depsgraph.h b/source/blender/depsgraph/intern/depsgraph.h
index 33d97e4b8b2..2f88199384d 100644
--- a/source/blender/depsgraph/intern/depsgraph.h
+++ b/source/blender/depsgraph/intern/depsgraph.h
@@ -88,9 +88,15 @@ struct Depsgraph {
/* Top-level time source node. */
TimeSourceNode *time_source;
+ /* The graph contains data-blocks whose visibility depends on evaluation (driven or animated). */
+ bool has_animated_visibility;
+
/* Indicates whether relations needs to be updated. */
bool need_update_relations;
+ /* Indicates whether indirect effect of nodes on a directly visible ones needs to be updated. */
+ bool need_update_nodes_visibility;
+
/* Indicated whether IDs in this graph are to be tagged as if they first appear visible, with
* an optional tag for their animation (time) update. */
bool need_tag_id_on_graph_visibility_update;
diff --git a/source/blender/depsgraph/intern/depsgraph_query_iter.cc b/source/blender/depsgraph/intern/depsgraph_query_iter.cc
index bcde1839a0f..171c9875e2a 100644
--- a/source/blender/depsgraph/intern/depsgraph_query_iter.cc
+++ b/source/blender/depsgraph/intern/depsgraph_query_iter.cc
@@ -219,7 +219,9 @@ bool deg_iterator_objects_step(DEGObjectIterData *data)
for (; data->id_node_index < data->num_id_nodes; data->id_node_index++) {
deg::IDNode *id_node = deg_graph->id_nodes[data->id_node_index];
- if (!id_node->is_directly_visible) {
+ /* Use the build time visibility so that the ID is not appearing/disappearing throughout
+ * animation export. */
+ if (!id_node->is_visible_on_build) {
continue;
}
@@ -338,10 +340,13 @@ static void DEG_iterator_ids_step(BLI_Iterator *iter, deg::IDNode *id_node, bool
{
ID *id_cow = id_node->id_cow;
- if (!id_node->is_directly_visible) {
+ /* Use the build time visibility so that the ID is not appearing/disappearing throughout
+ * animation export. */
+ if (!id_node->is_visible_on_build) {
iter->skip = true;
return;
}
+
if (only_updated && !(id_cow->recalc & ID_RECALC_ALL)) {
/* Node-tree is considered part of the data-block. */
bNodeTree *ntree = ntreeFromID(id_cow);
diff --git a/source/blender/depsgraph/intern/eval/deg_eval.cc b/source/blender/depsgraph/intern/eval/deg_eval.cc
index 47f2a8ca219..426e04a5cf9 100644
--- a/source/blender/depsgraph/intern/eval/deg_eval.cc
+++ b/source/blender/depsgraph/intern/eval/deg_eval.cc
@@ -37,6 +37,7 @@
#include "intern/eval/deg_eval_copy_on_write.h"
#include "intern/eval/deg_eval_flush.h"
#include "intern/eval/deg_eval_stats.h"
+#include "intern/eval/deg_eval_visibility.h"
#include "intern/node/deg_node.h"
#include "intern/node/deg_node_component.h"
#include "intern/node/deg_node_id.h"
@@ -69,6 +70,9 @@ enum class EvaluationStage {
* involved. */
COPY_ON_WRITE,
+ /* Evaluate actual ID nodes visiblity based on the current state of animation and drivers. */
+ DYNAMIC_VISIBILITY,
+
/* Threaded evaluation of all possible operations. */
THREADED_EVALUATION,
@@ -83,7 +87,8 @@ struct DepsgraphEvalState {
Depsgraph *graph;
bool do_stats;
EvaluationStage stage;
- bool need_single_thread_pass;
+ bool need_update_pending_parents = true;
+ bool need_single_thread_pass = false;
};
void evaluate_node(const DepsgraphEvalState *state, OperationNode *operation_node)
@@ -122,24 +127,31 @@ void deg_task_run_func(TaskPool *pool, void *taskdata)
schedule_children(state, operation_node, schedule_node_to_pool, pool);
}
-bool check_operation_node_visible(OperationNode *op_node)
+bool check_operation_node_visible(const DepsgraphEvalState *state, OperationNode *op_node)
{
const ComponentNode *comp_node = op_node->owner;
- /* Special exception, copy on write component is to be always evaluated,
- * to keep copied "database" in a consistent state. */
+ /* Special case for copy on write component: it is to be always evaluated, to keep copied
+ * "database" in a consistent state. */
if (comp_node->type == NodeType::COPY_ON_WRITE) {
return true;
}
- return comp_node->affects_directly_visible;
+
+ /* Special case for dynamic visiblity pass: the actual visibility is not yet known, so limit to
+ * only operations which affects visibility. */
+ if (state->stage == EvaluationStage::DYNAMIC_VISIBILITY) {
+ return op_node->flag & OperationFlag::DEPSOP_FLAG_AFFECTS_VISIBILITY;
+ }
+
+ return comp_node->affects_visible_id;
}
-void calculate_pending_parents_for_node(OperationNode *node)
+void calculate_pending_parents_for_node(const DepsgraphEvalState *state, OperationNode *node)
{
/* Update counters, applies for both visible and invisible IDs. */
node->num_links_pending = 0;
node->scheduled = false;
/* Invisible IDs requires no pending operations. */
- if (!check_operation_node_visible(node)) {
+ if (!check_operation_node_visible(state, node)) {
return;
}
/* No need to bother with anything if node is not tagged for update. */
@@ -153,7 +165,7 @@ void calculate_pending_parents_for_node(OperationNode *node)
* calculation, but how is it possible that visible object depends
* on an invisible? This is something what is prohibited after
* deg_graph_build_flush_layers(). */
- if (!check_operation_node_visible(from)) {
+ if (!check_operation_node_visible(state, from)) {
continue;
}
/* No need to wait for operation which is up to date. */
@@ -165,20 +177,24 @@ void calculate_pending_parents_for_node(OperationNode *node)
}
}
-void calculate_pending_parents(Depsgraph *graph)
+void calculate_pending_parents_if_needed(DepsgraphEvalState *state)
{
- for (OperationNode *node : graph->operations) {
- calculate_pending_parents_for_node(node);
+ if (!state->need_update_pending_parents) {
+ return;
}
+
+ for (OperationNode *node : state->graph->operations) {
+ calculate_pending_parents_for_node(state, node);
+ }
+
+ state->need_update_pending_parents = false;
}
void initialize_execution(DepsgraphEvalState *state, Depsgraph *graph)
{
- const bool do_stats = state->do_stats;
- calculate_pending_parents(graph);
/* Clear tags and other things which needs to be clear. */
- for (OperationNode *node : graph->operations) {
- if (do_stats) {
+ if (state->do_stats) {
+ for (OperationNode *node : graph->operations) {
node->stats.reset_current();
}
}
@@ -203,11 +219,10 @@ bool need_evaluate_operation_at_stage(DepsgraphEvalState *state,
case EvaluationStage::COPY_ON_WRITE:
return (component_node->type == NodeType::COPY_ON_WRITE);
+ case EvaluationStage::DYNAMIC_VISIBILITY:
+ return operation_node->flag & OperationFlag::DEPSOP_FLAG_AFFECTS_VISIBILITY;
+
case EvaluationStage::THREADED_EVALUATION:
- /* Sanity check: copy-on-write node should be evaluated already. This will be indicated by
- * scheduled flag (we assume that scheduled operations have been actually handled by previous
- * stage). */
- BLI_assert(operation_node->scheduled || component_node->type != NodeType::COPY_ON_WRITE);
if (is_metaball_object_operation(operation_node)) {
state->need_single_thread_pass = true;
return false;
@@ -233,7 +248,7 @@ void schedule_node(DepsgraphEvalState *state,
ScheduleFunctionArgs... schedule_function_args)
{
/* No need to schedule nodes of invisible ID. */
- if (!check_operation_node_visible(node)) {
+ if (!check_operation_node_visible(state, node)) {
return;
}
/* No need to schedule operations which are not tagged for update, they are
@@ -317,6 +332,8 @@ void evaluate_graph_threaded_stage(DepsgraphEvalState *state,
{
state->stage = stage;
+ calculate_pending_parents_if_needed(state);
+
schedule_graph(state, schedule_node_to_pool, task_pool);
BLI_task_pool_work_and_wait(task_pool);
}
@@ -328,6 +345,8 @@ void evaluate_graph_single_threaded_if_needed(DepsgraphEvalState *state)
return;
}
+ BLI_assert(!state->need_update_pending_parents);
+
state->stage = EvaluationStage::SINGLE_THREADED_WORKAROUND;
GSQueue *evaluation_queue = BLI_gsqueue_new(sizeof(OperationNode *));
@@ -392,7 +411,6 @@ void deg_evaluate_on_refresh(Depsgraph *graph)
DepsgraphEvalState state;
state.graph = graph;
state.do_stats = graph->debug.do_time_debug();
- state.need_single_thread_pass = false;
/* Prepare all nodes for evaluation. */
initialize_execution(&state, graph);
@@ -403,6 +421,10 @@ void deg_evaluate_on_refresh(Depsgraph *graph)
* that if a dependency graph has a cycle evaluation functions will always "see" valid expanded
* datablock. It might not be evaluated yet, but at least the datablock will be valid.
*
+ * - If there is potentially dynamically changing visibility in the graph update the actual
+ * nodes visibilities, so that actual heavy data evaluation can benefit from knowledge that
+ * something heavy is not currently visible.
+ *
* - Multi-threaded evaluation of all possible nodes.
* Certain operations (and their subtrees) could be ignored. For example, meta-balls are not
* safe from threading point of view, so the threaded evaluation will stop at the metaball
@@ -413,6 +435,24 @@ void deg_evaluate_on_refresh(Depsgraph *graph)
TaskPool *task_pool = deg_evaluate_task_pool_create(&state);
evaluate_graph_threaded_stage(&state, task_pool, EvaluationStage::COPY_ON_WRITE);
+
+ if (graph->has_animated_visibility) {
+ /* Update pending parents including only the ones which are affecting operations which are
+ * affecting visibility. */
+ state.need_update_pending_parents = true;
+
+ evaluate_graph_threaded_stage(&state, task_pool, EvaluationStage::DYNAMIC_VISIBILITY);
+
+ deg_graph_flush_visibility_flags_if_needed(graph);
+
+ /* Update parents to an updated visibility and evaluation stage.
+ *
+ * Need to do it regardless of whether visibility is actually changed or not: current state of
+ * the pending parents are all zeroes because it was previously calculated for only visibility
+ * related nodes and those are fully evaluated by now. */
+ state.need_update_pending_parents = true;
+ }
+
evaluate_graph_threaded_stage(&state, task_pool, EvaluationStage::THREADED_EVALUATION);
BLI_task_pool_free(task_pool);
diff --git a/source/blender/depsgraph/intern/eval/deg_eval_visibility.cc b/source/blender/depsgraph/intern/eval/deg_eval_visibility.cc
new file mode 100644
index 00000000000..8bf26e45e2a
--- /dev/null
+++ b/source/blender/depsgraph/intern/eval/deg_eval_visibility.cc
@@ -0,0 +1,147 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2022 Blender Foundation. All rights reserved. */
+
+/** \file
+ * \ingroup depsgraph
+ */
+
+#include "intern/eval/deg_eval_visibility.h"
+
+#include "DNA_layer_types.h"
+#include "DNA_object_types.h"
+
+#include "BLI_assert.h"
+#include "BLI_stack.h"
+
+#include "DEG_depsgraph.h"
+
+#include "intern/depsgraph.h"
+#include "intern/depsgraph_relation.h"
+#include "intern/node/deg_node.h"
+#include "intern/node/deg_node_component.h"
+#include "intern/node/deg_node_id.h"
+#include "intern/node/deg_node_operation.h"
+
+namespace blender::deg {
+
+void deg_evaluate_object_node_visibility(::Depsgraph *depsgraph, IDNode *id_node)
+{
+ BLI_assert(GS(id_node->id_cow->name) == ID_OB);
+
+ Depsgraph *graph = reinterpret_cast<Depsgraph *>(depsgraph);
+ const Object *object = reinterpret_cast<const Object *>(id_node->id_cow);
+
+ DEG_debug_print_eval(depsgraph, __func__, object->id.name, &object->id);
+
+ const int required_flags = (graph->mode == DAG_EVAL_VIEWPORT) ? BASE_ENABLED_VIEWPORT :
+ BASE_ENABLED_RENDER;
+
+ const bool is_enabled = object->base_flag & required_flags;
+
+ if (id_node->is_enabled_on_eval != is_enabled) {
+ id_node->is_enabled_on_eval = is_enabled;
+
+ /* Tag dependency graph for changed visibility, so that it is updated on all dependencies prior
+ * to a pass of an actual evaluation. */
+ graph->need_update_nodes_visibility = true;
+ }
+}
+
+void deg_graph_flush_visibility_flags(Depsgraph *graph)
+{
+ enum {
+ DEG_NODE_VISITED = (1 << 0),
+ };
+
+ for (IDNode *id_node : graph->id_nodes) {
+ for (ComponentNode *comp_node : id_node->components.values()) {
+ comp_node->possibly_affects_visible_id = id_node->is_visible_on_build;
+ comp_node->affects_visible_id = id_node->is_visible_on_build && id_node->is_enabled_on_eval;
+ }
+ }
+
+ BLI_Stack *stack = BLI_stack_new(sizeof(OperationNode *), "DEG flush layers stack");
+
+ for (OperationNode *op_node : graph->operations) {
+ op_node->custom_flags = 0;
+ op_node->num_links_pending = 0;
+ for (Relation *rel : op_node->outlinks) {
+ if ((rel->from->type == NodeType::OPERATION) && (rel->flag & RELATION_FLAG_CYCLIC) == 0) {
+ ++op_node->num_links_pending;
+ }
+ }
+ if (op_node->num_links_pending == 0) {
+ BLI_stack_push(stack, &op_node);
+ op_node->custom_flags |= DEG_NODE_VISITED;
+ }
+ }
+
+ while (!BLI_stack_is_empty(stack)) {
+ OperationNode *op_node;
+ BLI_stack_pop(stack, &op_node);
+
+ /* Flush flags to parents. */
+ for (Relation *rel : op_node->inlinks) {
+ if (rel->from->type == NodeType::OPERATION) {
+ const OperationNode *op_to = reinterpret_cast<const OperationNode *>(rel->to);
+ const ComponentNode *comp_to = op_to->owner;
+ OperationNode *op_from = reinterpret_cast<OperationNode *>(rel->from);
+ ComponentNode *comp_from = op_from->owner;
+
+ const bool target_possibly_affects_visible_id = comp_to->possibly_affects_visible_id;
+ const bool target_affects_visible_id = comp_to->affects_visible_id;
+
+ op_from->flag |= (op_to->flag & OperationFlag::DEPSOP_FLAG_AFFECTS_VISIBILITY);
+
+ /* Visibility component forces all components of the current ID to be considered as
+ * affecting directly visible. */
+ if (comp_from->type == NodeType::VISIBILITY) {
+ const IDNode *id_node_from = comp_from->owner;
+ if (target_possibly_affects_visible_id) {
+ for (ComponentNode *comp_node : id_node_from->components.values()) {
+ comp_node->possibly_affects_visible_id |= target_possibly_affects_visible_id;
+ }
+ }
+ if (target_affects_visible_id) {
+ for (ComponentNode *comp_node : id_node_from->components.values()) {
+ comp_node->affects_visible_id |= target_affects_visible_id;
+ }
+ }
+ }
+ else {
+ comp_from->possibly_affects_visible_id |= target_possibly_affects_visible_id;
+ comp_from->affects_visible_id |= target_affects_visible_id;
+ }
+ }
+ }
+
+ /* Schedule parent nodes. */
+ for (Relation *rel : op_node->inlinks) {
+ if (rel->from->type == NodeType::OPERATION) {
+ OperationNode *op_from = (OperationNode *)rel->from;
+ if ((rel->flag & RELATION_FLAG_CYCLIC) == 0) {
+ BLI_assert(op_from->num_links_pending > 0);
+ --op_from->num_links_pending;
+ }
+ if ((op_from->num_links_pending == 0) && (op_from->custom_flags & DEG_NODE_VISITED) == 0) {
+ BLI_stack_push(stack, &op_from);
+ op_from->custom_flags |= DEG_NODE_VISITED;
+ }
+ }
+ }
+ }
+ BLI_stack_free(stack);
+
+ graph->need_update_nodes_visibility = false;
+}
+
+void deg_graph_flush_visibility_flags_if_needed(Depsgraph *graph)
+{
+ if (!graph->need_update_nodes_visibility) {
+ return;
+ }
+
+ deg_graph_flush_visibility_flags(graph);
+}
+
+} // namespace blender::deg
diff --git a/source/blender/depsgraph/intern/eval/deg_eval_visibility.h b/source/blender/depsgraph/intern/eval/deg_eval_visibility.h
new file mode 100644
index 00000000000..9e9db8ab34a
--- /dev/null
+++ b/source/blender/depsgraph/intern/eval/deg_eval_visibility.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2022 Blender Foundation. All rights reserved. */
+
+/** \file
+ * \ingroup depsgraph
+ */
+
+#pragma once
+
+struct Depsgraph;
+
+namespace blender::deg {
+
+struct Depsgraph;
+struct IDNode;
+
+/* Evaluate actual node visibility flags based on the current state of object's visibility
+ * restriction flags. */
+void deg_evaluate_object_node_visibility(::Depsgraph *depsgraph, IDNode *id_node);
+
+/* Flush both static and dynamic visibility flags from leaves up to the roots, making it possible
+ * to know whether a node has affect on something (potentially) visible. */
+void deg_graph_flush_visibility_flags(Depsgraph *graph);
+void deg_graph_flush_visibility_flags_if_needed(Depsgraph *graph);
+
+} // namespace blender::deg
diff --git a/source/blender/depsgraph/intern/node/deg_node_component.cc b/source/blender/depsgraph/intern/node/deg_node_component.cc
index cfcbec46569..32942f45a4a 100644
--- a/source/blender/depsgraph/intern/node/deg_node_component.cc
+++ b/source/blender/depsgraph/intern/node/deg_node_component.cc
@@ -67,7 +67,10 @@ uint64_t ComponentNode::OperationIDKey::hash() const
}
ComponentNode::ComponentNode()
- : entry_operation(nullptr), exit_operation(nullptr), affects_directly_visible(false)
+ : entry_operation(nullptr),
+ exit_operation(nullptr),
+ possibly_affects_visible_id(false),
+ affects_visible_id(false)
{
operations_map = new Map<ComponentNode::OperationIDKey, OperationNode *>();
}
@@ -90,7 +93,7 @@ string ComponentNode::identifier() const
const string idname = this->owner->name;
const string typebuf = "" + to_string(static_cast<int>(type)) + ")";
return typebuf + name + " : " + idname +
- "( affects_directly_visible: " + (affects_directly_visible ? "true" : "false") + ")";
+ "( affects_visible_id: " + (affects_visible_id ? "true" : "false") + ")";
}
OperationNode *ComponentNode::find_operation(OperationIDKey key) const
diff --git a/source/blender/depsgraph/intern/node/deg_node_component.h b/source/blender/depsgraph/intern/node/deg_node_component.h
index 375a26ab49b..cbc86ccd78d 100644
--- a/source/blender/depsgraph/intern/node/deg_node_component.h
+++ b/source/blender/depsgraph/intern/node/deg_node_component.h
@@ -137,9 +137,17 @@ struct ComponentNode : public Node {
return true;
}
- /* Denotes whether this component affects (possibly indirectly) on a
- * directly visible object. */
- bool affects_directly_visible;
+ /* The component has (possibly indirect) effect on a data-block whose node has
+ * is_visible_on_build set to true.
+ *
+ * This field is ensured to be up-to-date prior to `IDNode::finalize_build()`. */
+ bool possibly_affects_visible_id;
+
+ /* Denotes whether this component actually affects (possibly indirectly) on a directly visible
+ * object. Includes possibly run-time visibility update of ID nodes.
+ *
+ * NOTE: Is only reliable after `deg_graph_flush_visibility()`. */
+ bool affects_visible_id;
};
/* ---------------------------------------- */
@@ -197,7 +205,7 @@ DEG_COMPONENT_NODE_DECLARE_GENERIC(Dupli);
DEG_COMPONENT_NODE_DECLARE_GENERIC(Audio);
DEG_COMPONENT_NODE_DECLARE_GENERIC(Armature);
DEG_COMPONENT_NODE_DECLARE_GENERIC(GenericDatablock);
-DEG_COMPONENT_NODE_DECLARE_NO_COW(Visibility);
+DEG_COMPONENT_NODE_DECLARE_NO_COW_TAG_ON_UPDATE(Visibility);
DEG_COMPONENT_NODE_DECLARE_GENERIC(Simulation);
DEG_COMPONENT_NODE_DECLARE_GENERIC(NTreeOutput);
@@ -214,7 +222,7 @@ struct SynchronizationComponentNode : public ComponentNode {
* The design is such that the synchronization is supposed to happen whenever any part of the
* ID changed/evaluated. Here we mark the component as "visible" so that genetic recalc flag
* flushing and scheduling will handle the component in a generic manner. */
- affects_directly_visible = true;
+ affects_visible_id = true;
}
DEG_COMPONENT_NODE_DECLARE;
diff --git a/source/blender/depsgraph/intern/node/deg_node_id.cc b/source/blender/depsgraph/intern/node/deg_node_id.cc
index 99224501e98..735d606ac9e 100644
--- a/source/blender/depsgraph/intern/node/deg_node_id.cc
+++ b/source/blender/depsgraph/intern/node/deg_node_id.cc
@@ -69,7 +69,8 @@ void IDNode::init(const ID *id, const char *UNUSED(subdata))
customdata_masks = DEGCustomDataMeshMasks();
previous_customdata_masks = DEGCustomDataMeshMasks();
linked_state = DEG_ID_LINKED_INDIRECTLY;
- is_directly_visible = true;
+ is_visible_on_build = true;
+ is_enabled_on_eval = true;
is_collection_fully_expanded = false;
has_base = false;
is_user_modified = false;
@@ -138,8 +139,8 @@ string IDNode::identifier() const
BLI_snprintf(orig_ptr, sizeof(orig_ptr), "%p", id_orig);
BLI_snprintf(cow_ptr, sizeof(cow_ptr), "%p", id_cow);
return string(nodeTypeAsString(type)) + " : " + name + " (orig: " + orig_ptr +
- ", eval: " + cow_ptr + ", is_directly_visible " +
- (is_directly_visible ? "true" : "false") + ")";
+ ", eval: " + cow_ptr + ", is_visible_on_build " +
+ (is_visible_on_build ? "true" : "false") + ")";
}
ComponentNode *IDNode::find_component(NodeType type, const char *name) const
@@ -188,7 +189,7 @@ IDComponentsMask IDNode::get_visible_components_mask() const
{
IDComponentsMask result = 0;
for (ComponentNode *comp_node : components.values()) {
- if (comp_node->affects_directly_visible) {
+ if (comp_node->possibly_affects_visible_id) {
const int component_type_as_int = static_cast<int>(comp_node->type);
BLI_assert(component_type_as_int < 64);
result |= (1ULL << component_type_as_int);
diff --git a/source/blender/depsgraph/intern/node/deg_node_id.h b/source/blender/depsgraph/intern/node/deg_node_id.h
index 406ca828049..7f0a656cb8d 100644
--- a/source/blender/depsgraph/intern/node/deg_node_id.h
+++ b/source/blender/depsgraph/intern/node/deg_node_id.h
@@ -91,8 +91,21 @@ struct IDNode : public Node {
eDepsNode_LinkedState_Type linked_state;
- /* Indicates the data-block is visible in the evaluated scene. */
- bool is_directly_visible;
+ /* Indicates the data-block is to be considered visible in the evaluated scene.
+ *
+ * This flag is set during dependency graph build where check for an actual visibility might not
+ * be available yet due to driven or animated restriction flags. So it is more of an intent or,
+ * in other words, plausibility of the data-block to be visible. */
+ bool is_visible_on_build;
+
+ /* Evaluated state of whether evaluation considered this data-block "enabled".
+ *
+ * For objects this is derived from the base restriction flags, which might be animated or
+ * driven. It is set to `BASE_ENABLED_<VIEWPORT, RENDER>` (depending on the graph mode) after
+ * the object's flags from layer were evaluated.
+ *
+ * For other data-types is currently always true. */
+ bool is_enabled_on_eval;
/* For the collection type of ID, denotes whether collection was fully
* recursed into. */
diff --git a/source/blender/depsgraph/intern/node/deg_node_operation.h b/source/blender/depsgraph/intern/node/deg_node_operation.h
index fd772fbce9d..656b27550f6 100644
--- a/source/blender/depsgraph/intern/node/deg_node_operation.h
+++ b/source/blender/depsgraph/intern/node/deg_node_operation.h
@@ -214,6 +214,9 @@ enum OperationFlag {
* and not for connecting to an operation. */
DEPSOP_FLAG_PINNED = (1 << 3),
+ /* The operation directly or indirectly affects ID node visibility. */
+ DEPSOP_FLAG_AFFECTS_VISIBILITY = (1 << 4),
+
/* Set of flags which gets flushed along the relations. */
DEPSOP_FLAG_FLUSH = (DEPSOP_FLAG_USER_MODIFIED),
};