Nodes: avoid O(n^2) when looking up interface input indices

Previously, the code was O(n^2) because it iterated over all interface sockets,
and for each it tried to find the corresponding index by iterating over all
inputs again. Now, a `VectorSet` is used to make finding the index `O(1)`. The
new utility function may also be useful elsewhere.

Pull Request: https://projects.blender.org/blender/blender/pulls/132272
This commit is contained in:
Jacques Lucke
2024-12-23 15:01:07 +01:00
parent 5af53a8b9d
commit 8bec7dce35
5 changed files with 36 additions and 12 deletions

View File

@@ -611,7 +611,7 @@ inline blender::Span<bNodeTreeInterfaceSocket *> bNodeTree::interface_inputs()
inline blender::Span<const bNodeTreeInterfaceSocket *> bNodeTree::interface_inputs() const
{
BLI_assert(this->tree_interface.items_cache_is_available());
return this->tree_interface.runtime->inputs_;
return this->tree_interface.runtime->inputs_.as_span();
}
inline blender::Span<bNodeTreeInterfaceSocket *> bNodeTree::interface_outputs()
@@ -623,7 +623,7 @@ inline blender::Span<bNodeTreeInterfaceSocket *> bNodeTree::interface_outputs()
inline blender::Span<const bNodeTreeInterfaceSocket *> bNodeTree::interface_outputs() const
{
BLI_assert(this->tree_interface.items_cache_is_available());
return this->tree_interface.runtime->outputs_;
return this->tree_interface.runtime->outputs_.as_span();
}
inline blender::Span<bNodeTreeInterfaceItem *> bNodeTree::interface_items()
@@ -635,7 +635,25 @@ inline blender::Span<bNodeTreeInterfaceItem *> bNodeTree::interface_items()
inline blender::Span<const bNodeTreeInterfaceItem *> bNodeTree::interface_items() const
{
BLI_assert(this->tree_interface.items_cache_is_available());
return this->tree_interface.runtime->items_;
return this->tree_interface.runtime->items_.as_span();
}
inline int bNodeTree::interface_input_index(const bNodeTreeInterfaceSocket &io_socket) const
{
BLI_assert(this->tree_interface.items_cache_is_available());
return this->tree_interface.runtime->inputs_.index_of_as(&io_socket);
}
inline int bNodeTree::interface_output_index(const bNodeTreeInterfaceSocket &io_socket) const
{
BLI_assert(this->tree_interface.items_cache_is_available());
return this->tree_interface.runtime->outputs_.index_of_as(&io_socket);
}
inline int bNodeTree::interface_item_index(const bNodeTreeInterfaceItem &io_item) const
{
BLI_assert(this->tree_interface.items_cache_is_available());
return this->tree_interface.runtime->items_.index_of_as(&io_item);
}
/** \} */

View File

@@ -15,6 +15,7 @@
#include "BLI_cache_mutex.hh"
#include "BLI_parameter_pack_utils.hh"
#include "BLI_vector.hh"
#include "BLI_vector_set.hh"
namespace blender::bke {
@@ -39,10 +40,10 @@ class bNodeTreeInterfaceRuntime {
CacheMutex items_cache_mutex_;
/* Runtime topology cache for linear access to items. */
Vector<bNodeTreeInterfaceItem *> items_;
VectorSet<bNodeTreeInterfaceItem *> items_;
/* Socket-only lists for input/output access by index. */
Vector<bNodeTreeInterfaceSocket *> inputs_;
Vector<bNodeTreeInterfaceSocket *> outputs_;
VectorSet<bNodeTreeInterfaceSocket *> inputs_;
VectorSet<bNodeTreeInterfaceSocket *> outputs_;
};
namespace node_interface {

View File

@@ -1423,13 +1423,13 @@ void bNodeTreeInterface::ensure_items_cache() const
bNodeTreeInterface &mutable_self = const_cast<bNodeTreeInterface &>(*this);
mutable_self.foreach_item([&](bNodeTreeInterfaceItem &item) {
runtime.items_.append(&item);
runtime.items_.add_new(&item);
if (bNodeTreeInterfaceSocket *socket = get_item_as<bNodeTreeInterfaceSocket>(&item)) {
if (socket->flag & NODE_INTERFACE_SOCKET_INPUT) {
runtime.inputs_.append(socket);
runtime.inputs_.add_new(socket);
}
if (socket->flag & NODE_INTERFACE_SOCKET_OUTPUT) {
runtime.outputs_.append(socket);
runtime.outputs_.add_new(socket);
}
}
return true;

View File

@@ -16,9 +16,11 @@
/** Workaround to forward-declare C++ type in C header. */
#ifdef __cplusplus
# include <BLI_vector.hh>
# include <string>
# include "BLI_vector.hh"
# include "BLI_vector_set.hh"
namespace blender {
template<typename T> class Span;
template<typename T> class MutableSpan;
@@ -847,6 +849,10 @@ typedef struct bNodeTree {
blender::Span<const bNodeTreeInterfaceSocket *> interface_outputs() const;
blender::Span<bNodeTreeInterfaceItem *> interface_items();
blender::Span<const bNodeTreeInterfaceItem *> interface_items() const;
int interface_input_index(const bNodeTreeInterfaceSocket &io_socket) const;
int interface_output_index(const bNodeTreeInterfaceSocket &io_socket) const;
int interface_item_index(const bNodeTreeInterfaceItem &io_item) const;
#endif
} bNodeTree;

View File

@@ -2154,8 +2154,7 @@ static void draw_property_for_socket(DrawGroupInputsContext &ctx,
uiLayout *row = uiLayoutRow(layout, true);
uiLayoutSetPropDecorate(row, true);
const int input_index =
const_cast<const bNodeTree *>(ctx.nmd.node_group)->interface_inputs().first_index(&socket);
const int input_index = ctx.nmd.node_group->interface_input_index(socket);
/* Use #uiItemPointerR to draw pointer properties because #uiItemR would not have enough
* information about what type of ID to select for editing the values. This is because