From d2f4ebcd6aa341e4a3604effab66bdcab753ec85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20T=C3=B6nne?= Date: Thu, 14 Sep 2023 14:13:07 +0200 Subject: [PATCH] Fix #112331: Add update tags directly in bNodeTreeInterface API methods Calling an API function after the node panels patch does not internally tag the node tree with `NTREE_CHANGED_INTERFACE` any more, because the node tree is not directly accessible from `bNodeTreeInterface`. Before node panels the API functions for interfaces could tag the tree directly for later update consideration, which now requires explicit tagging calls. The fix is to add a flag and mutex directly to `bNodeTreeInterface`, so API methods can tag after updates. This mostly copies runtime data concepts from `bNodeTree`. The `ensure_interface_cache` method is equivalent to `ensure_topology_cache` and should be called before accessing `interface_inputs` and similar cache data. Pull Request: https://projects.blender.org/blender/blender/pulls/111741 --- source/blender/blenkernel/BKE_node_runtime.hh | 18 +-- .../blenkernel/BKE_node_tree_interface.hh | 31 ++++- .../blender/blenkernel/BKE_node_tree_update.h | 2 - source/blender/blenkernel/intern/node.cc | 2 +- .../blender/blenkernel/intern/node_runtime.cc | 9 -- .../intern/node_tree_anonymous_attributes.cc | 1 + .../intern/node_tree_field_inferencing.cc | 1 + .../blenkernel/intern/node_tree_interface.cc | 114 +++++++++++++++--- .../blenkernel/intern/node_tree_update.cc | 41 +++---- .../blenloader/intern/versioning_250.cc | 2 +- .../intern/builder/deg_builder_nodes.cc | 2 +- .../intern/builder/deg_builder_relations.cc | 1 + .../editors/geometry/node_group_operator.cc | 2 +- .../interface_template_node_tree_interface.cc | 6 +- .../editors/object/object_relations.cc | 2 +- .../editors/space_node/link_drag_search.cc | 1 - .../editors/space_node/node_templates.cc | 1 + .../makesdna/DNA_node_tree_interface_types.h | 33 +++++ source/blender/makesdna/DNA_node_types.h | 6 + .../intern/rna_node_tree_interface.cc | 19 +-- .../blender/makesrna/intern/rna_nodetree.cc | 3 +- source/blender/modifiers/intern/MOD_nodes.cc | 5 +- .../nodes/intern/geometry_nodes_execute.cc | 3 +- .../intern/geometry_nodes_lazy_function.cc | 2 + 24 files changed, 214 insertions(+), 93 deletions(-) diff --git a/source/blender/blenkernel/BKE_node_runtime.hh b/source/blender/blenkernel/BKE_node_runtime.hh index 57035fcc184..dbf990a9d4f 100644 --- a/source/blender/blenkernel/BKE_node_runtime.hh +++ b/source/blender/blenkernel/BKE_node_runtime.hh @@ -171,7 +171,6 @@ class bNodeTreeRuntime : NonCopyable, NonMovable { bool has_undefined_nodes_or_sockets = false; bNode *group_output_node = nullptr; Vector root_frames; - bNodeTreeInterfaceCache interface_cache; }; /** @@ -546,22 +545,27 @@ inline blender::Span bNodeTree::nested_node_refs_span() const return {this->nested_node_refs, this->nested_node_refs_num}; } +inline void bNodeTree::ensure_interface_cache() const +{ + this->tree_interface.ensure_items_cache(); +} + inline blender::Span bNodeTree::interface_inputs() const { - BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this)); - return this->runtime->interface_cache.inputs; + BLI_assert(this->tree_interface.items_cache_is_available()); + return this->tree_interface.runtime->inputs_; } inline blender::Span bNodeTree::interface_outputs() const { - BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this)); - return this->runtime->interface_cache.outputs; + BLI_assert(this->tree_interface.items_cache_is_available()); + return this->tree_interface.runtime->outputs_; } inline blender::Span bNodeTree::interface_items() const { - BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this)); - return this->runtime->interface_cache.items; + BLI_assert(this->tree_interface.items_cache_is_available()); + return this->tree_interface.runtime->items_; } /** \} */ diff --git a/source/blender/blenkernel/BKE_node_tree_interface.hh b/source/blender/blenkernel/BKE_node_tree_interface.hh index 49dec1478a2..d1db4be6ce7 100644 --- a/source/blender/blenkernel/BKE_node_tree_interface.hh +++ b/source/blender/blenkernel/BKE_node_tree_interface.hh @@ -12,18 +12,37 @@ #include #include +#include "BLI_cache_mutex.hh" #include "BLI_parameter_pack_utils.hh" #include "BLI_vector.hh" namespace blender::bke { -/* Runtime topology cache for linear access to items. */ -struct bNodeTreeInterfaceCache { - Vector items; - Vector inputs; - Vector outputs; +class NodeTreeMainUpdater; - void rebuild(bNodeTreeInterface &tree_interface); +class bNodeTreeInterfaceRuntime { + friend bNodeTreeInterface; + friend bNodeTree; + + private: + /** + * Keeps track of what changed in the node tree until the next update. + * Should not be changed directly, instead use the functions in `BKE_node_tree_update.h`. + * #NodeTreeInterfaceChangedFlag. + */ + uint32_t changed_flag_ = 0; + + /** + * Protects access to item cache variables below. This is necessary so that the cache can be + * updated on a const #bNodeTreeInterface. + */ + CacheMutex items_cache_mutex_; + + /* Runtime topology cache for linear access to items. */ + Vector items_; + /* Socket-only lists for input/output access by index. */ + Vector inputs_; + Vector outputs_; }; namespace node_interface { diff --git a/source/blender/blenkernel/BKE_node_tree_update.h b/source/blender/blenkernel/BKE_node_tree_update.h index ef2c59d2591..0969e3a22e9 100644 --- a/source/blender/blenkernel/BKE_node_tree_update.h +++ b/source/blender/blenkernel/BKE_node_tree_update.h @@ -54,8 +54,6 @@ void BKE_ntree_update_tag_link_mute(struct bNodeTree *ntree, struct bNodeLink *l void BKE_ntree_update_tag_active_output_changed(struct bNodeTree *ntree); /** Used after file loading when run-time data on the tree has not been initialized yet. */ void BKE_ntree_update_tag_missing_runtime_data(struct bNodeTree *ntree); -/** Used when the interface sockets/values have changed. */ -void BKE_ntree_update_tag_interface(struct bNodeTree *ntree); /** Used when change parent node. */ void BKE_ntree_update_tag_parent_change(struct bNodeTree *ntree, struct bNode *node); /** Used when an id data block changed that might be used by nodes that need to be updated. */ diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index 69f0937e7f3..3f5138af2b7 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -1101,7 +1101,7 @@ static void node_tree_asset_pre_save(void *asset_ptr, AssetMetaData *asset_data) BKE_asset_metadata_idprop_ensure(asset_data, idprop::create("type", node_tree.type).release()); auto inputs = idprop::create_group("inputs"); auto outputs = idprop::create_group("outputs"); - node_tree.ensure_topology_cache(); + node_tree.ensure_interface_cache(); for (const bNodeTreeInterfaceSocket *socket : node_tree.interface_inputs()) { auto property = idprop::create(socket->name, socket->socket_type); IDP_AddToGroup(inputs.get(), property.release()); diff --git a/source/blender/blenkernel/intern/node_runtime.cc b/source/blender/blenkernel/intern/node_runtime.cc index 1cebf7f0d20..db70c332c67 100644 --- a/source/blender/blenkernel/intern/node_runtime.cc +++ b/source/blender/blenkernel/intern/node_runtime.cc @@ -24,14 +24,6 @@ void preprocess_geometry_node_tree_for_evaluation(bNodeTree &tree_cow) blender::nodes::ensure_geometry_nodes_lazy_function_graph(tree_cow); } -static void update_interface(const bNodeTree &ntree) -{ - bNodeTreeRuntime &tree_runtime = *ntree.runtime; - /* const_cast needed because the cache stores mutable item pointers, but needs a mutable - * interface in order to get them. The interface itself is not modified here. */ - tree_runtime.interface_cache.rebuild(const_cast(ntree.tree_interface)); -} - static void update_node_vector(const bNodeTree &ntree) { bNodeTreeRuntime &tree_runtime = *ntree.runtime; @@ -538,7 +530,6 @@ static void ensure_topology_cache(const bNodeTree &ntree) { bNodeTreeRuntime &tree_runtime = *ntree.runtime; tree_runtime.topology_cache_mutex.ensure([&]() { - update_interface(ntree); update_node_vector(ntree); update_link_vector(ntree); update_socket_vectors_and_owner_node(ntree); diff --git a/source/blender/blenkernel/intern/node_tree_anonymous_attributes.cc b/source/blender/blenkernel/intern/node_tree_anonymous_attributes.cc index 4d6fa828f2f..f4c74789a5c 100644 --- a/source/blender/blenkernel/intern/node_tree_anonymous_attributes.cc +++ b/source/blender/blenkernel/intern/node_tree_anonymous_attributes.cc @@ -203,6 +203,7 @@ static AnonymousAttributeInferencingResult analyse_anonymous_attribute_usages( const bNodeTree &tree) { BLI_assert(!tree.has_available_link_cycle()); + tree.ensure_interface_cache(); ResourceScope scope; const Array relations_by_node = get_relations_by_node(tree, scope); diff --git a/source/blender/blenkernel/intern/node_tree_field_inferencing.cc b/source/blender/blenkernel/intern/node_tree_field_inferencing.cc index 97c872de5b8..563c1bcacef 100644 --- a/source/blender/blenkernel/intern/node_tree_field_inferencing.cc +++ b/source/blender/blenkernel/intern/node_tree_field_inferencing.cc @@ -689,6 +689,7 @@ bool update_field_inferencing(const bNodeTree &tree) { BLI_assert(tree.type == NTREE_GEOMETRY); tree.ensure_topology_cache(); + tree.ensure_interface_cache(); const Span nodes = tree.all_nodes(); ResourceScope scope; diff --git a/source/blender/blenkernel/intern/node_tree_interface.cc b/source/blender/blenkernel/intern/node_tree_interface.cc index 5e6a74a754b..1b4b809cbe8 100644 --- a/source/blender/blenkernel/intern/node_tree_interface.cc +++ b/source/blender/blenkernel/intern/node_tree_interface.cc @@ -20,6 +20,15 @@ #include "DNA_node_tree_interface_types.h" #include "DNA_node_types.h" +/** + * These flags are used by the `changed_flag` field in #bNodeTreeInterfaceRuntime. + */ +enum NodeTreeInterfaceChangedFlag { + NODE_INTERFACE_CHANGED_NOTHING = 0, + NODE_INTERFACE_CHANGED_ITEMS = (1 << 1), + NODE_INTERFACE_CHANGED_ALL = -1, +}; + namespace blender::bke::node_interface { namespace socket_types { @@ -1021,6 +1030,9 @@ static bNodeTreeInterfacePanel *make_panel(const int uid, void bNodeTreeInterface::init_data() { + this->runtime = MEM_new(__func__); + this->tag_missing_runtime_data(); + /* Root panel is allowed to contain child panels. */ root_panel.flag |= NODE_INTERFACE_PANEL_ALLOW_CHILD_PANELS; } @@ -1029,10 +1041,15 @@ void bNodeTreeInterface::copy_data(const bNodeTreeInterface &src, int flag) { item_types::panel_init(this->root_panel, src.root_panel.items(), flag, nullptr); this->active_index = src.active_index; + + this->runtime = MEM_new(__func__); + this->tag_missing_runtime_data(); } void bNodeTreeInterface::free_data() { + MEM_delete(this->runtime); + /* Called when freeing the main database, don't do user refcount here. */ this->root_panel.clear(false); } @@ -1047,6 +1064,9 @@ void bNodeTreeInterface::write(BlendWriter *writer) void bNodeTreeInterface::read_data(BlendDataReader *reader) { item_types::item_read_data(reader, this->root_panel.item); + + this->runtime = MEM_new(__func__); + this->tag_missing_runtime_data(); } bNodeTreeInterfaceItem *bNodeTreeInterface::active_item() @@ -1109,6 +1129,8 @@ bNodeTreeInterfaceSocket *bNodeTreeInterface::add_socket(blender::StringRefNull if (new_socket) { parent->add_item(new_socket->item); } + + this->tag_items_changed(); return new_socket; } @@ -1129,6 +1151,8 @@ bNodeTreeInterfaceSocket *bNodeTreeInterface::insert_socket(blender::StringRefNu if (new_socket) { parent->insert_item(new_socket->item, position); } + + this->tag_items_changed(); return new_socket; } @@ -1151,6 +1175,8 @@ bNodeTreeInterfacePanel *bNodeTreeInterface::add_panel(blender::StringRefNull na if (new_panel) { parent->add_item(new_panel->item); } + + this->tag_items_changed(); return new_panel; } @@ -1174,6 +1200,8 @@ bNodeTreeInterfacePanel *bNodeTreeInterface::insert_panel(blender::StringRefNull if (new_panel) { parent->insert_item(new_panel->item, position); } + + this->tag_items_changed(); return new_panel; } @@ -1197,6 +1225,7 @@ bNodeTreeInterfaceItem *bNodeTreeInterface::add_item_copy(const bNodeTreeInterfa item_types::item_copy(*citem, item, 0, [&]() { return this->next_uid++; }); parent->add_item(*citem); + this->tag_items_changed(); return citem; } @@ -1221,6 +1250,7 @@ bNodeTreeInterfaceItem *bNodeTreeInterface::insert_item_copy(const bNodeTreeInte item_types::item_copy(*citem, item, 0, [&]() { return this->next_uid++; }); parent->insert_item(*citem, position); + this->tag_items_changed(); return citem; } @@ -1239,14 +1269,17 @@ bool bNodeTreeInterface::remove_item(bNodeTreeInterfaceItem &item, bool move_con } } if (parent->remove_item(item, true)) { + this->tag_items_changed(); return true; } + return false; } void bNodeTreeInterface::clear_items() { root_panel.clear(true); + this->tag_items_changed(); } bool bNodeTreeInterface::move_item(bNodeTreeInterfaceItem &item, const int new_position) @@ -1255,7 +1288,12 @@ bool bNodeTreeInterface::move_item(bNodeTreeInterfaceItem &item, const int new_p if (parent == nullptr) { return false; } - return parent->move_item(item, new_position); + + if (parent->move_item(item, new_position)) { + this->tag_items_changed(); + return true; + } + return false; } bool bNodeTreeInterface::move_item_to_parent(bNodeTreeInterfaceItem &item, @@ -1273,13 +1311,17 @@ bool bNodeTreeInterface::move_item_to_parent(bNodeTreeInterfaceItem &item, return false; } if (parent == new_parent) { - return parent->move_item(item, new_position); + if (parent->move_item(item, new_position)) { + this->tag_items_changed(); + return true; + } } else { /* Note: only remove and reinsert when parents different, otherwise removing the item can * change the desired target position! */ if (parent->remove_item(item, false)) { new_parent->insert_item(item, new_position); + this->tag_items_changed(); return true; } } @@ -1291,27 +1333,59 @@ void bNodeTreeInterface::foreach_id(LibraryForeachIDData *cb) item_types::item_foreach_id(cb, root_panel.item); } -namespace blender::bke { - -void bNodeTreeInterfaceCache::rebuild(bNodeTreeInterface &interface) +bool bNodeTreeInterface::items_cache_is_available() const { - /* Rebuild draw-order list of interface items for linear access. */ - items.clear(); - inputs.clear(); - outputs.clear(); + return !this->runtime->items_cache_mutex_.is_dirty(); +} - interface.foreach_item([&](bNodeTreeInterfaceItem &item) { - items.append(&item); - if (bNodeTreeInterfaceSocket *socket = get_item_as(&item)) { - if (socket->flag & NODE_INTERFACE_SOCKET_INPUT) { - inputs.append(socket); +void bNodeTreeInterface::ensure_items_cache() const +{ + blender::bke::bNodeTreeInterfaceRuntime &runtime = *this->runtime; + + runtime.items_cache_mutex_.ensure([&]() { + /* Rebuild draw-order list of interface items for linear access. */ + runtime.items_.clear(); + runtime.inputs_.clear(); + runtime.outputs_.clear(); + + /* Items in the cache are mutable pointers, but node tree update considers ID data to be + * immutable when caching. DNA ListBase pointers can be mutable even if their container is + * const, but the items returned by #foreach_item inherit qualifiers from the container. */ + bNodeTreeInterface &mutable_self = const_cast(*this); + + mutable_self.foreach_item([&](bNodeTreeInterfaceItem &item) { + runtime.items_.append(&item); + if (bNodeTreeInterfaceSocket *socket = get_item_as(&item)) { + if (socket->flag & NODE_INTERFACE_SOCKET_INPUT) { + runtime.inputs_.append(socket); + } + if (socket->flag & NODE_INTERFACE_SOCKET_OUTPUT) { + runtime.outputs_.append(socket); + } } - if (socket->flag & NODE_INTERFACE_SOCKET_OUTPUT) { - outputs.append(socket); - } - } - return true; + return true; + }); }); } -} // namespace blender::bke +void bNodeTreeInterface::tag_missing_runtime_data() +{ + this->runtime->changed_flag_ |= NODE_INTERFACE_CHANGED_ALL; + this->runtime->items_cache_mutex_.tag_dirty(); +} + +bool bNodeTreeInterface::is_changed() const +{ + return this->runtime->changed_flag_ != NODE_INTERFACE_CHANGED_NOTHING; +} + +void bNodeTreeInterface::tag_items_changed() +{ + this->runtime->changed_flag_ |= NODE_INTERFACE_CHANGED_ITEMS; + this->runtime->items_cache_mutex_.tag_dirty(); +} + +void bNodeTreeInterface::reset_changed_flags() +{ + this->runtime->changed_flag_ = NODE_INTERFACE_CHANGED_NOTHING; +} diff --git a/source/blender/blenkernel/intern/node_tree_update.cc b/source/blender/blenkernel/intern/node_tree_update.cc index 9694d665649..f4ab915718a 100644 --- a/source/blender/blenkernel/intern/node_tree_update.cc +++ b/source/blender/blenkernel/intern/node_tree_update.cc @@ -46,13 +46,12 @@ enum eNodeTreeChangedFlag { NTREE_CHANGED_ANY = (1 << 1), NTREE_CHANGED_NODE_PROPERTY = (1 << 2), NTREE_CHANGED_NODE_OUTPUT = (1 << 3), - NTREE_CHANGED_INTERFACE = (1 << 4), - NTREE_CHANGED_LINK = (1 << 5), - NTREE_CHANGED_REMOVED_NODE = (1 << 6), - NTREE_CHANGED_REMOVED_SOCKET = (1 << 7), - NTREE_CHANGED_SOCKET_PROPERTY = (1 << 8), - NTREE_CHANGED_INTERNAL_LINK = (1 << 9), - NTREE_CHANGED_PARENT = (1 << 10), + NTREE_CHANGED_LINK = (1 << 4), + NTREE_CHANGED_REMOVED_NODE = (1 << 5), + NTREE_CHANGED_REMOVED_SOCKET = (1 << 6), + NTREE_CHANGED_SOCKET_PROPERTY = (1 << 7), + NTREE_CHANGED_INTERNAL_LINK = (1 << 8), + NTREE_CHANGED_PARENT = (1 << 9), NTREE_CHANGED_ALL = -1, }; @@ -163,6 +162,12 @@ static int get_internal_link_type_priority(const bNodeSocketType *from, const bN return -1; } +/* Check both the tree's own tags and the interface tags. */ +static bool is_tree_changed(const bNodeTree &tree) +{ + return tree.runtime->changed_flag != NTREE_CHANGED_NOTHING || tree.tree_interface.is_changed(); +} + using TreeNodePair = std::pair; using ObjectModifierPair = std::pair; using NodeSocketPair = std::pair; @@ -296,7 +301,7 @@ class NodeTreeMainUpdater { { Vector changed_ntrees; FOREACH_NODETREE_BEGIN (bmain_, ntree, id) { - if (ntree->runtime->changed_flag != NTREE_CHANGED_NOTHING) { + if (is_tree_changed(*ntree)) { changed_ntrees.append(ntree); } } @@ -314,7 +319,7 @@ class NodeTreeMainUpdater { if (root_ntrees.size() == 1) { bNodeTree *ntree = root_ntrees[0]; - if (ntree->runtime->changed_flag == NTREE_CHANGED_NOTHING) { + if (!is_tree_changed(*ntree)) { return; } const TreeUpdateResult result = this->update_tree(*ntree); @@ -327,7 +332,7 @@ class NodeTreeMainUpdater { if (!is_single_tree_update) { Vector ntrees_in_order = this->get_tree_update_order(root_ntrees); for (bNodeTree *ntree : ntrees_in_order) { - if (ntree->runtime->changed_flag == NTREE_CHANGED_NOTHING) { + if (!is_tree_changed(*ntree)) { continue; } if (!update_result_by_tree_.contains(ntree)) { @@ -503,9 +508,7 @@ class NodeTreeMainUpdater { ntreeTexCheckCyclics(&ntree); } - if (ntree.runtime->changed_flag & NTREE_CHANGED_INTERFACE || - ntree.runtime->changed_flag & NTREE_CHANGED_ANY) - { + if (ntree.tree_interface.is_changed()) { result.interface_changed = true; } @@ -579,7 +582,7 @@ class NodeTreeMainUpdater { /* Currently we have no way to tell if a node needs to be updated when a link changed. */ return true; } - if (ntree.runtime->changed_flag & NTREE_CHANGED_INTERFACE) { + if (ntree.tree_interface.is_changed()) { if (ELEM(node.type, NODE_GROUP_INPUT, NODE_GROUP_OUTPUT)) { return true; } @@ -716,8 +719,7 @@ class NodeTreeMainUpdater { { /* Don't trigger preview removal when only those flags are set. */ const uint32_t allowed_flags = NTREE_CHANGED_LINK | NTREE_CHANGED_SOCKET_PROPERTY | - NTREE_CHANGED_NODE_PROPERTY | NTREE_CHANGED_NODE_OUTPUT | - NTREE_CHANGED_INTERFACE; + NTREE_CHANGED_NODE_PROPERTY | NTREE_CHANGED_NODE_OUTPUT; if ((ntree.runtime->changed_flag & allowed_flags) == ntree.runtime->changed_flag) { return; } @@ -1243,6 +1245,8 @@ class NodeTreeMainUpdater { socket->runtime->changed_flag = NTREE_CHANGED_NOTHING; } } + + ntree.tree_interface.reset_changed_flags(); } }; @@ -1342,11 +1346,6 @@ void BKE_ntree_update_tag_missing_runtime_data(bNodeTree *ntree) add_tree_tag(ntree, NTREE_CHANGED_ALL); } -void BKE_ntree_update_tag_interface(bNodeTree *ntree) -{ - add_tree_tag(ntree, NTREE_CHANGED_INTERFACE); -} - void BKE_ntree_update_tag_parent_change(bNodeTree *ntree, bNode *node) { add_node_tag(ntree, node, NTREE_CHANGED_PARENT); diff --git a/source/blender/blenloader/intern/versioning_250.cc b/source/blender/blenloader/intern/versioning_250.cc index b911552c217..68693513dd6 100644 --- a/source/blender/blenloader/intern/versioning_250.cc +++ b/source/blender/blenloader/intern/versioning_250.cc @@ -579,7 +579,7 @@ static bNodeSocket *do_versions_node_group_add_socket_2_56_2(bNodeTree *ngroup, BLI_addtail(in_out == SOCK_IN ? &ngroup->inputs_legacy : &ngroup->outputs_legacy, gsock); - BKE_ntree_update_tag_interface(ngroup); + ngroup->tree_interface.tag_items_changed(); return gsock; } diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc index 04f26ab6f30..f7649c7cf3e 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc @@ -1960,7 +1960,7 @@ void DepsgraphNodeBuilder::build_nodetree(bNodeTree *ntree) } /* Needed for interface cache. */ - ntree->ensure_topology_cache(); + ntree->ensure_interface_cache(); for (bNodeTreeInterfaceSocket *socket : ntree->interface_inputs()) { build_idproperties(socket->properties); } diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc index b91fb685cee..2b9f094fc82 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc @@ -2935,6 +2935,7 @@ void DepsgraphRelationBuilder::build_nodetree(bNodeTree *ntree) } } + ntree->ensure_interface_cache(); for (bNodeTreeInterfaceSocket *socket : ntree->interface_inputs()) { build_idproperties(socket->properties); } diff --git a/source/blender/editors/geometry/node_group_operator.cc b/source/blender/editors/geometry/node_group_operator.cc index 936c38ef111..f512c339926 100644 --- a/source/blender/editors/geometry/node_group_operator.cc +++ b/source/blender/editors/geometry/node_group_operator.cc @@ -429,7 +429,7 @@ static void run_node_group_ui(bContext *C, wmOperator *op) return; } - node_tree->ensure_topology_cache(); + node_tree->ensure_interface_cache(); int input_index = 0; for (bNodeTreeInterfaceSocket *io_socket : node_tree->interface_inputs()) { draw_property_for_socket( diff --git a/source/blender/editors/interface/interface_template_node_tree_interface.cc b/source/blender/editors/interface/interface_template_node_tree_interface.cc index 8d433b22a4e..e7937239c9f 100644 --- a/source/blender/editors/interface/interface_template_node_tree_interface.cc +++ b/source/blender/editors/interface/interface_template_node_tree_interface.cc @@ -151,7 +151,7 @@ class NodeSocketViewItem : public BasicTreeViewItem { bool rename(const bContext &C, StringRefNull new_name) override { socket_.name = BLI_strdup(new_name.c_str()); - BKE_ntree_update_tag_interface(&nodetree_); + nodetree_.tree_interface.tag_items_changed(); ED_node_tree_propagate_change(&C, CTX_data_main(&C), &nodetree_); return true; } @@ -208,7 +208,7 @@ class NodePanelViewItem : public BasicTreeViewItem { bool rename(const bContext &C, StringRefNull new_name) override { panel_.name = BLI_strdup(new_name.c_str()); - BKE_ntree_update_tag_interface(&nodetree_); + nodetree_.tree_interface.tag_items_changed(); ED_node_tree_propagate_change(&C, CTX_data_main(&C), &nodetree_); return true; } @@ -389,7 +389,6 @@ bool NodeSocketDropTarget::on_drop(bContext *C, const DragInfo &drag_info) const interface.move_item_to_parent(*drag_item, parent, index); /* General update */ - BKE_ntree_update_tag_interface(&nodetree); ED_node_tree_propagate_change(C, CTX_data_main(C), &nodetree); return true; } @@ -480,7 +479,6 @@ bool NodePanelDropTarget::on_drop(bContext *C, const DragInfo &drag_info) const interface.move_item_to_parent(*drag_item, parent, index); /* General update */ - BKE_ntree_update_tag_interface(&nodetree); ED_node_tree_propagate_change(C, CTX_data_main(C), &nodetree); return true; } diff --git a/source/blender/editors/object/object_relations.cc b/source/blender/editors/object/object_relations.cc index cb8294bfd36..559993b2505 100644 --- a/source/blender/editors/object/object_relations.cc +++ b/source/blender/editors/object/object_relations.cc @@ -2966,7 +2966,7 @@ char *ED_object_ot_drop_geometry_nodes_tooltip(bContext *C, static bool check_geometry_node_group_sockets(wmOperator *op, const bNodeTree *tree) { - tree->ensure_topology_cache(); + tree->ensure_interface_cache(); if (!tree->interface_inputs().is_empty()) { const bNodeTreeInterfaceSocket *first_input = tree->interface_inputs()[0]; if (!first_input) { diff --git a/source/blender/editors/space_node/link_drag_search.cc b/source/blender/editors/space_node/link_drag_search.cc index 95de5019ac0..2e89939e74c 100644 --- a/source/blender/editors/space_node/link_drag_search.cc +++ b/source/blender/editors/space_node/link_drag_search.cc @@ -94,7 +94,6 @@ static void add_group_input_node_fn(nodes::LinkSearchOpParams ¶ms) NODE_INTERFACE_SOCKET_INPUT, nullptr); socket_iface->init_from_socket_instance(¶ms.socket); - BKE_ntree_update_tag_interface(¶ms.node_tree); bNode &group_input = params.add_node("NodeGroupInput"); diff --git a/source/blender/editors/space_node/node_templates.cc b/source/blender/editors/space_node/node_templates.cc index 5917cfa6c52..dabad469933 100644 --- a/source/blender/editors/space_node/node_templates.cc +++ b/source/blender/editors/space_node/node_templates.cc @@ -337,6 +337,7 @@ static Vector ui_node_link_items(NodeLinkArg *arg, continue; } + ngroup->ensure_interface_cache(); Span iosockets = (in_out == SOCK_IN ? ngroup->interface_inputs() : ngroup->interface_outputs()); diff --git a/source/blender/makesdna/DNA_node_tree_interface_types.h b/source/blender/makesdna/DNA_node_tree_interface_types.h index f42b4218e70..19e4be818d6 100644 --- a/source/blender/makesdna/DNA_node_tree_interface_types.h +++ b/source/blender/makesdna/DNA_node_tree_interface_types.h @@ -19,6 +19,15 @@ # include #endif +#ifdef __cplusplus +namespace blender::bke { +class bNodeTreeInterfaceRuntime; +} +using bNodeTreeInterfaceRuntimeHandle = blender::bke::bNodeTreeInterfaceRuntime; +#else +typedef struct bNodeTreeInterfaceRuntimeHandle bNodeTreeInterfaceRuntimeHandle; +#endif + struct bContext; struct bNodeSocket; struct bNodeSocketType; @@ -211,6 +220,8 @@ typedef struct bNodeTreeInterface { int active_index; int next_uid; + bNodeTreeInterfaceRuntimeHandle *runtime; + #ifdef __cplusplus /** Initialize data of new interface instance. */ @@ -397,7 +408,29 @@ typedef struct bNodeTreeInterface { root_panel.foreach_item(fn, /*include_self=*/include_root); } + /** Callback for every ID pointer in the interface data. */ void foreach_id(LibraryForeachIDData *cb); + /** True if the items cache is ready to use. */ + bool items_cache_is_available() const; + + /** Ensure the items cache can be accessed. */ + void ensure_items_cache() const; + + /** True if any runtime change flag is set. */ + bool is_changed() const; + + /** + * Tag runtime data and invalidate the cache. + * Must be called after any direct change to interface DNA data. + */ + void tag_items_changed(); + + /** Reset runtime flags after updates have been processed. */ + void reset_changed_flags(); + + private: + void tag_missing_runtime_data(); + #endif } bNodeTreeInterface; diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index 00a011d7846..ecbd0fd0061 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -767,6 +767,12 @@ typedef struct bNodeTree { /** Zones in the node tree. Currently there are only simulation zones in geometry nodes. */ const blender::bke::bNodeTreeZones *zones() const; + /** + * Update a run-time cache for the node tree interface based on it's current state. + * This should be done before accessing interface item spans below. + */ + void ensure_interface_cache() const; + /* Cached interface item lists. */ blender::Span interface_inputs() const; blender::Span interface_outputs() const; diff --git a/source/blender/makesrna/intern/rna_node_tree_interface.cc b/source/blender/makesrna/intern/rna_node_tree_interface.cc index 3269351eff5..6b39366e0e0 100644 --- a/source/blender/makesrna/intern/rna_node_tree_interface.cc +++ b/source/blender/makesrna/intern/rna_node_tree_interface.cc @@ -52,7 +52,7 @@ namespace node_interface = blender::bke::node_interface; static void rna_NodeTreeInterfaceItem_update(Main *bmain, Scene * /*scene*/, PointerRNA *ptr) { bNodeTree *ntree = reinterpret_cast(ptr->owner_id); - BKE_ntree_update_tag_interface(ntree); + ntree->tree_interface.tag_items_changed(); ED_node_tree_propagate_change(nullptr, bmain, ntree); } @@ -85,7 +85,7 @@ static char *rna_NodeTreeInterfaceItem_path(const PointerRNA *ptr) return nullptr; } - ntree->ensure_topology_cache(); + ntree->ensure_interface_cache(); for (const int index : ntree->interface_items().index_range()) { if (ntree->interface_items()[index] == item) { return BLI_sprintfN("interface.ui_items[%d]", index); @@ -449,7 +449,6 @@ static bNodeTreeInterfaceSocket *rna_NodeTreeInterfaceItems_new_socket( BKE_report(reports, RPT_ERROR, "Unable to create socket"); } else { - BKE_ntree_update_tag_interface(ntree); ED_node_tree_propagate_change(nullptr, bmain, ntree); WM_main_add_notifier(NC_NODE | NA_EDITED, ntree); } @@ -489,7 +488,6 @@ static bNodeTreeInterfacePanel *rna_NodeTreeInterfaceItems_new_panel( } else { bNodeTree *ntree = reinterpret_cast(id); - BKE_ntree_update_tag_interface(ntree); ED_node_tree_propagate_change(nullptr, bmain, ntree); WM_main_add_notifier(NC_NODE | NA_EDITED, ntree); } @@ -533,7 +531,6 @@ static bNodeTreeInterfaceItem *rna_NodeTreeInterfaceItems_copy_to_parent( } else { bNodeTree *ntree = reinterpret_cast(id); - BKE_ntree_update_tag_interface(ntree); ED_node_tree_propagate_change(nullptr, bmain, ntree); WM_main_add_notifier(NC_NODE | NA_EDITED, ntree); } @@ -561,7 +558,6 @@ static void rna_NodeTreeInterfaceItems_remove(ID *id, interface->remove_item(*item, move_content_to_parent); bNodeTree *ntree = reinterpret_cast(id); - BKE_ntree_update_tag_interface(ntree); ED_node_tree_propagate_change(nullptr, bmain, ntree); WM_main_add_notifier(NC_NODE | NA_EDITED, ntree); } @@ -571,7 +567,6 @@ static void rna_NodeTreeInterfaceItems_clear(ID *id, bNodeTreeInterface *interfa interface->clear_items(); bNodeTree *ntree = reinterpret_cast(id); - BKE_ntree_update_tag_interface(ntree); ED_node_tree_propagate_change(nullptr, bmain, ntree); WM_main_add_notifier(NC_NODE | NA_EDITED, ntree); } @@ -585,7 +580,6 @@ static void rna_NodeTreeInterfaceItems_move(ID *id, interface->move_item(*item, to_position); bNodeTree *ntree = reinterpret_cast(id); - BKE_ntree_update_tag_interface(ntree); ED_node_tree_propagate_change(nullptr, bmain, ntree); WM_main_add_notifier(NC_NODE | NA_EDITED, ntree); } @@ -608,7 +602,6 @@ static void rna_NodeTreeInterfaceItems_move_to_parent(ID *id, interface->move_item_to_parent(*item, parent, to_position); bNodeTree *ntree = reinterpret_cast(id); - BKE_ntree_update_tag_interface(ntree); ED_node_tree_propagate_change(nullptr, bmain, ntree); WM_main_add_notifier(NC_NODE | NA_EDITED, ntree); } @@ -748,7 +741,7 @@ static void rna_NodeTreeInterface_items_begin(CollectionPropertyIterator *iter, return; } - ntree->ensure_topology_cache(); + ntree->ensure_interface_cache(); rna_iterator_array_begin(iter, const_cast(ntree->interface_items().data()), sizeof(bNodeTreeInterfaceItem *), @@ -764,7 +757,7 @@ static int rna_NodeTreeInterface_items_length(PointerRNA *ptr) return 0; } - ntree->ensure_topology_cache(); + ntree->ensure_interface_cache(); return ntree->interface_items().size(); } @@ -775,7 +768,7 @@ static int rna_NodeTreeInterface_items_lookup_int(PointerRNA *ptr, int index, Po return 0; } - ntree->ensure_topology_cache(); + ntree->ensure_interface_cache(); if (!ntree->interface_items().index_range().contains(index)) { return false; } @@ -794,7 +787,7 @@ static int rna_NodeTreeInterface_items_lookup_string(PointerRNA *ptr, return 0; } - ntree->ensure_topology_cache(); + ntree->ensure_interface_cache(); for (bNodeTreeInterfaceItem *item : ntree->interface_items()) { switch (item->item_type) { case NODE_INTERFACE_SOCKET: { diff --git a/source/blender/makesrna/intern/rna_nodetree.cc b/source/blender/makesrna/intern/rna_nodetree.cc index 38c2d4ee572..8dbb62a1d64 100644 --- a/source/blender/makesrna/intern/rna_nodetree.cc +++ b/source/blender/makesrna/intern/rna_nodetree.cc @@ -1290,8 +1290,7 @@ static bool rna_NodeTree_contains_tree(bNodeTree *tree, bNodeTree *sub_tree) static void rna_NodeTree_interface_update(bNodeTree *ntree, bContext *C) { Main *bmain = CTX_data_main(C); - - BKE_ntree_update_tag_interface(ntree); + ntree->tree_interface.tag_items_changed(); ED_node_tree_propagate_change(nullptr, bmain, ntree); } diff --git a/source/blender/modifiers/intern/MOD_nodes.cc b/source/blender/modifiers/intern/MOD_nodes.cc index acb732760f9..d32cc6af414 100644 --- a/source/blender/modifiers/intern/MOD_nodes.cc +++ b/source/blender/modifiers/intern/MOD_nodes.cc @@ -622,6 +622,7 @@ static void check_property_socket_sync(const Object *ob, ModifierData *md) int geometry_socket_count = 0; + nmd->node_group->ensure_interface_cache(); for (const int i : nmd->node_group->interface_inputs().index_range()) { const bNodeTreeInterfaceSocket *socket = nmd->node_group->interface_inputs()[i]; const bNodeSocketType *typeinfo = socket->socket_typeinfo(); @@ -1022,9 +1023,9 @@ static void modifyGeometry(ModifierData *md, BKE_modifier_get_original(ctx->object, &nmd->modifier)); const bNodeTree &tree = *nmd->node_group; - tree.ensure_topology_cache(); check_property_socket_sync(ctx->object, md); + tree.ensure_topology_cache(); const bNode *output_node = tree.group_output_node(); if (output_node == nullptr) { BKE_modifier_set_error(ctx->object, md, "Node group must have a group output node"); @@ -1499,7 +1500,7 @@ static void panel_draw(const bContext *C, Panel *panel) if (nmd->node_group != nullptr && nmd->settings.properties != nullptr) { PointerRNA bmain_ptr = RNA_main_pointer_create(bmain); - nmd->node_group->ensure_topology_cache(); + nmd->node_group->ensure_interface_cache(); for (const int socket_index : nmd->node_group->interface_inputs().index_range()) { const bNodeTreeInterfaceSocket *socket = nmd->node_group->interface_inputs()[socket_index]; diff --git a/source/blender/nodes/intern/geometry_nodes_execute.cc b/source/blender/nodes/intern/geometry_nodes_execute.cc index 276c790e923..e8827b5e3dd 100644 --- a/source/blender/nodes/intern/geometry_nodes_execute.cc +++ b/source/blender/nodes/intern/geometry_nodes_execute.cc @@ -597,6 +597,7 @@ bke::GeometrySet execute_geometry_nodes_on_geometry( LinearAllocator<> allocator; Vector inputs_to_destruct; + btree.ensure_interface_cache(); int input_index = -1; for (const int i : btree.interface_inputs().index_range()) { input_index++; @@ -668,7 +669,7 @@ void update_input_properties_from_node_tree(const bNodeTree &tree, const bool use_bool_for_use_attribute, IDProperty &properties) { - tree.ensure_topology_cache(); + tree.ensure_interface_cache(); const Span tree_inputs = tree.interface_inputs(); for (const int i : tree_inputs.index_range()) { const bNodeTreeInterfaceSocket &socket = *tree_inputs[i]; diff --git a/source/blender/nodes/intern/geometry_nodes_lazy_function.cc b/source/blender/nodes/intern/geometry_nodes_lazy_function.cc index 01def678904..e0fcf18e7f9 100644 --- a/source/blender/nodes/intern/geometry_nodes_lazy_function.cc +++ b/source/blender/nodes/intern/geometry_nodes_lazy_function.cc @@ -1808,6 +1808,7 @@ struct GeometryNodesLazyFunctionGraphBuilder { void build() { btree_.ensure_topology_cache(); + btree_.ensure_interface_cache(); mapping_ = &lf_graph_info_->mapping; conversions_ = &bke::get_implicit_type_conversions(); @@ -3851,6 +3852,7 @@ const GeometryNodesLazyFunctionGraphInfo *ensure_geometry_nodes_lazy_function_gr const bNodeTree &btree) { btree.ensure_topology_cache(); + btree.ensure_interface_cache(); if (btree.has_available_link_cycle()) { return nullptr; }