Fix #112331: Add update tags directly in bNodeTreeInterface API methods #111741
|
@ -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_;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
|
|
@ -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
|
||||
* #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
Hans Goudey
commented
Are the public methods still necessary with Are the public methods still necessary with `friend bNodeTreeInterface`? I'd think they could just be accessed directly there.
Lukas Tönne
commented
They are accessed from They are accessed from `bNodeTree::interface_inputs` etc. Of course i can also make bNodeTree a friend too ...
Hans Goudey
commented
Ah I see, I think that makes sense Ah I see, I think that makes sense
|
||||
namespace node_interface {
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
Hans Goudey
commented
Missing 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
Hans Goudey
commented
Functions in the class should be prefixed with 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
Hans Goudey
commented
This indirection doesn't seem necessary IMO. It would probably be simpler to just put the contents of 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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -2935,6 +2935,7 @@ void DepsgraphRelationBuilder::build_nodetree(bNodeTree *ntree)
|
|||
}
|
||||
}
|
||||
|
||||
ntree->ensure_interface_cache();
|
||||
for (bNodeTreeInterfaceSocket *socket : ntree->interface_inputs()) {
|
||||
build_idproperties(socket->properties);
|
||||
}
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -94,7 +94,6 @@ static void add_group_input_node_fn(nodes::LinkSearchOpParams ¶ms)
|
|||
NODE_INTERFACE_SOCKET_INPUT,
|
||||
nullptr);
|
||||
socket_iface->init_from_socket_instance(¶ms.socket);
|
||||
BKE_ntree_update_tag_interface(¶ms.node_tree);
|
||||
|
||||
bNode &group_input = params.add_node("NodeGroupInput");
|
||||
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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
Hans Goudey
commented
Maybe Maybe `private` instead of `protected`? Not sure this will be used as a base class ever.
Hans Goudey
commented
Seems to still say 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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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: {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
NodeTreeInterfaceChangedFlag