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:
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user