Fix #143551: Cache invalidation causes crash when changing node tree item properties
Changing a node tree item property (such as the default value) was using a very broad and generic "tag" function which invalidates the runtime items cache. This also invalidates any python iterators due to the API using the runtime cache. Changing node tree items in a loop will then crash. It's not necessary to invalidate the runtime items cache when the actual item pointers have not changed. Most RNA updates only change superficial properties, or at most require a recursive node tree update due to change of identifiers or types. This PR introduces a simpler "tag" function to only tag for tree updates by not rebuild the entire runtime items cache. It also renames existing functions and docstrings to better explain what each of them does and should be used for. The `NodeTreeInterfaceChangedFlag` is removed completely because it is only ever used as a simple boolean indicator of "item changes" that require a cache rebuild. It is replaced with an atomic bool like flags used for runtime caches. Pull Request: https://projects.blender.org/blender/blender/pulls/143932
This commit is contained in:
@@ -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<bool> interface_changed_ = true;
|
||||
|
||||
/**
|
||||
* Protects access to item cache variables below. This is necessary so that the cache can be
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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<bNodeTree *, bNode *>;
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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<bContext &>(C), new_name.c_str());
|
||||
return true;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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<bNodeSocketValueVector *>(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*/,
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user