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
This commit is contained in:
Lukas Tönne
2023-09-14 14:13:07 +02:00
parent 346b83ca01
commit d2f4ebcd6a
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`.
* #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_;
};
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();
}
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<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. */
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;
}