diff --git a/source/blender/blenkernel/intern/cpp_types.cc b/source/blender/blenkernel/intern/cpp_types.cc index b824c57f650..f5822325e6d 100644 --- a/source/blender/blenkernel/intern/cpp_types.cc +++ b/source/blender/blenkernel/intern/cpp_types.cc @@ -36,7 +36,8 @@ BLI_CPP_TYPE_MAKE(Image *, CPPTypeFlags::BasicType) BLI_CPP_TYPE_MAKE(Material *, CPPTypeFlags::BasicType) BLI_CPP_TYPE_MAKE(MStringProperty, CPPTypeFlags::None); -BLI_CPP_TYPE_MAKE(blender::nodes::MenuValue, CPPTypeFlags::EqualityComparable); +BLI_CPP_TYPE_MAKE(blender::nodes::MenuValue, + CPPTypeFlags::Hashable | CPPTypeFlags::EqualityComparable); BLI_CPP_TYPE_MAKE(blender::nodes::BundlePtr, CPPTypeFlags::EqualityComparable); BLI_CPP_TYPE_MAKE(blender::nodes::ClosurePtr, CPPTypeFlags::EqualityComparable); BLI_CPP_TYPE_MAKE(blender::nodes::ListPtr, CPPTypeFlags::EqualityComparable); diff --git a/source/blender/modifiers/CMakeLists.txt b/source/blender/modifiers/CMakeLists.txt index 07d33f39bc3..ce8eff402ee 100644 --- a/source/blender/modifiers/CMakeLists.txt +++ b/source/blender/modifiers/CMakeLists.txt @@ -143,6 +143,7 @@ set(LIB PRIVATE bf::geometry PRIVATE bf::intern::guardedalloc PRIVATE bf::extern::fmtlib + PRIVATE bf::extern::xxhash PRIVATE bf::nodes PRIVATE bf::render PRIVATE bf::windowmanager diff --git a/source/blender/modifiers/MOD_nodes.hh b/source/blender/modifiers/MOD_nodes.hh index 449bb85c39f..d98432a7ac3 100644 --- a/source/blender/modifiers/MOD_nodes.hh +++ b/source/blender/modifiers/MOD_nodes.hh @@ -6,6 +6,9 @@ #include +#include "BLI_array.hh" +#include "NOD_socket_usage_inference_fwd.hh" + struct NodesModifierData; struct NodesModifierDataBlock; struct Object; @@ -28,6 +31,18 @@ void MOD_nodes_update_interface(Object *object, NodesModifierData *nmd); namespace blender { +class NodesModifierUsageInferenceCache { + private: + uint64_t input_values_hash_ = 0; + + public: + Array inputs; + Array outputs; + + void ensure(const NodesModifierData &nmd); + void reset(); +}; + struct NodesModifierRuntime { /** * Contains logged information from the last evaluation. @@ -42,6 +57,11 @@ struct NodesModifierRuntime { * used by the evaluated modifier. */ std::shared_ptr cache; + /** + * Cache the usage of the node group inputs and outputs to accelerate drawing the UI when no + * properties change. + */ + NodesModifierUsageInferenceCache usage_cache; }; void nodes_modifier_data_block_destruct(NodesModifierDataBlock *data_block, bool do_id_user); diff --git a/source/blender/modifiers/intern/MOD_nodes.cc b/source/blender/modifiers/intern/MOD_nodes.cc index 07ed02c9303..f7b0abd067c 100644 --- a/source/blender/modifiers/intern/MOD_nodes.cc +++ b/source/blender/modifiers/intern/MOD_nodes.cc @@ -10,6 +10,7 @@ #include #include #include +#include #include "MEM_guardedalloc.h" @@ -451,6 +452,7 @@ void MOD_nodes_update_interface(Object *object, NodesModifierData *nmd) update_id_properties_from_node_group(nmd); update_bakes_from_node_group(*nmd); update_panels_from_node_group(*nmd); + nmd->runtime->usage_cache.reset(); DEG_id_tag_update(&object->id, ID_RECALC_GEOMETRY); } @@ -1952,6 +1954,63 @@ static void modify_geometry_set(ModifierData *md, modifyGeometry(md, ctx, *geometry_set); } +void NodesModifierUsageInferenceCache::ensure(const NodesModifierData &nmd) +{ + if (!nmd.node_group) { + this->reset(); + return; + } + if (ID_MISSING(&nmd.node_group->id)) { + this->reset(); + return; + } + const bNodeTree &tree = *nmd.node_group; + tree.ensure_interface_cache(); + tree.ensure_topology_cache(); + ResourceScope scope; + const Vector group_input_values = + nodes::get_geometry_nodes_input_inference_values(tree, nmd.settings.properties, scope); + + /* Compute the hash of the input values. This has to be done everytime currently, because there + * is no reliable callback yet that is called any of the modifier properties changes. */ + XXH3_state_t *state = XXH3_createState(); + XXH3_64bits_reset(state); + BLI_SCOPED_DEFER([&]() { XXH3_freeState(state); }); + for (const int input_i : IndexRange(nmd.node_group->interface_inputs().size())) { + const nodes::InferenceValue &value = group_input_values[input_i]; + XXH3_64bits_update(state, &input_i, sizeof(input_i)); + if (value.is_primitive_value()) { + const void *value_ptr = value.get_primitive_ptr(); + const bNodeTreeInterfaceSocket &io_socket = *nmd.node_group->interface_inputs()[input_i]; + const CPPType &base_type = *io_socket.socket_typeinfo()->base_cpp_type; + uint64_t value_hash = base_type.hash_or_fallback(value_ptr, 0); + XXH3_64bits_update(state, &value_hash, sizeof(value_hash)); + } + } + const uint64_t new_input_values_hash = XXH3_64bits_digest(state); + if (new_input_values_hash == input_values_hash_) { + if (this->inputs.size() == tree.interface_inputs().size() && + this->outputs.size() == tree.interface_outputs().size()) + { + /* The cache is up to date, so return early. */ + return; + } + } + /* Compute the new usage inference result. */ + this->inputs.reinitialize(tree.interface_inputs().size()); + this->outputs.reinitialize(tree.interface_outputs().size()); + nodes::socket_usage_inference::infer_group_interface_usage( + tree, group_input_values, inputs, outputs); + input_values_hash_ = new_input_values_hash; +} + +void NodesModifierUsageInferenceCache::reset() +{ + input_values_hash_ = 0; + this->inputs = {}; + this->outputs = {}; +} + static void panel_draw(const bContext *C, Panel *panel) { uiLayout *layout = panel->layout; diff --git a/source/blender/nodes/intern/geometry_nodes_caller_ui.cc b/source/blender/nodes/intern/geometry_nodes_caller_ui.cc index dbaabb835d9..88835b3944d 100644 --- a/source/blender/nodes/intern/geometry_nodes_caller_ui.cc +++ b/source/blender/nodes/intern/geometry_nodes_caller_ui.cc @@ -1008,11 +1008,9 @@ void draw_geometry_nodes_modifier_ui(const bContext &C, PointerRNA *modifier_ptr } if (nmd.node_group != nullptr && nmd.settings.properties != nullptr) { - nmd.node_group->ensure_interface_cache(); - ctx.input_usages.reinitialize(nmd.node_group->interface_inputs().size()); - ctx.output_usages.reinitialize(nmd.node_group->interface_outputs().size()); - nodes::socket_usage_inference::infer_group_interface_usage( - *nmd.node_group, ctx.properties, ctx.input_usages, ctx.output_usages); + nmd.runtime->usage_cache.ensure(nmd); + ctx.input_usages = nmd.runtime->usage_cache.inputs; + ctx.output_usages = nmd.runtime->usage_cache.outputs; draw_interface_panel_content(ctx, &layout, nmd.node_group->tree_interface.root_panel); }