diff options
author | Jacques Lucke <jacques@blender.org> | 2022-08-31 13:15:57 +0300 |
---|---|---|
committer | Jacques Lucke <jacques@blender.org> | 2022-08-31 13:16:13 +0300 |
commit | 25e307d725d0b924fb0e87e4ffde84f915b74310 (patch) | |
tree | 016cf4a37ccbc2fbdf40c92cd16e96a9090bcd01 /source/blender/blenkernel/BKE_node_runtime.hh | |
parent | 5a60535a20393a4215f7a03fe77d927bacb71438 (diff) |
Nodes: move NodeTreeRef functionality into node runtime data
The purpose of `NodeTreeRef` was to speed up various queries on a read-only
`bNodeTree`. Not that we have runtime data in nodes and sockets, we can also
store the result of some queries there. This has some benefits:
* No need for a read-only separate node tree data structure which increased
complexity.
* Makes it easier to reuse cached queries in more parts of Blender that can
benefit from it.
A downside is that we loose some type safety that we got by having different
types for input and output sockets, as well as internal and non-internal links.
This patch also refactors `DerivedNodeTree` so that it does not use
`NodeTreeRef` anymore, but uses `bNodeTree` directly instead.
To provide a convenient API (that is also close to what `NodeTreeRef` has), a
new approach is implemented: `bNodeTree`, `bNode`, `bNodeSocket` and `bNodeLink`
now have C++ methods declared in `DNA_node_types.h` which are implemented in
`BKE_node_runtime.hh`. To make this work, `makesdna` now skips c++ sections when
parsing dna header files.
No user visible changes are expected.
Differential Revision: https://developer.blender.org/D15491
Diffstat (limited to 'source/blender/blenkernel/BKE_node_runtime.hh')
-rw-r--r-- | source/blender/blenkernel/BKE_node_runtime.hh | 439 |
1 files changed, 437 insertions, 2 deletions
diff --git a/source/blender/blenkernel/BKE_node_runtime.hh b/source/blender/blenkernel/BKE_node_runtime.hh index f5fb53f962b..64325959ce5 100644 --- a/source/blender/blenkernel/BKE_node_runtime.hh +++ b/source/blender/blenkernel/BKE_node_runtime.hh @@ -3,9 +3,20 @@ #pragma once #include <memory> +#include <mutex> -#include "BLI_sys_types.h" +#include "BLI_multi_value_map.hh" #include "BLI_utility_mixins.hh" +#include "BLI_vector.hh" + +#include "DNA_node_types.h" + +#include "BKE_node.h" + +struct bNode; +struct bNodeSocket; +struct bNodeTree; +struct bNodeType; namespace blender::nodes { struct FieldInferencingInterface; @@ -36,6 +47,32 @@ class bNodeTreeRuntime : NonCopyable, NonMovable { /** Information about how inputs and outputs of the node group interact with fields. */ std::unique_ptr<nodes::FieldInferencingInterface> field_inferencing_interface; + + /** + * Protects access to all topology cache variables below. This is necessary so that the cache can + * be updated on a const #bNodeTree. + */ + std::mutex topology_cache_mutex; + bool topology_cache_is_dirty = true; + bool topology_cache_exists = false; + /** + * Under some circumstances, it can be useful to use the cached data while editing the + * #bNodeTree. By default, this is protected against using an assert. + */ + mutable std::atomic<int> allow_use_dirty_topology_cache = 0; + + /** Only valid when #topology_cache_is_dirty is false. */ + Vector<bNode *> nodes; + Vector<bNodeLink *> links; + Vector<bNodeSocket *> sockets; + Vector<bNodeSocket *> input_sockets; + Vector<bNodeSocket *> output_sockets; + MultiValueMap<const bNodeType *, bNode *> nodes_by_type; + Vector<bNode *> toposort_left_to_right; + Vector<bNode *> toposort_right_to_left; + bool has_link_cycle = false; + bool has_undefined_nodes_or_sockets = false; + bNode *group_output_node = nullptr; }; /** @@ -47,12 +84,24 @@ class bNodeSocketRuntime : NonCopyable, NonMovable { public: /** * References a socket declaration that is owned by `node->declaration`. This is only runtime - * data. It has to be updated when the node declaration changes. + * data. It has to be updated when the node declaration changes. Access can be allowed by using + * #AllowUsingOutdatedInfo. */ const SocketDeclarationHandle *declaration = nullptr; /** #eNodeTreeChangedFlag. */ uint32_t changed_flag = 0; + + /** Only valid when #topology_cache_is_dirty is false. */ + Vector<bNodeLink *> directly_linked_links; + Vector<bNodeSocket *> directly_linked_sockets; + Vector<bNodeSocket *> logically_linked_sockets; + Vector<bNodeSocket *> logically_linked_skipped_sockets; + bNode *owner_node = nullptr; + bNodeSocket *internal_link_input = nullptr; + int index_in_node = -1; + int index_in_all_sockets = -1; + int index_in_inout_sockets = -1; }; /** @@ -84,6 +133,392 @@ class bNodeRuntime : NonCopyable, NonMovable { /** #eNodeTreeChangedFlag. */ uint32_t changed_flag = 0; + + /** Only valid if #topology_cache_is_dirty is false. */ + Vector<bNodeSocket *> inputs; + Vector<bNodeSocket *> outputs; + Vector<bNodeLink *> internal_links; + Map<StringRefNull, bNodeSocket *> inputs_by_identifier; + Map<StringRefNull, bNodeSocket *> outputs_by_identifier; + int index_in_tree = -1; + bool has_linked_inputs = false; + bool has_linked_outputs = false; + bNodeTree *owner_tree = nullptr; }; +namespace node_tree_runtime { + +class AllowUsingOutdatedInfo : NonCopyable, NonMovable { + private: + const bNodeTree &tree_; + + public: + AllowUsingOutdatedInfo(const bNodeTree &tree) : tree_(tree) + { + tree_.runtime->allow_use_dirty_topology_cache.fetch_add(1); + } + + ~AllowUsingOutdatedInfo() + { + tree_.runtime->allow_use_dirty_topology_cache.fetch_sub(1); + } +}; + +inline bool topology_cache_is_available(const bNodeTree &tree) +{ + if (!tree.runtime->topology_cache_exists) { + return false; + } + if (tree.runtime->allow_use_dirty_topology_cache.load() > 0) { + return true; + } + return !tree.runtime->topology_cache_is_dirty; +} + +inline bool topology_cache_is_available(const bNode &node) +{ + const bNodeTree *ntree = node.runtime->owner_tree; + if (ntree == nullptr) { + return false; + } + return topology_cache_is_available(*ntree); +} + +inline bool topology_cache_is_available(const bNodeSocket &socket) +{ + const bNode *node = socket.runtime->owner_node; + if (node == nullptr) { + return false; + } + return topology_cache_is_available(*node); +} + +} // namespace node_tree_runtime + } // namespace blender::bke + +/* -------------------------------------------------------------------- */ +/** \name #bNodeTree Inline Methods + * \{ */ + +inline blender::Span<bNode *> bNodeTree::nodes_by_type(const blender::StringRefNull type_idname) +{ + BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this)); + return this->runtime->nodes_by_type.lookup(nodeTypeFind(type_idname.c_str())); +} + +inline blender::Span<const bNode *> bNodeTree::nodes_by_type( + const blender::StringRefNull type_idname) const +{ + BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this)); + return this->runtime->nodes_by_type.lookup(nodeTypeFind(type_idname.c_str())); +} + +inline blender::Span<const bNode *> bNodeTree::toposort_left_to_right() const +{ + BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this)); + return this->runtime->toposort_left_to_right; +} + +inline blender::Span<const bNode *> bNodeTree::toposort_right_to_left() const +{ + BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this)); + return this->runtime->toposort_right_to_left; +} + +inline blender::Span<const bNode *> bNodeTree::all_nodes() const +{ + BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this)); + return this->runtime->nodes; +} + +inline blender::Span<bNode *> bNodeTree::all_nodes() +{ + BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this)); + return this->runtime->nodes; +} + +inline bool bNodeTree::has_link_cycle() const +{ + BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this)); + return this->runtime->has_link_cycle; +} + +inline bool bNodeTree::has_undefined_nodes_or_sockets() const +{ + BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this)); + return this->runtime->has_undefined_nodes_or_sockets; +} + +inline const bNode *bNodeTree::group_output_node() const +{ + BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this)); + return this->runtime->group_output_node; +} + +inline blender::Span<const bNodeSocket *> bNodeTree::all_input_sockets() const +{ + BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this)); + return this->runtime->input_sockets; +} + +inline blender::Span<bNodeSocket *> bNodeTree::all_input_sockets() +{ + BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this)); + return this->runtime->input_sockets; +} + +inline blender::Span<const bNodeSocket *> bNodeTree::all_output_sockets() const +{ + BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this)); + return this->runtime->output_sockets; +} + +inline blender::Span<bNodeSocket *> bNodeTree::all_output_sockets() +{ + BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this)); + return this->runtime->output_sockets; +} + +inline blender::Span<const bNodeSocket *> bNodeTree::all_sockets() const +{ + BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this)); + return this->runtime->sockets; +} + +inline blender::Span<bNodeSocket *> bNodeTree::all_sockets() +{ + BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this)); + return this->runtime->sockets; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #bNode Inline Methods + * \{ */ + +inline blender::Span<bNodeSocket *> bNode::input_sockets() +{ + BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this)); + return this->runtime->inputs; +} + +inline blender::Span<bNodeSocket *> bNode::output_sockets() +{ + BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this)); + return this->runtime->outputs; +} + +inline blender::Span<const bNodeSocket *> bNode::input_sockets() const +{ + BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this)); + return this->runtime->inputs; +} + +inline blender::Span<const bNodeSocket *> bNode::output_sockets() const +{ + BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this)); + return this->runtime->outputs; +} + +inline bNodeSocket &bNode::input_socket(int index) +{ + BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this)); + return *this->runtime->inputs[index]; +} + +inline bNodeSocket &bNode::output_socket(int index) +{ + BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this)); + return *this->runtime->outputs[index]; +} + +inline const bNodeSocket &bNode::input_socket(int index) const +{ + BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this)); + return *this->runtime->inputs[index]; +} + +inline const bNodeSocket &bNode::output_socket(int index) const +{ + BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this)); + return *this->runtime->outputs[index]; +} + +inline const bNodeSocket &bNode::input_by_identifier(blender::StringRef identifier) const +{ + BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this)); + return *this->runtime->inputs_by_identifier.lookup_as(identifier); +} + +inline const bNodeSocket &bNode::output_by_identifier(blender::StringRef identifier) const +{ + BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this)); + return *this->runtime->outputs_by_identifier.lookup_as(identifier); +} + +inline blender::StringRefNull bNode::label_or_name() const +{ + if (this->label[0] == '\0') { + return this->name; + } + return this->label; +} + +inline bool bNode::is_muted() const +{ + return this->flag & NODE_MUTED; +} + +inline bool bNode::is_reroute() const +{ + return this->type == NODE_REROUTE; +} + +inline bool bNode::is_frame() const +{ + return this->type == NODE_FRAME; +} + +inline bool bNode::is_group() const +{ + return ELEM(this->type, NODE_GROUP, NODE_CUSTOM_GROUP); +} + +inline bool bNode::is_group_input() const +{ + return this->type == NODE_GROUP_INPUT; +} + +inline bool bNode::is_group_output() const +{ + return this->type == NODE_GROUP_OUTPUT; +} + +inline blender::Span<const bNodeLink *> bNode::internal_links_span() const +{ + BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this)); + return this->runtime->internal_links; +} + +inline const blender::nodes::NodeDeclaration *bNode::declaration() const +{ + BLI_assert(this->runtime->declaration != nullptr); + return this->runtime->declaration; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #bNodeLink Inline Methods + * \{ */ + +inline bool bNodeLink::is_muted() const +{ + return this->flag & NODE_LINK_MUTED; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #bNodeSocket Inline Methods + * \{ */ + +inline int bNodeSocket::index() const +{ + BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this)); + return this->runtime->index_in_node; +} + +inline int bNodeSocket::index_in_tree() const +{ + BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this)); + return this->runtime->index_in_all_sockets; +} + +inline bool bNodeSocket::is_available() const +{ + return (this->flag & SOCK_UNAVAIL) == 0; +} + +inline bNode &bNodeSocket::owner_node() +{ + BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this)); + return *this->runtime->owner_node; +} + +inline const bNodeTree &bNodeSocket::owner_tree() const +{ + BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this)); + return *this->runtime->owner_node->runtime->owner_tree; +} + +inline blender::Span<const bNodeSocket *> bNodeSocket::logically_linked_sockets() const +{ + BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this)); + return this->runtime->logically_linked_sockets; +} + +inline blender::Span<const bNodeLink *> bNodeSocket::directly_linked_links() const +{ + BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this)); + return this->runtime->directly_linked_links; +} + +inline blender::Span<bNodeLink *> bNodeSocket::directly_linked_links() +{ + BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this)); + return this->runtime->directly_linked_links; +} + +inline blender::Span<const bNodeSocket *> bNodeSocket::directly_linked_sockets() const +{ + BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this)); + return this->runtime->directly_linked_sockets; +} + +inline bool bNodeSocket::is_directly_linked() const +{ + return !this->directly_linked_links().is_empty(); +} + +inline bool bNodeSocket::is_logically_linked() const +{ + return !this->logically_linked_sockets().is_empty(); +} + +inline const bNodeSocket *bNodeSocket::internal_link_input() const +{ + BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this)); + BLI_assert(this->in_out == SOCK_OUT); + return this->runtime->internal_link_input; +} + +template<typename T> const T *bNodeSocket::default_value_typed() const +{ + return static_cast<const T *>(this->default_value); +} + +inline bool bNodeSocket::is_input() const +{ + return this->in_out == SOCK_IN; +} + +inline bool bNodeSocket::is_output() const +{ + return this->in_out == SOCK_OUT; +} + +inline bool bNodeSocket::is_multi_input() const +{ + return this->flag & SOCK_MULTI_INPUT; +} + +inline const bNode &bNodeSocket::owner_node() const +{ + BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this)); + return *this->runtime->owner_node; +} + +/** \} */ |