diff options
13 files changed, 1369 insertions, 144 deletions
diff --git a/source/blender/blenlib/BLI_span.hh b/source/blender/blenlib/BLI_span.hh index fe511793c46..c32ba0826df 100644 --- a/source/blender/blenlib/BLI_span.hh +++ b/source/blender/blenlib/BLI_span.hh @@ -94,7 +94,7 @@ template<typename T> class Span { using iterator = const T *; using size_type = int64_t; - private: + protected: const T *data_ = nullptr; int64_t size_ = 0; @@ -477,7 +477,7 @@ template<typename T> class MutableSpan { using iterator = T *; using size_type = int64_t; - private: + protected: T *data_; int64_t size_; diff --git a/source/blender/blenlib/BLI_virtual_array.hh b/source/blender/blenlib/BLI_virtual_array.hh index f9b0aaa7de6..3868f5acae9 100644 --- a/source/blender/blenlib/BLI_virtual_array.hh +++ b/source/blender/blenlib/BLI_virtual_array.hh @@ -37,6 +37,7 @@ * see of the increased compile time and binary size is worth it. */ +#include "BLI_array.hh" #include "BLI_span.hh" namespace blender { @@ -71,6 +72,11 @@ template<typename T> class VArray { return size_ == 0; } + IndexRange index_range() const + { + return IndexRange(size_); + } + /* Returns true when the virtual array is stored as a span internally. */ bool is_span() const { @@ -82,13 +88,13 @@ template<typename T> class VArray { /* Returns the internally used span of the virtual array. This invokes undefined behavior is the * virtual array is not stored as a span internally. */ - Span<T> get_span() const + Span<T> get_internal_span() const { BLI_assert(this->is_span()); if (size_ == 0) { return {}; } - return this->get_span_impl(); + return this->get_internal_span_impl(); } /* Returns true when the virtual array returns the same value for every index. */ @@ -102,20 +108,35 @@ template<typename T> class VArray { /* Returns the value that is returned for every index. This invokes undefined behavior if the * virtual array would not return the same value for every index. */ - T get_single() const + T get_internal_single() const { BLI_assert(this->is_single()); if (size_ == 1) { return this->get(0); } - return this->get_single_impl(); + return this->get_internal_single_impl(); } + /* Get the element at a specific index. Note that this operator cannot be used to assign values + * to an index, because the return value is not a reference. */ T operator[](const int64_t index) const { return this->get(index); } + /* Copy the entire virtual array into a span. */ + void materialize(MutableSpan<T> r_span) const + { + BLI_assert(size_ == r_span.size()); + this->materialize_impl(r_span); + } + + void materialize_to_uninitialized(MutableSpan<T> r_span) const + { + BLI_assert(size_ == r_span.size()); + this->materialize_to_uninitialized_impl(r_span); + } + protected: virtual T get_impl(const int64_t index) const = 0; @@ -124,7 +145,7 @@ template<typename T> class VArray { return false; } - virtual Span<T> get_span_impl() const + virtual Span<T> get_internal_span_impl() const { BLI_assert_unreachable(); return {}; @@ -135,56 +156,198 @@ template<typename T> class VArray { return false; } - virtual T get_single_impl() const + virtual T get_internal_single_impl() const { /* Provide a default implementation, so that subclasses don't have to provide it. This method * should never be called because `is_single_impl` returns false by default. */ BLI_assert_unreachable(); return T(); } + + virtual void materialize_impl(MutableSpan<T> r_span) const + { + if (this->is_span()) { + const Span<T> span = this->get_internal_span(); + initialized_copy_n(span.data(), size_, r_span.data()); + } + else if (this->is_single()) { + const T single = this->get_internal_single(); + initialized_fill_n(r_span.data(), size_, single); + } + else { + const int64_t size = size_; + for (int64_t i = 0; i < size; i++) { + r_span[i] = this->get(i); + } + } + } + + virtual void materialize_to_uninitialized_impl(MutableSpan<T> r_span) const + { + if (this->is_span()) { + const Span<T> span = this->get_internal_span(); + uninitialized_copy_n(span.data(), size_, r_span.data()); + } + else if (this->is_single()) { + const T single = this->get_internal_single(); + uninitialized_fill_n(r_span.data(), size_, single); + } + else { + const int64_t size = size_; + T *dst = r_span.data(); + for (int64_t i = 0; i < size; i++) { + new (dst + i) T(this->get(i)); + } + } + } +}; + +/* Similar to VArray, but the elements are mutable. */ +template<typename T> class VMutableArray : public VArray<T> { + public: + VMutableArray(const int64_t size) : VArray<T>(size) + { + } + + void set(const int64_t index, T value) + { + BLI_assert(index >= 0); + BLI_assert(index < this->size_); + this->set_impl(index, std::move(value)); + } + + /* Copy the values from the source span to all elements in the virtual array. */ + void set_all(Span<T> src) + { + BLI_assert(src.size() == this->size_); + this->set_all_impl(src); + } + + MutableSpan<T> get_internal_span() + { + BLI_assert(this->is_span()); + Span<T> span = static_cast<const VArray<T> *>(this)->get_internal_span(); + return MutableSpan<T>(const_cast<T *>(span.data()), span.size()); + } + + protected: + virtual void set_impl(const int64_t index, T value) = 0; + + virtual void set_all_impl(Span<T> src) + { + if (this->is_span()) { + const MutableSpan<T> span = this->get_internal_span(); + initialized_copy_n(src.data(), this->size_, span.data()); + } + else { + const int64_t size = this->size_; + for (int64_t i = 0; i < size; i++) { + this->set(i, src[i]); + } + } + } }; /** - * A virtual array implementation for a span. This class is final so that it can be devirtualized - * by the compiler in some cases (e.g. when #devirtualize_varray is used). + * A virtual array implementation for a span. Methods in this class are final so that it can be + * devirtualized by the compiler in some cases (e.g. when #devirtualize_varray is used). */ -template<typename T> class VArrayForSpan final : public VArray<T> { - private: - const T *data_; +template<typename T> class VArray_For_Span : public VArray<T> { + protected: + const T *data_ = nullptr; public: - VArrayForSpan(const Span<T> data) : VArray<T>(data.size()), data_(data.data()) + VArray_For_Span(const Span<T> data) : VArray<T>(data.size()), data_(data.data()) { } protected: - T get_impl(const int64_t index) const override + VArray_For_Span(const int64_t size) : VArray<T>(size) + { + } + + T get_impl(const int64_t index) const final + { + return data_[index]; + } + + bool is_span_impl() const final + { + return true; + } + + Span<T> get_internal_span_impl() const final + { + return Span<T>(data_, this->size_); + } +}; + +template<typename T> class VMutableArray_For_MutableSpan : public VMutableArray<T> { + protected: + T *data_ = nullptr; + + public: + VMutableArray_For_MutableSpan(const MutableSpan<T> data) + : VMutableArray<T>(data.size()), data_(data.data()) + { + } + + protected: + VMutableArray_For_MutableSpan(const int64_t size) : VMutableArray<T>(size) + { + } + + T get_impl(const int64_t index) const final { return data_[index]; } + void set_impl(const int64_t index, T value) final + { + data_[index] = value; + } + bool is_span_impl() const override { return true; } - Span<T> get_span_impl() const override + Span<T> get_internal_span_impl() const override { return Span<T>(data_, this->size_); } }; /** + * A variant of `VArray_For_Span` that owns the underlying data. + * The `Container` type has to implement a `size()` and `data()` method. + * The `data()` method has to return a pointer to the first element in the continuous array of + * elements. + */ +template<typename Container, typename T = typename Container::value_type> +class VArray_For_ArrayContainer : public VArray_For_Span<T> { + private: + Container container_; + + public: + VArray_For_ArrayContainer(Container container) + : VArray_For_Span<T>((int64_t)container.size()), container_(std::move(container)) + { + this->data_ = container_.data(); + } +}; + +/** * A virtual array implementation that returns the same value for every index. This class is final * so that it can be devirtualized by the compiler in some cases (e.g. when #devirtualize_varray is * used). */ -template<typename T> class VArrayForSingle final : public VArray<T> { +template<typename T> class VArray_For_Single final : public VArray<T> { private: T value_; public: - VArrayForSingle(T value, const int64_t size) : VArray<T>(size), value_(std::move(value)) + VArray_For_Single(T value, const int64_t size) : VArray<T>(size), value_(std::move(value)) { } @@ -199,7 +362,7 @@ template<typename T> class VArrayForSingle final : public VArray<T> { return this->size_ == 1; } - Span<T> get_span_impl() const override + Span<T> get_internal_span_impl() const override { return Span<T>(&value_, 1); } @@ -209,13 +372,171 @@ template<typename T> class VArrayForSingle final : public VArray<T> { return true; } - T get_single_impl() const override + T get_internal_single_impl() const override { return value_; } }; /** + * In many cases a virtual array is a span internally. In those cases, access to individual could + * be much more efficient than calling a virtual method. When the underlying virtual array is not a + * span, this class allocates a new array and copies the values over. + * + * This should be used in those cases: + * - All elements in the virtual array are accessed multiple times. + * - In most cases, the underlying virtual array is a span, so no copy is necessary to benefit + * from faster access. + * - An API is called, that does not accept virtual arrays, but only spans. + */ +template<typename T> class VArray_Span final : public Span<T> { + private: + const VArray<T> &varray_; + Array<T> owned_data_; + + public: + VArray_Span(const VArray<T> &varray) : Span<T>(), varray_(varray) + { + this->size_ = varray_.size(); + if (varray_.is_span()) { + this->data_ = varray_.get_internal_span().data(); + } + else { + owned_data_.~Array(); + new (&owned_data_) Array<T>(varray_.size(), NoInitialization{}); + varray_.materialize_to_uninitialized(owned_data_); + this->data_ = owned_data_.data(); + } + } +}; + +/** + * Same as VArray_Span, but for a mutable span. + * The important thing to note is that when changing this span, the results might not be + * immediately reflected in the underlying virtual array (only when the virtual array is a span + * internally). The #save method can be used to write all changes to the underlying virtual array, + * if necessary. + */ +template<typename T> class VMutableArray_Span final : public MutableSpan<T> { + private: + VMutableArray<T> &varray_; + Array<T> owned_data_; + bool save_has_been_called_ = false; + bool show_not_saved_warning_ = true; + + public: + /* Create a span for any virtual array. This is cheap when the virtual array is a span itself. If + * not, a new array has to be allocated as a wrapper for the underlying virtual array. */ + VMutableArray_Span(VMutableArray<T> &varray, const bool copy_values_to_span = true) + : MutableSpan<T>(), varray_(varray) + { + this->size_ = varray_.size(); + if (varray_.is_span()) { + this->data_ = varray_.get_internal_span().data(); + } + else { + if (copy_values_to_span) { + owned_data_.~Array(); + new (&owned_data_) Array<T>(varray_.size(), NoInitialization{}); + varray_.materialize_to_uninitialized(owned_data_); + } + else { + owned_data_.reinitialize(varray_.size()); + } + this->data_ = owned_data_.data(); + } + } + + ~VMutableArray_Span() + { + if (show_not_saved_warning_) { + if (!save_has_been_called_) { + std::cout << "Warning: Call `save()` to make sure that changes persist in all cases.\n"; + } + } + } + + /* Write back all values from a temporary allocated array to the underlying virtual array. */ + void save() + { + save_has_been_called_ = true; + if (this->data_ != owned_data_.data()) { + return; + } + varray_.set_all(owned_data_); + } + + void disable_not_applied_warning() + { + show_not_saved_warning_ = false; + } +}; + +/** + * This class makes it easy to create a virtual array for an existing function or lambda. The + * `GetFunc` should take a single `index` argument and return the value at that index. + */ +template<typename T, typename GetFunc> class VArray_For_Func final : public VArray<T> { + private: + GetFunc get_func_; + + public: + VArray_For_Func(const int64_t size, GetFunc get_func) + : VArray<T>(size), get_func_(std::move(get_func)) + { + } + + private: + T get_impl(const int64_t index) const override + { + return get_func_(index); + } +}; + +template<typename StructT, typename ElemT, ElemT (*GetFunc)(const StructT &)> +class VArray_For_DerivedSpan : public VArray<ElemT> { + private: + const StructT *data_; + + public: + VArray_For_DerivedSpan(const Span<StructT> data) : VArray<ElemT>(data.size()), data_(data.data()) + { + } + + private: + ElemT get_impl(const int64_t index) const override + { + return GetFunc(data_[index]); + } +}; + +template<typename StructT, + typename ElemT, + ElemT (*GetFunc)(const StructT &), + void (*SetFunc)(StructT &, ElemT)> +class VMutableArray_For_DerivedSpan : public VMutableArray<ElemT> { + private: + StructT *data_; + + public: + VMutableArray_For_DerivedSpan(const MutableSpan<StructT> data) + : VMutableArray<ElemT>(data.size()), data_(data.data()) + { + } + + private: + ElemT get_impl(const int64_t index) const override + { + return GetFunc(data_[index]); + } + + void set_impl(const int64_t index, ElemT value) override + { + SetFunc(data_[index], std::move(value)); + } +}; + +/** * Generate multiple versions of the given function optimized for different virtual arrays. * One has to be careful with nesting multiple devirtualizations, because that results in an * exponential number of function instantiations (increasing compile time and binary size). @@ -229,14 +550,14 @@ inline void devirtualize_varray(const VArray<T> &varray, const Func &func, bool /* Support disabling the devirtualization to simplify benchmarking. */ if (enable) { if (varray.is_single()) { - /* `VArrayForSingle` can be used for devirtualization, because it is declared `final`. */ - const VArrayForSingle<T> varray_single{varray.get_single(), varray.size()}; + /* `VArray_For_Single` can be used for devirtualization, because it is declared `final`. */ + const VArray_For_Single<T> varray_single{varray.get_internal_single(), varray.size()}; func(varray_single); return; } if (varray.is_span()) { - /* `VArrayForSpan` can be used for devirtualization, because it is declared `final`. */ - const VArrayForSpan<T> varray_span{varray.get_span()}; + /* `VArray_For_Span` can be used for devirtualization, because it is declared `final`. */ + const VArray_For_Span<T> varray_span{varray.get_internal_span()}; func(varray_span); return; } @@ -262,26 +583,26 @@ inline void devirtualize_varray2(const VArray<T1> &varray1, const bool is_single1 = varray1.is_single(); const bool is_single2 = varray2.is_single(); if (is_span1 && is_span2) { - const VArrayForSpan<T1> varray1_span{varray1.get_span()}; - const VArrayForSpan<T2> varray2_span{varray2.get_span()}; + const VArray_For_Span<T1> varray1_span{varray1.get_internal_span()}; + const VArray_For_Span<T2> varray2_span{varray2.get_internal_span()}; func(varray1_span, varray2_span); return; } if (is_span1 && is_single2) { - const VArrayForSpan<T1> varray1_span{varray1.get_span()}; - const VArrayForSingle<T2> varray2_single{varray2.get_single(), varray2.size()}; + const VArray_For_Span<T1> varray1_span{varray1.get_internal_span()}; + const VArray_For_Single<T2> varray2_single{varray2.get_internal_single(), varray2.size()}; func(varray1_span, varray2_single); return; } if (is_single1 && is_span2) { - const VArrayForSingle<T1> varray1_single{varray1.get_single(), varray1.size()}; - const VArrayForSpan<T2> varray2_span{varray2.get_span()}; + const VArray_For_Single<T1> varray1_single{varray1.get_internal_single(), varray1.size()}; + const VArray_For_Span<T2> varray2_span{varray2.get_internal_span()}; func(varray1_single, varray2_span); return; } if (is_single1 && is_single2) { - const VArrayForSingle<T1> varray1_single{varray1.get_single(), varray1.size()}; - const VArrayForSingle<T2> varray2_single{varray2.get_single(), varray2.size()}; + const VArray_For_Single<T1> varray1_single{varray1.get_internal_single(), varray1.size()}; + const VArray_For_Single<T2> varray2_single{varray2.get_internal_single(), varray2.size()}; func(varray1_single, varray2_single); return; } diff --git a/source/blender/blenlib/tests/BLI_virtual_array_test.cc b/source/blender/blenlib/tests/BLI_virtual_array_test.cc index ac25229cd69..a6d2ca10315 100644 --- a/source/blender/blenlib/tests/BLI_virtual_array_test.cc +++ b/source/blender/blenlib/tests/BLI_virtual_array_test.cc @@ -1,26 +1,29 @@ /* Apache License, Version 2.0 */ +#include "BLI_array.hh" #include "BLI_strict_flags.h" +#include "BLI_vector.hh" +#include "BLI_vector_set.hh" #include "BLI_virtual_array.hh" #include "testing/testing.h" namespace blender::tests { -TEST(virtual_array, ForSpan) +TEST(virtual_array, Span) { std::array<int, 5> data = {3, 4, 5, 6, 7}; - VArrayForSpan<int> varray{data}; + VArray_For_Span<int> varray{data}; EXPECT_EQ(varray.size(), 5); EXPECT_EQ(varray.get(0), 3); EXPECT_EQ(varray.get(4), 7); EXPECT_TRUE(varray.is_span()); EXPECT_FALSE(varray.is_single()); - EXPECT_EQ(varray.get_span().data(), data.data()); + EXPECT_EQ(varray.get_internal_span().data(), data.data()); } -TEST(virtual_array, ForSingle) +TEST(virtual_array, Single) { - VArrayForSingle<int> varray{10, 4}; + VArray_For_Single<int> varray{10, 4}; EXPECT_EQ(varray.size(), 4); EXPECT_EQ(varray.get(0), 10); EXPECT_EQ(varray.get(3), 10); @@ -28,4 +31,124 @@ TEST(virtual_array, ForSingle) EXPECT_TRUE(varray.is_single()); } +TEST(virtual_array, Array) +{ + Array<int> array = {1, 2, 3, 5, 8}; + { + VArray_For_ArrayContainer varray{array}; + EXPECT_EQ(varray.size(), 5); + EXPECT_EQ(varray[0], 1); + EXPECT_EQ(varray[2], 3); + EXPECT_EQ(varray[3], 5); + EXPECT_TRUE(varray.is_span()); + } + { + VArray_For_ArrayContainer varray{std::move(array)}; + EXPECT_EQ(varray.size(), 5); + EXPECT_EQ(varray[0], 1); + EXPECT_EQ(varray[2], 3); + EXPECT_EQ(varray[3], 5); + EXPECT_TRUE(varray.is_span()); + } + { + VArray_For_ArrayContainer varray{array}; /* NOLINT: bugprone-use-after-move */ + EXPECT_TRUE(varray.is_empty()); + } +} + +TEST(virtual_array, Vector) +{ + Vector<int> vector = {9, 8, 7, 6}; + VArray_For_ArrayContainer varray{std::move(vector)}; + EXPECT_EQ(varray.size(), 4); + EXPECT_EQ(varray[0], 9); + EXPECT_EQ(varray[3], 6); +} + +TEST(virtual_array, StdVector) +{ + std::vector<int> vector = {5, 6, 7, 8}; + VArray_For_ArrayContainer varray{std::move(vector)}; + EXPECT_EQ(varray.size(), 4); + EXPECT_EQ(varray[0], 5); + EXPECT_EQ(varray[1], 6); +} + +TEST(virtual_array, StdArray) +{ + std::array<int, 4> array = {2, 3, 4, 5}; + VArray_For_ArrayContainer varray{array}; + EXPECT_EQ(varray.size(), 4); + EXPECT_EQ(varray[0], 2); + EXPECT_EQ(varray[1], 3); +} + +TEST(virtual_array, VectorSet) +{ + VectorSet<int> vector_set = {5, 3, 7, 3, 3, 5, 1}; + VArray_For_ArrayContainer varray{std::move(vector_set)}; + EXPECT_TRUE(vector_set.is_empty()); /* NOLINT: bugprone-use-after-move. */ + EXPECT_EQ(varray.size(), 4); + EXPECT_EQ(varray[0], 5); + EXPECT_EQ(varray[1], 3); + EXPECT_EQ(varray[2], 7); + EXPECT_EQ(varray[3], 1); +} + +TEST(virtual_array, Func) +{ + auto func = [](int64_t index) { return (int)(index * index); }; + VArray_For_Func<int, decltype(func)> varray{10, func}; + EXPECT_EQ(varray.size(), 10); + EXPECT_EQ(varray[0], 0); + EXPECT_EQ(varray[3], 9); + EXPECT_EQ(varray[9], 81); +} + +TEST(virtual_array, AsSpan) +{ + auto func = [](int64_t index) { return (int)(10 * index); }; + VArray_For_Func<int, decltype(func)> func_varray{10, func}; + VArray_Span span_varray{func_varray}; + EXPECT_EQ(span_varray.size(), 10); + Span<int> span = span_varray; + EXPECT_EQ(span.size(), 10); + EXPECT_EQ(span[0], 0); + EXPECT_EQ(span[3], 30); + EXPECT_EQ(span[6], 60); +} + +static int get_x(const std::array<int, 3> &item) +{ + return item[0]; +} + +static void set_x(std::array<int, 3> &item, int value) +{ + item[0] = value; +} + +TEST(virtual_array, DerivedSpan) +{ + Vector<std::array<int, 3>> vector; + vector.append({3, 4, 5}); + vector.append({1, 1, 1}); + { + VArray_For_DerivedSpan<std::array<int, 3>, int, get_x> varray{vector}; + EXPECT_EQ(varray.size(), 2); + EXPECT_EQ(varray[0], 3); + EXPECT_EQ(varray[1], 1); + } + { + VMutableArray_For_DerivedSpan<std::array<int, 3>, int, get_x, set_x> varray{vector}; + EXPECT_EQ(varray.size(), 2); + EXPECT_EQ(varray[0], 3); + EXPECT_EQ(varray[1], 1); + varray.set(0, 10); + varray.set(1, 20); + EXPECT_EQ(vector[0][0], 10); + EXPECT_EQ(vector[1][0], 20); + } +} + } // namespace blender::tests diff --git a/source/blender/functions/FN_generic_span.hh b/source/blender/functions/FN_generic_span.hh index 31b67dd3d70..ea2bd49fa09 100644 --- a/source/blender/functions/FN_generic_span.hh +++ b/source/blender/functions/FN_generic_span.hh @@ -30,7 +30,7 @@ namespace blender::fn { * A generic span. It behaves just like a blender::Span<T>, but the type is only known at run-time. */ class GSpan { - private: + protected: const CPPType *type_; const void *data_; int64_t size_; @@ -92,7 +92,7 @@ class GSpan { * known at run-time. */ class GMutableSpan { - private: + protected: const CPPType *type_; void *data_; int64_t size_; diff --git a/source/blender/functions/FN_generic_vector_array.hh b/source/blender/functions/FN_generic_vector_array.hh index ae6eb8a614f..b02ed471875 100644 --- a/source/blender/functions/FN_generic_vector_array.hh +++ b/source/blender/functions/FN_generic_vector_array.hh @@ -123,7 +123,7 @@ template<typename T> class GVectorArray_TypedMutableRef { void extend(const int64_t index, const VArray<T> &values) { - GVArrayForVArray<T> array{values}; + GVArray_For_VArray<T> array{values}; this->extend(index, array); } @@ -134,12 +134,12 @@ template<typename T> class GVectorArray_TypedMutableRef { }; /* A generic virtual vector array implementation for a `GVectorArray`. */ -class GVVectorArrayForGVectorArray : public GVVectorArray { +class GVVectorArray_For_GVectorArray : public GVVectorArray { private: const GVectorArray &vector_array_; public: - GVVectorArrayForGVectorArray(const GVectorArray &vector_array) + GVVectorArray_For_GVectorArray(const GVectorArray &vector_array) : GVVectorArray(vector_array.type(), vector_array.size()), vector_array_(vector_array) { } diff --git a/source/blender/functions/FN_generic_virtual_array.hh b/source/blender/functions/FN_generic_virtual_array.hh index c6230730a8d..00f6cacd2e1 100644 --- a/source/blender/functions/FN_generic_virtual_array.hh +++ b/source/blender/functions/FN_generic_virtual_array.hh @@ -23,12 +23,17 @@ * the data type is only known at runtime. */ +#include <optional> + #include "BLI_virtual_array.hh" #include "FN_generic_span.hh" namespace blender::fn { +template<typename T> class GVArray_Typed; +template<typename T> class GVMutableArray_Typed; + /* A generically typed version of `VArray<T>`. */ class GVArray { protected: @@ -86,13 +91,13 @@ class GVArray { /* Returns the internally used span of the virtual array. This invokes undefined behavior is the * virtual array is not stored as a span internally. */ - GSpan get_span() const + GSpan get_internal_span() const { BLI_assert(this->is_span()); if (size_ == 0) { return GSpan(*type_); } - return this->get_span_impl(); + return this->get_internal_span_impl(); } /* Returns true when the virtual array returns the same value for every index. */ @@ -107,57 +112,133 @@ class GVArray { /* Copies the value that is used for every element into `r_value`, which is expected to point to * initialized memory. This invokes undefined behavior if the virtual array would not return the * same value for every index. */ - void get_single(void *r_value) const + void get_internal_single(void *r_value) const { BLI_assert(this->is_single()); if (size_ == 1) { this->get(0, r_value); } - this->get_single_impl(r_value); + this->get_internal_single_impl(r_value); } - /* Same as `get_single`, but `r_value` points to initialized memory. */ + /* Same as `get_internal_single`, but `r_value` points to initialized memory. */ void get_single_to_uninitialized(void *r_value) const { type_->construct_default(r_value); - this->get_single(r_value); + this->get_internal_single(r_value); } void materialize_to_uninitialized(const IndexMask mask, void *dst) const; + template<typename T> const VArray<T> *try_get_internal_varray() const + { + BLI_assert(type_->is<T>()); + return (const VArray<T> *)this->try_get_internal_varray_impl(); + } + + /* Create a typed virtual array for this generic virtual array. */ + template<typename T> GVArray_Typed<T> typed() const + { + return GVArray_Typed<T>(*this); + } + protected: virtual void get_impl(const int64_t index, void *r_value) const; virtual void get_to_uninitialized_impl(const int64_t index, void *r_value) const = 0; virtual bool is_span_impl() const; - virtual GSpan get_span_impl() const; + virtual GSpan get_internal_span_impl() const; virtual bool is_single_impl() const; - virtual void get_single_impl(void *UNUSED(r_value)) const; + virtual void get_internal_single_impl(void *UNUSED(r_value)) const; + + virtual const void *try_get_internal_varray_impl() const; +}; + +/* Similar to GVArray, but supports changing the elements in the virtual array. */ +class GVMutableArray : public GVArray { + public: + GVMutableArray(const CPPType &type, const int64_t size) : GVArray(type, size) + { + } + + void set_by_copy(const int64_t index, const void *value) + { + BLI_assert(index >= 0); + BLI_assert(index < size_); + this->set_by_copy_impl(index, value); + } + + void set_by_move(const int64_t index, void *value) + { + BLI_assert(index >= 0); + BLI_assert(index < size_); + this->set_by_move_impl(index, value); + } + + void set_by_relocate(const int64_t index, void *value) + { + BLI_assert(index >= 0); + BLI_assert(index < size_); + this->set_by_relocate_impl(index, value); + } + + GMutableSpan get_internal_span() + { + BLI_assert(this->is_span()); + GSpan span = static_cast<const GVArray *>(this)->get_internal_span(); + return GMutableSpan(span.type(), const_cast<void *>(span.data()), span.size()); + } + + template<typename T> VMutableArray<T> *try_get_internal_mutable_varray() + { + BLI_assert(type_->is<T>()); + return (VMutableArray<T> *)this->try_get_internal_mutable_varray_impl(); + } + + /* Create a typed virtual array for this generic virtual array. */ + template<typename T> GVMutableArray_Typed<T> typed() + { + return GVMutableArray_Typed<T>(*this); + } + + void fill(const void *value); + + protected: + virtual void set_by_copy_impl(const int64_t index, const void *value); + virtual void set_by_relocate_impl(const int64_t index, void *value); + virtual void set_by_move_impl(const int64_t index, void *value) = 0; + + virtual void *try_get_internal_mutable_varray_impl(); }; -class GVArrayForGSpan : public GVArray { +class GVArray_For_GSpan : public GVArray { protected: - const void *data_; + const void *data_ = nullptr; const int64_t element_size_; public: - GVArrayForGSpan(const GSpan span) + GVArray_For_GSpan(const GSpan span) : GVArray(span.type(), span.size()), data_(span.data()), element_size_(span.type().size()) { } protected: + GVArray_For_GSpan(const CPPType &type, const int64_t size) + : GVArray(type, size), element_size_(type.size()) + { + } + void get_impl(const int64_t index, void *r_value) const override; void get_to_uninitialized_impl(const int64_t index, void *r_value) const override; bool is_span_impl() const override; - GSpan get_span_impl() const override; + GSpan get_internal_span_impl() const override; }; -class GVArrayForEmpty : public GVArray { +class GVArray_For_Empty : public GVArray { public: - GVArrayForEmpty(const CPPType &type) : GVArray(type, 0) + GVArray_For_Empty(const CPPType &type) : GVArray(type, 0) { } @@ -168,108 +249,618 @@ class GVArrayForEmpty : public GVArray { } }; -class GVArrayForSingleValueRef : public GVArray { - private: - const void *value_; +class GVMutableArray_For_GMutableSpan : public GVMutableArray { + protected: + void *data_ = nullptr; + const int64_t element_size_; + + public: + GVMutableArray_For_GMutableSpan(const GMutableSpan span) + : GVMutableArray(span.type(), span.size()), + data_(span.data()), + element_size_(span.type().size()) + { + } + + protected: + GVMutableArray_For_GMutableSpan(const CPPType &type, const int64_t size) + : GVMutableArray(type, size), element_size_(type.size()) + { + } + + void get_impl(const int64_t index, void *r_value) const override; + void get_to_uninitialized_impl(const int64_t index, void *r_value) const override; + + void set_by_copy_impl(const int64_t index, const void *value) override; + void set_by_move_impl(const int64_t index, void *value) override; + void set_by_relocate_impl(const int64_t index, void *value) override; + + bool is_span_impl() const override; + GSpan get_internal_span_impl() const override; +}; + +/* Generic virtual array where each element has the same value. The value is not owned. */ +class GVArray_For_SingleValueRef : public GVArray { + protected: + const void *value_ = nullptr; public: - GVArrayForSingleValueRef(const CPPType &type, const int64_t size, const void *value) + GVArray_For_SingleValueRef(const CPPType &type, const int64_t size, const void *value) : GVArray(type, size), value_(value) { } protected: + GVArray_For_SingleValueRef(const CPPType &type, const int64_t size) : GVArray(type, size) + { + } + void get_impl(const int64_t index, void *r_value) const override; void get_to_uninitialized_impl(const int64_t index, void *r_value) const override; bool is_span_impl() const override; - GSpan get_span_impl() const override; + GSpan get_internal_span_impl() const override; bool is_single_impl() const override; - void get_single_impl(void *r_value) const override; + void get_internal_single_impl(void *r_value) const override; }; -template<typename T> class GVArrayForVArray : public GVArray { - private: - const VArray<T> &array_; +/* Same as GVArray_For_SingleValueRef, but the value is owned. */ +class GVArray_For_SingleValue : public GVArray_For_SingleValueRef { + public: + GVArray_For_SingleValue(const CPPType &type, const int64_t size, const void *value); + ~GVArray_For_SingleValue(); +}; + +/* Used to convert a typed virtual array into a generic one. */ +template<typename T> class GVArray_For_VArray : public GVArray { + protected: + const VArray<T> *varray_ = nullptr; public: - GVArrayForVArray(const VArray<T> &array) - : GVArray(CPPType::get<T>(), array.size()), array_(array) + GVArray_For_VArray(const VArray<T> &varray) + : GVArray(CPPType::get<T>(), varray.size()), varray_(&varray) { } protected: + GVArray_For_VArray(const int64_t size) : GVArray(CPPType::get<T>(), size) + { + } + void get_impl(const int64_t index, void *r_value) const override { - *(T *)r_value = array_.get(index); + *(T *)r_value = varray_->get(index); } void get_to_uninitialized_impl(const int64_t index, void *r_value) const override { - new (r_value) T(array_.get(index)); + new (r_value) T(varray_->get(index)); } bool is_span_impl() const override { - return array_.is_span(); + return varray_->is_span(); } - GSpan get_span_impl() const override + GSpan get_internal_span_impl() const override { - return GSpan(array_.get_span()); + return GSpan(varray_->get_internal_span()); } bool is_single_impl() const override { - return array_.is_single(); + return varray_->is_single(); + } + + void get_internal_single_impl(void *r_value) const override + { + *(T *)r_value = varray_->get_internal_single(); } - void get_single_impl(void *r_value) const override + const void *try_get_internal_varray_impl() const override { - *(T *)r_value = array_.get_single(); + return varray_; } }; -template<typename T> class VArrayForGVArray : public VArray<T> { - private: - const GVArray &array_; +/* Used to convert any generic virtual array into a typed one. */ +template<typename T> class VArray_For_GVArray : public VArray<T> { + protected: + const GVArray *varray_ = nullptr; public: - VArrayForGVArray(const GVArray &array) : VArray<T>(array.size()), array_(array) + VArray_For_GVArray(const GVArray &varray) : VArray<T>(varray.size()), varray_(&varray) { - BLI_assert(array_.type().template is<T>()); + BLI_assert(varray_->type().template is<T>()); } protected: + VArray_For_GVArray(const int64_t size) : VArray<T>(size) + { + } + T get_impl(const int64_t index) const override { T value; - array_.get(index, &value); + varray_->get(index, &value); return value; } bool is_span_impl() const override { - return array_.is_span(); + return varray_->is_span(); } - Span<T> get_span_impl() const override + Span<T> get_internal_span_impl() const override { - return array_.get_span().template typed<T>(); + return varray_->get_internal_span().template typed<T>(); } bool is_single_impl() const override { - return array_.is_single(); + return varray_->is_single(); + } + + T get_internal_single_impl() const override + { + T value; + varray_->get_internal_single(&value); + return value; + } +}; + +/* Used to convert an generic mutable virtual array into a typed one. */ +template<typename T> class VMutableArray_For_GVMutableArray : public VMutableArray<T> { + protected: + GVMutableArray *varray_ = nullptr; + + public: + VMutableArray_For_GVMutableArray(GVMutableArray &varray) + : VMutableArray<T>(varray.size()), varray_(&varray) + { + BLI_assert(varray.type().template is<T>()); + } + + VMutableArray_For_GVMutableArray(const int64_t size) : VMutableArray<T>(size) + { } - T get_single_impl() const override + private: + T get_impl(const int64_t index) const override { T value; - array_.get_single(&value); + varray_->get(index, &value); return value; } + + void set_impl(const int64_t index, T value) override + { + varray_->set_by_relocate(index, &value); + } + + bool is_span_impl() const override + { + return varray_->is_span(); + } + + Span<T> get_internal_span_impl() const override + { + return varray_->get_internal_span().template typed<T>(); + } + + bool is_single_impl() const override + { + return varray_->is_single(); + } + + T get_internal_single_impl() const override + { + T value; + varray_->get_internal_single(&value); + return value; + } +}; + +/* Used to convert any typed virtual mutable array into a generic one. */ +template<typename T> class GVMutableArray_For_VMutableArray : public GVMutableArray { + protected: + VMutableArray<T> *varray_ = nullptr; + + public: + GVMutableArray_For_VMutableArray(VMutableArray<T> &varray) + : GVMutableArray(CPPType::get<T>(), varray.size()), varray_(&varray) + { + } + + protected: + GVMutableArray_For_VMutableArray(const int64_t size) : GVMutableArray(CPPType::get<T>(), size) + { + } + + void get_impl(const int64_t index, void *r_value) const override + { + *(T *)r_value = varray_->get(index); + } + + void get_to_uninitialized_impl(const int64_t index, void *r_value) const override + { + new (r_value) T(varray_->get(index)); + } + + bool is_span_impl() const override + { + return varray_->is_span(); + } + + GSpan get_internal_span_impl() const override + { + Span<T> span = varray_->get_internal_span(); + return span; + } + + bool is_single_impl() const override + { + return varray_->is_single(); + } + + void get_internal_single_impl(void *r_value) const override + { + *(T *)r_value = varray_->get_internal_single(); + } + + void set_by_copy_impl(const int64_t index, const void *value) override + { + const T &value_ = *(const T *)value; + varray_->set(index, value_); + } + + void set_by_relocate_impl(const int64_t index, void *value) override + { + T &value_ = *(T *)value; + varray_->set(index, std::move(value_)); + value_.~T(); + } + + void set_by_move_impl(const int64_t index, void *value) override + { + T &value_ = *(T *)value; + varray_->set(index, std::move(value_)); + } + + const void *try_get_internal_varray_impl() const override + { + return (const VArray<T> *)varray_; + } + + void *try_get_internal_mutable_varray_impl() override + { + return varray_; + } +}; + +/* A generic version of VArray_Span. */ +class GVArray_GSpan : public GSpan { + private: + const GVArray &varray_; + void *owned_data_ = nullptr; + + public: + GVArray_GSpan(const GVArray &varray); + ~GVArray_GSpan(); +}; + +/* A generic version of VMutableArray_Span. */ +class GVMutableArray_GSpan : public GMutableSpan { + private: + GVMutableArray &varray_; + void *owned_data_ = nullptr; + bool save_has_been_called_ = false; + bool show_not_saved_warning_ = true; + + public: + GVMutableArray_GSpan(GVMutableArray &varray, bool copy_values_to_span = true); + ~GVMutableArray_GSpan(); + + void save(); + void disable_not_applied_warning(); +}; + +/* Similar to GVArray_GSpan, but the resulting span is typed. */ +template<typename T> class GVArray_Span : public Span<T> { + private: + GVArray_GSpan varray_gspan_; + + public: + GVArray_Span(const GVArray &varray) : varray_gspan_(varray) + { + BLI_assert(varray.type().is<T>()); + this->data_ = (const T *)varray_gspan_.data(); + this->size_ = varray_gspan_.size(); + } +}; + +template<typename T> class GVArray_For_OwnedVArray : public GVArray_For_VArray<T> { + private: + std::unique_ptr<VArray<T>> owned_varray_; + + public: + /* Takes ownership of varray and passes a reference to the base class. */ + GVArray_For_OwnedVArray(std::unique_ptr<VArray<T>> varray) + : GVArray_For_VArray<T>(*varray), owned_varray_(std::move(varray)) + { + } +}; + +template<typename T> class VArray_For_OwnedGVArray : public VArray_For_GVArray<T> { + private: + std::unique_ptr<VArray<T>> owned_varray_; + + public: + /* Takes ownership of varray and passes a reference to the base class. */ + VArray_For_OwnedGVArray(std::unique_ptr<GVArray> varray) + : VArray_For_GVArray<T>(*varray), owned_varray_(std::move(varray)) + { + } +}; + +template<typename T> +class GVMutableArray_For_OwnedVMutableArray : public GVMutableArray_For_VMutableArray<T> { + private: + std::unique_ptr<VMutableArray<T>> owned_varray_; + + public: + /* Takes ownership of varray and passes a reference to the base class. */ + GVMutableArray_For_OwnedVMutableArray(std::unique_ptr<VMutableArray<T>> varray) + : GVMutableArray_For_VMutableArray<T>(*varray), owned_varray_(std::move(varray)) + { + } +}; + +template<typename T> +class VMutableArray_For_OwnedGVMutableArray : public VMutableArray_For_GVMutableArray<T> { + private: + std::unique_ptr<GVMutableArray> owned_varray_; + + public: + /* Takes ownership of varray and passes a reference to the base class. */ + VMutableArray_For_OwnedGVMutableArray(std::unique_ptr<GVMutableArray> varray) + : VMutableArray_For_GVMutableArray<T>(*varray), owned_varray_(std::move(varray)) + { + } +}; + +/* Utility to embed a typed virtual array into a generic one. This avoids one allocation and give + * the compiler more opportunity to optimize the generic virtual array. */ +template<typename T, typename VArrayT> +class GVArray_For_EmbeddedVArray : public GVArray_For_VArray<T> { + private: + VArrayT embedded_varray_; + + public: + template<typename... Args> + GVArray_For_EmbeddedVArray(const int64_t size, Args &&... args) + : GVArray_For_VArray<T>(size), embedded_varray_(std::forward<Args>(args)...) + { + this->varray_ = &embedded_varray_; + } +}; + +/* Same as GVArray_For_EmbeddedVArray, but for mutable virtual arrays. */ +template<typename T, typename VMutableArrayT> +class GVMutableArray_For_EmbeddedVMutableArray : public GVMutableArray_For_VMutableArray<T> { + private: + VMutableArrayT embedded_varray_; + + public: + template<typename... Args> + GVMutableArray_For_EmbeddedVMutableArray(const int64_t size, Args &&... args) + : GVMutableArray_For_VMutableArray<T>(size), embedded_varray_(std::forward<Args>(args)...) + { + this->varray_ = &embedded_varray_; + } +}; + +/* Same as VArray_For_ArrayContainer, but for a generic virtual array. */ +template<typename Container, typename T = typename Container::value_type> +class GVArray_For_ArrayContainer + : public GVArray_For_EmbeddedVArray<T, VArray_For_ArrayContainer<Container, T>> { + public: + GVArray_For_ArrayContainer(Container container) + : GVArray_For_EmbeddedVArray<T, VArray_For_ArrayContainer<Container, T>>( + container.size(), std::move(container)) + { + } +}; + +/* Same as VArray_For_DerivedSpan, but for a generic virtual array. */ +template<typename StructT, typename ElemT, ElemT (*GetFunc)(const StructT &)> +class GVArray_For_DerivedSpan + : public GVArray_For_EmbeddedVArray<ElemT, VArray_For_DerivedSpan<StructT, ElemT, GetFunc>> { + public: + GVArray_For_DerivedSpan(const Span<StructT> data) + : GVArray_For_EmbeddedVArray<ElemT, VArray_For_DerivedSpan<StructT, ElemT, GetFunc>>( + data.size(), data) + { + } +}; + +/* Same as VMutableArray_For_DerivedSpan, but for a generic virtual array. */ +template<typename StructT, + typename ElemT, + ElemT (*GetFunc)(const StructT &), + void (*SetFunc)(StructT &, ElemT)> +class GVMutableArray_For_DerivedSpan + : public GVMutableArray_For_EmbeddedVMutableArray< + ElemT, + VMutableArray_For_DerivedSpan<StructT, ElemT, GetFunc, SetFunc>> { + public: + GVMutableArray_For_DerivedSpan(const MutableSpan<StructT> data) + : GVMutableArray_For_EmbeddedVMutableArray< + ElemT, + VMutableArray_For_DerivedSpan<StructT, ElemT, GetFunc, SetFunc>>(data.size(), data) + { + } +}; + +/* Same as VArray_For_Span, but for a generic virtual array. */ +template<typename T> +class GVArray_For_Span : public GVArray_For_EmbeddedVArray<T, VArray_For_Span<T>> { + public: + GVArray_For_Span(const Span<T> data) + : GVArray_For_EmbeddedVArray<T, VArray_For_Span<T>>(data.size(), data) + { + } +}; + +/* Same as VMutableArray_For_MutableSpan, but for a generic virtual array. */ +template<typename T> +class GVMutableArray_For_MutableSpan + : public GVMutableArray_For_EmbeddedVMutableArray<T, VMutableArray_For_MutableSpan<T>> { + public: + GVMutableArray_For_MutableSpan(const MutableSpan<T> data) + : GVMutableArray_For_EmbeddedVMutableArray<T, VMutableArray_For_MutableSpan<T>>(data.size(), + data) + { + } +}; + +/** + * Utility class to create the "best" typed virtual array for a given generic virtual array. + * In most cases we don't just want to use VArray_For_GVArray, because it adds an additional + * indirection on element-access that can be avoided in many cases (e.g. when the virtual array is + * just a span or single value). + * + * This is not a virtual array itself, but is used to get a virtual array. + */ +template<typename T> class GVArray_Typed { + private: + const VArray<T> *varray_; + /* Of these optional virtual arrays, at most one is constructed at any time. */ + std::optional<VArray_For_Span<T>> varray_span_; + std::optional<VArray_For_Single<T>> varray_single_; + std::optional<VArray_For_GVArray<T>> varray_any_; + std::unique_ptr<GVArray> owned_gvarray_; + + public: + explicit GVArray_Typed(const GVArray &gvarray) + { + BLI_assert(gvarray.type().is<T>()); + if (gvarray.is_span()) { + const GSpan span = gvarray.get_internal_span(); + varray_span_.emplace(span.typed<T>()); + varray_ = &*varray_span_; + } + else if (gvarray.is_single()) { + T single_value; + gvarray.get_internal_single(&single_value); + varray_single_.emplace(single_value, gvarray.size()); + varray_ = &*varray_single_; + } + else if (const VArray<T> *internal_varray = gvarray.try_get_internal_varray<T>()) { + varray_ = internal_varray; + } + else { + varray_any_.emplace(gvarray); + varray_ = &*varray_any_; + } + } + + /* Same as the constructor above, but also takes ownership of the passed in virtual array. */ + explicit GVArray_Typed(std::unique_ptr<GVArray> gvarray) : GVArray_Typed(*gvarray) + { + owned_gvarray_ = std::move(gvarray); + } + + const VArray<T> &operator*() const + { + return *varray_; + } + + const VArray<T> *operator->() const + { + return varray_; + } + + /* Support implicit cast to the typed virtual array for convenience when `varray->typed<T>()` is + * used within an expression. */ + operator const VArray<T> &() const + { + return *varray_; + } + + T operator[](const int64_t index) const + { + return varray_->get(index); + } + + int64_t size() const + { + return varray_->size(); + } + + IndexRange index_range() const + { + return IndexRange(this->size()); + } +}; + +/* Same as GVArray_Typed, but for mutable virtual arrays. */ +template<typename T> class GVMutableArray_Typed { + private: + VMutableArray<T> *varray_; + std::optional<VMutableArray_For_MutableSpan<T>> varray_span_; + std::optional<VMutableArray_For_GVMutableArray<T>> varray_any_; + std::unique_ptr<GVMutableArray> owned_gvarray_; + + public: + explicit GVMutableArray_Typed(GVMutableArray &gvarray) + { + BLI_assert(gvarray.type().is<T>()); + if (gvarray.is_span()) { + const GMutableSpan span = gvarray.get_internal_span(); + varray_span_.emplace(span.typed<T>()); + varray_ = &*varray_span_; + } + else if (VMutableArray<T> *internal_varray = gvarray.try_get_internal_mutable_varray<T>()) { + varray_ = internal_varray; + } + else { + varray_any_.emplace(gvarray); + varray_ = &*varray_any_; + } + } + + explicit GVMutableArray_Typed(std::unique_ptr<GVMutableArray> gvarray) + : GVMutableArray_Typed(*gvarray) + { + owned_gvarray_ = std::move(gvarray); + } + + VMutableArray<T> &operator*() + { + return *varray_; + } + + VMutableArray<T> *operator->() + { + return varray_; + } + + operator VMutableArray<T> &() + { + return *varray_; + } + + T operator[](const int64_t index) const + { + return varray_->get(index); + } + + int64_t size() const + { + return varray_->size(); + } }; } // namespace blender::fn diff --git a/source/blender/functions/FN_generic_virtual_vector_array.hh b/source/blender/functions/FN_generic_virtual_vector_array.hh index ef3f53b5c25..4155a55a801 100644 --- a/source/blender/functions/FN_generic_virtual_vector_array.hh +++ b/source/blender/functions/FN_generic_virtual_vector_array.hh @@ -100,13 +100,13 @@ class GVVectorArray { } }; -class GVArrayForGVVectorArrayIndex : public GVArray { +class GVArray_For_GVVectorArrayIndex : public GVArray { private: const GVVectorArray &vector_array_; const int64_t index_; public: - GVArrayForGVVectorArrayIndex(const GVVectorArray &vector_array, const int64_t index) + GVArray_For_GVVectorArrayIndex(const GVVectorArray &vector_array, const int64_t index) : GVArray(vector_array.type(), vector_array.get_vector_size(index)), vector_array_(vector_array), index_(index) @@ -118,12 +118,12 @@ class GVArrayForGVVectorArrayIndex : public GVArray { void get_to_uninitialized_impl(const int64_t index_in_vector, void *r_value) const override; }; -class GVVectorArrayForSingleGVArray : public GVVectorArray { +class GVVectorArray_For_SingleGVArray : public GVVectorArray { private: const GVArray &array_; public: - GVVectorArrayForSingleGVArray(const GVArray &array, const int64_t size) + GVVectorArray_For_SingleGVArray(const GVArray &array, const int64_t size) : GVVectorArray(array.type(), size), array_(array) { } @@ -137,12 +137,12 @@ class GVVectorArrayForSingleGVArray : public GVVectorArray { bool is_single_vector_impl() const override; }; -class GVVectorArrayForSingleGSpan : public GVVectorArray { +class GVVectorArray_For_SingleGSpan : public GVVectorArray { private: const GSpan span_; public: - GVVectorArrayForSingleGSpan(const GSpan span, const int64_t size) + GVVectorArray_For_SingleGSpan(const GSpan span, const int64_t size) : GVVectorArray(span.type(), size), span_(span) { } @@ -156,12 +156,12 @@ class GVVectorArrayForSingleGSpan : public GVVectorArray { bool is_single_vector_impl() const override; }; -template<typename T> class VVectorArrayForGVVectorArray : public VVectorArray<T> { +template<typename T> class VVectorArray_For_GVVectorArray : public VVectorArray<T> { private: const GVVectorArray &vector_array_; public: - VVectorArrayForGVVectorArray(const GVVectorArray &vector_array) + VVectorArray_For_GVVectorArray(const GVVectorArray &vector_array) : VVectorArray<T>(vector_array.size()), vector_array_(vector_array) { } diff --git a/source/blender/functions/FN_multi_function_params.hh b/source/blender/functions/FN_multi_function_params.hh index 72ebc0d9b94..3b15f0278f3 100644 --- a/source/blender/functions/FN_multi_function_params.hh +++ b/source/blender/functions/FN_multi_function_params.hh @@ -55,13 +55,13 @@ class MFParamsBuilder { template<typename T> void add_readonly_single_input(const T *value, StringRef expected_name = "") { - this->add_readonly_single_input(scope_.construct<GVArrayForSingleValueRef>( + this->add_readonly_single_input(scope_.construct<GVArray_For_SingleValueRef>( __func__, CPPType::get<T>(), min_array_size_, value), expected_name); } void add_readonly_single_input(const GSpan span, StringRef expected_name = "") { - this->add_readonly_single_input(scope_.construct<GVArrayForGSpan>(__func__, span), + this->add_readonly_single_input(scope_.construct<GVArray_For_GSpan>(__func__, span), expected_name); } void add_readonly_single_input(const GVArray &ref, StringRef expected_name = "") @@ -74,7 +74,7 @@ class MFParamsBuilder { void add_readonly_vector_input(const GVectorArray &vector_array, StringRef expected_name = "") { this->add_readonly_vector_input( - scope_.construct<GVVectorArrayForGVectorArray>(__func__, vector_array), expected_name); + scope_.construct<GVVectorArray_For_GVectorArray>(__func__, vector_array), expected_name); } void add_readonly_vector_input(const GVVectorArray &ref, StringRef expected_name = "") { @@ -177,7 +177,7 @@ class MFParams { template<typename T> const VArray<T> &readonly_single_input(int param_index, StringRef name = "") { const GVArray &array = this->readonly_single_input(param_index, name); - return builder_->scope_.construct<VArrayForGVArray<T>>(__func__, array); + return builder_->scope_.construct<VArray_For_GVArray<T>>(__func__, array); } const GVArray &readonly_single_input(int param_index, StringRef name = "") { @@ -202,7 +202,7 @@ class MFParams { const VVectorArray<T> &readonly_vector_input(int param_index, StringRef name = "") { const GVVectorArray &vector_array = this->readonly_vector_input(param_index, name); - return builder_->scope_.construct<VVectorArrayForGVVectorArray<T>>(__func__, vector_array); + return builder_->scope_.construct<VVectorArray_For_GVVectorArray<T>>(__func__, vector_array); } const GVVectorArray &readonly_vector_input(int param_index, StringRef name = "") { diff --git a/source/blender/functions/intern/generic_vector_array.cc b/source/blender/functions/intern/generic_vector_array.cc index b3c5517cc43..3335b07e559 100644 --- a/source/blender/functions/intern/generic_vector_array.cc +++ b/source/blender/functions/intern/generic_vector_array.cc @@ -60,21 +60,21 @@ void GVectorArray::extend(const int64_t index, const GVArray &values) void GVectorArray::extend(const int64_t index, const GSpan values) { - GVArrayForGSpan varray{values}; + GVArray_For_GSpan varray{values}; this->extend(index, varray); } void GVectorArray::extend(IndexMask mask, const GVVectorArray &values) { for (const int i : mask) { - GVArrayForGVVectorArrayIndex array{values, i}; + GVArray_For_GVVectorArrayIndex array{values, i}; this->extend(i, array); } } void GVectorArray::extend(IndexMask mask, const GVectorArray &values) { - GVVectorArrayForGVectorArray virtual_values{values}; + GVVectorArray_For_GVectorArray virtual_values{values}; this->extend(mask, virtual_values); } diff --git a/source/blender/functions/intern/generic_virtual_array.cc b/source/blender/functions/intern/generic_virtual_array.cc index 9380eb257b2..e11501828f8 100644 --- a/source/blender/functions/intern/generic_virtual_array.cc +++ b/source/blender/functions/intern/generic_virtual_array.cc @@ -18,6 +18,10 @@ namespace blender::fn { +/* -------------------------------------------------------------------- + * GVArray. + */ + void GVArray::materialize_to_uninitialized(const IndexMask mask, void *dst) const { for (const int64_t i : mask) { @@ -37,7 +41,7 @@ bool GVArray::is_span_impl() const return false; } -GSpan GVArray::get_span_impl() const +GSpan GVArray::get_internal_span_impl() const { BLI_assert(false); return GSpan(*type_); @@ -48,60 +52,246 @@ bool GVArray::is_single_impl() const return false; } -void GVArray::get_single_impl(void *UNUSED(r_value)) const +void GVArray::get_internal_single_impl(void *UNUSED(r_value)) const { BLI_assert(false); } -void GVArrayForGSpan::get_impl(const int64_t index, void *r_value) const +const void *GVArray::try_get_internal_varray_impl() const +{ + return nullptr; +} + +/* -------------------------------------------------------------------- + * GVMutableArray. + */ + +void GVMutableArray::set_by_copy_impl(const int64_t index, const void *value) +{ + BUFFER_FOR_CPP_TYPE_VALUE(*type_, buffer); + type_->copy_to_uninitialized(value, buffer); + this->set_by_move_impl(index, buffer); + type_->destruct(buffer); +} + +void GVMutableArray::set_by_relocate_impl(const int64_t index, void *value) +{ + this->set_by_move_impl(index, value); + type_->destruct(value); +} + +void *GVMutableArray::try_get_internal_mutable_varray_impl() +{ + return nullptr; +} + +void GVMutableArray::fill(const void *value) +{ + if (this->is_span()) { + const GMutableSpan span = this->get_internal_span(); + type_->fill_initialized(value, span.data(), size_); + } + else { + for (int64_t i : IndexRange(size_)) { + this->set_by_copy(i, value); + } + } +} + +/* -------------------------------------------------------------------- + * GVArray_For_GSpan. + */ + +void GVArray_For_GSpan::get_impl(const int64_t index, void *r_value) const { type_->copy_to_initialized(POINTER_OFFSET(data_, element_size_ * index), r_value); } -void GVArrayForGSpan::get_to_uninitialized_impl(const int64_t index, void *r_value) const +void GVArray_For_GSpan::get_to_uninitialized_impl(const int64_t index, void *r_value) const { type_->copy_to_uninitialized(POINTER_OFFSET(data_, element_size_ * index), r_value); } -bool GVArrayForGSpan::is_span_impl() const +bool GVArray_For_GSpan::is_span_impl() const { return true; } -GSpan GVArrayForGSpan::get_span_impl() const +GSpan GVArray_For_GSpan::get_internal_span_impl() const { return GSpan(*type_, data_, size_); } -void GVArrayForSingleValueRef::get_impl(const int64_t UNUSED(index), void *r_value) const +/* -------------------------------------------------------------------- + * GVMutableArray_For_GMutableSpan. + */ + +void GVMutableArray_For_GMutableSpan::get_impl(const int64_t index, void *r_value) const +{ + type_->copy_to_initialized(POINTER_OFFSET(data_, element_size_ * index), r_value); +} + +void GVMutableArray_For_GMutableSpan::get_to_uninitialized_impl(const int64_t index, + void *r_value) const +{ + type_->copy_to_uninitialized(POINTER_OFFSET(data_, element_size_ * index), r_value); +} + +void GVMutableArray_For_GMutableSpan::set_by_copy_impl(const int64_t index, const void *value) +{ + type_->copy_to_initialized(value, POINTER_OFFSET(data_, element_size_ * index)); +} + +void GVMutableArray_For_GMutableSpan::set_by_move_impl(const int64_t index, void *value) +{ + type_->move_to_initialized(value, POINTER_OFFSET(data_, element_size_ * index)); +} + +void GVMutableArray_For_GMutableSpan::set_by_relocate_impl(const int64_t index, void *value) +{ + type_->relocate_to_initialized(value, POINTER_OFFSET(data_, element_size_ * index)); +} + +bool GVMutableArray_For_GMutableSpan::is_span_impl() const +{ + return true; +} + +GSpan GVMutableArray_For_GMutableSpan::get_internal_span_impl() const +{ + return GSpan(*type_, data_, size_); +} + +/* -------------------------------------------------------------------- + * GVArray_For_SingleValueRef. + */ + +void GVArray_For_SingleValueRef::get_impl(const int64_t UNUSED(index), void *r_value) const { type_->copy_to_initialized(value_, r_value); } -void GVArrayForSingleValueRef::get_to_uninitialized_impl(const int64_t UNUSED(index), - void *r_value) const +void GVArray_For_SingleValueRef::get_to_uninitialized_impl(const int64_t UNUSED(index), + void *r_value) const { type_->copy_to_uninitialized(value_, r_value); } -bool GVArrayForSingleValueRef::is_span_impl() const +bool GVArray_For_SingleValueRef::is_span_impl() const { return size_ == 1; } -GSpan GVArrayForSingleValueRef::get_span_impl() const +GSpan GVArray_For_SingleValueRef::get_internal_span_impl() const { return GSpan{*type_, value_, 1}; } -bool GVArrayForSingleValueRef::is_single_impl() const +bool GVArray_For_SingleValueRef::is_single_impl() const { return true; } -void GVArrayForSingleValueRef::get_single_impl(void *r_value) const +void GVArray_For_SingleValueRef::get_internal_single_impl(void *r_value) const { type_->copy_to_initialized(value_, r_value); } +/* -------------------------------------------------------------------- + * GVArray_For_SingleValue. + */ + +GVArray_For_SingleValue::GVArray_For_SingleValue(const CPPType &type, + const int64_t size, + const void *value) + : GVArray_For_SingleValueRef(type, size) +{ + value_ = MEM_mallocN_aligned(type.size(), type.alignment(), __func__); + type.copy_to_uninitialized(value, (void *)value_); +} + +GVArray_For_SingleValue::~GVArray_For_SingleValue() +{ + type_->destruct((void *)value_); + MEM_freeN((void *)value_); +} + +/* -------------------------------------------------------------------- + * GVArray_GSpan. + */ + +GVArray_GSpan::GVArray_GSpan(const GVArray &varray) : GSpan(varray.type()), varray_(varray) +{ + size_ = varray_.size(); + if (varray_.is_span()) { + data_ = varray_.get_internal_span().data(); + } + else { + owned_data_ = MEM_mallocN_aligned(type_->size() * size_, type_->alignment(), __func__); + varray_.materialize_to_uninitialized(IndexRange(size_), owned_data_); + data_ = owned_data_; + } +} + +GVArray_GSpan::~GVArray_GSpan() +{ + if (owned_data_ != nullptr) { + type_->destruct_n(owned_data_, size_); + MEM_freeN(owned_data_); + } +} + +/* -------------------------------------------------------------------- + * GVMutableArray_GSpan. + */ + +GVMutableArray_GSpan::GVMutableArray_GSpan(GVMutableArray &varray, const bool copy_values_to_span) + : GMutableSpan(varray.type()), varray_(varray) +{ + size_ = varray_.size(); + if (varray_.is_span()) { + data_ = varray_.get_internal_span().data(); + } + else { + owned_data_ = MEM_mallocN_aligned(type_->size() * size_, type_->alignment(), __func__); + if (copy_values_to_span) { + varray_.materialize_to_uninitialized(IndexRange(size_), owned_data_); + } + else { + type_->construct_default_n(owned_data_, size_); + } + data_ = owned_data_; + } +} + +GVMutableArray_GSpan::~GVMutableArray_GSpan() +{ + if (show_not_saved_warning_) { + if (!save_has_been_called_) { + std::cout << "Warning: Call `apply()` to make sure that changes persist in all cases.\n"; + } + } + if (owned_data_ != nullptr) { + type_->destruct_n(owned_data_, size_); + MEM_freeN(owned_data_); + } +} + +void GVMutableArray_GSpan::save() +{ + save_has_been_called_ = true; + if (data_ != owned_data_) { + return; + } + const int64_t element_size = type_->size(); + for (int64_t i : IndexRange(size_)) { + varray_.set_by_copy(i, POINTER_OFFSET(owned_data_, element_size * i)); + } +} + +void GVMutableArray_GSpan::disable_not_applied_warning() +{ + show_not_saved_warning_ = false; +} + } // namespace blender::fn diff --git a/source/blender/functions/intern/generic_virtual_vector_array.cc b/source/blender/functions/intern/generic_virtual_vector_array.cc index f6504cee41e..aa3d90883c6 100644 --- a/source/blender/functions/intern/generic_virtual_vector_array.cc +++ b/source/blender/functions/intern/generic_virtual_vector_array.cc @@ -18,48 +18,48 @@ namespace blender::fn { -void GVArrayForGVVectorArrayIndex::get_impl(const int64_t index_in_vector, void *r_value) const +void GVArray_For_GVVectorArrayIndex::get_impl(const int64_t index_in_vector, void *r_value) const { vector_array_.get_vector_element(index_, index_in_vector, r_value); } -void GVArrayForGVVectorArrayIndex::get_to_uninitialized_impl(const int64_t index_in_vector, - void *r_value) const +void GVArray_For_GVVectorArrayIndex::get_to_uninitialized_impl(const int64_t index_in_vector, + void *r_value) const { type_->construct_default(r_value); vector_array_.get_vector_element(index_, index_in_vector, r_value); } -int64_t GVVectorArrayForSingleGVArray::get_vector_size_impl(const int64_t UNUSED(index)) const +int64_t GVVectorArray_For_SingleGVArray::get_vector_size_impl(const int64_t UNUSED(index)) const { return array_.size(); } -void GVVectorArrayForSingleGVArray::get_vector_element_impl(const int64_t UNUSED(index), - const int64_t index_in_vector, - void *r_value) const +void GVVectorArray_For_SingleGVArray::get_vector_element_impl(const int64_t UNUSED(index), + const int64_t index_in_vector, + void *r_value) const { array_.get(index_in_vector, r_value); } -bool GVVectorArrayForSingleGVArray::is_single_vector_impl() const +bool GVVectorArray_For_SingleGVArray::is_single_vector_impl() const { return true; } -int64_t GVVectorArrayForSingleGSpan::get_vector_size_impl(const int64_t UNUSED(index)) const +int64_t GVVectorArray_For_SingleGSpan::get_vector_size_impl(const int64_t UNUSED(index)) const { return span_.size(); } -void GVVectorArrayForSingleGSpan::get_vector_element_impl(const int64_t UNUSED(index), - const int64_t index_in_vector, - void *r_value) const +void GVVectorArray_For_SingleGSpan::get_vector_element_impl(const int64_t UNUSED(index), + const int64_t index_in_vector, + void *r_value) const { type_->copy_to_initialized(span_[index_in_vector], r_value); } -bool GVVectorArrayForSingleGSpan::is_single_vector_impl() const +bool GVVectorArray_For_SingleGSpan::is_single_vector_impl() const { return true; } diff --git a/source/blender/functions/intern/multi_function_network_evaluation.cc b/source/blender/functions/intern/multi_function_network_evaluation.cc index 86ac4f6a179..9a0cb0c35ce 100644 --- a/source/blender/functions/intern/multi_function_network_evaluation.cc +++ b/source/blender/functions/intern/multi_function_network_evaluation.cc @@ -974,11 +974,11 @@ const GVArray &MFNetworkEvaluationStorage::get_single_input__full(const MFInputS if (any_value->type == ValueType::OwnSingle) { OwnSingleValue *value = static_cast<OwnSingleValue *>(any_value); if (value->is_single_allocated) { - return scope.construct<GVArrayForSingleValueRef>( + return scope.construct<GVArray_For_SingleValueRef>( __func__, value->span.type(), min_array_size_, value->span.data()); } - return scope.construct<GVArrayForGSpan>(__func__, value->span); + return scope.construct<GVArray_For_GSpan>(__func__, value->span); } if (any_value->type == ValueType::InputSingle) { InputSingleValue *value = static_cast<InputSingleValue *>(any_value); @@ -987,11 +987,11 @@ const GVArray &MFNetworkEvaluationStorage::get_single_input__full(const MFInputS if (any_value->type == ValueType::OutputSingle) { OutputSingleValue *value = static_cast<OutputSingleValue *>(any_value); BLI_assert(value->is_computed); - return scope.construct<GVArrayForGSpan>(__func__, value->span); + return scope.construct<GVArray_For_GSpan>(__func__, value->span); } BLI_assert(false); - return scope.construct<GVArrayForEmpty>(__func__, CPPType::get<float>()); + return scope.construct<GVArray_For_Empty>(__func__, CPPType::get<float>()); } const GVArray &MFNetworkEvaluationStorage::get_single_input__single(const MFInputSocket &socket, @@ -1004,7 +1004,7 @@ const GVArray &MFNetworkEvaluationStorage::get_single_input__single(const MFInpu if (any_value->type == ValueType::OwnSingle) { OwnSingleValue *value = static_cast<OwnSingleValue *>(any_value); BLI_assert(value->span.size() == 1); - return scope.construct<GVArrayForGSpan>(__func__, value->span); + return scope.construct<GVArray_For_GSpan>(__func__, value->span); } if (any_value->type == ValueType::InputSingle) { InputSingleValue *value = static_cast<InputSingleValue *>(any_value); @@ -1015,11 +1015,11 @@ const GVArray &MFNetworkEvaluationStorage::get_single_input__single(const MFInpu OutputSingleValue *value = static_cast<OutputSingleValue *>(any_value); BLI_assert(value->is_computed); BLI_assert(value->span.size() == 1); - return scope.construct<GVArrayForGSpan>(__func__, value->span); + return scope.construct<GVArray_For_GSpan>(__func__, value->span); } BLI_assert(false); - return scope.construct<GVArrayForEmpty>(__func__, CPPType::get<float>()); + return scope.construct<GVArray_For_Empty>(__func__, CPPType::get<float>()); } const GVVectorArray &MFNetworkEvaluationStorage::get_vector_input__full( @@ -1033,10 +1033,10 @@ const GVVectorArray &MFNetworkEvaluationStorage::get_vector_input__full( OwnVectorValue *value = static_cast<OwnVectorValue *>(any_value); if (value->vector_array->size() == 1) { GSpan span = (*value->vector_array)[0]; - return scope.construct<GVVectorArrayForSingleGSpan>(__func__, span, min_array_size_); + return scope.construct<GVVectorArray_For_SingleGSpan>(__func__, span, min_array_size_); } - return scope.construct<GVVectorArrayForGVectorArray>(__func__, *value->vector_array); + return scope.construct<GVVectorArray_For_GVectorArray>(__func__, *value->vector_array); } if (any_value->type == ValueType::InputVector) { InputVectorValue *value = static_cast<InputVectorValue *>(any_value); @@ -1044,11 +1044,11 @@ const GVVectorArray &MFNetworkEvaluationStorage::get_vector_input__full( } if (any_value->type == ValueType::OutputVector) { OutputVectorValue *value = static_cast<OutputVectorValue *>(any_value); - return scope.construct<GVVectorArrayForGVectorArray>(__func__, *value->vector_array); + return scope.construct<GVVectorArray_For_GVectorArray>(__func__, *value->vector_array); } BLI_assert(false); - return scope.construct<GVVectorArrayForSingleGSpan>(__func__, GSpan(CPPType::get<float>()), 0); + return scope.construct<GVVectorArray_For_SingleGSpan>(__func__, GSpan(CPPType::get<float>()), 0); } const GVVectorArray &MFNetworkEvaluationStorage::get_vector_input__single( @@ -1061,7 +1061,7 @@ const GVVectorArray &MFNetworkEvaluationStorage::get_vector_input__single( if (any_value->type == ValueType::OwnVector) { OwnVectorValue *value = static_cast<OwnVectorValue *>(any_value); BLI_assert(value->vector_array->size() == 1); - return scope.construct<GVVectorArrayForGVectorArray>(__func__, *value->vector_array); + return scope.construct<GVVectorArray_For_GVectorArray>(__func__, *value->vector_array); } if (any_value->type == ValueType::InputVector) { InputVectorValue *value = static_cast<InputVectorValue *>(any_value); @@ -1071,11 +1071,11 @@ const GVVectorArray &MFNetworkEvaluationStorage::get_vector_input__single( if (any_value->type == ValueType::OutputVector) { OutputVectorValue *value = static_cast<OutputVectorValue *>(any_value); BLI_assert(value->vector_array->size() == 1); - return scope.construct<GVVectorArrayForGVectorArray>(__func__, *value->vector_array); + return scope.construct<GVVectorArray_For_GVectorArray>(__func__, *value->vector_array); } BLI_assert(false); - return scope.construct<GVVectorArrayForSingleGSpan>(__func__, GSpan(CPPType::get<float>()), 0); + return scope.construct<GVVectorArray_For_SingleGSpan>(__func__, GSpan(CPPType::get<float>()), 0); } /** \} */ diff --git a/source/blender/functions/tests/FN_multi_function_network_test.cc b/source/blender/functions/tests/FN_multi_function_network_test.cc index 51e116b5983..7b9738e5ca4 100644 --- a/source/blender/functions/tests/FN_multi_function_network_test.cc +++ b/source/blender/functions/tests/FN_multi_function_network_test.cc @@ -223,7 +223,7 @@ TEST(multi_function_network, Test2) Array<int> output_value_2(5, -1); MFParamsBuilder params(network_fn, 5); - GVVectorArrayForSingleGSpan inputs_1{input_value_1.as_span(), 5}; + GVVectorArray_For_SingleGSpan inputs_1{input_value_1.as_span(), 5}; params.add_readonly_vector_input(inputs_1); params.add_readonly_single_input(&input_value_2); params.add_vector_output(output_value_1); |