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.
285 lines
9.7 KiB
C++
285 lines
9.7 KiB
C++
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
|
|
#pragma once
|
|
|
|
#include "BLI_bit_span.hh"
|
|
|
|
namespace blender::bits {
|
|
|
|
namespace detail {
|
|
|
|
/**
|
|
* Evaluates the expression on one or more bit spans and stores the result in the first.
|
|
*
|
|
* The expected type for the expression is:
|
|
* (BitInt ...one_or_more_args) -> BitInt
|
|
*/
|
|
template<typename ExprFn, typename FirstBitSpanT, typename... BitSpanT>
|
|
inline void mix_into_first_expr(ExprFn &&expr,
|
|
const FirstBitSpanT &first_arg,
|
|
const BitSpanT &...args)
|
|
{
|
|
const int64_t size = first_arg.size();
|
|
BLI_assert(((size == args.size()) && ...));
|
|
if (size == 0) {
|
|
return;
|
|
}
|
|
|
|
if constexpr (all_bounded_spans<FirstBitSpanT, BitSpanT...>) {
|
|
BitInt *first_data = first_arg.data();
|
|
const int64_t first_offset = first_arg.offset();
|
|
const int64_t full_ints_num = first_arg.full_ints_num();
|
|
/* Compute expression without any masking, all the spans are expected to be aligned to the
|
|
* beginning of a #BitInt. */
|
|
for (const int64_t i : IndexRange(full_ints_num)) {
|
|
first_data[i] = expr(first_data[i], args.data()[i]...);
|
|
}
|
|
/* Compute expression for the remaining bits. */
|
|
if (const int64_t final_bits = first_arg.final_bits_num()) {
|
|
const BitInt result = expr(first_data[full_ints_num] >> first_offset,
|
|
(args.data()[full_ints_num] >> args.offset())...);
|
|
const BitInt mask = mask_range_bits(first_offset, final_bits);
|
|
first_data[full_ints_num] = ((result << first_offset) & mask) |
|
|
(first_data[full_ints_num] & ~mask);
|
|
}
|
|
}
|
|
else {
|
|
/* Fallback or arbitrary bit spans. This could be implemented more efficiently but adds more
|
|
* complexity and is not necessary yet. */
|
|
for (const int64_t i : IndexRange(size)) {
|
|
const bool result = expr(BitInt(first_arg[i].test()), BitInt(args[i].test())...) != 0;
|
|
first_arg[i].set(result);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Evaluates the expression on one or more bit spans and returns true when the result contains a 1
|
|
* anywhere.
|
|
*
|
|
* The expected type for the expression is:
|
|
* (BitInt ...one_or_more_args) -> BitInt
|
|
*/
|
|
template<typename ExprFn, typename FirstBitSpanT, typename... BitSpanT>
|
|
inline bool any_set_expr(ExprFn &&expr, const FirstBitSpanT &first_arg, const BitSpanT &...args)
|
|
{
|
|
const int64_t size = first_arg.size();
|
|
BLI_assert(((size == args.size()) && ...));
|
|
if (size == 0) {
|
|
return false;
|
|
}
|
|
|
|
if constexpr (all_bounded_spans<FirstBitSpanT, BitSpanT...>) {
|
|
const BitInt *first_data = first_arg.data();
|
|
const int64_t full_ints_num = first_arg.full_ints_num();
|
|
/* Compute expression without any masking, all the spans are expected to be aligned to the
|
|
* beginning of a #BitInt. */
|
|
for (const int64_t i : IndexRange(full_ints_num)) {
|
|
if (expr(first_data[i], args.data()[i]...) != 0) {
|
|
return true;
|
|
}
|
|
}
|
|
/* Compute expression for the remaining bits. */
|
|
if (const int64_t final_bits = first_arg.final_bits_num()) {
|
|
const BitInt result = expr(first_data[full_ints_num] >> first_arg.offset(),
|
|
(args.data()[full_ints_num] >> args.offset())...);
|
|
const BitInt mask = mask_first_n_bits(final_bits);
|
|
if ((result & mask) != 0) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
else {
|
|
/* Fallback or arbitrary bit spans. This could be implemented more efficiently but adds more
|
|
* complexity and is not necessary yet. */
|
|
for (const int64_t i : IndexRange(size)) {
|
|
const BitInt result = expr(BitInt(first_arg[i].test()), BitInt(args[i].test())...);
|
|
if (result != 0) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Evaluates the expression on one or more bit spans and calls the `handle` function for each bit
|
|
* index where the result is 1.
|
|
*
|
|
* The expected type for the expression is:
|
|
* (BitInt ...one_or_more_args) -> BitInt
|
|
*/
|
|
template<typename ExprFn, typename HandleFn, typename FirstBitSpanT, typename... BitSpanT>
|
|
inline void foreach_1_index_expr(ExprFn &&expr,
|
|
HandleFn &&handle,
|
|
const FirstBitSpanT &first_arg,
|
|
const BitSpanT &...args)
|
|
{
|
|
const int64_t size = first_arg.size();
|
|
BLI_assert(((size == args.size()) && ...));
|
|
if (size == 0) {
|
|
return;
|
|
}
|
|
|
|
if constexpr (all_bounded_spans<FirstBitSpanT, BitSpanT...>) {
|
|
const BitInt *first_data = first_arg.data();
|
|
const int64_t full_ints_num = first_arg.full_ints_num();
|
|
/* Iterate over full ints without any bit masks. */
|
|
for (const int64_t int_i : IndexRange(full_ints_num)) {
|
|
BitInt tmp = expr(first_data[int_i], args.data()[int_i]...);
|
|
const int64_t offset = int_i << BitToIntIndexShift;
|
|
while (tmp != 0) {
|
|
static_assert(std::is_same_v<BitInt, uint64_t>);
|
|
const int index = bitscan_forward_uint64(tmp);
|
|
handle(index + offset);
|
|
tmp &= ~mask_single_bit(index);
|
|
}
|
|
}
|
|
/* Iterate over remaining bits. */
|
|
if (const int64_t final_bits = first_arg.final_bits_num()) {
|
|
BitInt tmp = expr(first_data[full_ints_num] >> first_arg.offset(),
|
|
(*args.data()[full_ints_num] >> args.offset())...) &
|
|
mask_first_n_bits(final_bits);
|
|
const int64_t offset = full_ints_num << BitToIntIndexShift;
|
|
while (tmp != 0) {
|
|
static_assert(std::is_same_v<BitInt, uint64_t>);
|
|
const int index = bitscan_forward_uint64(tmp);
|
|
handle(index + offset);
|
|
tmp &= ~mask_single_bit(index);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
/* Fallback or arbitrary bit spans. This could be implemented more efficiently but adds more
|
|
* complexity and is not necessary yet. */
|
|
for (const int64_t i : IndexRange(size)) {
|
|
const BitInt result = expr(BitInt(first_arg[i].test()), BitInt(args[i].test())...);
|
|
if (result) {
|
|
handle(i);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
} // namespace detail
|
|
|
|
template<typename ExprFn, typename FirstBitSpanT, typename... BitSpanT>
|
|
inline void mix_into_first_expr(ExprFn &&expr,
|
|
const FirstBitSpanT &first_arg,
|
|
const BitSpanT &...args)
|
|
{
|
|
detail::mix_into_first_expr(expr, to_best_bit_span(first_arg), to_best_bit_span(args)...);
|
|
}
|
|
|
|
template<typename ExprFn, typename FirstBitSpanT, typename... BitSpanT>
|
|
inline bool any_set_expr(ExprFn &&expr, const FirstBitSpanT &first_arg, const BitSpanT &...args)
|
|
{
|
|
return detail::any_set_expr(expr, to_best_bit_span(first_arg), to_best_bit_span(args)...);
|
|
}
|
|
|
|
template<typename ExprFn, typename HandleFn, typename FirstBitSpanT, typename... BitSpanT>
|
|
inline void foreach_1_index_expr(ExprFn &&expr,
|
|
HandleFn &&handle,
|
|
const FirstBitSpanT &first_arg,
|
|
const BitSpanT &...args)
|
|
{
|
|
detail::foreach_1_index_expr(
|
|
expr, handle, to_best_bit_span(first_arg), to_best_bit_span(args)...);
|
|
}
|
|
|
|
template<typename FirstBitSpanT, typename... BitSpanT>
|
|
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)
|
|
{
|
|
mix_into_first_expr(
|
|
[](auto /*first*/, auto... rest) { return (rest | ...); }, first_arg, args...);
|
|
}
|
|
|
|
template<typename FirstBitSpanT, typename... BitSpanT>
|
|
inline void inplace_and(FirstBitSpanT &first_arg, const BitSpanT &...args)
|
|
{
|
|
mix_into_first_expr([](const auto... x) { return (x & ...); }, first_arg, args...);
|
|
}
|
|
|
|
template<typename... BitSpanT>
|
|
inline void operator|=(MutableBitSpan first_arg, const BitSpanT &...args)
|
|
{
|
|
inplace_or(first_arg, args...);
|
|
}
|
|
|
|
template<typename... BitSpanT>
|
|
inline void operator|=(MutableBoundedBitSpan first_arg, const BitSpanT &...args)
|
|
{
|
|
inplace_or(first_arg, args...);
|
|
}
|
|
|
|
template<typename... BitSpanT>
|
|
inline void operator&=(MutableBitSpan first_arg, const BitSpanT &...args)
|
|
{
|
|
inplace_and(first_arg, args...);
|
|
}
|
|
|
|
template<typename... BitSpanT>
|
|
inline void operator&=(MutableBoundedBitSpan first_arg, const BitSpanT &...args)
|
|
{
|
|
inplace_and(first_arg, args...);
|
|
}
|
|
|
|
template<typename... BitSpanT> inline bool has_common_set_bits(const BitSpanT &...args)
|
|
{
|
|
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);
|
|
}
|
|
|
|
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
|