BLI: refactor IndexMask for better performance and memory usage #104629

Merged
Jacques Lucke merged 254 commits from JacquesLucke/blender:index-mask-refactor into main 2023-05-24 18:11:47 +02:00
3 changed files with 146 additions and 0 deletions
Showing only changes of commit eae30305d8 - Show all commits

View File

@ -69,6 +69,64 @@ struct IndexMaskSegment {
OffsetSpan<int64_t, int16_t> indices;
};
struct Expr {
enum class Type {
Atomic,
Union,
Difference,
Complement,
Intersection,
};
Type type;
Expr(const Type type) : type(type)
{
}
};
struct AtomicExpr : public Expr {
const IndexMask *mask;
AtomicExpr(const IndexMask &mask) : Expr(Type::Atomic), mask(&mask)
{
}
};
struct UnionExpr : public Expr {
Vector<const Expr *> children;
UnionExpr(Vector<const Expr *> children) : Expr(Type::Union), children(std::move(children))
{
}
};
struct DifferenceExpr : public Expr {
const Expr *base = nullptr;
Vector<const Expr *> children;
DifferenceExpr(const Expr &base, Vector<const Expr *> children)
: Expr(Type::Difference), base(&base), children(std::move(children))
{
}
};
struct ComplementExpr : public Expr {
const Expr *base = nullptr;
ComplementExpr(const Expr &base) : Expr(Type::Complement), base(&base)
{
}
};
struct IntersectionExpr : public Expr {
Vector<const Expr *> children;
IntersectionExpr(Vector<const Expr *> children)
: Expr(Type::Intersection), children(std::move(children))
{
}
};
class IndexMask {
private:
IndexMaskData data_;
@ -98,6 +156,7 @@ class IndexMask {
template<typename T>
static IndexMask from_indices(Span<T> indices, LinearAllocator<> &allocator);
static IndexMask from_bits(BitSpan bits, LinearAllocator<> &allocator, int64_t offset = 0);
static IndexMask from_expr(const Expr &expr, IndexRange universe, LinearAllocator<> &allocator);
template<typename T> void to_indices(MutableSpan<T> r_indices) const;
void to_bits(MutableBitSpan r_bits, int64_t offset = 0) const;

View File

@ -3,6 +3,7 @@
#include "BLI_array.hh"
#include "BLI_enumerable_thread_specific.hh"
#include "BLI_index_mask2.hh"
#include "BLI_set.hh"
#include "BLI_strict_flags.h"
#include "BLI_task.hh"
#include "BLI_timeit.hh"
@ -363,6 +364,74 @@ IndexMask IndexMask::from_bits(const BitSpan bits,
return bits_to_index_mask(bits, offset, allocator);
}
static Set<int64_t> eval_expr(const Expr &base_expr, const IndexRange universe)
{
Set<int64_t> result;
switch (base_expr.type) {
case Expr::Type::Atomic: {
const AtomicExpr &expr = static_cast<const AtomicExpr &>(base_expr);
expr.mask->foreach_index([&](const int64_t i) {
BLI_assert(universe.contains(i));
result.add_new(i);
});
break;
}
case Expr::Type::Union: {
const UnionExpr &expr = static_cast<const UnionExpr &>(base_expr);
for (const Expr *child : expr.children) {
const Set<int64_t> child_result = eval_expr(*child, universe);
for (const int64_t i : child_result) {
result.add(i);
}
}
break;
}
case Expr::Type::Difference: {
const DifferenceExpr &expr = static_cast<const DifferenceExpr &>(base_expr);
result = eval_expr(*expr.base, universe);
for (const Expr *child : expr.children) {
const Set<int64_t> child_result = eval_expr(*child, universe);
for (const int64_t i : child_result) {
result.remove(i);
}
}
break;
}
case Expr::Type::Complement: {
const ComplementExpr &expr = static_cast<const ComplementExpr &>(base_expr);
const Set<int64_t> child_result = eval_expr(*expr.base, universe);
for (const int64_t i : universe) {
if (!child_result.contains(i)) {
result.add_new(i);
}
}
break;
}
case Expr::Type::Intersection: {
const IntersectionExpr &expr = static_cast<const IntersectionExpr &>(base_expr);
BLI_assert(!expr.children.is_empty());
result = eval_expr(*expr.children.first(), universe);
for (const Expr *child : expr.children.as_span().drop_front(1)) {
const Set<int64_t> child_result = eval_expr(*child, universe);
result.remove_if([&](const int64_t i) { return !child_result.contains(i); });
}
break;
}
}
return result;
}
IndexMask IndexMask::from_expr(const Expr &expr,
const IndexRange universe,
LinearAllocator<> &allocator)
{
const Set<int64_t> indices_set = eval_expr(expr, universe);
Vector<int64_t> indices;
indices.extend(indices_set.begin(), indices_set.end());
std::sort(indices.begin(), indices.end());
return IndexMask::from_indices<int64_t>(indices, allocator);
}
template<typename T> void IndexMask::to_indices(MutableSpan<T> r_indices) const
{
unique_sorted_indices::from_index_mask(*this, r_indices);

View File

@ -198,4 +198,22 @@ TEST(index_mask2, ForeachRange)
EXPECT_EQ(ranges[2], IndexRange(40, 2));
}
TEST(index_mask2, Expr)
{
LinearAllocator<> allocator;
const IndexMask mask1(IndexRange(10, 5));
const IndexMask mask2(IndexRange(40, 5));
const IndexMask mask3 = IndexMask::from_indices<int>({12, 13, 20, 21, 22}, allocator);
const AtomicExpr expr1{mask1};
const AtomicExpr expr2{mask2};
const AtomicExpr expr3{mask3};
const UnionExpr union_expr({&expr1, &expr2});
const DifferenceExpr difference_expr(union_expr, {&expr3});
const IndexMask result = IndexMask::from_expr(difference_expr, IndexRange(100), allocator);
std::cout << result << "\n";
}
} // namespace blender::index_mask::tests