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. */