diff --git a/source/blender/blenkernel/BKE_node_tree_interface.hh b/source/blender/blenkernel/BKE_node_tree_interface.hh index 157ea618017..808c8964d39 100644 --- a/source/blender/blenkernel/BKE_node_tree_interface.hh +++ b/source/blender/blenkernel/BKE_node_tree_interface.hh @@ -29,11 +29,9 @@ class bNodeTreeInterfaceRuntime { 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.hh`. - * #NodeTreeInterfaceChangedFlag. + * Changes have been made to the interface items that invalidate dependent trees. */ - uint32_t changed_flag_ = 0; + std::atomic interface_changed_ = true; /** * Protects access to item cache variables below. This is necessary so that the cache can be diff --git a/source/blender/blenkernel/intern/node_tree_interface.cc b/source/blender/blenkernel/intern/node_tree_interface.cc index 41f47f0e021..998943f528a 100644 --- a/source/blender/blenkernel/intern/node_tree_interface.cc +++ b/source/blender/blenkernel/intern/node_tree_interface.cc @@ -27,15 +27,6 @@ using blender::StringRef; -/** - * 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 { @@ -1526,24 +1517,39 @@ void bNodeTreeInterface::ensure_items_cache() const }); } -void bNodeTreeInterface::tag_missing_runtime_data() +void bNodeTreeInterface::tag_interface_changed() { - this->runtime->changed_flag_ |= NODE_INTERFACE_CHANGED_ALL; - this->runtime->items_cache_mutex_.tag_dirty(); + this->runtime->interface_changed_.store(true); } -bool bNodeTreeInterface::is_changed() const +bool bNodeTreeInterface::requires_dependent_tree_updates() const { - return this->runtime->changed_flag_ != NODE_INTERFACE_CHANGED_NOTHING; + return this->runtime->interface_changed_.load(std::memory_order_relaxed); } void bNodeTreeInterface::tag_items_changed() { - this->runtime->changed_flag_ |= NODE_INTERFACE_CHANGED_ITEMS; + this->tag_interface_changed(); this->runtime->items_cache_mutex_.tag_dirty(); } -void bNodeTreeInterface::reset_changed_flags() +void bNodeTreeInterface::tag_items_changed_generic() { - this->runtime->changed_flag_ = NODE_INTERFACE_CHANGED_NOTHING; + /* Perform a full update since we don't know what changed exactly. */ + this->tag_items_changed(); +} + +void bNodeTreeInterface::tag_item_property_changed() +{ + this->tag_interface_changed(); +} + +void bNodeTreeInterface::tag_missing_runtime_data() +{ + this->tag_items_changed(); +} + +void bNodeTreeInterface::reset_interface_changed() +{ + this->runtime->interface_changed_.store(false); } diff --git a/source/blender/blenkernel/intern/node_tree_update.cc b/source/blender/blenkernel/intern/node_tree_update.cc index acc5a3cce16..85b281fb383 100644 --- a/source/blender/blenkernel/intern/node_tree_update.cc +++ b/source/blender/blenkernel/intern/node_tree_update.cc @@ -196,7 +196,8 @@ static int get_internal_link_type_priority(const bNodeSocketType *from, const bN /* 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(); + return tree.runtime->changed_flag != NTREE_CHANGED_NOTHING || + tree.tree_interface.requires_dependent_tree_updates(); } using TreeNodePair = std::pair; @@ -571,7 +572,7 @@ class NodeTreeMainUpdater { ntreeTexCheckCyclics(&ntree); } - if (ntree.tree_interface.is_changed()) { + if (ntree.tree_interface.requires_dependent_tree_updates()) { result.interface_changed = true; } @@ -658,7 +659,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.tree_interface.is_changed()) { + if (ntree.tree_interface.requires_dependent_tree_updates()) { if (node.is_group_input() || node.is_group_output()) { return true; } @@ -1888,7 +1889,7 @@ class NodeTreeMainUpdater { } } - ntree.tree_interface.reset_changed_flags(); + ntree.tree_interface.reset_interface_changed(); } /** diff --git a/source/blender/editors/interface/templates/interface_template_node_tree_interface.cc b/source/blender/editors/interface/templates/interface_template_node_tree_interface.cc index 11a13020a6d..22a1bf67e0a 100644 --- a/source/blender/editors/interface/templates/interface_template_node_tree_interface.cc +++ b/source/blender/editors/interface/templates/interface_template_node_tree_interface.cc @@ -150,7 +150,7 @@ class NodeSocketViewItem : public BasicTreeViewItem { MEM_SAFE_FREE(socket_.name); socket_.name = BLI_strdup(new_name.c_str()); - nodetree_.tree_interface.tag_items_changed(); + nodetree_.tree_interface.tag_item_property_changed(); BKE_main_ensure_invariants(*CTX_data_main(&C), nodetree_.id); ED_undo_push(&const_cast(C), new_name.c_str()); return true; diff --git a/source/blender/makesdna/DNA_node_tree_interface_types.h b/source/blender/makesdna/DNA_node_tree_interface_types.h index 51f2401b50d..f6ea90ce2e2 100644 --- a/source/blender/makesdna/DNA_node_tree_interface_types.h +++ b/source/blender/makesdna/DNA_node_tree_interface_types.h @@ -467,19 +467,26 @@ typedef struct bNodeTreeInterface { /** Ensure the items cache can be accessed. */ void ensure_items_cache() const; - /** True if any runtime change flag is set. */ - bool is_changed() const; + /** True if any trees and nodes depending on the interface require updates. */ + bool requires_dependent_tree_updates() const; + + /** Call after changing the items list. */ + void tag_items_changed(); + /** Call after generic user changes through the API. */ + void tag_items_changed_generic(); + /** Call after changing an item property. */ + void tag_item_property_changed(); /** - * Tag runtime data and invalidate the cache. - * Must be called after any direct change to interface DNA data. + * Reset flag to indicate that dependent trees have been updated. + * Should only be called by #NodeTreeMainUpdater. */ - void tag_items_changed(); - - /** Reset runtime flags after updates have been processed. */ - void reset_changed_flags(); + void reset_interface_changed(); private: + /** Tag after interface changes that require updates to dependent trees. */ + void tag_interface_changed(); + /** Invalidate caches and force full tree update after loading DNA. */ void tag_missing_runtime_data(); #endif diff --git a/source/blender/makesrna/intern/rna_node_socket.cc b/source/blender/makesrna/intern/rna_node_socket.cc index 420d59e6339..8645bd0d140 100644 --- a/source/blender/makesrna/intern/rna_node_socket.cc +++ b/source/blender/makesrna/intern/rna_node_socket.cc @@ -1062,7 +1062,7 @@ static void rna_def_node_socket_interface_float(BlenderRNA *brna, RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); RNA_def_property_ui_text(prop, "Subtype", "Subtype of the default value"); RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_UNIT); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeTreeInterfaceSocket_value_update"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeTreeInterfaceItem_update"); prop = RNA_def_property(srna, "default_value", PROP_FLOAT, subtype); RNA_def_property_float_sdna(prop, nullptr, "value"); @@ -1071,19 +1071,19 @@ static void rna_def_node_socket_interface_float(BlenderRNA *brna, RNA_def_property_float_funcs( prop, nullptr, nullptr, "rna_NodeTreeInterfaceSocketFloat_default_value_range"); RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeTreeInterfaceSocket_value_update"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeTreeInterfaceItem_update"); prop = RNA_def_property(srna, "min_value", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, nullptr, "min"); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); RNA_def_property_ui_text(prop, "Minimum Value", "Minimum value"); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeTreeInterfaceSocket_value_update"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeTreeInterfaceItem_update"); prop = RNA_def_property(srna, "max_value", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, nullptr, "max"); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); RNA_def_property_ui_text(prop, "Maximum Value", "Maximum value"); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeTreeInterfaceSocket_value_update"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeTreeInterfaceItem_update"); RNA_def_struct_sdna_from(srna, "bNodeTreeInterfaceSocket", nullptr); @@ -1151,7 +1151,7 @@ static void rna_def_node_socket_interface_int(BlenderRNA *brna, RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); RNA_def_property_ui_text(prop, "Subtype", "Subtype of the default value"); RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_UNIT); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeTreeInterfaceSocket_value_update"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeTreeInterfaceItem_update"); prop = RNA_def_property(srna, "default_value", PROP_INT, subtype); RNA_def_property_int_sdna(prop, nullptr, "value"); @@ -1159,19 +1159,19 @@ static void rna_def_node_socket_interface_int(BlenderRNA *brna, RNA_def_property_int_funcs( prop, nullptr, nullptr, "rna_NodeTreeInterfaceSocketInt_default_value_range"); RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeTreeInterfaceSocket_value_update"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeTreeInterfaceItem_update"); prop = RNA_def_property(srna, "min_value", PROP_INT, PROP_NONE); RNA_def_property_int_sdna(prop, nullptr, "min"); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); RNA_def_property_ui_text(prop, "Minimum Value", "Minimum value"); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeTreeInterfaceSocket_value_update"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeTreeInterfaceItem_update"); prop = RNA_def_property(srna, "max_value", PROP_INT, PROP_NONE); RNA_def_property_int_sdna(prop, nullptr, "max"); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); RNA_def_property_ui_text(prop, "Maximum Value", "Maximum value"); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeTreeInterfaceSocket_value_update"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeTreeInterfaceItem_update"); RNA_def_struct_sdna_from(srna, "bNodeTreeInterfaceSocket", nullptr); @@ -1215,7 +1215,7 @@ static void rna_def_node_socket_interface_bool(BlenderRNA *brna, const char *ide RNA_def_property_boolean_sdna(prop, nullptr, "value", 1); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeTreeInterfaceSocket_value_update"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeTreeInterfaceItem_update"); RNA_def_struct_sdna_from(srna, "bNodeTreeInterfaceSocket", nullptr); @@ -1260,7 +1260,7 @@ static void rna_def_node_socket_interface_rotation(BlenderRNA *brna, const char RNA_def_property_float_sdna(prop, nullptr, "value_euler"); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeTreeInterfaceSocket_value_update"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeTreeInterfaceItem_update"); RNA_def_struct_sdna_from(srna, "bNodeTreeInterfaceSocket", nullptr); @@ -1341,7 +1341,7 @@ static void rna_def_node_socket_interface_vector(BlenderRNA *brna, RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); RNA_def_property_ui_text(prop, "Subtype", "Subtype of the default value"); RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_UNIT); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeTreeInterfaceSocket_value_update"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeTreeInterfaceItem_update"); prop = RNA_def_property(srna, "dimensions", PROP_INT, PROP_NONE); RNA_def_property_int_sdna(prop, nullptr, "dimensions"); @@ -1358,19 +1358,19 @@ static void rna_def_node_socket_interface_vector(BlenderRNA *brna, RNA_def_property_float_funcs( prop, nullptr, nullptr, "rna_NodeTreeInterfaceSocketVector_default_value_range"); RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeTreeInterfaceSocket_value_update"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeTreeInterfaceItem_update"); prop = RNA_def_property(srna, "min_value", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, nullptr, "min"); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); RNA_def_property_ui_text(prop, "Minimum Value", "Minimum value"); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeTreeInterfaceSocket_value_update"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeTreeInterfaceItem_update"); prop = RNA_def_property(srna, "max_value", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, nullptr, "max"); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); RNA_def_property_ui_text(prop, "Maximum Value", "Maximum value"); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeTreeInterfaceSocket_value_update"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeTreeInterfaceItem_update"); RNA_def_struct_sdna_from(srna, "bNodeTreeInterfaceSocket", nullptr); @@ -1414,7 +1414,7 @@ static void rna_def_node_socket_interface_color(BlenderRNA *brna, const char *id RNA_def_property_float_sdna(prop, nullptr, "value"); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeTreeInterfaceSocket_value_update"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeTreeInterfaceItem_update"); RNA_def_struct_sdna_from(srna, "bNodeTreeInterfaceSocket", nullptr); @@ -1470,14 +1470,14 @@ static void rna_def_node_socket_interface_string(BlenderRNA *brna, RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); RNA_def_property_ui_text(prop, "Subtype", "Subtype of the default value"); RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_UNIT); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeTreeInterfaceSocket_value_update"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeTreeInterfaceItem_update"); prop = RNA_def_property(srna, "default_value", PROP_STRING, subtype); RNA_def_property_string_sdna(prop, nullptr, "value"); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); // TODO: Do I need to call RNA_def_property_string_funcs() ? RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeTreeInterfaceSocket_value_update"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeTreeInterfaceItem_update"); RNA_def_struct_sdna_from(srna, "bNodeTreeInterfaceSocket", nullptr); @@ -1526,7 +1526,7 @@ static void rna_def_node_socket_interface_menu(BlenderRNA *brna, const char *ide RNA_def_property_enum_funcs(prop, nullptr, nullptr, "RNA_node_tree_interface_socket_menu_itemf"); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeTreeInterfaceSocket_value_update"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeTreeInterfaceItem_update"); RNA_def_struct_sdna_from(srna, "bNodeTreeInterfaceSocket", nullptr); @@ -1591,7 +1591,7 @@ static void rna_def_node_socket_interface_object(BlenderRNA *brna, const char *i RNA_def_property_pointer_sdna(prop, nullptr, "value"); RNA_def_property_struct_type(prop, "Object"); RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeTreeInterfaceSocket_value_update"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeTreeInterfaceItem_update"); RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT); RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); @@ -1635,7 +1635,7 @@ static void rna_def_node_socket_interface_image(BlenderRNA *brna, const char *id RNA_def_property_pointer_sdna(prop, nullptr, "value"); RNA_def_property_struct_type(prop, "Image"); RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeTreeInterfaceSocket_value_update"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeTreeInterfaceItem_update"); RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT); RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); @@ -1742,7 +1742,7 @@ static void rna_def_node_socket_interface_collection(BlenderRNA *brna, const cha RNA_def_property_pointer_sdna(prop, nullptr, "value"); RNA_def_property_struct_type(prop, "Collection"); RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeTreeInterfaceSocket_value_update"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeTreeInterfaceItem_update"); RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT); RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); @@ -1786,7 +1786,7 @@ static void rna_def_node_socket_interface_texture(BlenderRNA *brna, const char * RNA_def_property_pointer_sdna(prop, nullptr, "value"); RNA_def_property_struct_type(prop, "Texture"); RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeTreeInterfaceSocket_value_update"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeTreeInterfaceItem_update"); RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT); RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); @@ -1832,7 +1832,7 @@ static void rna_def_node_socket_interface_material(BlenderRNA *brna, const char RNA_def_property_pointer_funcs( prop, nullptr, nullptr, nullptr, "rna_NodeTreeInterfaceSocketMaterial_default_value_poll"); RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeTreeInterfaceSocket_value_update"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeTreeInterfaceItem_update"); RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT); RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); diff --git a/source/blender/makesrna/intern/rna_node_tree_interface.cc b/source/blender/makesrna/intern/rna_node_tree_interface.cc index 8b4a97f93f9..bb485316414 100644 --- a/source/blender/makesrna/intern/rna_node_tree_interface.cc +++ b/source/blender/makesrna/intern/rna_node_tree_interface.cc @@ -119,7 +119,7 @@ static void rna_NodeTreeInterfaceItem_update(Main *bmain, Scene * /*scene*/, Poi /* This can happen because of the dummy socket in #rna_NodeTreeInterfaceSocket_register. */ return; } - ntree->tree_interface.tag_items_changed(); + ntree->tree_interface.tag_item_property_changed(); BKE_main_ensure_invariants(*bmain, ntree->id); } @@ -908,13 +908,6 @@ static const EnumPropertyItem *rna_NodeTreeInterfaceSocketString_subtype_itemf( return rna_subtype_filter_itemf({PROP_FILEPATH, PROP_NONE}, r_free); } -/* using a context update function here, to avoid searching the node if possible */ -static void rna_NodeTreeInterfaceSocket_value_update(Main *bmain, Scene *scene, PointerRNA *ptr) -{ - /* default update */ - rna_NodeTreeInterfaceItem_update(bmain, scene, ptr); -} - /* If the dimensions of the vector socket changed, we need to update the socket type, since each * dimensions value has its own sub-type. */ static void rna_NodeTreeInterfaceSocketVector_dimensions_update(Main *bmain, @@ -937,7 +930,7 @@ static void rna_NodeTreeInterfaceSocketVector_dimensions_update(Main *bmain, /* Restore existing default value. */ *static_cast(socket->socket_data) = default_value; - rna_NodeTreeInterfaceSocket_value_update(bmain, scene, ptr); + rna_NodeTreeInterfaceItem_update(bmain, scene, ptr); } static bool rna_NodeTreeInterfaceSocketMaterial_default_value_poll(PointerRNA * /*ptr*/, diff --git a/source/blender/makesrna/intern/rna_nodetree.cc b/source/blender/makesrna/intern/rna_nodetree.cc index 6b96e4cb92c..9eda06d54a7 100644 --- a/source/blender/makesrna/intern/rna_nodetree.cc +++ b/source/blender/makesrna/intern/rna_nodetree.cc @@ -1704,7 +1704,7 @@ static void rna_NodeTree_debug_zone_lazy_function_graph( static void rna_NodeTree_interface_update(bNodeTree *ntree, bContext *C) { Main *bmain = CTX_data_main(C); - ntree->tree_interface.tag_items_changed(); + ntree->tree_interface.tag_items_changed_generic(); BKE_main_ensure_invariants(*bmain, ntree->id); } diff --git a/tests/python/bl_node_group_interface.py b/tests/python/bl_node_group_interface.py index bdd01f4ef2c..af20fb10caa 100644 --- a/tests/python/bl_node_group_interface.py +++ b/tests/python/bl_node_group_interface.py @@ -469,6 +469,37 @@ class CompositorNodeGroupInterfaceTest(AbstractNodeGroupInterfaceTest, NodeGroup self.do_test_remove("NodeSocketFloat") +class NodeTreeItemsIteratorTest(AbstractNodeGroupInterfaceTest, NodeGroupInterfaceTests): + tree_type = "ShaderNodeTree" + group_node_type = "ShaderNodeGroup" + + def setUp(self): + super().setUp() + self.material = bpy.data.materials.new("test") + self.material.use_nodes = True + self.main_tree = self.material.node_tree + + # Regression test for changes while iterating over tree interface items (#143551). + # The iterator should remain valid when changing properties of a tree item. + def test_items_iterator(self): + tree, group_node = self.make_group_and_instance() + + tree.interface.new_socket("Input 0", socket_type="NodeSocketFloat", in_out='INPUT') + tree.interface.new_socket("Input 1", socket_type="NodeSocketBool", in_out='INPUT') + # The cache vector has a fixed buffer for small sizes, add enough sockets to force reallocation. + for i in range(20): + tree.interface.new_socket(f"Input {2+i}", socket_type="NodeSocketColor", in_out='INPUT') + + # Iterate over items and change properties. The loop iterator must remain valid. + for item in tree.interface.items_tree: + if item.socket_type == "NodeSocketFloat": + item.default_value = 500.0 + elif item.socket_type == "NodeSocketColor": + item.default_value = (1, 0, 0, 1) + elif item.socket_type == "NodeSocketBool": + item.default_value = True + + def main(): global args import argparse