Fix #113773: repeat zone does not propagate anonymous attributes sometimes
The repeat zone needs some special treatment during anonymous attribute inferencing, because it propagates those attributes directly from the repeat input to the repeat output node and vice versa. Now the algorithm uses multiple passes if necessary to reach a stable inferencing result. Pull Request: https://projects.blender.org/blender/blender/pulls/113970
This commit is contained in:
@@ -7,12 +7,14 @@
|
||||
#include "BKE_node_runtime.hh"
|
||||
#include "BKE_node_tree_anonymous_attributes.hh"
|
||||
#include "BKE_node_tree_dot_export.hh"
|
||||
#include "BKE_node_tree_zones.hh"
|
||||
|
||||
#include "BLI_bit_group_vector.hh"
|
||||
#include "BLI_bit_span_ops.hh"
|
||||
|
||||
#include "BLI_resource_scope.hh"
|
||||
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
namespace blender::bke::anonymous_attribute_inferencing {
|
||||
@@ -199,6 +201,21 @@ class bNodeTreeToDotOptionsForAnonymousAttributeInferencing : public bNodeTreeTo
|
||||
}
|
||||
};
|
||||
|
||||
static bool or_into_each_other(MutableBoundedBitSpan a, MutableBoundedBitSpan b)
|
||||
{
|
||||
if (bits::spans_equal(a, b)) {
|
||||
return false;
|
||||
}
|
||||
a |= b;
|
||||
b |= a;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool or_into_each_other(BitGroupVector<> &vec, const int64_t a, const int64_t b)
|
||||
{
|
||||
return or_into_each_other(vec[a], vec[b]);
|
||||
}
|
||||
|
||||
static AnonymousAttributeInferencingResult analyse_anonymous_attribute_usages(
|
||||
const bNodeTree &tree)
|
||||
{
|
||||
@@ -208,6 +225,22 @@ static AnonymousAttributeInferencingResult analyse_anonymous_attribute_usages(
|
||||
ResourceScope scope;
|
||||
const Array<const aal::RelationsInNode *> relations_by_node = get_relations_by_node(tree, scope);
|
||||
|
||||
/* Repeat zones need some special behavior because they can propagate anonymous attributes from
|
||||
* right to left (from the repeat output to the repeat input node). */
|
||||
const bNodeTreeZones *zones = tree.zones();
|
||||
Vector<const bNodeTreeZone *> repeat_zones_to_consider;
|
||||
if (zones) {
|
||||
for (const std::unique_ptr<bNodeTreeZone> &zone : zones->zones) {
|
||||
if (ELEM(nullptr, zone->input_node, zone->output_node)) {
|
||||
continue;
|
||||
}
|
||||
if (zone->output_node->type != GEO_NODE_REPEAT_OUTPUT) {
|
||||
continue;
|
||||
}
|
||||
repeat_zones_to_consider.append(zone.get());
|
||||
}
|
||||
}
|
||||
|
||||
Vector<FieldSource> all_field_sources;
|
||||
Vector<GeometrySource> all_geometry_sources;
|
||||
|
||||
@@ -304,44 +337,72 @@ static AnonymousAttributeInferencingResult analyse_anonymous_attribute_usages(
|
||||
|
||||
/* 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];
|
||||
auto pass_left_to_right = [&]() {
|
||||
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 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];
|
||||
}
|
||||
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;
|
||||
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];
|
||||
}
|
||||
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];
|
||||
}
|
||||
};
|
||||
|
||||
while (true) {
|
||||
pass_left_to_right();
|
||||
|
||||
/* Repeat zones may need multiple inference passes. That's because anonymous attributes
|
||||
* propagated to a repeat output node also come out of the corresponding repeat input node. */
|
||||
bool changed = false;
|
||||
for (const bNodeTreeZone *zone : repeat_zones_to_consider) {
|
||||
const auto &storage = *static_cast<const NodeGeometryRepeatOutput *>(
|
||||
zone->output_node->storage);
|
||||
for (const int i : IndexRange(storage.items_num)) {
|
||||
const bNodeSocket &body_input_socket = zone->input_node->output_socket(i);
|
||||
const bNodeSocket &body_output_socket = zone->output_node->input_socket(i);
|
||||
const int in_index = body_input_socket.index_in_tree();
|
||||
const int out_index = body_output_socket.index_in_tree();
|
||||
|
||||
changed |= or_into_each_other(propagated_fields_by_socket, in_index, out_index);
|
||||
changed |= or_into_each_other(propagated_geometries_by_socket, in_index, out_index);
|
||||
changed |= or_into_each_other(available_fields_by_geometry_socket, in_index, out_index);
|
||||
}
|
||||
}
|
||||
if (!changed) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -350,8 +411,8 @@ static AnonymousAttributeInferencingResult analyse_anonymous_attribute_usages(
|
||||
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. */
|
||||
/* 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) {
|
||||
@@ -414,38 +475,64 @@ static AnonymousAttributeInferencingResult analyse_anonymous_attribute_usages(
|
||||
|
||||
/* 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 (!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->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];
|
||||
auto pass_right_to_left = [&]() {
|
||||
for (const bNode *node : tree.toposort_right_to_left()) {
|
||||
for (const bNodeSocket *socket : node->output_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->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];
|
||||
}
|
||||
}
|
||||
}
|
||||
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()];
|
||||
}
|
||||
}
|
||||
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];
|
||||
};
|
||||
|
||||
while (true) {
|
||||
pass_right_to_left();
|
||||
|
||||
/* Data required by a repeat input node will also be required by the repeat output node,
|
||||
* because that's where the data comes from after the first iteration. */
|
||||
bool changed = false;
|
||||
for (const bNodeTreeZone *zone : repeat_zones_to_consider) {
|
||||
const auto &storage = *static_cast<const NodeGeometryRepeatOutput *>(
|
||||
zone->output_node->storage);
|
||||
for (const int i : IndexRange(storage.items_num)) {
|
||||
const bNodeSocket &body_input_socket = zone->input_node->output_socket(i);
|
||||
const bNodeSocket &body_output_socket = zone->output_node->input_socket(i);
|
||||
const int in_index = body_input_socket.index_in_tree();
|
||||
const int out_index = body_output_socket.index_in_tree();
|
||||
|
||||
changed |= or_into_each_other(required_fields_by_geometry_socket, in_index, out_index);
|
||||
changed |= or_into_each_other(propagate_to_output_by_geometry_socket, in_index, out_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()];
|
||||
if (!changed) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -238,9 +238,23 @@ template<typename... BitSpanT> inline bool has_common_set_bits(const BitSpanT &.
|
||||
return any_set_expr([](const auto... x) { return (x & ...); }, args...);
|
||||
}
|
||||
|
||||
template<typename BitSpanT> inline bool any_bit_set(const BitSpanT &arg)
|
||||
{
|
||||
return has_common_set_bits(arg);
|
||||
}
|
||||
|
||||
template<typename BitSpanT, typename Fn> inline void foreach_1_index(const BitSpanT &data, Fn &&fn)
|
||||
{
|
||||
foreach_1_index_expr([](const BitInt x) { return x; }, fn, data);
|
||||
}
|
||||
|
||||
template<typename BitSpanT1, typename BitSpanT2>
|
||||
inline bool spans_equal(const BitSpanT1 &a, const BitSpanT2 &b)
|
||||
{
|
||||
if (a.size() != b.size()) {
|
||||
return false;
|
||||
}
|
||||
return !any_set_expr([](const BitInt a, const BitInt b) { return a ^ b; }, a, b);
|
||||
}
|
||||
|
||||
} // namespace blender::bits
|
||||
|
||||
Reference in New Issue
Block a user