From f12f7800c2965c721a4eb4baeb6b1ab129bfa375 Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Thu, 4 Aug 2022 15:04:25 +0200 Subject: Depsgraph: Optimize evaluation of dependencies of disabled modifiers Solves long-standing issue when dependencies of disabled modifiers are evaluated. Simple test case: no drivers or animation. Manually enabling modifier is expected to bring FPS up, enabling modifier will bring FPS (sine evaluation can not be avoided) F13336690 More complex test case: modifier visibility is driven by an animated property. In am ideal world FPS during property being zero is fast and when property is 1 the FPS is low. F13336691. Differential Revision: https://developer.blender.org/D15625 --- .../depsgraph/intern/builder/deg_builder.cc | 19 ++++++++ .../blender/depsgraph/intern/builder/deg_builder.h | 2 + .../depsgraph/intern/builder/deg_builder_nodes.cc | 24 ++++++++- .../intern/builder/deg_builder_relations.cc | 10 ++++ .../depsgraph/intern/builder/deg_builder_rna.cc | 12 ++++- .../blender/depsgraph/intern/depsgraph_relation.h | 2 + source/blender/depsgraph/intern/eval/deg_eval.cc | 2 +- .../depsgraph/intern/eval/deg_eval_visibility.cc | 57 +++++++++++++++++++++- .../depsgraph/intern/eval/deg_eval_visibility.h | 3 ++ .../depsgraph/intern/node/deg_node_operation.cc | 10 ++++ .../depsgraph/intern/node/deg_node_operation.h | 3 ++ 11 files changed, 139 insertions(+), 5 deletions(-) (limited to 'source/blender/depsgraph') diff --git a/source/blender/depsgraph/intern/builder/deg_builder.cc b/source/blender/depsgraph/intern/builder/deg_builder.cc index 5353f71685c..097c377ece4 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder.cc @@ -13,6 +13,7 @@ #include "DNA_anim_types.h" #include "DNA_armature_types.h" #include "DNA_layer_types.h" +#include "DNA_modifier_types.h" #include "DNA_object_types.h" #include "BLI_stack.h" @@ -95,6 +96,24 @@ bool DepsgraphBuilder::is_object_visibility_animated(const Object *object) return cache_->isPropertyAnimated(&object->id, property_id); } +bool DepsgraphBuilder::is_modifier_visibility_animated(const Object *object, + const ModifierData *modifier) +{ + AnimatedPropertyID property_id; + if (graph_->mode == DAG_EVAL_VIEWPORT) { + property_id = AnimatedPropertyID( + &object->id, &RNA_Modifier, (void *)modifier, "show_viewport"); + } + else if (graph_->mode == DAG_EVAL_RENDER) { + property_id = AnimatedPropertyID(&object->id, &RNA_Modifier, (void *)modifier, "show_render"); + } + else { + BLI_assert_msg(0, "Unknown evaluation mode."); + return false; + } + return cache_->isPropertyAnimated(&object->id, property_id); +} + bool DepsgraphBuilder::check_pchan_has_bbone(const Object *object, const bPoseChannel *pchan) { BLI_assert(object->type == OB_ARMATURE); diff --git a/source/blender/depsgraph/intern/builder/deg_builder.h b/source/blender/depsgraph/intern/builder/deg_builder.h index c44e5fd5f4d..5d043f1fd3a 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder.h +++ b/source/blender/depsgraph/intern/builder/deg_builder.h @@ -10,6 +10,7 @@ struct Base; struct ID; struct Main; +struct ModifierData; struct Object; struct bPoseChannel; @@ -25,6 +26,7 @@ class DepsgraphBuilder { virtual bool need_pull_base_into_graph(const Base *base); virtual bool is_object_visibility_animated(const Object *object); + virtual bool is_modifier_visibility_animated(const Object *object, const ModifierData *modifier); 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); diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc index 025b2cf21a8..dd62a6cdea2 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc @@ -879,9 +879,31 @@ void DepsgraphNodeBuilder::build_object_modifiers(Object *object) return; } + const ModifierMode modifier_mode = (graph_->mode == DAG_EVAL_VIEWPORT) ? eModifierMode_Realtime : + eModifierMode_Render; + + IDNode *id_node = find_id_node(&object->id); + + add_operation_node(&object->id, + NodeType::GEOMETRY, + OperationCode::VISIBILITY, + [id_node](::Depsgraph *depsgraph) { + deg_evaluate_object_modifiers_mode_node_visibility(depsgraph, id_node); + }); + LISTBASE_FOREACH (ModifierData *, modifier, &object->modifiers) { - add_operation_node( + OperationNode *modifier_node = add_operation_node( &object->id, NodeType::GEOMETRY, OperationCode::MODIFIER, nullptr, modifier->name); + + /* Mute modifier mode if the modifier is not enabled for the dependency graph mode. + * This handles static (non-animated) mode of the modifier. */ + if ((modifier->mode & modifier_mode) == 0) { + modifier_node->flag |= DEPSOP_FLAG_MUTE; + } + + if (is_modifier_visibility_animated(object, modifier)) { + graph_->has_animated_visibility = true; + } } BuilderWalkUserData data; diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc index 8b51ed214a2..d6ee1286fc4 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc @@ -877,6 +877,16 @@ void DepsgraphRelationBuilder::build_object_modifiers(Object *object) &object->id, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL_INIT); const OperationKey eval_key(&object->id, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL); + const ComponentKey object_visibility_key(&object->id, NodeType::VISIBILITY); + const OperationKey modifier_visibility_key( + &object->id, NodeType::GEOMETRY, OperationCode::VISIBILITY); + add_relation(modifier_visibility_key, + object_visibility_key, + "modifier -> object visibility", + RELATION_NO_VISIBILITY_CHANGE); + + add_relation(modifier_visibility_key, eval_key, "modifier visibility -> geometry eval"); + ModifierUpdateDepsgraphContext ctx = {}; ctx.scene = scene_; ctx.object = object; diff --git a/source/blender/depsgraph/intern/builder/deg_builder_rna.cc b/source/blender/depsgraph/intern/builder/deg_builder_rna.cc index 5202ada5408..d94746fb7fa 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_rna.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_rna.cc @@ -225,6 +225,10 @@ RNANodeIdentifier RNANodeQuery::construct_node_identifier(const PointerRNA *ptr, } return node_identifier; } + + const char *prop_identifier = prop != nullptr ? RNA_property_identifier((PropertyRNA *)prop) : + ""; + if (RNA_struct_is_a(ptr->type, &RNA_Constraint)) { const Object *object = reinterpret_cast(ptr->owner_id); const bConstraint *constraint = static_cast(ptr->data); @@ -264,6 +268,13 @@ RNANodeIdentifier RNANodeQuery::construct_node_identifier(const PointerRNA *ptr, return node_identifier; } } + else if (RNA_struct_is_a(ptr->type, &RNA_Modifier) && + (contains(prop_identifier, "show_viewport") || + contains(prop_identifier, "show_render"))) { + node_identifier.type = NodeType::GEOMETRY; + node_identifier.operation_code = OperationCode::VISIBILITY; + return node_identifier; + } else if (RNA_struct_is_a(ptr->type, &RNA_Mesh) || RNA_struct_is_a(ptr->type, &RNA_Modifier) || RNA_struct_is_a(ptr->type, &RNA_GpencilModifier) || RNA_struct_is_a(ptr->type, &RNA_Spline) || RNA_struct_is_a(ptr->type, &RNA_TextBox) || @@ -290,7 +301,6 @@ RNANodeIdentifier RNANodeQuery::construct_node_identifier(const PointerRNA *ptr, else if (ptr->type == &RNA_Object) { /* Transforms props? */ if (prop != nullptr) { - const char *prop_identifier = RNA_property_identifier((PropertyRNA *)prop); /* TODO(sergey): How to optimize this? */ if (contains(prop_identifier, "location") || contains(prop_identifier, "matrix_basis") || contains(prop_identifier, "matrix_channel") || diff --git a/source/blender/depsgraph/intern/depsgraph_relation.h b/source/blender/depsgraph/intern/depsgraph_relation.h index 1bacb9abfa6..3f316fa84e8 100644 --- a/source/blender/depsgraph/intern/depsgraph_relation.h +++ b/source/blender/depsgraph/intern/depsgraph_relation.h @@ -28,6 +28,8 @@ enum RelationFlag { RELATION_FLAG_GODMODE = (1 << 4), /* Relation will check existence before being added. */ RELATION_CHECK_BEFORE_ADD = (1 << 5), + /* The relation does not participate in visibility checks. */ + RELATION_NO_VISIBILITY_CHANGE = (1 << 6), }; /* B depends on A (A -> B) */ diff --git a/source/blender/depsgraph/intern/eval/deg_eval.cc b/source/blender/depsgraph/intern/eval/deg_eval.cc index 9b2ce2bb707..cd0015ff717 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval.cc +++ b/source/blender/depsgraph/intern/eval/deg_eval.cc @@ -437,7 +437,7 @@ void deg_evaluate_on_refresh(Depsgraph *graph) evaluate_graph_threaded_stage(&state, task_pool, EvaluationStage::COPY_ON_WRITE); - if (graph->has_animated_visibility) { + if (graph->has_animated_visibility || graph->need_update_nodes_visibility) { /* Update pending parents including only the ones which are affecting operations which are * affecting visibility. */ state.need_update_pending_parents = true; diff --git a/source/blender/depsgraph/intern/eval/deg_eval_visibility.cc b/source/blender/depsgraph/intern/eval/deg_eval_visibility.cc index 05f7631b0d4..7b6aec0a73c 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval_visibility.cc +++ b/source/blender/depsgraph/intern/eval/deg_eval_visibility.cc @@ -8,6 +8,7 @@ #include "intern/eval/deg_eval_visibility.h" #include "DNA_layer_types.h" +#include "DNA_modifier_types.h" #include "DNA_object_types.h" #include "BLI_assert.h" @@ -47,6 +48,38 @@ void deg_evaluate_object_node_visibility(::Depsgraph *depsgraph, IDNode *id_node } } +void deg_evaluate_object_modifiers_mode_node_visibility(::Depsgraph *depsgraph, IDNode *id_node) +{ + BLI_assert(GS(id_node->id_cow->name) == ID_OB); + + Depsgraph *graph = reinterpret_cast(depsgraph); + const Object *object = reinterpret_cast(id_node->id_cow); + + DEG_debug_print_eval(depsgraph, __func__, object->id.name, &object->id); + + if (BLI_listbase_is_empty(&object->modifiers)) { + return; + } + + const ModifierMode modifier_mode = (graph->mode == DAG_EVAL_VIEWPORT) ? eModifierMode_Realtime : + eModifierMode_Render; + + const ComponentNode *geometry_component = id_node->find_component(NodeType::GEOMETRY); + LISTBASE_FOREACH (ModifierData *, modifier, &object->modifiers) { + OperationNode *modifier_node = geometry_component->find_operation(OperationCode::MODIFIER, + modifier->name); + + const bool modifier_enabled = modifier->mode & modifier_mode; + const int mute_flag = modifier_enabled ? 0 : DEPSOP_FLAG_MUTE; + if ((modifier_node->flag & DEPSOP_FLAG_MUTE) != mute_flag) { + modifier_node->flag &= ~DEPSOP_FLAG_MUTE; + modifier_node->flag |= mute_flag; + + graph->need_update_nodes_visibility = true; + } + } +} + void deg_graph_flush_visibility_flags(Depsgraph *graph) { enum { @@ -116,10 +149,30 @@ void deg_graph_flush_visibility_flags(Depsgraph *graph) OperationNode *op_from = reinterpret_cast(rel->from); ComponentNode *comp_from = op_from->owner; + op_from->flag |= (op_to->flag & OperationFlag::DEPSOP_FLAG_AFFECTS_VISIBILITY); + + if (rel->flag & RELATION_NO_VISIBILITY_CHANGE) { + continue; + } + 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); + bool target_affects_visible_id = comp_to->affects_visible_id; + + /* This is a bit arbitrary but the idea here is following: + * + * - When another object is used by a disabled modifier we do not want that object to + * be considered needed for evaluation. + * + * - However, we do not want to take mute flag during visibility propagation within the + * same object. Otherwise drivers and transform dependencies of the geometry component + * entry component might not be properly handled. + * + * This code works fine for muting modifiers, but might need tweaks when mute is used for + * something else. */ + if (comp_from != comp_to && (op_to->flag & DEPSOP_FLAG_MUTE)) { + target_affects_visible_id = false; + } /* Visibility component forces all components of the current ID to be considered as * affecting directly visible. */ diff --git a/source/blender/depsgraph/intern/eval/deg_eval_visibility.h b/source/blender/depsgraph/intern/eval/deg_eval_visibility.h index 9e9db8ab34a..6544654f3cf 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval_visibility.h +++ b/source/blender/depsgraph/intern/eval/deg_eval_visibility.h @@ -18,6 +18,9 @@ struct IDNode; * restriction flags. */ void deg_evaluate_object_node_visibility(::Depsgraph *depsgraph, IDNode *id_node); +/* Update node visibility flags based on actual modifiers mode flags. */ +void deg_evaluate_object_modifiers_mode_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); diff --git a/source/blender/depsgraph/intern/node/deg_node_operation.cc b/source/blender/depsgraph/intern/node/deg_node_operation.cc index 4c8c8089ac1..65adfded6b3 100644 --- a/source/blender/depsgraph/intern/node/deg_node_operation.cc +++ b/source/blender/depsgraph/intern/node/deg_node_operation.cc @@ -227,6 +227,16 @@ void OperationNode::tag_update(Depsgraph *graph, eUpdateSource source) * the evaluated clues that evaluation needs to happen again. */ graph->add_entry_tag(this); + /* Enforce dynamic visibility codepath update. + * This ensures visibility flags are consistently propagated throughout the dependency graph when + * there is no animated visibility in the graph. + * + * For example this ensures that graph is updated properly when manually toggling non-animated + * modifier visibility. */ + if (opcode == OperationCode::VISIBILITY) { + graph->need_update_nodes_visibility = true; + } + /* Tag for update, but also note that this was the source of an update. */ flag |= (DEPSOP_FLAG_NEEDS_UPDATE | DEPSOP_FLAG_DIRECTLY_MODIFIED); switch (source) { diff --git a/source/blender/depsgraph/intern/node/deg_node_operation.h b/source/blender/depsgraph/intern/node/deg_node_operation.h index 317bb4a2a93..cb3beb56556 100644 --- a/source/blender/depsgraph/intern/node/deg_node_operation.h +++ b/source/blender/depsgraph/intern/node/deg_node_operation.h @@ -219,6 +219,9 @@ enum OperationFlag { /* The operation directly or indirectly affects ID node visibility. */ DEPSOP_FLAG_AFFECTS_VISIBILITY = (1 << 4), + /* Evaluation of the node is temporarily disabled. */ + DEPSOP_FLAG_MUTE = (1 << 5), + /* Set of flags which gets flushed along the relations. */ DEPSOP_FLAG_FLUSH = (DEPSOP_FLAG_USER_MODIFIED), }; -- cgit v1.2.3