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).
This commit is contained in:
Jacques Lucke
2023-06-14 13:56:57 +02:00
parent b169469016
commit 7b61dcf6bc
8 changed files with 498 additions and 747 deletions

View File

@@ -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<nodes::FieldInferencingInterface> field_inferencing_interface;
/** Information about usage of anonymous attributes within the group. */
std::unique_ptr<nodes::aal::RelationsInNode> anonymous_attribute_relations;
std::unique_ptr<anonymous_attribute_inferencing::AnonymousAttributeInferencingResult>
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<const nodes::aal::RelationsInNode *> get_relations_by_node(const bNodeTree &tree,
ResourceScope &scope);
bool update_anonymous_attribute_relations(bNodeTree &tree);
} // namespace anonymous_attribute_inferencing
} // namespace blender::bke
/* -------------------------------------------------------------------- */

View File

@@ -0,0 +1,80 @@
/* SPDX-FileCopyrightText: 2023 Blender Foundation
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include <variant>
#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<InputFieldSource, SocketFieldSource> data;
/** Geometry source which may contain the anonymous attributes referenced by this field. */
Vector<int> 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<InputGeometrySource, SocketGeometrySource> data;
/** Field sources that originate in this geometry source. */
Vector<int> field_sources;
};
struct AnonymousAttributeInferencingResult {
/** All field sockets that may introduce new anonymous attributes into the node tree. */
Vector<FieldSource> all_field_sources;
/** All geometry sockets that may introduce new anonymous attributes into the node tree. */
Vector<GeometrySource> 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<int> 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<const nodes::aal::RelationsInNode *> get_relations_by_node(const bNodeTree &tree,
ResourceScope &scope);
bool update_anonymous_attribute_relations(bNodeTree &tree);
} // namespace blender::bke::anonymous_attribute_inferencing

View File

@@ -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

View File

@@ -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<FieldInferencingInterface>(
*ntree_src->runtime->field_inferencing_interface);
}
if (ntree_src->runtime->anonymous_attribute_relations) {
dst_runtime.anonymous_attribute_relations =
std::make_unique<blender::nodes::anonymous_attribute_lifetime::RelationsInNode>(
*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<AnonymousAttributeInferencingResult>(
*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<SocketFieldSource>(&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<SocketGeometrySource>(&geometry_source.data))
{
socket_geometry_source->socket = socket_map.lookup(socket_geometry_source->socket);
}
}
}
if (flag & LIB_ID_COPY_NO_PREVIEW) {

View File

@@ -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<aal::RelationsInNode>();
}
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<const aal::RelationsInNode *> 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<int> find_linked_group_inputs(
const bNodeTree &tree,
const bNodeSocket &group_output_socket,
const FunctionRef<Vector<int>(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<const bNodeSocket *> found_sockets;
Stack<const bNodeSocket *> sockets_to_check;
BLI_assert(!tree.has_available_link_cycle());
Vector<int> input_indices;
ResourceScope scope;
const Array<const aal::RelationsInNode *> relations_by_node = get_relations_by_node(tree, scope);
found_sockets.add_new(&group_output_socket);
sockets_to_check.push(&group_output_socket);
Vector<FieldSource> all_field_sources;
Vector<GeometrySource> 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<const bNodeSocket *, int> field_source_by_socket;
Map<const bNodeSocket *, int> 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<InputFieldSource>(&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<SocketFieldSource>(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<InputGeometrySource>(&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<SocketGeometrySource>(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<int> 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<InputGeometrySource>(&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<SocketGeometrySource>(
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<InputFieldSource>(&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<const aal::RelationsInNode *> 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<int> input_indices = find_linked_group_inputs(
tree, *group_output_socket, [&](const bNodeSocket &output_socket) {
Vector<int> 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<const aal::RelationsInNode *> 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<int> input_indices = find_linked_group_inputs(
tree, *group_output_socket, [&](const bNodeSocket &output_socket) {
Vector<int> 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<Vector<int>> find_available_on_outputs(
const bNodeSocket &initial_group_output_socket,
const bNode &group_output_node,
const Span<const aal::RelationsInNode *> relations_by_node)
{
Set<const bNodeSocket *> geometry_sockets;
{
/* Find the nodes that added anonymous attributes to the field. */
Set<const bNodeSocket *> found_sockets;
Stack<const bNodeSocket *> 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<const bNodeSocket *> found_sockets;
Stack<const bNodeSocket *> 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<int> 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<const aal::RelationsInNode *> 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<Vector<int>> 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<int> find_eval_on_inputs(const bNodeTree &tree,
const int field_input_index,
const Span<const aal::RelationsInNode *> relations_by_node)
{
const Span<const bNode *> group_input_nodes = tree.group_input_nodes();
Set<const bNodeSocket *> geometry_sockets;
{
/* Find all the nodes that evaluate the input field. */
Set<const bNodeSocket *> found_sockets;
Stack<const bNodeSocket *> 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<InputFieldSource>(&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<const bNodeSocket *> found_sockets;
Stack<const bNodeSocket *> sockets_to_check;
Vector<int> 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<const aal::RelationsInNode *> 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<int> 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<const aal::RelationsInNode *> relations_by_node = get_relations_by_node(tree, scope);
std::unique_ptr<aal::RelationsInNode> new_relations = std::make_unique<aal::RelationsInNode>();
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<AnonymousAttributeInferencingResult>(std::move(result));
return group_interface_changed;
}

View File

@@ -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"

View File

@@ -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

View File

@@ -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<const bNodeSocket *> 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<int> 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<Vector<lf::OutputSocket *>, lf::OutputSocket *>;
void build_attribute_propagation_sets()
{
ResourceScope scope;
const Array<const aal::RelationsInNode *> relations_by_node =
bke::anonymous_attribute_inferencing::get_relations_by_node(btree_, scope);
using namespace bke::anonymous_attribute_inferencing;
VectorSet<AttributeReferenceKey> attribute_reference_keys;
/* Indexed by reference key index. */
Vector<AttributeReferenceInfo> 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<const aal::RelationsInNode *> relations_by_node,
VectorSet<AttributeReferenceKey> &r_attribute_reference_keys,
Vector<AttributeReferenceInfo> &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<LazyFunctionForAnonymousAttributeSetExtract>(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<lf::OutputSocket *>(
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<lf::OutputSocket *>(
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<const aal::RelationsInNode *> relations_by_node,
const Span<AttributeReferenceKey> attribute_reference_keys,
const Span<AttributeReferenceInfo> 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<const aal::RelationsInNode *> relations_by_node,
const VectorSet<AttributeReferenceKey> &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<AttributeReferenceKey> attribute_reference_keys,
const Span<AttributeReferenceInfo> 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<lf::OutputSocket *, lf::OutputSocket *> 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<LazyFunctionForAnonymousAttributeSetExtract>(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<const bNodeSocket *, lf::InputSocket *> 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<lf::OutputSocket *> attribute_set_sockets;
Vector<lf::OutputSocket *> 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<lf::InputSocket *>(
mapping_->group_input_usage_sockets[key.index])
->origin();
break;
}
case AttributeReferenceKeyType::OutputGeometry: {
lf_socket_usage = const_cast<lf::OutputSocket *>(
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<InputFieldSource>(&field_source.data)) {
lf_socket_usage = const_cast<lf::InputSocket *>(
mapping_->group_input_usage_sockets[input_field->input_index])
->origin();
lf::OutputSocket *lf_field_socket = const_cast<lf::OutputSocket *>(
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<SocketFieldSource>(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<lf::OutputSocket *>(
mapping_->group_output_used_sockets[output_geometry_index]);
if (lf_socket_usage) {
lf::OutputSocket *lf_attribute_set_socket = const_cast<lf::OutputSocket *>(
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<Vector<lf::OutputSocket *>, lf::OutputSocket *>;
/**
* Join multiple attributes set into a single attribute set that can be passed into a node.
*/