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
This commit is contained in:
Sergey Sharybin 2022-08-04 15:04:25 +02:00
parent cfbe11563a
commit f12f7800c2
Notes: blender-bot 2023-02-14 08:42:54 +01:00
Referenced by commit 385bd0c4e9, Fix T103685: Animation on objects that are disabled is ignored
Referenced by commit c26d49e854, Fix T101906: Modifier apply not working if target object is in excluded collection
Referenced by issue #103685, Regression: Animation on objects that are disabled from render is ignored while Render Animation
Referenced by issue #101906, Regression: Modifier apply not working if target object is in excluded collection
Referenced by issue #101851, Regression: Duplicating a particle system crashes Blender
Referenced by issue #100820, Regression: Crash when renaming a modifier when other modifier's visibility is driven by a driver
Referenced by issue #100653, Reported object statistics are very wrong (under reported).
Referenced by issue #100656, Rigid body/cloth simulation not occurring.
Referenced by issue #100470, Regression: Changing the domain type from gas to liquid crashes Blender
Referenced by issue #100394, Regression: Duplicating a modifier causes a crash
11 changed files with 140 additions and 6 deletions

View File

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

View File

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

View File

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

View File

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

View File

@ -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<const Object *>(ptr->owner_id);
const bConstraint *constraint = static_cast<const bConstraint *>(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") ||

View File

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

View File

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

View File

@ -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 *>(depsgraph);
const Object *object = reinterpret_cast<const Object *>(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,11 +149,31 @@ void deg_graph_flush_visibility_flags(Depsgraph *graph)
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);
if (rel->flag & RELATION_NO_VISIBILITY_CHANGE) {
continue;
}
const bool target_possibly_affects_visible_id = comp_to->possibly_affects_visible_id;
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. */
if (comp_from->type == NodeType::VISIBILITY) {

View File

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

View File

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

View File

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