Fix #114041: freeze connecting capture attribute to repeat output node

The issue was that a node was supposed to propagate an anonymous
attribute that is only created further to the right in the tree. This does not
during inferencing, where uses of fields can only come to the right of its
creation. Note, all fields coming out of the repeat input/output node are
new field sources during inferencing.

Now, only field sources that are passed from the outside into the repeat zone
can be propagated from the repeat output to the repeat input node.

Solving this also showed another issue where anonymous attributes are
not properly propagated through a repeat zone where there is no link between
the repeat input and output node. In such cases, data is still propagated between
those two nodes when the number of iterations is zero.
This commit is contained in:
Jacques Lucke
2023-10-23 21:26:57 +02:00
parent 009c4b819c
commit 312d8b1d66
2 changed files with 83 additions and 3 deletions

View File

@@ -201,6 +201,18 @@ class bNodeTreeToDotOptionsForAnonymousAttributeInferencing : public bNodeTreeTo
}
};
static bool or_into_each_other_masked(MutableBoundedBitSpan a,
MutableBoundedBitSpan b,
const BoundedBitSpan mask)
{
if (bits::spans_equal_masked(a, b, mask)) {
return false;
}
bits::inplace_or_masked(a, mask, b);
bits::inplace_or_masked(b, mask, a);
return true;
}
static bool or_into_each_other(MutableBoundedBitSpan a, MutableBoundedBitSpan b)
{
if (bits::spans_equal(a, b)) {
@@ -211,6 +223,14 @@ static bool or_into_each_other(MutableBoundedBitSpan a, MutableBoundedBitSpan b)
return true;
}
static bool or_into_each_other_masked(BitGroupVector<> &vec,
const int64_t a,
const int64_t b,
const BoundedBitSpan mask)
{
return or_into_each_other_masked(vec[a], vec[b], mask);
}
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]);
@@ -378,6 +398,24 @@ static AnonymousAttributeInferencingResult analyse_anonymous_attribute_usages(
available_fields_by_geometry_socket[dst_index] |=
available_fields_by_geometry_socket[src_index];
}
if (node->type == GEO_NODE_REPEAT_OUTPUT && zones) {
/* If the amount of iterations is zero, the data is directly forwarded from the Repeat
* Input to the Repeat Output node. Therefor, all anonymous attributes may be propagated as
* well. */
const bNodeTreeZone *zone = zones->get_zone_by_node(node->identifier);
if (const bNode *input_node = zone->input_node) {
const int items_num = node->output_sockets().size();
for (const int i : IndexRange(items_num)) {
const int src_index = input_node->input_socket(i + 1).index_in_tree();
const int dst_index = node->output_socket(i).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];
}
}
}
}
};
@@ -390,15 +428,33 @@ static AnonymousAttributeInferencingResult analyse_anonymous_attribute_usages(
for (const bNodeTreeZone *zone : repeat_zones_to_consider) {
const auto &storage = *static_cast<const NodeGeometryRepeatOutput *>(
zone->output_node->storage);
/* Only field and geometry sources that come before the repeat zone, can be propagated from
* the repeat output to the repeat input node. Otherwise, a socket can depend on the field
* source that only comes later in the tree, which leads to a cyclic dependency. */
BitVector<> input_propagated_fields(all_field_sources.size(), false);
BitVector<> input_propagated_geometries(all_geometry_sources.size(), false);
for (const bNodeSocket *socket : zone->input_node->input_sockets()) {
const int src = socket->index_in_tree();
input_propagated_fields |= propagated_fields_by_socket[src];
input_propagated_geometries |= propagated_geometries_by_socket[src];
}
for (const bNodeLink *link : zone->border_links) {
const int src = link->fromsock->index_in_tree();
input_propagated_fields |= propagated_fields_by_socket[src];
input_propagated_geometries |= propagated_geometries_by_socket[src];
}
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);
changed |= or_into_each_other_masked(
propagated_fields_by_socket, in_index, out_index, input_propagated_fields);
changed |= or_into_each_other_masked(
propagated_geometries_by_socket, in_index, out_index, input_propagated_geometries);
changed |= or_into_each_other_masked(
available_fields_by_geometry_socket, in_index, out_index, input_propagated_fields);
}
}
if (!changed) {

View File

@@ -196,6 +196,18 @@ inline void inplace_or(FirstBitSpanT &first_arg, const BitSpanT &...args)
mix_into_first_expr([](const auto... x) { return (x | ...); }, first_arg, args...);
}
template<typename FirstBitSpanT, typename MaskBitSpanT, typename... BitSpanT>
inline void inplace_or_masked(FirstBitSpanT &first_arg,
const MaskBitSpanT &mask,
const BitSpanT &...args)
{
mix_into_first_expr(
[](const BitInt a, const BitInt mask, const auto... x) { return a | ((x | ...) & mask); },
first_arg,
mask,
args...);
}
template<typename FirstBitSpanT, typename... BitSpanT>
inline void copy_from_or(FirstBitSpanT &first_arg, const BitSpanT &...args)
{
@@ -257,4 +269,16 @@ inline bool spans_equal(const BitSpanT1 &a, const BitSpanT2 &b)
return !any_set_expr([](const BitInt a, const BitInt b) { return a ^ b; }, a, b);
}
template<typename BitSpanT1, typename BitSpanT2, typename BitSpanT3>
inline bool spans_equal_masked(const BitSpanT1 &a, const BitSpanT2 &b, const BitSpanT3 &mask)
{
BLI_assert(mask.size() == a.size());
BLI_assert(mask.size() == b.size());
return !bits::any_set_expr(
[](const BitInt a, const BitInt b, const BitInt mask) { return (a ^ b) & mask; },
a,
b,
mask);
}
} // namespace blender::bits