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:
@@ -13,6 +13,7 @@
|
|||||||
#include "DNA_anim_types.h"
|
#include "DNA_anim_types.h"
|
||||||
#include "DNA_armature_types.h"
|
#include "DNA_armature_types.h"
|
||||||
#include "DNA_layer_types.h"
|
#include "DNA_layer_types.h"
|
||||||
|
#include "DNA_modifier_types.h"
|
||||||
#include "DNA_object_types.h"
|
#include "DNA_object_types.h"
|
||||||
|
|
||||||
#include "BLI_stack.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);
|
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)
|
bool DepsgraphBuilder::check_pchan_has_bbone(const Object *object, const bPoseChannel *pchan)
|
||||||
{
|
{
|
||||||
BLI_assert(object->type == OB_ARMATURE);
|
BLI_assert(object->type == OB_ARMATURE);
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
struct Base;
|
struct Base;
|
||||||
struct ID;
|
struct ID;
|
||||||
struct Main;
|
struct Main;
|
||||||
|
struct ModifierData;
|
||||||
struct Object;
|
struct Object;
|
||||||
struct bPoseChannel;
|
struct bPoseChannel;
|
||||||
|
|
||||||
@@ -25,6 +26,7 @@ class DepsgraphBuilder {
|
|||||||
virtual bool need_pull_base_into_graph(const Base *base);
|
virtual bool need_pull_base_into_graph(const Base *base);
|
||||||
|
|
||||||
virtual bool is_object_visibility_animated(const Object *object);
|
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(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 bPoseChannel *pchan);
|
||||||
|
|||||||
@@ -879,9 +879,31 @@ void DepsgraphNodeBuilder::build_object_modifiers(Object *object)
|
|||||||
return;
|
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) {
|
LISTBASE_FOREACH (ModifierData *, modifier, &object->modifiers) {
|
||||||
add_operation_node(
|
OperationNode *modifier_node = add_operation_node(
|
||||||
&object->id, NodeType::GEOMETRY, OperationCode::MODIFIER, nullptr, modifier->name);
|
&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;
|
BuilderWalkUserData data;
|
||||||
|
|||||||
@@ -877,6 +877,16 @@ void DepsgraphRelationBuilder::build_object_modifiers(Object *object)
|
|||||||
&object->id, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL_INIT);
|
&object->id, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL_INIT);
|
||||||
const OperationKey eval_key(&object->id, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL);
|
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 = {};
|
ModifierUpdateDepsgraphContext ctx = {};
|
||||||
ctx.scene = scene_;
|
ctx.scene = scene_;
|
||||||
ctx.object = object;
|
ctx.object = object;
|
||||||
|
|||||||
@@ -225,6 +225,10 @@ RNANodeIdentifier RNANodeQuery::construct_node_identifier(const PointerRNA *ptr,
|
|||||||
}
|
}
|
||||||
return node_identifier;
|
return node_identifier;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char *prop_identifier = prop != nullptr ? RNA_property_identifier((PropertyRNA *)prop) :
|
||||||
|
"";
|
||||||
|
|
||||||
if (RNA_struct_is_a(ptr->type, &RNA_Constraint)) {
|
if (RNA_struct_is_a(ptr->type, &RNA_Constraint)) {
|
||||||
const Object *object = reinterpret_cast<const Object *>(ptr->owner_id);
|
const Object *object = reinterpret_cast<const Object *>(ptr->owner_id);
|
||||||
const bConstraint *constraint = static_cast<const bConstraint *>(ptr->data);
|
const bConstraint *constraint = static_cast<const bConstraint *>(ptr->data);
|
||||||
@@ -264,6 +268,13 @@ RNANodeIdentifier RNANodeQuery::construct_node_identifier(const PointerRNA *ptr,
|
|||||||
return node_identifier;
|
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) ||
|
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_GpencilModifier) ||
|
||||||
RNA_struct_is_a(ptr->type, &RNA_Spline) || RNA_struct_is_a(ptr->type, &RNA_TextBox) ||
|
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) {
|
else if (ptr->type == &RNA_Object) {
|
||||||
/* Transforms props? */
|
/* Transforms props? */
|
||||||
if (prop != nullptr) {
|
if (prop != nullptr) {
|
||||||
const char *prop_identifier = RNA_property_identifier((PropertyRNA *)prop);
|
|
||||||
/* TODO(sergey): How to optimize this? */
|
/* TODO(sergey): How to optimize this? */
|
||||||
if (contains(prop_identifier, "location") || contains(prop_identifier, "matrix_basis") ||
|
if (contains(prop_identifier, "location") || contains(prop_identifier, "matrix_basis") ||
|
||||||
contains(prop_identifier, "matrix_channel") ||
|
contains(prop_identifier, "matrix_channel") ||
|
||||||
|
|||||||
@@ -28,6 +28,8 @@ enum RelationFlag {
|
|||||||
RELATION_FLAG_GODMODE = (1 << 4),
|
RELATION_FLAG_GODMODE = (1 << 4),
|
||||||
/* Relation will check existence before being added. */
|
/* Relation will check existence before being added. */
|
||||||
RELATION_CHECK_BEFORE_ADD = (1 << 5),
|
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) */
|
/* B depends on A (A -> B) */
|
||||||
|
|||||||
@@ -437,7 +437,7 @@ void deg_evaluate_on_refresh(Depsgraph *graph)
|
|||||||
|
|
||||||
evaluate_graph_threaded_stage(&state, task_pool, EvaluationStage::COPY_ON_WRITE);
|
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
|
/* Update pending parents including only the ones which are affecting operations which are
|
||||||
* affecting visibility. */
|
* affecting visibility. */
|
||||||
state.need_update_pending_parents = true;
|
state.need_update_pending_parents = true;
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
#include "intern/eval/deg_eval_visibility.h"
|
#include "intern/eval/deg_eval_visibility.h"
|
||||||
|
|
||||||
#include "DNA_layer_types.h"
|
#include "DNA_layer_types.h"
|
||||||
|
#include "DNA_modifier_types.h"
|
||||||
#include "DNA_object_types.h"
|
#include "DNA_object_types.h"
|
||||||
|
|
||||||
#include "BLI_assert.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)
|
void deg_graph_flush_visibility_flags(Depsgraph *graph)
|
||||||
{
|
{
|
||||||
enum {
|
enum {
|
||||||
@@ -116,11 +149,31 @@ void deg_graph_flush_visibility_flags(Depsgraph *graph)
|
|||||||
OperationNode *op_from = reinterpret_cast<OperationNode *>(rel->from);
|
OperationNode *op_from = reinterpret_cast<OperationNode *>(rel->from);
|
||||||
ComponentNode *comp_from = op_from->owner;
|
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);
|
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
|
/* Visibility component forces all components of the current ID to be considered as
|
||||||
* affecting directly visible. */
|
* affecting directly visible. */
|
||||||
if (comp_from->type == NodeType::VISIBILITY) {
|
if (comp_from->type == NodeType::VISIBILITY) {
|
||||||
|
|||||||
@@ -18,6 +18,9 @@ struct IDNode;
|
|||||||
* restriction flags. */
|
* restriction flags. */
|
||||||
void deg_evaluate_object_node_visibility(::Depsgraph *depsgraph, IDNode *id_node);
|
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
|
/* 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. */
|
* 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(Depsgraph *graph);
|
||||||
|
|||||||
@@ -227,6 +227,16 @@ void OperationNode::tag_update(Depsgraph *graph, eUpdateSource source)
|
|||||||
* the evaluated clues that evaluation needs to happen again. */
|
* the evaluated clues that evaluation needs to happen again. */
|
||||||
graph->add_entry_tag(this);
|
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. */
|
/* Tag for update, but also note that this was the source of an update. */
|
||||||
flag |= (DEPSOP_FLAG_NEEDS_UPDATE | DEPSOP_FLAG_DIRECTLY_MODIFIED);
|
flag |= (DEPSOP_FLAG_NEEDS_UPDATE | DEPSOP_FLAG_DIRECTLY_MODIFIED);
|
||||||
switch (source) {
|
switch (source) {
|
||||||
|
|||||||
@@ -219,6 +219,9 @@ enum OperationFlag {
|
|||||||
/* The operation directly or indirectly affects ID node visibility. */
|
/* The operation directly or indirectly affects ID node visibility. */
|
||||||
DEPSOP_FLAG_AFFECTS_VISIBILITY = (1 << 4),
|
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. */
|
/* Set of flags which gets flushed along the relations. */
|
||||||
DEPSOP_FLAG_FLUSH = (DEPSOP_FLAG_USER_MODIFIED),
|
DEPSOP_FLAG_FLUSH = (DEPSOP_FLAG_USER_MODIFIED),
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user