Fix #112331: Add update tags directly in bNodeTreeInterface API methods #111741

Merged
Lukas Tönne merged 11 commits from LukasTonne/blender:tree-interface-update-tags into main 2023-09-14 14:13:15 +02:00
24 changed files with 214 additions and 93 deletions

View File

@ -171,7 +171,6 @@ class bNodeTreeRuntime : NonCopyable, NonMovable {
bool has_undefined_nodes_or_sockets = false;
bNode *group_output_node = nullptr;
Vector<bNode *> root_frames;
bNodeTreeInterfaceCache interface_cache;
};
/**
@ -546,22 +545,27 @@ inline blender::Span<bNestedNodeRef> 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<bNodeTreeInterfaceSocket *> 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<bNodeTreeInterfaceSocket *> 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<bNodeTreeInterfaceItem *> 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_;
}
/** \} */

View File

@ -12,18 +12,37 @@
#include <queue>
#include <type_traits>
#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<bNodeTreeInterfaceItem *> items;
Vector<bNodeTreeInterfaceSocket *> inputs;
Vector<bNodeTreeInterfaceSocket *> 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`.
LukasTonne marked this conversation as resolved
Review

NodeTreeInterfaceChangedFlag

`NodeTreeInterfaceChangedFlag`
* #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<bNodeTreeInterfaceItem *> items_;
/* Socket-only lists for input/output access by index. */
Vector<bNodeTreeInterfaceSocket *> inputs_;
Vector<bNodeTreeInterfaceSocket *> outputs_;
};
LukasTonne marked this conversation as resolved Outdated

Are the public methods still necessary with friend bNodeTreeInterface? I'd think they could just be accessed directly there.

Are the public methods still necessary with `friend bNodeTreeInterface`? I'd think they could just be accessed directly there.

They are accessed from bNodeTree::interface_inputs etc. Of course i can also make bNodeTree a friend too ...

They are accessed from `bNodeTree::interface_inputs` etc. Of course i can also make bNodeTree a friend too ...

Ah I see, I think that makes sense

Ah I see, I think that makes sense
namespace node_interface {

View File

@ -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. */

View File

@ -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());

View File

@ -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<bNodeTreeInterface &>(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);

View File

@ -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<const aal::RelationsInNode *> relations_by_node = get_relations_by_node(tree, scope);

View File

@ -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<const bNode *> nodes = tree.all_nodes();
ResourceScope scope;

View File

@ -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<blender::bke::bNodeTreeInterfaceRuntime>(__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<blender::bke::bNodeTreeInterfaceRuntime>(__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<blender::bke::bNodeTreeInterfaceRuntime>(__func__);
this->tag_missing_runtime_data();
LukasTonne marked this conversation as resolved Outdated

Missing this-> for member function calls.

Missing `this->` for member function calls.
}
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;
LukasTonne marked this conversation as resolved Outdated

Functions in the class should be prefixed with this-> to make that clear (same reason we use the this-> prefix for non-private class variables).

Functions in the class should be prefixed with `this->` to make that clear (same reason we use the `this->` prefix for non-private class variables).
}
@ -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)) {
LukasTonne marked this conversation as resolved
Review

This indirection doesn't seem necessary IMO. It would probably be simpler to just put the contents of bNodeTreeInterfaceRuntime::ensure_items_cache in here. That's also how it works for the geometry caches. The runtime struct just serves as storage for the cached data, it doesn't contain any functionality.

EDIT: I wrote more about this in the top-level review comment.

This indirection doesn't seem necessary IMO. It would probably be simpler to just put the contents of `bNodeTreeInterfaceRuntime::ensure_items_cache` in here. That's also how it works for the geometry caches. The runtime struct just serves as storage for the cached data, it doesn't contain any functionality. EDIT: I wrote more about this in the top-level review comment.
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<bNodeTreeInterfaceSocket>(&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<bNodeTreeInterface &>(*this);
mutable_self.foreach_item([&](bNodeTreeInterfaceItem &item) {
runtime.items_.append(&item);
if (bNodeTreeInterfaceSocket *socket = get_item_as<bNodeTreeInterfaceSocket>(&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;
}

View File

@ -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<bNodeTree *, bNode *>;
using ObjectModifierPair = std::pair<Object *, ModifierData *>;
using NodeSocketPair = std::pair<bNode *, bNodeSocket *>;
@ -296,7 +301,7 @@ class NodeTreeMainUpdater {
{
Vector<bNodeTree *> 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<bNodeTree *> 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);

View File

@ -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;
}

View File

@ -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);
}

View File

@ -2935,6 +2935,7 @@ void DepsgraphRelationBuilder::build_nodetree(bNodeTree *ntree)
}
}
ntree->ensure_interface_cache();
for (bNodeTreeInterfaceSocket *socket : ntree->interface_inputs()) {
build_idproperties(socket->properties);
}

View File

@ -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(

View File

@ -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;
}

View File

@ -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) {

View File

@ -94,7 +94,6 @@ static void add_group_input_node_fn(nodes::LinkSearchOpParams &params)
NODE_INTERFACE_SOCKET_INPUT,
nullptr);
socket_iface->init_from_socket_instance(&params.socket);
BKE_ntree_update_tag_interface(&params.node_tree);
bNode &group_input = params.add_node("NodeGroupInput");

View File

@ -337,6 +337,7 @@ static Vector<NodeLinkItem> ui_node_link_items(NodeLinkArg *arg,
continue;
}
ngroup->ensure_interface_cache();
Span<bNodeTreeInterfaceSocket *> iosockets = (in_out == SOCK_IN ?
ngroup->interface_inputs() :
ngroup->interface_outputs());

View File

@ -19,6 +19,15 @@
# include <memory>
#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. */
LukasTonne marked this conversation as resolved
Review

Maybe private instead of protected? Not sure this will be used as a base class ever.

Maybe `private` instead of `protected`? Not sure this will be used as a base class ever.
Review

Seems to still say protected here

Seems to still say `protected` here
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;

View File

@ -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<bNodeTreeInterfaceSocket *> interface_inputs() const;
blender::Span<bNodeTreeInterfaceSocket *> interface_outputs() const;

View File

@ -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<bNodeTree *>(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<bNodeTree *>(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<bNodeTree *>(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<bNodeTree *>(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<bNodeTree *>(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<bNodeTree *>(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<bNodeTree *>(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<bNodeTreeInterfaceItem **>(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: {

View File

@ -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);
}

View File

@ -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];

View File

@ -597,6 +597,7 @@ bke::GeometrySet execute_geometry_nodes_on_geometry(
LinearAllocator<> allocator;
Vector<GMutablePointer> 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<const bNodeTreeInterfaceSocket *> tree_inputs = tree.interface_inputs();
for (const int i : tree_inputs.index_range()) {
const bNodeTreeInterfaceSocket &socket = *tree_inputs[i];

View File

@ -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;
}