From 7b61dcf6bccf1f8fad8ae0132a2edad883f6bb72 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Wed, 14 Jun 2023 13:56:57 +0200 Subject: [PATCH] Geometry Nodes: deduplicate anonymous attribute analysis algorithm Previously, there were two independent algorithms for analysing how anonymous attributes are used in a node tree: One that just computed the `aal::RelationsInNode` for an entire node tree and one that performed a more in depth analysis to determine how far anonymous attributes should be propagated. As it turns out, both operations can also be done at the same time and the result can be cached on the node tree. This reduces the amount of code and allows for better code reuse. This simplification is likely only an intermediate step as things will probably have to be refactored further to support e.g. serial loops (#108896). --- source/blender/blenkernel/BKE_node_runtime.hh | 11 +- .../BKE_node_tree_anonymous_attributes.hh | 80 ++ source/blender/blenkernel/CMakeLists.txt | 1 + source/blender/blenkernel/intern/node.cc | 24 +- .../intern/node_tree_anonymous_attributes.cc | 688 ++++++++---------- .../blenkernel/intern/node_tree_update.cc | 1 + .../blender/blenlib/BLI_bit_group_vector.hh | 14 + .../intern/geometry_nodes_lazy_function.cc | 426 ++--------- 8 files changed, 498 insertions(+), 747 deletions(-) create mode 100644 source/blender/blenkernel/BKE_node_tree_anonymous_attributes.hh diff --git a/source/blender/blenkernel/BKE_node_runtime.hh b/source/blender/blenkernel/BKE_node_runtime.hh index 3962c0c96b8..0208c7beecb 100644 --- a/source/blender/blenkernel/BKE_node_runtime.hh +++ b/source/blender/blenkernel/BKE_node_runtime.hh @@ -36,6 +36,9 @@ namespace aal = anonymous_attribute_lifetime; namespace blender::bke::node_tree_zones { class TreeZones; } +namespace blender::bke::anonymous_attribute_inferencing { +struct AnonymousAttributeInferencingResult; +}; namespace blender { @@ -121,7 +124,8 @@ class bNodeTreeRuntime : NonCopyable, NonMovable { /** Information about how inputs and outputs of the node group interact with fields. */ std::unique_ptr field_inferencing_interface; /** Information about usage of anonymous attributes within the group. */ - std::unique_ptr anonymous_attribute_relations; + std::unique_ptr + anonymous_attribute_inferencing; /** * For geometry nodes, a lazy function graph with some additional info is cached. This is used to @@ -349,11 +353,6 @@ inline bool topology_cache_is_available(const bNodeSocket &socket) namespace node_field_inferencing { bool update_field_inferencing(const bNodeTree &tree); } -namespace anonymous_attribute_inferencing { -Array get_relations_by_node(const bNodeTree &tree, - ResourceScope &scope); -bool update_anonymous_attribute_relations(bNodeTree &tree); -} // namespace anonymous_attribute_inferencing } // namespace blender::bke /* -------------------------------------------------------------------- */ diff --git a/source/blender/blenkernel/BKE_node_tree_anonymous_attributes.hh b/source/blender/blenkernel/BKE_node_tree_anonymous_attributes.hh new file mode 100644 index 00000000000..e475c69a5d9 --- /dev/null +++ b/source/blender/blenkernel/BKE_node_tree_anonymous_attributes.hh @@ -0,0 +1,80 @@ +/* SPDX-FileCopyrightText: 2023 Blender Foundation + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include + +#include "DNA_node_types.h" + +#include "BLI_bit_group_vector.hh" +#include "BLI_vector.hh" + +#include "NOD_node_declaration.hh" + +namespace blender::bke::anonymous_attribute_inferencing { + +/** Represents a group input socket that may be a field. */ +struct InputFieldSource { + int input_index; +}; + +/** Represents an output of a node that is a field which may contain a new anonymous attribute. */ +struct SocketFieldSource { + const bNodeSocket *socket; +}; + +struct FieldSource { + std::variant data; + /** Geometry source which may contain the anonymous attributes referenced by this field. */ + Vector geometry_sources; +}; + +/** Represents a geometry group input. */ +struct InputGeometrySource { + int input_index; +}; + +/** Represents a geometry output of a node that may contain a new anonymous attribute. */ +struct SocketGeometrySource { + const bNodeSocket *socket; +}; + +struct GeometrySource { + std::variant data; + /** Field sources that originate in this geometry source. */ + Vector field_sources; +}; + +struct AnonymousAttributeInferencingResult { + /** All field sockets that may introduce new anonymous attributes into the node tree. */ + Vector all_field_sources; + /** All geometry sockets that may introduce new anonymous attributes into the node tree. */ + Vector all_geometry_sources; + + /** Encodes which field sources are propagated to every field socket. */ + BitGroupVector<> propagated_fields_by_socket; + /** Encodes which geometry sources are propagated to every geometry socket. */ + BitGroupVector<> propagated_geometries_by_socket; + /** Encodes which field sources may be available on every geometry socket. */ + BitGroupVector<> available_fields_by_geometry_socket; + /** + * Encodes which field sources have to be propagated to each geometry socket at run-time to + * ensure correct evaluation. + */ + BitGroupVector<> required_fields_by_geometry_socket; + + /** Set of geometry output indices which may propagate attributes from input geometries. */ + VectorSet propagated_output_geometry_indices; + /** Encodes to which group outputs a geometry is propagated to. */ + BitGroupVector<> propagate_to_output_by_geometry_socket; + + nodes::aal::RelationsInNode tree_relations; +}; + +Array get_relations_by_node(const bNodeTree &tree, + ResourceScope &scope); +bool update_anonymous_attribute_relations(bNodeTree &tree); + +} // namespace blender::bke::anonymous_attribute_inferencing diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index 09433e99f29..08513343830 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -453,6 +453,7 @@ set(SRC BKE_node.h BKE_node.hh BKE_node_runtime.hh + BKE_node_tree_anonymous_attributes.hh BKE_node_tree_dot_export.hh BKE_node_tree_update.h BKE_node_tree_zones.hh diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index 39d3d2b37eb..d79a141aff2 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -66,6 +66,7 @@ #include "BKE_main.h" #include "BKE_node.hh" #include "BKE_node_runtime.hh" +#include "BKE_node_tree_anonymous_attributes.hh" #include "BKE_node_tree_update.h" #include "BKE_node_tree_zones.hh" #include "BKE_type_conversions.hh" @@ -223,10 +224,25 @@ static void ntree_copy_data(Main * /*bmain*/, ID *id_dst, const ID *id_src, cons dst_runtime.field_inferencing_interface = std::make_unique( *ntree_src->runtime->field_inferencing_interface); } - if (ntree_src->runtime->anonymous_attribute_relations) { - dst_runtime.anonymous_attribute_relations = - std::make_unique( - *ntree_src->runtime->anonymous_attribute_relations); + if (ntree_src->runtime->anonymous_attribute_inferencing) { + using namespace blender::bke::anonymous_attribute_inferencing; + dst_runtime.anonymous_attribute_inferencing = + std::make_unique( + *ntree_src->runtime->anonymous_attribute_inferencing); + for (FieldSource &field_source : + dst_runtime.anonymous_attribute_inferencing->all_field_sources) { + if (auto *socket_field_source = std::get_if(&field_source.data)) { + socket_field_source->socket = socket_map.lookup(socket_field_source->socket); + } + } + for (GeometrySource &geometry_source : + dst_runtime.anonymous_attribute_inferencing->all_geometry_sources) + { + if (auto *socket_geometry_source = std::get_if(&geometry_source.data)) + { + socket_geometry_source->socket = socket_map.lookup(socket_geometry_source->socket); + } + } } if (flag & LIB_ID_COPY_NO_PREVIEW) { diff --git a/source/blender/blenkernel/intern/node_tree_anonymous_attributes.cc b/source/blender/blenkernel/intern/node_tree_anonymous_attributes.cc index aa1f66d169b..e74b008f5ee 100644 --- a/source/blender/blenkernel/intern/node_tree_anonymous_attributes.cc +++ b/source/blender/blenkernel/intern/node_tree_anonymous_attributes.cc @@ -5,17 +5,23 @@ #include "NOD_node_declaration.hh" #include "BKE_node_runtime.hh" +#include "BKE_node_tree_anonymous_attributes.hh" +#include "BKE_node_tree_dot_export.hh" + +#include "BLI_bit_group_vector.hh" +#include "BLI_bit_span_ops.hh" -#include "BLI_multi_value_map.hh" #include "BLI_resource_scope.hh" -#include "BLI_set.hh" -#include "BLI_stack.hh" -#include "BLI_timeit.hh" namespace blender::bke::anonymous_attribute_inferencing { namespace aal = nodes::aal; using nodes::NodeDeclaration; +static bool is_possible_field_socket(const bNodeSocket &socket) +{ + return ELEM(socket.type, SOCK_FLOAT, SOCK_VECTOR, SOCK_RGBA, SOCK_BOOLEAN, SOCK_INT); +} + static bool socket_is_field(const bNodeSocket &socket) { return socket.display_shape == SOCK_DISPLAY_SHAPE_DIAMOND; @@ -30,8 +36,8 @@ static const aal::RelationsInNode &get_relations_in_node(const bNode &node, Reso return scope.construct(); } - BLI_assert(group->runtime->anonymous_attribute_relations); - return *group->runtime->anonymous_attribute_relations; + BLI_assert(group->runtime->anonymous_attribute_inferencing); + return group->runtime->anonymous_attribute_inferencing->tree_relations; } } if (node.is_reroute()) { @@ -110,409 +116,345 @@ Array get_relations_by_node(const bNodeTree &tree, return relations_by_node; } -/** - * Start at a group output socket and find all linked group inputs. - */ -static Vector find_linked_group_inputs( - const bNodeTree &tree, - const bNodeSocket &group_output_socket, - const FunctionRef(const bNodeSocket &)> get_linked_node_inputs) +class bNodeTreeToDotOptionsForAnonymousAttributeInferencing : public bNodeTreeToDotOptions { + private: + const AnonymousAttributeInferencingResult &result_; + + public: + bNodeTreeToDotOptionsForAnonymousAttributeInferencing( + const AnonymousAttributeInferencingResult &result) + : result_(result) + { + } + + std::string socket_name(const bNodeSocket &socket) const + { + if (socket.type == SOCK_GEOMETRY) { + std::stringstream ss; + ss << socket.identifier << " ["; + bits::foreach_1_index(result_.required_fields_by_geometry_socket[socket.index_in_tree()], + [&](const int i) { ss << i << ","; }); + ss << "]"; + return ss.str(); + } + else if (is_possible_field_socket(socket)) { + std::stringstream ss; + ss << socket.identifier << " ["; + bits::foreach_1_index(result_.propagated_fields_by_socket[socket.index_in_tree()], + [&](const int i) { ss << i << ","; }); + ss << "]"; + return ss.str(); + } + return socket.identifier; + } +}; + +static AnonymousAttributeInferencingResult analyse_anonymous_attribute_usages( + const bNodeTree &tree) { - Set found_sockets; - Stack sockets_to_check; + BLI_assert(!tree.has_available_link_cycle()); - Vector input_indices; + ResourceScope scope; + const Array relations_by_node = get_relations_by_node(tree, scope); - found_sockets.add_new(&group_output_socket); - sockets_to_check.push(&group_output_socket); + Vector all_field_sources; + Vector all_geometry_sources; - while (!sockets_to_check.is_empty()) { - const bNodeSocket &socket = *sockets_to_check.pop(); - if (socket.is_input()) { - for (const bNodeLink *link : socket.directly_linked_links()) { - if (link->is_used()) { - const bNodeSocket &from_socket = *link->fromsock; - if (found_sockets.add(&from_socket)) { - sockets_to_check.push(&from_socket); - } + /* Find input field and geometry sources. */ + for (const int i : tree.interface_inputs().index_range()) { + const bNodeSocket &interface_socket = *tree.interface_inputs()[i]; + if (interface_socket.type == SOCK_GEOMETRY) { + all_geometry_sources.append_and_get_index({InputGeometrySource{i}}); + } + else if (is_possible_field_socket(interface_socket)) { + all_field_sources.append_and_get_index({InputFieldSource{i}}); + } + } + for (const int geometry_source_index : all_geometry_sources.index_range()) { + for (const int field_source_index : all_field_sources.index_range()) { + all_geometry_sources[geometry_source_index].field_sources.append(field_source_index); + all_field_sources[field_source_index].geometry_sources.append(geometry_source_index); + } + } + + /* Find socket field and geometry sources. */ + Map field_source_by_socket; + Map geometry_source_by_socket; + for (const bNode *node : tree.all_nodes()) { + const aal::RelationsInNode &relations = *relations_by_node[node->index()]; + for (const aal::AvailableRelation &relation : relations.available_relations) { + const bNodeSocket &geometry_socket = node->output_socket(relation.geometry_output); + const bNodeSocket &field_socket = node->output_socket(relation.field_output); + if (!field_socket.is_available()) { + continue; + } + if (!field_socket.is_directly_linked()) { + continue; + } + + const int field_source_index = field_source_by_socket.lookup_or_add_cb(&field_socket, [&]() { + return all_field_sources.append_and_get_index({SocketFieldSource{&field_socket}}); + }); + const int geometry_source_index = geometry_source_by_socket.lookup_or_add_cb( + &geometry_socket, [&]() { + return all_geometry_sources.append_and_get_index( + {SocketGeometrySource{&geometry_socket}}); + }); + + all_field_sources[field_source_index].geometry_sources.append(geometry_source_index); + all_geometry_sources[geometry_source_index].field_sources.append(field_source_index); + } + } + + const int sockets_num = tree.all_sockets().size(); + BitGroupVector<> propagated_fields_by_socket(sockets_num, all_field_sources.size(), false); + BitGroupVector<> propagated_geometries_by_socket( + sockets_num, all_geometry_sources.size(), false); + BitGroupVector<> available_fields_by_geometry_socket( + sockets_num, all_field_sources.size(), false); + + /* Insert field and geometry sources into the maps for the first inferencing pass. */ + for (const int field_source_index : all_field_sources.index_range()) { + const FieldSource &field_source = all_field_sources[field_source_index]; + if (const auto *input_field = std::get_if(&field_source.data)) { + for (const bNode *node : tree.group_input_nodes()) { + const bNodeSocket &socket = node->output_socket(input_field->input_index); + propagated_fields_by_socket[socket.index_in_tree()][field_source_index].set(); + } + } + else { + const auto &socket_field = std::get(field_source.data); + propagated_fields_by_socket[socket_field.socket->index_in_tree()][field_source_index].set(); + } + } + for (const int geometry_source_index : all_geometry_sources.index_range()) { + const GeometrySource &geometry_source = all_geometry_sources[geometry_source_index]; + if (const auto *input_geometry = std::get_if(&geometry_source.data)) { + for (const bNode *node : tree.group_input_nodes()) { + const bNodeSocket &socket = node->output_socket(input_geometry->input_index); + const int socket_i = socket.index_in_tree(); + propagated_geometries_by_socket[socket_i][geometry_source_index].set(); + for (const int field_source_index : geometry_source.field_sources) { + available_fields_by_geometry_socket[socket_i][field_source_index].set(); } } } else { - const bNode &node = socket.owner_node(); - for (const int input_index : get_linked_node_inputs(socket)) { - const bNodeSocket &input_socket = node.input_socket(input_index); - if (input_socket.is_available()) { - if (found_sockets.add(&input_socket)) { - sockets_to_check.push(&input_socket); - } - } + const auto &socket_geometry = std::get(geometry_source.data); + const int socket_i = socket_geometry.socket->index_in_tree(); + propagated_geometries_by_socket[socket_i][geometry_source_index].set(); + for (const int field_source_index : geometry_source.field_sources) { + available_fields_by_geometry_socket[socket_i][field_source_index].set(); } } } - for (const bNode *node : tree.group_input_nodes()) { + /* Inferencing pass from left to right to figure out where fields and geometries may be + * propagated to. */ + for (const bNode *node : tree.toposort_left_to_right()) { + for (const bNodeSocket *socket : node->input_sockets()) { + if (!socket->is_available()) { + continue; + } + const int dst_index = socket->index_in_tree(); + for (const bNodeLink *link : socket->directly_linked_links()) { + if (link->is_used()) { + const int src_index = link->fromsock->index_in_tree(); + propagated_fields_by_socket[dst_index] |= propagated_fields_by_socket[src_index]; + propagated_geometries_by_socket[dst_index] |= propagated_geometries_by_socket[src_index]; + available_fields_by_geometry_socket[dst_index] |= + available_fields_by_geometry_socket[src_index]; + } + } + } + const aal::RelationsInNode &relations = *relations_by_node[node->index()]; + for (const aal::ReferenceRelation &relation : relations.reference_relations) { + const bNodeSocket &from_socket = node->input_socket(relation.from_field_input); + const bNodeSocket &to_socket = node->output_socket(relation.to_field_output); + if (!from_socket.is_available() || !to_socket.is_available()) { + continue; + } + const int src_index = from_socket.index_in_tree(); + const int dst_index = to_socket.index_in_tree(); + propagated_fields_by_socket[dst_index] |= propagated_fields_by_socket[src_index]; + } + for (const aal::PropagateRelation &relation : relations.propagate_relations) { + const bNodeSocket &from_socket = node->input_socket(relation.from_geometry_input); + const bNodeSocket &to_socket = node->output_socket(relation.to_geometry_output); + if (!from_socket.is_available() || !to_socket.is_available()) { + continue; + } + const int src_index = from_socket.index_in_tree(); + const int dst_index = to_socket.index_in_tree(); + propagated_geometries_by_socket[dst_index] |= propagated_geometries_by_socket[src_index]; + available_fields_by_geometry_socket[dst_index] |= + available_fields_by_geometry_socket[src_index]; + } + } + + BitGroupVector<> required_fields_by_geometry_socket( + sockets_num, all_field_sources.size(), false); + VectorSet propagated_output_geometry_indices; + aal::RelationsInNode tree_relations; + + /* Create #PropagateRelation, #AvailableRelation and #ReferenceRelation for the tree based on the + * propagated data from above. */ + if (const bNode *group_output_node = tree.group_output_node()) { + for (const bNodeSocket *socket : group_output_node->input_sockets().drop_back(1)) { + if (socket->type == SOCK_GEOMETRY) { + const BoundedBitSpan propagated_geometries = + propagated_geometries_by_socket[socket->index_in_tree()]; + bits::foreach_1_index(propagated_geometries, [&](const int geometry_source_index) { + const GeometrySource &geometry_source = all_geometry_sources[geometry_source_index]; + if (const auto *input_geometry = std::get_if(&geometry_source.data)) + { + tree_relations.propagate_relations.append( + aal::PropagateRelation{input_geometry->input_index, socket->index()}); + propagated_output_geometry_indices.add(socket->index()); + } + else { + [[maybe_unused]] const auto &socket_geometry = std::get( + geometry_source.data); + for (const int field_source_index : geometry_source.field_sources) { + for (const bNodeSocket *other_socket : + group_output_node->input_sockets().drop_back(1)) { + if (!is_possible_field_socket(*other_socket)) { + continue; + } + if (propagated_fields_by_socket[other_socket->index_in_tree()][field_source_index] + .test()) { + tree_relations.available_relations.append( + aal::AvailableRelation{other_socket->index(), socket->index()}); + required_fields_by_geometry_socket[socket->index_in_tree()][field_source_index] + .set(); + } + } + } + } + }); + } + else if (is_possible_field_socket(*socket)) { + const BoundedBitSpan propagated_fields = + propagated_fields_by_socket[socket->index_in_tree()]; + bits::foreach_1_index(propagated_fields, [&](const int field_source_index) { + const FieldSource &field_source = all_field_sources[field_source_index]; + if (const auto *input_field = std::get_if(&field_source.data)) { + tree_relations.reference_relations.append( + aal::ReferenceRelation{input_field->input_index, socket->index()}); + } + }); + } + } + } + + /* Initialize map for second inferencing pass. */ + BitGroupVector<> propagate_to_output_by_geometry_socket( + sockets_num, propagated_output_geometry_indices.size(), false); + for (const aal::PropagateRelation &relation : tree_relations.propagate_relations) { + const bNodeSocket &socket = tree.group_output_node()->input_socket( + relation.to_geometry_output); + propagate_to_output_by_geometry_socket[socket.index_in_tree()] + [propagated_output_geometry_indices.index_of( + relation.to_geometry_output)] + .set(); + } + + /* Inferencing pass from right to left to determine which anonymous attributes have to be + * propagated to which geometry sockets. */ + for (const bNode *node : tree.toposort_right_to_left()) { for (const bNodeSocket *socket : node->output_sockets()) { - if (found_sockets.contains(socket)) { - input_indices.append_non_duplicates(socket->index()); + if (!socket->is_available()) { + continue; } - } - } - - return input_indices; -} - -static void infer_propagate_relations(const bNodeTree &tree, - const Span relations_by_node, - const bNode &group_output_node, - aal::RelationsInNode &r_relations) -{ - for (const bNodeSocket *group_output_socket : group_output_node.input_sockets().drop_back(1)) { - if (group_output_socket->type != SOCK_GEOMETRY) { - continue; - } - const Vector input_indices = find_linked_group_inputs( - tree, *group_output_socket, [&](const bNodeSocket &output_socket) { - Vector indices; - for (const aal::PropagateRelation &relation : - relations_by_node[output_socket.owner_node().index()]->propagate_relations) - { - if (relation.to_geometry_output == output_socket.index()) { - indices.append(relation.from_geometry_input); - } - } - return indices; - }); - for (const int input_index : input_indices) { - aal::PropagateRelation relation; - relation.from_geometry_input = input_index; - relation.to_geometry_output = group_output_socket->index(); - r_relations.propagate_relations.append(relation); - } - } -} - -static void infer_reference_relations(const bNodeTree &tree, - const Span relations_by_node, - const bNode &group_output_node, - aal::RelationsInNode &r_relations) -{ - for (const bNodeSocket *group_output_socket : group_output_node.input_sockets().drop_back(1)) { - if (!socket_is_field(*group_output_socket)) { - continue; - } - const Vector input_indices = find_linked_group_inputs( - tree, *group_output_socket, [&](const bNodeSocket &output_socket) { - Vector indices; - for (const aal::ReferenceRelation &relation : - relations_by_node[output_socket.owner_node().index()]->reference_relations) - { - if (relation.to_field_output == output_socket.index()) { - indices.append(relation.from_field_input); - } - } - return indices; - }); - for (const int input_index : input_indices) { - if (tree.runtime->field_inferencing_interface->inputs[input_index] != - nodes::InputSocketFieldType::None) - { - aal::ReferenceRelation relation; - relation.from_field_input = input_index; - relation.to_field_output = group_output_socket->index(); - r_relations.reference_relations.append(relation); - } - } - } -} - -/** - * Find group output geometries that contain anonymous attributes referenced by the field. - * If `nullopt` is returned, the field does not depend on any anonymous attribute created in this - * node tree. - */ -static std::optional> find_available_on_outputs( - const bNodeSocket &initial_group_output_socket, - const bNode &group_output_node, - const Span relations_by_node) -{ - Set geometry_sockets; - - { - /* Find the nodes that added anonymous attributes to the field. */ - Set found_sockets; - Stack sockets_to_check; - - found_sockets.add_new(&initial_group_output_socket); - sockets_to_check.push(&initial_group_output_socket); - - while (!sockets_to_check.is_empty()) { - const bNodeSocket &socket = *sockets_to_check.pop(); - if (socket.is_input()) { - for (const bNodeLink *link : socket.directly_linked_links()) { - if (link->is_used()) { - const bNodeSocket &from_socket = *link->fromsock; - if (found_sockets.add(&from_socket)) { - sockets_to_check.push(&from_socket); - } - } - } - } - else { - const bNode &node = socket.owner_node(); - const aal::RelationsInNode &relations = *relations_by_node[node.index()]; - for (const aal::AvailableRelation &relation : relations.available_relations) { - if (socket.index() == relation.field_output) { - const bNodeSocket &geometry_output = node.output_socket(relation.geometry_output); - if (geometry_output.is_available()) { - geometry_sockets.add(&geometry_output); - } - } - } - for (const aal::ReferenceRelation &relation : relations.reference_relations) { - if (socket.index() == relation.to_field_output) { - const bNodeSocket &field_input = node.input_socket(relation.from_field_input); - if (field_input.is_available()) { - if (found_sockets.add(&field_input)) { - sockets_to_check.push(&field_input); - } - } - } - } - } - } - } - - if (geometry_sockets.is_empty()) { - /* The field does not depend on any anonymous attribute created within this node tree. */ - return std::nullopt; - } - - /* Find the group output geometries that contain the anonymous attribute referenced by the field - * output. */ - Set found_sockets; - Stack sockets_to_check; - - for (const bNodeSocket *socket : geometry_sockets) { - found_sockets.add_new(socket); - sockets_to_check.push(socket); - } - - while (!sockets_to_check.is_empty()) { - const bNodeSocket &socket = *sockets_to_check.pop(); - if (socket.is_input()) { - const bNode &node = socket.owner_node(); - const aal::RelationsInNode &relations = *relations_by_node[node.index()]; - for (const aal::PropagateRelation &relation : relations.propagate_relations) { - if (socket.index() == relation.from_geometry_input) { - const bNodeSocket &output_socket = node.output_socket(relation.to_geometry_output); - if (output_socket.is_available()) { - if (found_sockets.add(&output_socket)) { - sockets_to_check.push(&output_socket); - } - } - } - } - } - else { - for (const bNodeLink *link : socket.directly_linked_links()) { + const int dst_index = socket->index_in_tree(); + for (const bNodeLink *link : socket->directly_linked_links()) { if (link->is_used()) { - const bNodeSocket &to_socket = *link->tosock; - if (found_sockets.add(&to_socket)) { - sockets_to_check.push(&to_socket); - } + const int src_index = link->tosock->index_in_tree(); + required_fields_by_geometry_socket[dst_index] |= + required_fields_by_geometry_socket[src_index]; + propagate_to_output_by_geometry_socket[dst_index] |= + propagate_to_output_by_geometry_socket[src_index]; } } } - } - - Vector output_indices; - for (const bNodeSocket *socket : group_output_node.input_sockets().drop_back(1)) { - if (found_sockets.contains(socket)) { - output_indices.append(socket->index()); + const aal::RelationsInNode &relations = *relations_by_node[node->index()]; + for (const aal::PropagateRelation &relation : relations.propagate_relations) { + const bNodeSocket &output_socket = node->output_socket(relation.to_geometry_output); + const bNodeSocket &input_socket = node->input_socket(relation.from_geometry_input); + const int src_index = output_socket.index_in_tree(); + const int dst_index = input_socket.index_in_tree(); + required_fields_by_geometry_socket[dst_index] |= + required_fields_by_geometry_socket[src_index]; + propagate_to_output_by_geometry_socket[dst_index] |= + propagate_to_output_by_geometry_socket[src_index]; + } + for (const aal::EvalRelation &relation : relations.eval_relations) { + const bNodeSocket &geometry_socket = node->input_socket(relation.geometry_input); + const bNodeSocket &field_socket = node->input_socket(relation.field_input); + required_fields_by_geometry_socket[geometry_socket.index_in_tree()] |= + propagated_fields_by_socket[field_socket.index_in_tree()]; } } - return output_indices; -} -static void infer_available_relations(const Span relations_by_node, - const bNode &group_output_node, - aal::RelationsInNode &r_relations) -{ - for (const bNodeSocket *group_output_socket : group_output_node.input_sockets().drop_back(1)) { - if (!socket_is_field(*group_output_socket)) { + /* Make sure that only available fields are also required. */ + required_fields_by_geometry_socket.all_bits() &= available_fields_by_geometry_socket.all_bits(); + + /* Create #EvalRelation for the tree. */ + for (const int interface_i : tree.interface_inputs().index_range()) { + const bNodeSocket &interface_socket = *tree.interface_inputs()[interface_i]; + if (interface_socket.type != SOCK_GEOMETRY) { continue; } - const std::optional> output_indices = find_available_on_outputs( - *group_output_socket, group_output_node, relations_by_node); - if (output_indices.has_value()) { - if (output_indices->is_empty()) { - r_relations.available_on_none.append(group_output_socket->index()); - } - else { - for (const int output_index : *output_indices) { - aal::AvailableRelation relation; - relation.field_output = group_output_socket->index(); - relation.geometry_output = output_index; - r_relations.available_relations.append(relation); - } - } + BitVector<> required_fields(all_field_sources.size(), false); + for (const bNode *node : tree.group_input_nodes()) { + const bNodeSocket &geometry_socket = node->output_socket(interface_i); + required_fields |= required_fields_by_geometry_socket[geometry_socket.index_in_tree()]; } - } -} - -/** - * Returns a list of all the geometry inputs that the field input may be evaluated on. - */ -static Vector find_eval_on_inputs(const bNodeTree &tree, - const int field_input_index, - const Span relations_by_node) -{ - const Span group_input_nodes = tree.group_input_nodes(); - Set geometry_sockets; - - { - /* Find all the nodes that evaluate the input field. */ - Set found_sockets; - Stack sockets_to_check; - - for (const bNode *node : group_input_nodes) { - const bNodeSocket &socket = node->output_socket(field_input_index); - found_sockets.add_new(&socket); - sockets_to_check.push(&socket); - } - - while (!sockets_to_check.is_empty()) { - const bNodeSocket &socket = *sockets_to_check.pop(); - if (socket.is_input()) { - const bNode &node = socket.owner_node(); - const aal::RelationsInNode &relations = *relations_by_node[node.index()]; - for (const aal::EvalRelation &relation : relations.eval_relations) { - if (socket.index() == relation.field_input) { - const bNodeSocket &geometry_input = node.input_socket(relation.geometry_input); - if (geometry_input.is_available()) { - geometry_sockets.add(&geometry_input); - } - } - } - for (const aal::ReferenceRelation &relation : relations.reference_relations) { - if (socket.index() == relation.from_field_input) { - const bNodeSocket &field_output = node.output_socket(relation.to_field_output); - if (field_output.is_available()) { - if (found_sockets.add(&field_output)) { - sockets_to_check.push(&field_output); - } - } - } - } + bits::foreach_1_index(required_fields, [&](const int field_source_index) { + const FieldSource &field_source = all_field_sources[field_source_index]; + if (const auto *input_field = std::get_if(&field_source.data)) { + tree_relations.eval_relations.append( + aal::EvalRelation{input_field->input_index, interface_i}); } - else { - for (const bNodeLink *link : socket.directly_linked_links()) { - if (link->is_used()) { - const bNodeSocket &to_socket = *link->tosock; - if (found_sockets.add(&to_socket)) { - sockets_to_check.push(&to_socket); - } - } - } - } - } + }); } - if (geometry_sockets.is_empty()) { - return {}; - } + AnonymousAttributeInferencingResult result{std::move(all_field_sources), + std::move(all_geometry_sources), + std::move(propagated_fields_by_socket), + std::move(propagated_geometries_by_socket), + std::move(available_fields_by_geometry_socket), + std::move(required_fields_by_geometry_socket), + std::move(propagated_output_geometry_indices), + std::move(propagate_to_output_by_geometry_socket), + std::move(tree_relations)}; - /* Find the group input geometries whose attributes are propagated to the nodes that evaluate the - * field. */ - Set found_sockets; - Stack sockets_to_check; - - Vector geometry_input_indices; - - for (const bNodeSocket *socket : geometry_sockets) { - found_sockets.add_new(socket); - sockets_to_check.push(socket); - } - - while (!sockets_to_check.is_empty()) { - const bNodeSocket &socket = *sockets_to_check.pop(); - if (socket.is_input()) { - for (const bNodeLink *link : socket.directly_linked_links()) { - if (link->is_used()) { - const bNodeSocket &from_socket = *link->fromsock; - if (found_sockets.add(&from_socket)) { - sockets_to_check.push(&from_socket); - } - } - } - } - else { - const bNode &node = socket.owner_node(); - if (node.is_group_input()) { - geometry_input_indices.append_non_duplicates(socket.index()); - } - else { - const aal::RelationsInNode &relations = *relations_by_node[node.index()]; - for (const aal::PropagateRelation &relation : relations.propagate_relations) { - if (socket.index() == relation.to_geometry_output) { - const bNodeSocket &input_socket = node.input_socket(relation.from_geometry_input); - if (input_socket.is_available()) { - if (found_sockets.add(&input_socket)) { - sockets_to_check.push(&input_socket); - } - } - } - } - } - } - } - - return geometry_input_indices; -} - -static void infer_eval_relations(const bNodeTree &tree, - const Span relations_by_node, - aal::RelationsInNode &r_relations) -{ - for (const int input_index : tree.interface_inputs().index_range()) { - if (tree.runtime->field_inferencing_interface->inputs[input_index] == - nodes::InputSocketFieldType::None) - { - continue; - } - const Vector geometry_input_indices = find_eval_on_inputs( - tree, input_index, relations_by_node); - for (const int geometry_input : geometry_input_indices) { - aal::EvalRelation relation; - relation.field_input = input_index; - relation.geometry_input = geometry_input; - r_relations.eval_relations.append(std::move(relation)); - } - } +/* Print analysis result for debugging purposes. */ +#if 0 + bNodeTreeToDotOptionsForAnonymousAttributeInferencing options{result}; + std::cout << "\n\n" << node_tree_to_dot(tree, options) << "\n\n"; +#endif + return result; } bool update_anonymous_attribute_relations(bNodeTree &tree) { tree.ensure_topology_cache(); - ResourceScope scope; - Array relations_by_node = get_relations_by_node(tree, scope); - - std::unique_ptr new_relations = std::make_unique(); - if (!tree.has_available_link_cycle()) { - if (const bNode *group_output_node = tree.group_output_node()) { - infer_propagate_relations(tree, relations_by_node, *group_output_node, *new_relations); - infer_reference_relations(tree, relations_by_node, *group_output_node, *new_relations); - infer_available_relations(relations_by_node, *group_output_node, *new_relations); - } - infer_eval_relations(tree, relations_by_node, *new_relations); + if (tree.has_available_link_cycle()) { + const bool changed = tree.runtime->anonymous_attribute_inferencing.get() != nullptr; + tree.runtime->anonymous_attribute_inferencing.reset(); + return changed; } - const bool group_interface_changed = !tree.runtime->anonymous_attribute_relations || - *tree.runtime->anonymous_attribute_relations != - *new_relations; - tree.runtime->anonymous_attribute_relations = std::move(new_relations); + AnonymousAttributeInferencingResult result = analyse_anonymous_attribute_usages(tree); + + const bool group_interface_changed = + !tree.runtime->anonymous_attribute_inferencing || + tree.runtime->anonymous_attribute_inferencing->tree_relations != result.tree_relations; + + tree.runtime->anonymous_attribute_inferencing = + std::make_unique(std::move(result)); return group_interface_changed; } diff --git a/source/blender/blenkernel/intern/node_tree_update.cc b/source/blender/blenkernel/intern/node_tree_update.cc index 8b5c1986ec6..f6d3b3daa88 100644 --- a/source/blender/blenkernel/intern/node_tree_update.cc +++ b/source/blender/blenkernel/intern/node_tree_update.cc @@ -19,6 +19,7 @@ #include "BKE_main.h" #include "BKE_node.hh" #include "BKE_node_runtime.hh" +#include "BKE_node_tree_anonymous_attributes.hh" #include "BKE_node_tree_update.h" #include "MOD_nodes.h" diff --git a/source/blender/blenlib/BLI_bit_group_vector.hh b/source/blender/blenlib/BLI_bit_group_vector.hh index aca23c20d1c..6c7825608ba 100644 --- a/source/blender/blenlib/BLI_bit_group_vector.hh +++ b/source/blender/blenlib/BLI_bit_group_vector.hh @@ -86,6 +86,20 @@ class BitGroupVector { { return IndexRange{this->size()}; } + + /** + * Get all stored bits. Note that this may also contain padding bits. This can be used to e.g. + * mix multiple #BitGroupVector. + */ + BoundedBitSpan all_bits() const + { + return data_; + } + + MutableBoundedBitSpan all_bits() + { + return data_; + } }; } // namespace blender::bits diff --git a/source/blender/nodes/intern/geometry_nodes_lazy_function.cc b/source/blender/nodes/intern/geometry_nodes_lazy_function.cc index 9f37d983ddd..9aaa35410a1 100644 --- a/source/blender/nodes/intern/geometry_nodes_lazy_function.cc +++ b/source/blender/nodes/intern/geometry_nodes_lazy_function.cc @@ -33,6 +33,7 @@ #include "BKE_compute_contexts.hh" #include "BKE_geometry_set.hh" +#include "BKE_node_tree_anonymous_attributes.hh" #include "BKE_type_conversions.hh" #include "FN_field_cpp_type.hh" @@ -1286,60 +1287,6 @@ class LazyFunctionForAnonymousAttributeSetJoin : public lf::LazyFunction { } }; -enum class AttributeReferenceKeyType { - /** Attribute referenced by a field passed into the group. */ - InputField, - /** Attributes referenced on the output geometry outside of the current group. */ - OutputGeometry, - /** Attribute referenced by a field created within the current group. */ - Socket, -}; - -/** - * Identifier for something that can reference anonymous attributes that should be propagated. - */ -struct AttributeReferenceKey { - AttributeReferenceKeyType type; - /* Used when type is InputField or OutputGeometry. */ - int index = 0; - /* Used when type is Socket. */ - const bNodeSocket *bsocket = nullptr; - - uint64_t hash() const - { - return get_default_hash_3(this->type, this->bsocket, this->index); - } - - friend bool operator==(const AttributeReferenceKey &a, const AttributeReferenceKey &b) - { - return a.type == b.type && a.bsocket == b.bsocket && a.index == b.index; - } - - friend std::ostream &operator<<(std::ostream &stream, const AttributeReferenceKey &value) - { - if (value.type == AttributeReferenceKeyType::InputField) { - stream << "Input Field: " << value.index; - } - else if (value.type == AttributeReferenceKeyType::OutputGeometry) { - stream << "Output Geometry: " << value.index; - } - else { - stream << "Socket: " << value.bsocket->owner_node().name << " -> " << value.bsocket->name; - } - return stream; - } -}; - -/** - * Additional information that corresponds to an #AttributeReferenceKey. - */ -struct AttributeReferenceInfo { - /** Output socket that contains an attribute set containing the referenced attributes. */ - lf::OutputSocket *lf_attribute_set_socket = nullptr; - /** Geometry sockets that contain the referenced attributes. */ - Vector initial_geometry_sockets; -}; - /** * Utility class to build a lazy-function graph based on a geometry nodes tree. * This is mainly a separate class because it makes it easier to have variables that can be @@ -2097,7 +2044,8 @@ struct GeometryNodesLazyFunctionGraphBuilder { */ void build_attribute_propagation_input_node() { - const aal::RelationsInNode &tree_relations = *btree_.runtime->anonymous_attribute_relations; + const aal::RelationsInNode &tree_relations = + btree_.runtime->anonymous_attribute_inferencing->tree_relations; Vector output_indices; for (const aal::PropagateRelation &relation : tree_relations.propagate_relations) { output_indices.append_non_duplicates(relation.to_geometry_output); @@ -2564,344 +2512,94 @@ struct GeometryNodesLazyFunctionGraphBuilder { } } + using JoinAttibuteSetsCache = Map, lf::OutputSocket *>; + void build_attribute_propagation_sets() { - ResourceScope scope; - const Array relations_by_node = - bke::anonymous_attribute_inferencing::get_relations_by_node(btree_, scope); + using namespace bke::anonymous_attribute_inferencing; - VectorSet attribute_reference_keys; - /* Indexed by reference key index. */ - Vector attribute_reference_infos; - this->build_attribute_references( - relations_by_node, attribute_reference_keys, attribute_reference_infos); + const AnonymousAttributeInferencingResult &result = + *btree_.runtime->anonymous_attribute_inferencing; - const int sockets_num = btree_.all_sockets().size(); - const int attribute_references_num = attribute_reference_keys.size(); - - /* The code below uses #BitGroupVector to store a set of attribute references per socket. Each - * socket has a bit span where each bit corresponds to one attribute reference. */ - BitGroupVector<> referenced_by_field_socket(sockets_num, attribute_references_num, false); - BitGroupVector<> propagated_to_geometry_socket(sockets_num, attribute_references_num, false); - this->gather_referenced_and_potentially_propagated_data(relations_by_node, - attribute_reference_keys, - attribute_reference_infos, - referenced_by_field_socket, - propagated_to_geometry_socket); - - BitGroupVector<> required_propagated_to_geometry_socket( - sockets_num, attribute_references_num, false); - this->gather_required_propagated_data(relations_by_node, - attribute_reference_keys, - referenced_by_field_socket, - propagated_to_geometry_socket, - required_propagated_to_geometry_socket); - - this->build_attribute_sets_to_propagate(attribute_reference_keys, - attribute_reference_infos, - required_propagated_to_geometry_socket); - } - - void build_attribute_references(const Span relations_by_node, - VectorSet &r_attribute_reference_keys, - Vector &r_attribute_reference_infos) - { - auto add_get_attributes_node = [&](lf::OutputSocket &lf_field_socket) -> lf::OutputSocket & { - const ValueOrFieldCPPType &type = *ValueOrFieldCPPType::get_from_self( - lf_field_socket.type()); - auto lazy_function = std::make_unique(type); - lf::Node &lf_node = lf_graph_->add_function(*lazy_function); - lf_graph_->add_link(lf_field_socket, lf_node.input(0)); - lf_graph_info_->functions.append(std::move(lazy_function)); - return lf_node.output(0); - }; - - /* Find nodes that create new anonymous attributes. */ - for (const bNode *node : btree_.all_nodes()) { - const aal::RelationsInNode &relations = *relations_by_node[node->index()]; - for (const aal::AvailableRelation &relation : relations.available_relations) { - const bNodeSocket &geometry_bsocket = node->output_socket(relation.geometry_output); - const bNodeSocket &field_bsocket = node->output_socket(relation.field_output); - if (!field_bsocket.is_available()) { - continue; - } - if (!field_bsocket.is_directly_linked()) { - continue; - } - AttributeReferenceKey key; - key.type = AttributeReferenceKeyType::Socket; - key.bsocket = &field_bsocket; - const int key_index = r_attribute_reference_keys.index_of_or_add(key); - if (key_index >= r_attribute_reference_infos.size()) { - AttributeReferenceInfo info; - lf::OutputSocket &lf_field_socket = *output_socket_map_.lookup(&field_bsocket); - info.lf_attribute_set_socket = &add_get_attributes_node(lf_field_socket); - r_attribute_reference_infos.append(info); - } - AttributeReferenceInfo &info = r_attribute_reference_infos[key_index]; - if (geometry_bsocket.is_available()) { - info.initial_geometry_sockets.append(&geometry_bsocket); - } - } - } - - /* Find field group inputs that are evaluated within this node tree. */ - const aal::RelationsInNode &tree_relations = *btree_.runtime->anonymous_attribute_relations; - for (const aal::EvalRelation &relation : tree_relations.eval_relations) { - AttributeReferenceKey key; - key.type = AttributeReferenceKeyType::InputField; - key.index = relation.field_input; - const int key_index = r_attribute_reference_keys.index_of_or_add(key); - if (key_index >= r_attribute_reference_infos.size()) { - AttributeReferenceInfo info; - lf::OutputSocket &lf_field_socket = *const_cast( - mapping_->group_input_sockets[relation.field_input]); - info.lf_attribute_set_socket = &add_get_attributes_node(lf_field_socket); - r_attribute_reference_infos.append(info); - } - AttributeReferenceInfo &info = r_attribute_reference_infos[key_index]; - for (const bNode *bnode : btree_.group_input_nodes()) { - info.initial_geometry_sockets.append(&bnode->output_socket(relation.geometry_input)); - } - } - /* Find group outputs that attributes need to be propagated to. */ - for (const aal::PropagateRelation &relation : tree_relations.propagate_relations) { - AttributeReferenceKey key; - key.type = AttributeReferenceKeyType::OutputGeometry; - key.index = relation.to_geometry_output; - const int key_index = r_attribute_reference_keys.index_of_or_add(key); - if (key_index >= r_attribute_reference_infos.size()) { - AttributeReferenceInfo info; - info.lf_attribute_set_socket = const_cast( - mapping_->attribute_set_by_geometry_output.lookup(relation.to_geometry_output)); - r_attribute_reference_infos.append(info); - } - AttributeReferenceInfo &info = r_attribute_reference_infos[key_index]; - for (const bNode *bnode : btree_.group_input_nodes()) { - info.initial_geometry_sockets.append(&bnode->output_socket(relation.from_geometry_input)); - } - } - } - - /** - * For every field socket, figure out which anonymous attributes it may reference. - * For every geometry socket, figure out which anonymous attributes may be propagated to it. - */ - void gather_referenced_and_potentially_propagated_data( - const Span relations_by_node, - const Span attribute_reference_keys, - const Span attribute_reference_infos, - BitGroupVector<> &r_referenced_by_field_socket, - BitGroupVector<> &r_propagated_to_geometry_socket) - { - /* Insert initial referenced/propagated attributes. */ - for (const int key_index : attribute_reference_keys.index_range()) { - const AttributeReferenceKey &key = attribute_reference_keys[key_index]; - const AttributeReferenceInfo &info = attribute_reference_infos[key_index]; - switch (key.type) { - case AttributeReferenceKeyType::InputField: { - for (const bNode *bnode : btree_.group_input_nodes()) { - const bNodeSocket &bsocket = bnode->output_socket(key.index); - r_referenced_by_field_socket[bsocket.index_in_tree()][key_index].set(); - } - break; - } - case AttributeReferenceKeyType::OutputGeometry: { - break; - } - case AttributeReferenceKeyType::Socket: { - r_referenced_by_field_socket[key.bsocket->index_in_tree()][key_index].set(); - break; - } - } - for (const bNodeSocket *geometry_bsocket : info.initial_geometry_sockets) { - r_propagated_to_geometry_socket[geometry_bsocket->index_in_tree()][key_index].set(); - } - } - /* Propagate attribute usages from left to right. */ - for (const bNode *bnode : btree_.toposort_left_to_right()) { - for (const bNodeSocket *bsocket : bnode->input_sockets()) { - if (bsocket->is_available()) { - const int dst_index = bsocket->index_in_tree(); - MutableBoundedBitSpan referenced_dst = r_referenced_by_field_socket[dst_index]; - MutableBoundedBitSpan propagated_dst = r_propagated_to_geometry_socket[dst_index]; - for (const bNodeLink *blink : bsocket->directly_linked_links()) { - if (blink->is_used()) { - const int src_index = blink->fromsock->index_in_tree(); - referenced_dst |= r_referenced_by_field_socket[src_index]; - propagated_dst |= r_propagated_to_geometry_socket[src_index]; - } - } - } - } - const aal::RelationsInNode &relations = *relations_by_node[bnode->index()]; - for (const aal::ReferenceRelation &relation : relations.reference_relations) { - const bNodeSocket &input_bsocket = bnode->input_socket(relation.from_field_input); - const bNodeSocket &output_bsocket = bnode->output_socket(relation.to_field_output); - if (!input_bsocket.is_available() || !output_bsocket.is_available()) { - continue; - } - r_referenced_by_field_socket[output_bsocket.index_in_tree()] |= - r_referenced_by_field_socket[input_bsocket.index_in_tree()]; - } - for (const aal::PropagateRelation &relation : relations.propagate_relations) { - const bNodeSocket &input_bsocket = bnode->input_socket(relation.from_geometry_input); - const bNodeSocket &output_bsocket = bnode->output_socket(relation.to_geometry_output); - if (!input_bsocket.is_available() || !output_bsocket.is_available()) { - continue; - } - r_propagated_to_geometry_socket[output_bsocket.index_in_tree()] |= - r_propagated_to_geometry_socket[input_bsocket.index_in_tree()]; - } - } - } - - /** - * Determines which anonymous attributes should be propagated to which geometry sockets. - */ - void gather_required_propagated_data( - const Span relations_by_node, - const VectorSet &attribute_reference_keys, - const BitGroupVector<> &referenced_by_field_socket, - const BitGroupVector<> &propagated_to_geometry_socket, - BitGroupVector<> &r_required_propagated_to_geometry_socket) - { - const aal::RelationsInNode &tree_relations = *btree_.runtime->anonymous_attribute_relations; - const int sockets_num = btree_.all_sockets().size(); - const int attribute_references_num = referenced_by_field_socket.group_size(); - BitGroupVector<> required_by_geometry_socket(sockets_num, attribute_references_num, false); - - /* Initialize required attributes at group output. */ - if (const bNode *group_output_bnode = btree_.group_output_node()) { - for (const aal::PropagateRelation &relation : tree_relations.propagate_relations) { - AttributeReferenceKey key; - key.type = AttributeReferenceKeyType::OutputGeometry; - key.index = relation.to_geometry_output; - const int key_index = attribute_reference_keys.index_of(key); - required_by_geometry_socket[group_output_bnode->input_socket(relation.to_geometry_output) - .index_in_tree()][key_index] - .set(); - } - for (const aal::AvailableRelation &relation : tree_relations.available_relations) { - const bNodeSocket &geometry_bsocket = group_output_bnode->input_socket( - relation.geometry_output); - const bNodeSocket &field_bsocket = group_output_bnode->input_socket(relation.field_output); - required_by_geometry_socket[geometry_bsocket.index_in_tree()] |= - referenced_by_field_socket[field_bsocket.index_in_tree()]; - } - } - - /* Propagate attribute usages from right to left. */ - BitVector<> required_attributes(attribute_references_num); - for (const bNode *bnode : btree_.toposort_right_to_left()) { - const aal::RelationsInNode &relations = *relations_by_node[bnode->index()]; - for (const bNodeSocket *bsocket : bnode->output_sockets()) { - if (!bsocket->is_available()) { - continue; - } - required_attributes.fill(false); - for (const bNodeLink *blink : bsocket->directly_linked_links()) { - if (blink->is_used()) { - const bNodeSocket &to_socket = *blink->tosock; - required_attributes |= required_by_geometry_socket[to_socket.index_in_tree()]; - } - } - required_attributes &= propagated_to_geometry_socket[bsocket->index_in_tree()]; - required_by_geometry_socket[bsocket->index_in_tree()] |= required_attributes; - bits::foreach_1_index(required_attributes, [&](const int key_index) { - const AttributeReferenceKey &key = attribute_reference_keys[key_index]; - if (key.type != AttributeReferenceKeyType::Socket || &key.bsocket->owner_node() != bnode) - { - r_required_propagated_to_geometry_socket[bsocket->index_in_tree()][key_index].set(); - } - }); - } - - for (const bNodeSocket *bsocket : bnode->input_sockets()) { - if (!bsocket->is_available()) { - continue; - } - required_attributes.fill(false); - for (const aal::PropagateRelation &relation : relations.propagate_relations) { - if (relation.from_geometry_input == bsocket->index()) { - const bNodeSocket &output_bsocket = bnode->output_socket(relation.to_geometry_output); - required_attributes |= required_by_geometry_socket[output_bsocket.index_in_tree()]; - } - } - for (const aal::EvalRelation &relation : relations.eval_relations) { - if (relation.geometry_input == bsocket->index()) { - const bNodeSocket &field_bsocket = bnode->input_socket(relation.field_input); - if (field_bsocket.is_available()) { - required_attributes |= referenced_by_field_socket[field_bsocket.index_in_tree()]; - } - } - } - required_attributes &= propagated_to_geometry_socket[bsocket->index_in_tree()]; - required_by_geometry_socket[bsocket->index_in_tree()] |= required_attributes; - } - } - } - - /** - * For every node that propagates attributes, prepare an attribute set containing information - * about which attributes should be propagated. - */ - void build_attribute_sets_to_propagate( - const Span attribute_reference_keys, - const Span attribute_reference_infos, - const BitGroupVector<> &required_propagated_to_geometry_socket) - { JoinAttibuteSetsCache join_attribute_sets_cache; - for (const auto [geometry_output_bsocket, lf_attribute_set_input] : + Map get_attributes_node_cache; + + auto add_get_attributes_node = [&](lf::OutputSocket &lf_field_socket) -> lf::OutputSocket & { + return *get_attributes_node_cache.lookup_or_add_cb(&lf_field_socket, [&]() { + const ValueOrFieldCPPType &type = *ValueOrFieldCPPType::get_from_self( + lf_field_socket.type()); + auto lazy_function = std::make_unique(type); + lf::Node &lf_node = lf_graph_->add_function(*lazy_function); + lf_graph_->add_link(lf_field_socket, lf_node.input(0)); + lf_graph_info_->functions.append(std::move(lazy_function)); + return &lf_node.output(0); + }); + }; + + for (const MapItem item : attribute_set_propagation_map_.items()) { - const BoundedBitSpan required = - required_propagated_to_geometry_socket[geometry_output_bsocket->index_in_tree()]; + const bNodeSocket &geometry_output_bsocket = *item.key; + lf::InputSocket &lf_attribute_set_input = *item.value; + + const BoundedBitSpan required_fields = + result.required_fields_by_geometry_socket[geometry_output_bsocket.index_in_tree()]; + const BoundedBitSpan required_output_propagations = + result.propagate_to_output_by_geometry_socket[geometry_output_bsocket.index_in_tree()]; Vector attribute_set_sockets; Vector used_sockets; - bits::foreach_1_index(required, [&](const int key_index) { - const AttributeReferenceKey &key = attribute_reference_keys[key_index]; - const AttributeReferenceInfo &info = attribute_reference_infos[key_index]; + bits::foreach_1_index(required_fields, [&](const int field_source_index) { + const FieldSource &field_source = result.all_field_sources[field_source_index]; lf::OutputSocket *lf_socket_usage = nullptr; - switch (key.type) { - case AttributeReferenceKeyType::InputField: { - lf_socket_usage = const_cast( - mapping_->group_input_usage_sockets[key.index]) - ->origin(); - break; - } - case AttributeReferenceKeyType::OutputGeometry: { - lf_socket_usage = const_cast( - mapping_->group_output_used_sockets[key.index]); - break; - } - case AttributeReferenceKeyType::Socket: { - lf_socket_usage = socket_is_used_map_[key.bsocket->index_in_tree()]; - break; + lf::OutputSocket *lf_attribute_set_socket = nullptr; + if (const auto *input_field = std::get_if(&field_source.data)) { + lf_socket_usage = const_cast( + mapping_->group_input_usage_sockets[input_field->input_index]) + ->origin(); + lf::OutputSocket *lf_field_socket = const_cast( + mapping_->group_input_sockets[input_field->input_index]); + lf_attribute_set_socket = &add_get_attributes_node(*lf_field_socket); + } + else { + const auto &socket_field = std::get(field_source.data); + if (&socket_field.socket->owner_node() == &geometry_output_bsocket.owner_node()) { + return; } + lf::OutputSocket *lf_field_socket = output_socket_map_.lookup(socket_field.socket); + lf_socket_usage = socket_is_used_map_[socket_field.socket->index_in_tree()]; + lf_attribute_set_socket = &add_get_attributes_node(*lf_field_socket); } if (lf_socket_usage) { - attribute_set_sockets.append(info.lf_attribute_set_socket); + attribute_set_sockets.append(lf_attribute_set_socket); + used_sockets.append(lf_socket_usage); + } + }); + bits::foreach_1_index(required_output_propagations, [&](const int i) { + const int output_geometry_index = result.propagated_output_geometry_indices[i]; + lf::OutputSocket *lf_socket_usage = const_cast( + mapping_->group_output_used_sockets[output_geometry_index]); + if (lf_socket_usage) { + lf::OutputSocket *lf_attribute_set_socket = const_cast( + mapping_->attribute_set_by_geometry_output.lookup(output_geometry_index)); + attribute_set_sockets.append(lf_attribute_set_socket); used_sockets.append(lf_socket_usage); } }); if (lf::OutputSocket *joined_attribute_set = this->join_attribute_sets( attribute_set_sockets, used_sockets, join_attribute_sets_cache)) { - lf_graph_->add_link(*joined_attribute_set, *lf_attribute_set_input); + lf_graph_->add_link(*joined_attribute_set, lf_attribute_set_input); } else { static const bke::AnonymousAttributeSet empty_set; - lf_attribute_set_input->set_default_value(&empty_set); + lf_attribute_set_input.set_default_value(&empty_set); } } } - using JoinAttibuteSetsCache = Map, lf::OutputSocket *>; - /** * Join multiple attributes set into a single attribute set that can be passed into a node. */