diff options
31 files changed, 1655 insertions, 1422 deletions
diff --git a/source/blender/blenkernel/BKE_attribute_access.hh b/source/blender/blenkernel/BKE_attribute_access.hh index 653c0f0d85d..120b4e08b9c 100644 --- a/source/blender/blenkernel/BKE_attribute_access.hh +++ b/source/blender/blenkernel/BKE_attribute_access.hh @@ -19,7 +19,7 @@ #include <mutex> #include "FN_cpp_type.hh" -#include "FN_spans.hh" +#include "FN_generic_span.hh" #include "BKE_attribute.h" diff --git a/source/blender/blenlib/BLI_virtual_array.hh b/source/blender/blenlib/BLI_virtual_array.hh new file mode 100644 index 00000000000..51d2e820504 --- /dev/null +++ b/source/blender/blenlib/BLI_virtual_array.hh @@ -0,0 +1,211 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#pragma once + +/** \file + * \ingroup bli + * + * A virtual array is a data structure that behaves similar to an array, but its elements are + * accessed through virtual methods. This improves the decoupling of a function from its callers, + * because it does not have to know exactly how the data is layed out in memory, or if it is stored + * in memory at all. It could just as well be computed on the fly. + * + * Taking a virtual array as parameter instead of a more specific non-virtual type has some + * tradeoffs. Access to individual elements of the individual elements is higher due to function + * call overhead. On the other hand, potential callers don't have to convert the data into the + * specific format required for the function. This can be a costly conversion if only few of the + * elements are accessed in the end. + * + * Functions taking a virtual array as input can still optimize for different data layouts. For + * example, they can check if the array is stored as an array internally or if it is the same + * element for all indices. Whether it is worth to optimize for different data layouts in a + * function has to be decided on a case by case basis. One should always do some benchmarking to + * see of the increased compile time and binary size is worth it. + */ + +#include "BLI_span.hh" + +namespace blender { + +/* An immutable virtual array. */ +template<typename T> class VArray { + protected: + int64_t size_; + + public: + VArray(const int64_t size) : size_(size) + { + BLI_assert(size_ >= 0); + } + + virtual ~VArray() = default; + + T get(const int64_t index) const + { + BLI_assert(index >= 0); + BLI_assert(index < size_); + return this->get_impl(index); + } + + int64_t size() const + { + return size_; + } + + bool is_empty() const + { + return size_ == 0; + } + + /* Returns true when the virtual array is stored as a span internally. */ + bool is_span() const + { + if (size_ == 0) { + return true; + } + return this->is_span_impl(); + } + + /* 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 + { + BLI_assert(this->is_span()); + if (size_ == 0) { + return {}; + } + return this->get_span_impl(); + } + + /* Returns true when the virtual array returns the same value for every index. */ + bool is_single() const + { + if (size_ == 1) { + return true; + } + return this->is_single_impl(); + } + + /* 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 + { + BLI_assert(this->is_single()); + if (size_ == 1) { + return this->get(0); + } + return this->get_single_impl(); + } + + T operator[](const int64_t index) const + { + return this->get(index); + } + + protected: + virtual T get_impl(const int64_t index) const = 0; + + virtual bool is_span_impl() const + { + return false; + } + + virtual Span<T> get_span_impl() const + { + BLI_assert(false); + return {}; + } + + virtual bool is_single_impl() const + { + return false; + } + + virtual T get_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(false); + return T(); + } +}; + +/* A virtual array implementation for a span. */ +template<typename T> class VArrayForSpan : public VArray<T> { + private: + const T *data_; + + public: + VArrayForSpan(const Span<T> data) : VArray<T>(data.size()), data_(data.data()) + { + } + + protected: + T get_impl(const int64_t index) const override + { + return data_[index]; + } + + bool is_span_impl() const override + { + return true; + } + + Span<T> get_span_impl() const override + { + return Span<T>(data_, this->size_); + } +}; + +/* A virtual array implementation that returns the same value for every index. */ +template<typename T> class VArrayForSingle : public VArray<T> { + private: + T value_; + + public: + VArrayForSingle(T value, const int64_t size) : VArray<T>(size), value_(std::move(value)) + { + } + + protected: + T get_impl(const int64_t UNUSED(index)) const override + { + return value_; + } + + bool is_span_impl() const override + { + return this->size_ == 1; + } + + Span<T> get_span_impl() const override + { + return Span<T>(&value_, 1); + } + + bool is_single_impl() const override + { + return true; + } + + T get_single_impl() const override + { + return value_; + } +}; + +} // namespace blender diff --git a/source/blender/blenlib/BLI_virtual_vector_array.hh b/source/blender/blenlib/BLI_virtual_vector_array.hh new file mode 100644 index 00000000000..ab5afd2d80a --- /dev/null +++ b/source/blender/blenlib/BLI_virtual_vector_array.hh @@ -0,0 +1,95 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#pragma once + +/** \file + * \ingroup bli + * + * A virtual vector array gives access to an array of vectors. The individual vectors in the array + * can have different sizes. + * + * The tradeoffs here are similar to virtual arrays. + */ + +#include "BLI_virtual_array.hh" + +namespace blender { + +/* A readonly virtual array of vectors. */ +template<typename T> class VVectorArray { + protected: + int64_t size_; + + public: + VVectorArray(const int64_t size) : size_(size) + { + BLI_assert(size >= 0); + } + + virtual ~VVectorArray() = default; + + /* Returns the number of vectors in the vector array. */ + int64_t size() const + { + return size_; + } + + /* Returns true when there is no vector in the vector array. */ + bool is_empty() const + { + return size_ == 0; + } + + /* Returns the size of the vector at the given index. */ + int64_t get_vector_size(const int64_t index) const + { + BLI_assert(index >= 0); + BLI_assert(index < size_); + return this->get_vector_size_impl(index); + } + + /* Returns an element from one of the vectors. */ + T get_vector_element(const int64_t index, const int64_t index_in_vector) const + { + BLI_assert(index >= 0); + BLI_assert(index < size_); + BLI_assert(index_in_vector >= 0); + BLI_assert(index_in_vector < this->get_vector_size(index)); + return this->get_vector_element_impl(index, index_in_vector); + } + + /* Returns true when the same vector is used at every index. */ + bool is_single_vector() const + { + if (size_ == 1) { + return true; + } + return this->is_single_vector_impl(); + } + + protected: + virtual int64_t get_vector_size_impl(const int64_t index) const = 0; + + virtual T get_vector_element_impl(const int64_t index, const int64_t index_in_vetor) const = 0; + + virtual bool is_single_vector_impl() const + { + return false; + } +}; + +} // namespace blender diff --git a/source/blender/blenlib/CMakeLists.txt b/source/blender/blenlib/CMakeLists.txt index 5a851b7b2cb..37b0f742b8b 100644 --- a/source/blender/blenlib/CMakeLists.txt +++ b/source/blender/blenlib/CMakeLists.txt @@ -296,6 +296,8 @@ set(SRC BLI_vector_set.hh BLI_vector_set_slots.hh BLI_vfontdata.h + BLI_virtual_array.hh + BLI_virtual_vector_array.hh BLI_voronoi_2d.h BLI_voxel.h BLI_winstuff.h @@ -432,6 +434,7 @@ if(WITH_GTESTS) tests/BLI_task_test.cc tests/BLI_vector_set_test.cc tests/BLI_vector_test.cc + tests/BLI_virtual_array_test.cc tests/BLI_exception_safety_test_utils.hh ) diff --git a/source/blender/blenlib/tests/BLI_virtual_array_test.cc b/source/blender/blenlib/tests/BLI_virtual_array_test.cc new file mode 100644 index 00000000000..ac25229cd69 --- /dev/null +++ b/source/blender/blenlib/tests/BLI_virtual_array_test.cc @@ -0,0 +1,31 @@ +/* Apache License, Version 2.0 */ + +#include "BLI_strict_flags.h" +#include "BLI_virtual_array.hh" +#include "testing/testing.h" + +namespace blender::tests { + +TEST(virtual_array, ForSpan) +{ + std::array<int, 5> data = {3, 4, 5, 6, 7}; + VArrayForSpan<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()); +} + +TEST(virtual_array, ForSingle) +{ + VArrayForSingle<int> varray{10, 4}; + EXPECT_EQ(varray.size(), 4); + EXPECT_EQ(varray.get(0), 10); + EXPECT_EQ(varray.get(3), 10); + EXPECT_FALSE(varray.is_span()); + EXPECT_TRUE(varray.is_single()); +} + +} // namespace blender::tests diff --git a/source/blender/functions/CMakeLists.txt b/source/blender/functions/CMakeLists.txt index 608d7287bb2..809294ad274 100644 --- a/source/blender/functions/CMakeLists.txt +++ b/source/blender/functions/CMakeLists.txt @@ -28,18 +28,23 @@ set(INC_SYS set(SRC intern/cpp_types.cc + intern/generic_vector_array.cc + intern/generic_virtual_array.cc + intern/generic_virtual_vector_array.cc intern/multi_function.cc intern/multi_function_builder.cc intern/multi_function_network.cc intern/multi_function_network_evaluation.cc intern/multi_function_network_optimization.cc - FN_array_spans.hh FN_cpp_type.hh FN_cpp_type_make.hh FN_generic_pointer.hh + FN_generic_span.hh FN_generic_value_map.hh FN_generic_vector_array.hh + FN_generic_virtual_array.hh + FN_generic_virtual_vector_array.hh FN_multi_function.hh FN_multi_function_builder.hh FN_multi_function_context.hh @@ -50,7 +55,6 @@ set(SRC FN_multi_function_param_type.hh FN_multi_function_params.hh FN_multi_function_signature.hh - FN_spans.hh ) set(LIB @@ -61,12 +65,11 @@ blender_add_lib(bf_functions "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") if(WITH_GTESTS) set(TEST_SRC - tests/FN_array_spans_test.cc tests/FN_cpp_type_test.cc + tests/FN_generic_span_test.cc tests/FN_generic_vector_array_test.cc tests/FN_multi_function_network_test.cc tests/FN_multi_function_test.cc - tests/FN_spans_test.cc ) set(TEST_LIB bf_functions diff --git a/source/blender/functions/FN_array_spans.hh b/source/blender/functions/FN_array_spans.hh deleted file mode 100644 index afcff944957..00000000000 --- a/source/blender/functions/FN_array_spans.hh +++ /dev/null @@ -1,206 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#pragma once - -/** \file - * \ingroup fn - * - * An ArraySpan is a span where every element contains an array (instead of a single element as is - * the case in a normal span). Its main use case is to reference many small arrays. - */ - -#include "FN_spans.hh" - -namespace blender::fn { - -/** - * Depending on the use case, the referenced data might have a different structure. More - * categories can be added when necessary. - */ -enum class VArraySpanCategory { - SingleArray, - StartsAndSizes, -}; - -template<typename T> class VArraySpanBase { - protected: - int64_t virtual_size_; - VArraySpanCategory category_; - - union { - struct { - const T *start; - int64_t size; - } single_array; - struct { - const T *const *starts; - const int64_t *sizes; - } starts_and_sizes; - } data_; - - public: - bool is_single_array() const - { - switch (category_) { - case VArraySpanCategory::SingleArray: - return true; - case VArraySpanCategory::StartsAndSizes: - return virtual_size_ == 1; - } - BLI_assert(false); - return false; - } - - bool is_empty() const - { - return this->virtual_size_ == 0; - } - - int64_t size() const - { - return this->virtual_size_; - } -}; - -/** - * A virtual array span. Every element of this span contains a virtual span. So it behaves like - * a blender::Span, but might not be backed up by an actual array. - */ -template<typename T> class VArraySpan : public VArraySpanBase<T> { - private: - friend class GVArraySpan; - - VArraySpan(const VArraySpanBase<void> &other) - { - memcpy(this, &other, sizeof(VArraySpanBase<void>)); - } - - public: - VArraySpan() - { - this->virtual_size_ = 0; - this->category_ = VArraySpanCategory::StartsAndSizes; - this->data_.starts_and_sizes.starts = nullptr; - this->data_.starts_and_sizes.sizes = nullptr; - } - - VArraySpan(Span<T> span, int64_t virtual_size) - { - BLI_assert(virtual_size >= 0); - this->virtual_size_ = virtual_size; - this->category_ = VArraySpanCategory::SingleArray; - this->data_.single_array.start = span.data(); - this->data_.single_array.size = span.size(); - } - - VArraySpan(Span<const T *> starts, Span<int64_t> sizes) - { - BLI_assert(starts.size() == sizes.size()); - this->virtual_size_ = starts.size(); - this->category_ = VArraySpanCategory::StartsAndSizes; - this->data_.starts_and_sizes.starts = starts.begin(); - this->data_.starts_and_sizes.sizes = sizes.begin(); - } - - VSpan<T> operator[](int64_t index) const - { - BLI_assert(index >= 0); - BLI_assert(index < this->virtual_size_); - switch (this->category_) { - case VArraySpanCategory::SingleArray: - return VSpan<T>(Span<T>(this->data_.single_array.start, this->data_.single_array.size)); - case VArraySpanCategory::StartsAndSizes: - return VSpan<T>(Span<T>(this->data_.starts_and_sizes.starts[index], - this->data_.starts_and_sizes.sizes[index])); - } - BLI_assert(false); - return {}; - } -}; - -/** - * A generic virtual array span. It's just like a VArraySpan, but the type is only known at - * run-time. - */ -class GVArraySpan : public VArraySpanBase<void> { - private: - const CPPType *type_; - - GVArraySpan() = default; - - public: - GVArraySpan(const CPPType &type) - { - this->type_ = &type; - this->virtual_size_ = 0; - this->category_ = VArraySpanCategory::StartsAndSizes; - this->data_.starts_and_sizes.starts = nullptr; - this->data_.starts_and_sizes.sizes = nullptr; - } - - GVArraySpan(GSpan array, int64_t virtual_size) - { - this->type_ = &array.type(); - this->virtual_size_ = virtual_size; - this->category_ = VArraySpanCategory::SingleArray; - this->data_.single_array.start = array.data(); - this->data_.single_array.size = array.size(); - } - - GVArraySpan(const CPPType &type, Span<const void *> starts, Span<int64_t> sizes) - { - BLI_assert(starts.size() == sizes.size()); - this->type_ = &type; - this->virtual_size_ = starts.size(); - this->category_ = VArraySpanCategory::StartsAndSizes; - this->data_.starts_and_sizes.starts = (void **)starts.begin(); - this->data_.starts_and_sizes.sizes = sizes.begin(); - } - - template<typename T> GVArraySpan(VArraySpan<T> other) - { - this->type_ = &CPPType::get<T>(); - memcpy(this, &other, sizeof(VArraySpanBase<void>)); - } - - const CPPType &type() const - { - return *this->type_; - } - - template<typename T> VArraySpan<T> typed() const - { - BLI_assert(type_->is<T>()); - return VArraySpan<T>(*this); - } - - GVSpan operator[](int64_t index) const - { - BLI_assert(index < virtual_size_); - switch (category_) { - case VArraySpanCategory::SingleArray: - return GVSpan(GSpan(*type_, data_.single_array.start, data_.single_array.size)); - case VArraySpanCategory::StartsAndSizes: - return GVSpan(GSpan( - *type_, data_.starts_and_sizes.starts[index], data_.starts_and_sizes.sizes[index])); - } - BLI_assert(false); - return GVSpan(*type_); - } -}; - -} // namespace blender::fn diff --git a/source/blender/functions/FN_generic_span.hh b/source/blender/functions/FN_generic_span.hh new file mode 100644 index 00000000000..31b67dd3d70 --- /dev/null +++ b/source/blender/functions/FN_generic_span.hh @@ -0,0 +1,158 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#pragma once + +/** \file + * \ingroup fn + */ + +#include "BLI_span.hh" + +#include "FN_cpp_type.hh" + +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: + const CPPType *type_; + const void *data_; + int64_t size_; + + public: + GSpan(const CPPType &type, const void *buffer, int64_t size) + : type_(&type), data_(buffer), size_(size) + { + BLI_assert(size >= 0); + BLI_assert(buffer != nullptr || size == 0); + BLI_assert(type.pointer_has_valid_alignment(buffer)); + } + + GSpan(const CPPType &type) : GSpan(type, nullptr, 0) + { + } + + template<typename T> + GSpan(Span<T> array) + : GSpan(CPPType::get<T>(), static_cast<const void *>(array.data()), array.size()) + { + } + + const CPPType &type() const + { + return *type_; + } + + bool is_empty() const + { + return size_ == 0; + } + + int64_t size() const + { + return size_; + } + + const void *data() const + { + return data_; + } + + const void *operator[](int64_t index) const + { + BLI_assert(index < size_); + return POINTER_OFFSET(data_, type_->size() * index); + } + + template<typename T> Span<T> typed() const + { + BLI_assert(type_->is<T>()); + return Span<T>(static_cast<const T *>(data_), size_); + } +}; + +/** + * A generic mutable span. It behaves just like a blender::MutableSpan<T>, but the type is only + * known at run-time. + */ +class GMutableSpan { + private: + const CPPType *type_; + void *data_; + int64_t size_; + + public: + GMutableSpan(const CPPType &type, void *buffer, int64_t size) + : type_(&type), data_(buffer), size_(size) + { + BLI_assert(size >= 0); + BLI_assert(buffer != nullptr || size == 0); + BLI_assert(type.pointer_has_valid_alignment(buffer)); + } + + GMutableSpan(const CPPType &type) : GMutableSpan(type, nullptr, 0) + { + } + + template<typename T> + GMutableSpan(MutableSpan<T> array) + : GMutableSpan(CPPType::get<T>(), static_cast<void *>(array.begin()), array.size()) + { + } + + operator GSpan() const + { + return GSpan(*type_, data_, size_); + } + + const CPPType &type() const + { + return *type_; + } + + bool is_empty() const + { + return size_ == 0; + } + + int64_t size() const + { + return size_; + } + + void *data() const + { + return data_; + } + + void *operator[](int64_t index) const + { + BLI_assert(index >= 0); + BLI_assert(index < size_); + return POINTER_OFFSET(data_, type_->size() * index); + } + + template<typename T> MutableSpan<T> typed() const + { + BLI_assert(type_->is<T>()); + return MutableSpan<T>(static_cast<T *>(data_), size_); + } +}; + +} // namespace blender::fn diff --git a/source/blender/functions/FN_generic_vector_array.hh b/source/blender/functions/FN_generic_vector_array.hh index dc5ad3c36ab..ae6eb8a614f 100644 --- a/source/blender/functions/FN_generic_vector_array.hh +++ b/source/blender/functions/FN_generic_vector_array.hh @@ -19,75 +19,52 @@ /** \file * \ingroup fn * - * A `GVectorArray` is a container for a fixed amount of dynamically growing arrays with a generic - * type. Its main use case is to store many small vectors with few separate allocations. Using this - * structure is generally more efficient than allocating each small vector separately. - * - * `GVectorArrayRef<T>` is a typed reference to a GVectorArray and makes it easier and safer to - * work with the class when the type is known at compile time. + * A`GVectorArray` is a container for a fixed amount of dynamically growing vectors with a generic + * data type. Its main use case is to store many small vectors with few separate allocations. Using + * this structure is generally more efficient than allocating each vector separately. */ -#include "FN_array_spans.hh" -#include "FN_cpp_type.hh" - #include "BLI_array.hh" #include "BLI_linear_allocator.hh" -#include "BLI_utility_mixins.hh" -namespace blender::fn { +#include "FN_generic_virtual_vector_array.hh" -template<typename T> class GVectorArrayRef; +namespace blender::fn { +/* An array of vectors containing elements of a generic type. */ class GVectorArray : NonCopyable, NonMovable { private: - const CPPType &type_; - int64_t element_size_; - Array<void *, 1> starts_; - Array<int64_t, 1> lengths_; - Array<int64_t, 1> capacities_; + struct Item { + void *start = nullptr; + int64_t length = 0; + int64_t capacity = 0; + }; + + /* Use a linear allocator to pack many small vectors together. Currently, memory from reallocated + * vectors is not reused. This can be improved in the future. */ LinearAllocator<> allocator_; - - template<typename T> friend class GVectorArrayRef; + /* The data type of individual elements. */ + const CPPType &type_; + /* The size of an individual element. This is inlined from `type_.size()` for easier access. */ + const int64_t element_size_; + /* The individual vectors. */ + Array<Item> items_; public: GVectorArray() = delete; - GVectorArray(const CPPType &type, int64_t array_size) - : type_(type), - element_size_(type.size()), - starts_(array_size), - lengths_(array_size), - capacities_(array_size) - { - starts_.as_mutable_span().fill(nullptr); - lengths_.as_mutable_span().fill(0); - capacities_.as_mutable_span().fill(0); - } - - ~GVectorArray() - { - if (type_.is_trivially_destructible()) { - return; - } + GVectorArray(const CPPType &type, int64_t array_size); - for (int64_t i : starts_.index_range()) { - type_.destruct_n(starts_[i], lengths_[i]); - } - } + ~GVectorArray(); - operator GVArraySpan() const + int64_t size() const { - return GVArraySpan(type_, starts_, lengths_); + return items_.size(); } bool is_empty() const { - return starts_.size() == 0; - } - - int64_t size() const - { - return starts_.size(); + return items_.is_empty(); } const CPPType &type() const @@ -95,109 +72,89 @@ class GVectorArray : NonCopyable, NonMovable { return type_; } - Span<const void *> starts() const - { - return starts_; - } - - Span<int64_t> lengths() const - { - return lengths_; - } - - void append(int64_t index, const void *src) - { - int64_t old_length = lengths_[index]; - if (old_length == capacities_[index]) { - this->grow_at_least_one(index); - } - - void *dst = POINTER_OFFSET(starts_[index], element_size_ * old_length); - type_.copy_to_uninitialized(src, dst); - lengths_[index]++; - } + void append(int64_t index, const void *value); - void extend(int64_t index, GVSpan span) - { - BLI_assert(type_ == span.type()); - for (int64_t i = 0; i < span.size(); i++) { - this->append(index, span[i]); - } - } + /* Add multiple elements to a single vector. */ + void extend(int64_t index, const GVArray &values); + void extend(int64_t index, GSpan values); - void extend(IndexMask mask, GVArraySpan array_span) - { - BLI_assert(type_ == array_span.type()); - BLI_assert(mask.min_array_size() <= array_span.size()); - for (int64_t i : mask) { - this->extend(i, array_span[i]); - } - } + /* Add multiple elements to multiple vectors. */ + void extend(IndexMask mask, const GVVectorArray &values); + void extend(IndexMask mask, const GVectorArray &values); - GMutableSpan operator[](int64_t index) - { - BLI_assert(index < starts_.size()); - return GMutableSpan(type_, starts_[index], lengths_[index]); - } - template<typename T> GVectorArrayRef<T> typed() - { - return GVectorArrayRef<T>(*this); - } + GMutableSpan operator[](int64_t index); + GSpan operator[](int64_t index) const; private: - void grow_at_least_one(int64_t index) - { - BLI_assert(lengths_[index] == capacities_[index]); - int64_t new_capacity = lengths_[index] * 2 + 1; - - void *new_buffer = allocator_.allocate(element_size_ * new_capacity, type_.alignment()); - type_.relocate_to_uninitialized_n(starts_[index], new_buffer, lengths_[index]); - - starts_[index] = new_buffer; - capacities_[index] = new_capacity; - } + void realloc_to_at_least(Item &item, int64_t min_capacity); }; -template<typename T> class GVectorArrayRef { +/* A non-owning typed mutable reference to an `GVectorArray`. It simplifies access when the type of + * the data is known at compile time. */ +template<typename T> class GVectorArray_TypedMutableRef { private: GVectorArray *vector_array_; public: - GVectorArrayRef(GVectorArray &vector_array) : vector_array_(&vector_array) + GVectorArray_TypedMutableRef(GVectorArray &vector_array) : vector_array_(&vector_array) { - BLI_assert(vector_array.type_.is<T>()); + BLI_assert(vector_array_->type().is<T>()); } - void append(int64_t index, const T &value) + int64_t size() const + { + return vector_array_->size(); + } + + bool is_empty() const + { + return vector_array_->is_empty(); + } + + void append(const int64_t index, const T &value) { vector_array_->append(index, &value); } - void extend(int64_t index, Span<T> values) + void extend(const int64_t index, const Span<T> values) { vector_array_->extend(index, values); } - void extend(int64_t index, VSpan<T> values) + void extend(const int64_t index, const VArray<T> &values) { - vector_array_->extend(index, GVSpan(values)); + GVArrayForVArray<T> array{values}; + this->extend(index, array); } - MutableSpan<T> operator[](int64_t index) + MutableSpan<T> operator[](const int64_t index) { - BLI_assert(index < vector_array_->starts_.size()); - return MutableSpan<T>(static_cast<T *>(vector_array_->starts_[index]), - vector_array_->lengths_[index]); + return (*vector_array_)[index].typed<T>(); } +}; - int64_t size() const +/* A generic virtual vector array implementation for a `GVectorArray`. */ +class GVVectorArrayForGVectorArray : public GVVectorArray { + private: + const GVectorArray &vector_array_; + + public: + GVVectorArrayForGVectorArray(const GVectorArray &vector_array) + : GVVectorArray(vector_array.type(), vector_array.size()), vector_array_(vector_array) { - return vector_array_->size(); } - bool is_empty() const + protected: + int64_t get_vector_size_impl(const int64_t index) const override { - return vector_array_->is_empty(); + return vector_array_[index].size(); + } + + void get_vector_element_impl(const int64_t index, + const int64_t index_in_vector, + void *r_value) const override + { + type_->copy_to_initialized(vector_array_[index][index_in_vector], r_value); } }; diff --git a/source/blender/functions/FN_generic_virtual_array.hh b/source/blender/functions/FN_generic_virtual_array.hh new file mode 100644 index 00000000000..c60476c4631 --- /dev/null +++ b/source/blender/functions/FN_generic_virtual_array.hh @@ -0,0 +1,275 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#pragma once + +/** \file + * \ingroup fn + * + * A generic virtual array is the same as a virtual array from blenlib, except for the fact that + * the data type is only known at runtime. + */ + +#include "BLI_virtual_array.hh" + +#include "FN_generic_span.hh" + +namespace blender::fn { + +/* A generically typed version of `VArray<T>`. */ +class GVArray { + protected: + const CPPType *type_; + int64_t size_; + + public: + GVArray(const CPPType &type, const int64_t size) : type_(&type), size_(size) + { + BLI_assert(size_ >= 0); + } + + virtual ~GVArray() = default; + + const CPPType &type() const + { + return *type_; + } + + int64_t size() const + { + return size_; + } + + bool is_empty() const + { + return size_; + } + + /* Copies the value at the given index into the provided storage. The `r_value` pointer is + * expected to point to initialized memory. */ + void get(const int64_t index, void *r_value) const + { + BLI_assert(index >= 0); + BLI_assert(index < size_); + this->get_impl(index, r_value); + } + + /* Same as `get`, but `r_value` is expected to point to uninitialized memory. */ + void get_to_uninitialized(const int64_t index, void *r_value) const + { + BLI_assert(index >= 0); + BLI_assert(index < size_); + this->get_to_uninitialized_impl(index, r_value); + } + + /* Returns true when the virtual array is stored as a span internally. */ + bool is_span() const + { + if (size_ == 0) { + return true; + } + return this->is_span_impl(); + } + + /* 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 + { + BLI_assert(this->is_span()); + if (size_ == 0) { + return GSpan(*type_); + } + return this->get_span_impl(); + } + + /* Returns true when the virtual array returns the same value for every index. */ + bool is_single() const + { + if (size_ == 1) { + return true; + } + return this->is_single_impl(); + } + + /* 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 + { + BLI_assert(this->is_single()); + if (size_ == 1) { + this->get(0, r_value); + } + this->get_single_impl(r_value); + } + + /* Same as `get_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); + } + + void materialize_to_uninitialized(const IndexMask mask, void *dst) const; + + 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 bool is_single_impl() const; + virtual void get_single_impl(void *UNUSED(r_value)) const; +}; + +class GVArrayForGSpan : public GVArray { + protected: + const void *data_; + const int64_t element_size_; + + public: + GVArrayForGSpan(const GSpan span) + : GVArray(span.type(), span.size()), data_(span.data()), element_size_(span.type().size()) + { + } + + protected: + 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; +}; + +class GVArrayForEmpty : public GVArray { + public: + GVArrayForEmpty(const CPPType &type) : GVArray(type, 0) + { + } + + protected: + void get_to_uninitialized_impl(const int64_t UNUSED(index), void *UNUSED(r_value)) const override + { + BLI_assert(false); + } +}; + +class GVArrayForSingleValueRef : public GVArray { + private: + const void *value_; + + public: + GVArrayForSingleValueRef(const CPPType &type, const int64_t size, const void *value) + : GVArray(type, size), value_(value) + { + } + + protected: + 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; + + bool is_single_impl() const override; + void get_single_impl(void *r_value) const override; +}; + +template<typename T> class GVArrayForVArray : public GVArray { + private: + const VArray<T> &array_; + + public: + GVArrayForVArray(const VArray<T> &array) + : GVArray(CPPType::get<T>(), array.size()), array_(array) + { + } + + protected: + void get_impl(const int64_t index, void *r_value) const override + { + *(T *)r_value = array_.get(index); + } + + void get_to_uninitialized_impl(const int64_t index, void *r_value) const override + { + new (r_value) T(array_.get(index)); + } + + bool is_span_impl() const override + { + return array_.is_span(); + } + + GSpan get_span_impl() const override + { + return GSpan(array_.get_span()); + } + + bool is_single_impl() const override + { + return array_.is_single(); + } + + void get_single_impl(void *r_value) const override + { + *(T *)r_value = array_.get_single(); + } +}; + +template<typename T> class VArrayForGVArray : public VArray<T> { + private: + const GVArray &array_; + + public: + VArrayForGVArray(const GVArray &array) : VArray<T>(array.size()), array_(array) + { + BLI_assert(array_.type().is<T>()); + } + + protected: + T get_impl(const int64_t index) const override + { + T value; + array_.get(index, &value); + return value; + } + + bool is_span_impl() const override + { + return array_.is_span(); + } + + Span<T> get_span_impl() const override + { + return array_.get_span().typed<T>(); + } + + bool is_single_impl() const override + { + return array_.is_single(); + } + + T get_single_impl() const override + { + T value; + array_.get_single(&value); + return value; + } +}; + +} // 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 new file mode 100644 index 00000000000..22d1e5641e5 --- /dev/null +++ b/source/blender/functions/FN_generic_virtual_vector_array.hh @@ -0,0 +1,188 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#pragma once + +/** \file + * \ingroup fn + * + * A generic virtual vector array is essentially the same as a virtual vector array from blenlib, + * but its data type is only known at runtime. + */ + +#include "FN_generic_virtual_array.hh" + +#include "BLI_virtual_vector_array.hh" + +namespace blender::fn { + +/* A generically typed version of `VVectorArray`. */ +class GVVectorArray { + protected: + const CPPType *type_; + int64_t size_; + + public: + GVVectorArray(const CPPType &type, const int64_t size) : type_(&type), size_(size) + { + } + + virtual ~GVVectorArray() = default; + + /* Returns the number of vectors in the vector array. */ + int64_t size() const + { + return size_; + } + + /* Returns true when there is no vector in the vector array. */ + bool is_empty() const + { + return size_ == 0; + } + + const CPPType &type() const + { + return *type_; + } + + /* Returns the size of the vector at the given index. */ + int64_t get_vector_size(const int64_t index) const + { + BLI_assert(index >= 0); + BLI_assert(index < size_); + return this->get_vector_size_impl(index); + } + + /* Copies an element from one of the vectors into `r_value`, which is expected to point to + * initialized memory. */ + void get_vector_element(const int64_t index, const int64_t index_in_vector, void *r_value) const + { + BLI_assert(index >= 0); + BLI_assert(index < size_); + BLI_assert(index_in_vector >= 0); + BLI_assert(index_in_vector < this->get_vector_size(index)); + this->get_vector_element_impl(index, index_in_vector, r_value); + } + + /* Returns true when the same vector is used at every index. */ + bool is_single_vector() const + { + if (size_ == 1) { + return true; + } + return this->is_single_vector_impl(); + } + + protected: + virtual int64_t get_vector_size_impl(const int64_t index) const = 0; + + virtual void get_vector_element_impl(const int64_t index, + const int64_t index_in_vector, + void *r_value) const = 0; + + virtual bool is_single_vector_impl() const + { + return false; + } +}; + +class GVArrayForGVVectorArrayIndex : public GVArray { + private: + const GVVectorArray &vector_array_; + const int64_t index_; + + public: + GVArrayForGVVectorArrayIndex(const GVVectorArray &vector_array, const int64_t index) + : GVArray(vector_array.type(), vector_array.get_vector_size(index)), + vector_array_(vector_array), + index_(index) + { + } + + protected: + void get_impl(const int64_t index_in_vector, void *r_value) const override; + void get_to_uninitialized_impl(const int64_t index_in_vector, void *r_value) const override; +}; + +class GVVectorArrayForSingleGVArray : public GVVectorArray { + private: + const GVArray &array_; + + public: + GVVectorArrayForSingleGVArray(const GVArray &array, const int64_t size) + : GVVectorArray(array.type(), size), array_(array) + { + } + + protected: + int64_t get_vector_size_impl(const int64_t index) const override; + void get_vector_element_impl(const int64_t index, + const int64_t index_in_vector, + void *r_value) const override; + + bool is_single_vector_impl() const override; +}; + +class GVVectorArrayForSingleGSpan : public GVVectorArray { + private: + const GSpan span_; + + public: + GVVectorArrayForSingleGSpan(const GSpan span, const int64_t size) + : GVVectorArray(span.type(), size), span_(span) + { + } + + protected: + int64_t get_vector_size_impl(const int64_t UNUSED(index)) const override; + void get_vector_element_impl(const int64_t UNUSED(index), + const int64_t index_in_vector, + void *r_value) const override; + + bool is_single_vector_impl() const override; +}; + +template<typename T> class VVectorArrayForGVVectorArray : public VVectorArray<T> { + private: + const GVVectorArray &vector_array_; + + public: + VVectorArrayForGVVectorArray(const GVVectorArray &vector_array) + : VVectorArray<T>(vector_array.size()), vector_array_(vector_array) + { + } + + protected: + int64_t get_vector_size_impl(const int64_t index) const override + { + return vector_array_.get_vector_size(index); + } + + T get_vector_element_impl(const int64_t index, const int64_t index_in_vector) const override + { + T value; + vector_array_.get_vector_element(index, index_in_vector, &value); + return value; + } + + bool is_single_vector_impl() const + { + return vector_array_.is_single_vector(); + } +}; + +} // namespace blender::fn diff --git a/source/blender/functions/FN_multi_function_builder.hh b/source/blender/functions/FN_multi_function_builder.hh index 0cd1bc262be..b73c41d3f59 100644 --- a/source/blender/functions/FN_multi_function_builder.hh +++ b/source/blender/functions/FN_multi_function_builder.hh @@ -38,7 +38,7 @@ namespace blender::fn { */ template<typename In1, typename Out1> class CustomMF_SI_SO : public MultiFunction { private: - using FunctionT = std::function<void(IndexMask, VSpan<In1>, MutableSpan<Out1>)>; + using FunctionT = std::function<void(IndexMask, const VArray<In1> &, MutableSpan<Out1>)>; FunctionT function_; public: @@ -57,7 +57,7 @@ template<typename In1, typename Out1> class CustomMF_SI_SO : public MultiFunctio template<typename ElementFuncT> static FunctionT create_function(ElementFuncT element_fn) { - return [=](IndexMask mask, VSpan<In1> in1, MutableSpan<Out1> out1) { + return [=](IndexMask mask, const VArray<In1> &in1, MutableSpan<Out1> out1) { mask.foreach_index( [&](int i) { new (static_cast<void *>(&out1[i])) Out1(element_fn(in1[i])); }); }; @@ -65,7 +65,7 @@ template<typename In1, typename Out1> class CustomMF_SI_SO : public MultiFunctio void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override { - VSpan<In1> in1 = params.readonly_single_input<In1>(0); + const VArray<In1> &in1 = params.readonly_single_input<In1>(0); MutableSpan<Out1> out1 = params.uninitialized_single_output<Out1>(1); function_(mask, in1, out1); } @@ -80,7 +80,8 @@ template<typename In1, typename Out1> class CustomMF_SI_SO : public MultiFunctio template<typename In1, typename In2, typename Out1> class CustomMF_SI_SI_SO : public MultiFunction { private: - using FunctionT = std::function<void(IndexMask, VSpan<In1>, VSpan<In2>, MutableSpan<Out1>)>; + using FunctionT = + std::function<void(IndexMask, const VArray<In1> &, const VArray<In2> &, MutableSpan<Out1>)>; FunctionT function_; public: @@ -100,7 +101,10 @@ class CustomMF_SI_SI_SO : public MultiFunction { template<typename ElementFuncT> static FunctionT create_function(ElementFuncT element_fn) { - return [=](IndexMask mask, VSpan<In1> in1, VSpan<In2> in2, MutableSpan<Out1> out1) { + return [=](IndexMask mask, + const VArray<In1> &in1, + const VArray<In2> &in2, + MutableSpan<Out1> out1) { mask.foreach_index( [&](int i) { new (static_cast<void *>(&out1[i])) Out1(element_fn(in1[i], in2[i])); }); }; @@ -108,8 +112,8 @@ class CustomMF_SI_SI_SO : public MultiFunction { void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override { - VSpan<In1> in1 = params.readonly_single_input<In1>(0); - VSpan<In2> in2 = params.readonly_single_input<In2>(1); + const VArray<In1> &in1 = params.readonly_single_input<In1>(0); + const VArray<In2> &in2 = params.readonly_single_input<In2>(1); MutableSpan<Out1> out1 = params.uninitialized_single_output<Out1>(2); function_(mask, in1, in2, out1); } @@ -125,8 +129,11 @@ class CustomMF_SI_SI_SO : public MultiFunction { template<typename In1, typename In2, typename In3, typename Out1> class CustomMF_SI_SI_SI_SO : public MultiFunction { private: - using FunctionT = - std::function<void(IndexMask, VSpan<In1>, VSpan<In2>, VSpan<In3>, MutableSpan<Out1>)>; + using FunctionT = std::function<void(IndexMask, + const VArray<In1> &, + const VArray<In2> &, + const VArray<In3> &, + MutableSpan<Out1>)>; FunctionT function_; public: @@ -148,9 +155,9 @@ class CustomMF_SI_SI_SI_SO : public MultiFunction { template<typename ElementFuncT> static FunctionT create_function(ElementFuncT element_fn) { return [=](IndexMask mask, - VSpan<In1> in1, - VSpan<In2> in2, - VSpan<In3> in3, + const VArray<In1> &in1, + const VArray<In2> &in2, + const VArray<In3> &in3, MutableSpan<Out1> out1) { mask.foreach_index([&](int i) { new (static_cast<void *>(&out1[i])) Out1(element_fn(in1[i], in2[i], in3[i])); @@ -160,9 +167,9 @@ class CustomMF_SI_SI_SI_SO : public MultiFunction { void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override { - VSpan<In1> in1 = params.readonly_single_input<In1>(0); - VSpan<In2> in2 = params.readonly_single_input<In2>(1); - VSpan<In3> in3 = params.readonly_single_input<In3>(2); + const VArray<In1> &in1 = params.readonly_single_input<In1>(0); + const VArray<In2> &in2 = params.readonly_single_input<In2>(1); + const VArray<In3> &in3 = params.readonly_single_input<In3>(2); MutableSpan<Out1> out1 = params.uninitialized_single_output<Out1>(3); function_(mask, in1, in2, in3, out1); } @@ -179,8 +186,12 @@ class CustomMF_SI_SI_SI_SO : public MultiFunction { template<typename In1, typename In2, typename In3, typename In4, typename Out1> class CustomMF_SI_SI_SI_SI_SO : public MultiFunction { private: - using FunctionT = std::function<void( - IndexMask, VSpan<In1>, VSpan<In2>, VSpan<In3>, VSpan<In4>, MutableSpan<Out1>)>; + using FunctionT = std::function<void(IndexMask, + const VArray<In1> &, + const VArray<In2> &, + const VArray<In3> &, + const VArray<In4> &, + MutableSpan<Out1>)>; FunctionT function_; public: @@ -203,10 +214,10 @@ class CustomMF_SI_SI_SI_SI_SO : public MultiFunction { template<typename ElementFuncT> static FunctionT create_function(ElementFuncT element_fn) { return [=](IndexMask mask, - VSpan<In1> in1, - VSpan<In2> in2, - VSpan<In3> in3, - VSpan<In4> in4, + const VArray<In1> &in1, + const VArray<In2> &in2, + const VArray<In3> &in3, + const VArray<In4> &in4, MutableSpan<Out1> out1) { mask.foreach_index([&](int i) { new (static_cast<void *>(&out1[i])) Out1(element_fn(in1[i], in2[i], in3[i], in4[i])); @@ -216,10 +227,10 @@ class CustomMF_SI_SI_SI_SI_SO : public MultiFunction { void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override { - VSpan<In1> in1 = params.readonly_single_input<In1>(0); - VSpan<In2> in2 = params.readonly_single_input<In2>(1); - VSpan<In3> in3 = params.readonly_single_input<In3>(2); - VSpan<In4> in4 = params.readonly_single_input<In4>(3); + const VArray<In1> &in1 = params.readonly_single_input<In1>(0); + const VArray<In2> &in2 = params.readonly_single_input<In2>(1); + const VArray<In3> &in3 = params.readonly_single_input<In3>(2); + const VArray<In4> &in4 = params.readonly_single_input<In4>(3); MutableSpan<Out1> out1 = params.uninitialized_single_output<Out1>(4); function_(mask, in1, in2, in3, in4, out1); } @@ -276,7 +287,7 @@ template<typename From, typename To> class CustomMF_Convert : public MultiFuncti void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override { - VSpan<From> inputs = params.readonly_single_input<From>(0); + const VArray<From> &inputs = params.readonly_single_input<From>(0); MutableSpan<To> outputs = params.uninitialized_single_output<To>(1); for (int64_t i : mask) { diff --git a/source/blender/functions/FN_multi_function_params.hh b/source/blender/functions/FN_multi_function_params.hh index ba2d1d0edd3..2d3a8dd650e 100644 --- a/source/blender/functions/FN_multi_function_params.hh +++ b/source/blender/functions/FN_multi_function_params.hh @@ -25,18 +25,22 @@ * the function. `MFParams` is then used inside the called function to access the parameters. */ +#include "BLI_resource_collector.hh" + #include "FN_generic_vector_array.hh" +#include "FN_generic_virtual_vector_array.hh" #include "FN_multi_function_signature.hh" namespace blender::fn { class MFParamsBuilder { private: + ResourceCollector resources_; const MFSignature *signature_; int64_t min_array_size_; - Vector<GVSpan> virtual_spans_; + Vector<const GVArray *> virtual_arrays_; Vector<GMutableSpan> mutable_spans_; - Vector<GVArraySpan> virtual_array_spans_; + Vector<const GVVectorArray *> virtual_vector_arrays_; Vector<GVectorArray *> vector_arrays_; friend class MFParams; @@ -51,21 +55,32 @@ class MFParamsBuilder { template<typename T> void add_readonly_single_input(const T *value, StringRef expected_name = "") { - this->add_readonly_single_input(GVSpan::FromSingle(CPPType::get<T>(), value, min_array_size_), + this->add_readonly_single_input(resources_.construct<GVArrayForSingleValueRef>( + __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(resources_.construct<GVArrayForGSpan>(__func__, span), expected_name); } - void add_readonly_single_input(GVSpan ref, StringRef expected_name = "") + void add_readonly_single_input(const GVArray &ref, StringRef expected_name = "") { this->assert_current_param_type(MFParamType::ForSingleInput(ref.type()), expected_name); BLI_assert(ref.size() >= min_array_size_); - virtual_spans_.append(ref); + virtual_arrays_.append(&ref); } - void add_readonly_vector_input(GVArraySpan ref, StringRef expected_name = "") + void add_readonly_vector_input(const GVectorArray &vector_array, StringRef expected_name = "") + { + this->add_readonly_vector_input( + resources_.construct<GVVectorArrayForGVectorArray>(__func__, vector_array), expected_name); + } + void add_readonly_vector_input(const GVVectorArray &ref, StringRef expected_name = "") { this->assert_current_param_type(MFParamType::ForVectorInput(ref.type()), expected_name); BLI_assert(ref.size() >= min_array_size_); - virtual_array_spans_.append(ref); + virtual_vector_arrays_.append(&ref); } template<typename T> void add_uninitialized_single_output(T *value, StringRef expected_name = "") @@ -121,6 +136,11 @@ class MFParamsBuilder { return *vector_arrays_[data_index]; } + ResourceCollector &resources() + { + return resources_; + } + private: void assert_current_param_type(MFParamType param_type, StringRef expected_name = "") { @@ -140,7 +160,7 @@ class MFParamsBuilder { int current_param_index() const { - return virtual_spans_.size() + mutable_spans_.size() + virtual_array_spans_.size() + + return virtual_arrays_.size() + mutable_spans_.size() + virtual_vector_arrays_.size() + vector_arrays_.size(); } }; @@ -154,15 +174,16 @@ class MFParams { { } - template<typename T> VSpan<T> readonly_single_input(int param_index, StringRef name = "") + template<typename T> const VArray<T> &readonly_single_input(int param_index, StringRef name = "") { - return this->readonly_single_input(param_index, name).typed<T>(); + const GVArray &array = this->readonly_single_input(param_index, name); + return builder_->resources_.construct<VArrayForGVArray<T>>(__func__, array); } - GVSpan readonly_single_input(int param_index, StringRef name = "") + const GVArray &readonly_single_input(int param_index, StringRef name = "") { this->assert_correct_param(param_index, name, MFParamType::SingleInput); int data_index = builder_->signature_->data_index(param_index); - return builder_->virtual_spans_[data_index]; + return *builder_->virtual_arrays_[data_index]; } template<typename T> @@ -177,20 +198,23 @@ class MFParams { return builder_->mutable_spans_[data_index]; } - template<typename T> VArraySpan<T> readonly_vector_input(int param_index, StringRef name = "") + template<typename T> + const VVectorArray<T> &readonly_vector_input(int param_index, StringRef name = "") { - return this->readonly_vector_input(param_index, name).typed<T>(); + const GVVectorArray &vector_array = this->readonly_vector_input(param_index, name); + return builder_->resources_.construct<VVectorArrayForGVVectorArray<T>>(__func__, vector_array); } - GVArraySpan readonly_vector_input(int param_index, StringRef name = "") + const GVVectorArray &readonly_vector_input(int param_index, StringRef name = "") { this->assert_correct_param(param_index, name, MFParamType::VectorInput); int data_index = builder_->signature_->data_index(param_index); - return builder_->virtual_array_spans_[data_index]; + return *builder_->virtual_vector_arrays_[data_index]; } - template<typename T> GVectorArrayRef<T> vector_output(int param_index, StringRef name = "") + template<typename T> + GVectorArray_TypedMutableRef<T> vector_output(int param_index, StringRef name = "") { - return this->vector_output(param_index, name).typed<T>(); + return {this->vector_output(param_index, name)}; } GVectorArray &vector_output(int param_index, StringRef name = "") { @@ -210,9 +234,10 @@ class MFParams { return builder_->mutable_spans_[data_index]; } - template<typename T> GVectorArrayRef<T> vector_mutable(int param_index, StringRef name = "") + template<typename T> + GVectorArray_TypedMutableRef<T> vector_mutable(int param_index, StringRef name = "") { - return this->vector_mutable(param_index, name).typed<T>(); + return {this->vector_mutable(param_index, name)}; } GVectorArray &vector_mutable(int param_index, StringRef name = "") { diff --git a/source/blender/functions/FN_multi_function_signature.hh b/source/blender/functions/FN_multi_function_signature.hh index ef51ddbaf24..e05ea1c2578 100644 --- a/source/blender/functions/FN_multi_function_signature.hh +++ b/source/blender/functions/FN_multi_function_signature.hh @@ -46,8 +46,8 @@ class MFSignatureBuilder { private: MFSignature &data_; int span_count_ = 0; - int virtual_span_count_ = 0; - int virtual_array_span_count_ = 0; + int virtual_array_count_ = 0; + int virtual_vector_array_count_ = 0; int vector_array_count_ = 0; public: @@ -83,10 +83,10 @@ class MFSignatureBuilder { switch (data_type.category()) { case MFDataType::Single: - data_.param_data_indices.append(virtual_span_count_++); + data_.param_data_indices.append(virtual_array_count_++); break; case MFDataType::Vector: - data_.param_data_indices.append(virtual_array_span_count_++); + data_.param_data_indices.append(virtual_vector_array_count_++); break; } } diff --git a/source/blender/functions/FN_spans.hh b/source/blender/functions/FN_spans.hh deleted file mode 100644 index 41c930581a4..00000000000 --- a/source/blender/functions/FN_spans.hh +++ /dev/null @@ -1,462 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#pragma once - -/** \file - * \ingroup fn - * - * This file implements multiple variants of a span for different use cases. There are two - * requirements of the function system that require span implementations other from - * blender::Span<T>. - * 1. The function system works with a run-time type system (see `CPPType`). Therefore, it has to - * deal with types in a generic way. The type of a Span<T> has to be known at compile time. - * 2. Span<T> expects an underlying memory buffer that is as large as the span. However, sometimes - * we can save some memory and processing when we know that all elements are the same. - * - * The first requirement is solved with generic spans, which use the "G" prefix. Those - * store a CPPType instance to keep track of the type that is currently stored. - * - * The second requirement is solved with virtual spans. A virtual span behaves like a normal span, - * but it might not be backed up by an actual array. Elements in a virtual span are always - * immutable. - * - * Different use cases require different combinations of these properties and therefore use - * different data structures. - */ - -#include "BLI_span.hh" - -#include "FN_cpp_type.hh" - -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: - const CPPType *type_; - const void *data_; - int64_t size_; - - public: - GSpan(const CPPType &type, const void *buffer, int64_t size) - : type_(&type), data_(buffer), size_(size) - { - BLI_assert(size >= 0); - BLI_assert(buffer != nullptr || size == 0); - BLI_assert(type.pointer_has_valid_alignment(buffer)); - } - - GSpan(const CPPType &type) : GSpan(type, nullptr, 0) - { - } - - template<typename T> - GSpan(Span<T> array) - : GSpan(CPPType::get<T>(), static_cast<const void *>(array.data()), array.size()) - { - } - - const CPPType &type() const - { - return *type_; - } - - bool is_empty() const - { - return size_ == 0; - } - - int64_t size() const - { - return size_; - } - - const void *data() const - { - return data_; - } - - const void *operator[](int64_t index) const - { - BLI_assert(index < size_); - return POINTER_OFFSET(data_, type_->size() * index); - } - - template<typename T> Span<T> typed() const - { - BLI_assert(type_->is<T>()); - return Span<T>(static_cast<const T *>(data_), size_); - } -}; - -/** - * A generic mutable span. It behaves just like a blender::MutableSpan<T>, but the type is only - * known at run-time. - */ -class GMutableSpan { - private: - const CPPType *type_; - void *data_; - int64_t size_; - - public: - GMutableSpan(const CPPType &type, void *buffer, int64_t size) - : type_(&type), data_(buffer), size_(size) - { - BLI_assert(size >= 0); - BLI_assert(buffer != nullptr || size == 0); - BLI_assert(type.pointer_has_valid_alignment(buffer)); - } - - GMutableSpan(const CPPType &type) : GMutableSpan(type, nullptr, 0) - { - } - - template<typename T> - GMutableSpan(MutableSpan<T> array) - : GMutableSpan(CPPType::get<T>(), static_cast<void *>(array.begin()), array.size()) - { - } - - operator GSpan() const - { - return GSpan(*type_, data_, size_); - } - - const CPPType &type() const - { - return *type_; - } - - bool is_empty() const - { - return size_ == 0; - } - - int64_t size() const - { - return size_; - } - - void *data() - { - return data_; - } - - void *operator[](int64_t index) - { - BLI_assert(index >= 0); - BLI_assert(index < size_); - return POINTER_OFFSET(data_, type_->size() * index); - } - - void *operator[](int64_t index) const - { - BLI_assert(index >= 0); - BLI_assert(index < size_); - return POINTER_OFFSET(data_, type_->size() * index); - } - - template<typename T> MutableSpan<T> typed() - { - BLI_assert(type_->is<T>()); - return MutableSpan<T>(static_cast<T *>(data_), size_); - } -}; - -enum class VSpanCategory { - Single, - FullArray, - FullPointerArray, -}; - -template<typename T> struct VSpanBase { - protected: - int64_t virtual_size_; - VSpanCategory category_; - union { - struct { - const T *data; - } single; - struct { - const T *data; - } full_array; - struct { - const T *const *data; - } full_pointer_array; - } data_; - - public: - bool is_single_element() const - { - switch (category_) { - case VSpanCategory::Single: - return true; - case VSpanCategory::FullArray: - return virtual_size_ == 1; - case VSpanCategory::FullPointerArray: - return virtual_size_ == 1; - } - BLI_assert(false); - return false; - } - - bool is_full_array() const - { - switch (category_) { - case VSpanCategory::Single: - return virtual_size_ == 1; - case VSpanCategory::FullArray: - return true; - case VSpanCategory::FullPointerArray: - return virtual_size_ <= 1; - } - BLI_assert(false); - return false; - } - - bool is_empty() const - { - return this->virtual_size_ == 0; - } - - int64_t size() const - { - return this->virtual_size_; - } -}; - -BLI_STATIC_ASSERT((sizeof(VSpanBase<void>) == sizeof(VSpanBase<AlignedBuffer<64, 64>>)), - "should not depend on the size of the type"); - -/** - * A virtual span. It behaves like a blender::Span<T>, but might not be backed up by an actual - * array. - */ -template<typename T> class VSpan : public VSpanBase<T> { - friend class GVSpan; - - VSpan(const VSpanBase<void> &values) - { - memcpy(this, &values, sizeof(VSpanBase<void>)); - } - - public: - VSpan() - { - this->virtual_size_ = 0; - this->category_ = VSpanCategory::FullArray; - this->data_.full_array.data = nullptr; - } - - VSpan(Span<T> values) - { - this->virtual_size_ = values.size(); - this->category_ = VSpanCategory::FullArray; - this->data_.full_array.data = values.begin(); - } - - VSpan(MutableSpan<T> values) : VSpan(Span<T>(values)) - { - } - - VSpan(Span<const T *> values) - { - this->virtual_size_ = values.size(); - this->category_ = VSpanCategory::FullPointerArray; - this->data_.full_pointer_array.data = values.begin(); - } - - static VSpan FromSingle(const T *value, int64_t virtual_size) - { - VSpan ref; - ref.virtual_size_ = virtual_size; - ref.category_ = VSpanCategory::Single; - ref.data_.single.data = value; - return ref; - } - - const T &operator[](int64_t index) const - { - BLI_assert(index >= 0); - BLI_assert(index < this->virtual_size_); - switch (this->category_) { - case VSpanCategory::Single: - return *this->data_.single.data; - case VSpanCategory::FullArray: - return this->data_.full_array.data[index]; - case VSpanCategory::FullPointerArray: - return *this->data_.full_pointer_array.data[index]; - } - BLI_assert(false); - return *this->data_.single.data; - } - - const T &as_single_element() const - { - BLI_assert(this->is_single_element()); - return (*this)[0]; - } - - Span<T> as_full_array() const - { - BLI_assert(this->is_full_array()); - if (this->virtual_size_ == 0) { - return Span<T>(); - } - const T *data = &(*this)[0]; - return Span<T>(data, this->virtual_size_); - } -}; - -/** - * A generic virtual span. It behaves like a blender::Span<T>, but the type is only known at - * run-time and it might not be backed up by an actual array. - */ -class GVSpan : public VSpanBase<void> { - private: - const CPPType *type_; - - GVSpan() = default; - - public: - GVSpan(const CPPType &type) - { - this->type_ = &type; - this->virtual_size_ = 0; - this->category_ = VSpanCategory::FullArray; - this->data_.full_array.data = nullptr; - } - - GVSpan(GSpan values) - { - this->type_ = &values.type(); - this->virtual_size_ = values.size(); - this->category_ = VSpanCategory::FullArray; - this->data_.full_array.data = values.data(); - } - - GVSpan(GMutableSpan values) : GVSpan(GSpan(values)) - { - } - - template<typename T> GVSpan(const VSpanBase<T> &values) - { - this->type_ = &CPPType::get<T>(); - memcpy(this, &values, sizeof(VSpanBase<void>)); - } - - template<typename T> GVSpan(Span<T> values) : GVSpan(GSpan(values)) - { - } - - template<typename T> GVSpan(MutableSpan<T> values) : GVSpan(GSpan(values)) - { - } - - static GVSpan FromSingle(const CPPType &type, const void *value, int64_t virtual_size) - { - GVSpan ref; - ref.type_ = &type; - ref.virtual_size_ = virtual_size; - ref.category_ = VSpanCategory::Single; - ref.data_.single.data = value; - return ref; - } - - static GVSpan FromSingleWithMaxSize(const CPPType &type, const void *value) - { - return GVSpan::FromSingle(type, value, INT64_MAX); - } - - static GVSpan FromDefault(const CPPType &type) - { - return GVSpan::FromSingleWithMaxSize(type, type.default_value()); - } - - static GVSpan FromFullPointerArray(const CPPType &type, const void *const *values, int64_t size) - { - GVSpan ref; - ref.type_ = &type; - ref.virtual_size_ = size; - ref.category_ = VSpanCategory::FullPointerArray; - ref.data_.full_pointer_array.data = values; - return ref; - } - - const CPPType &type() const - { - return *this->type_; - } - - const void *operator[](int64_t index) const - { - BLI_assert(index >= 0); - BLI_assert(index < this->virtual_size_); - switch (this->category_) { - case VSpanCategory::Single: - return this->data_.single.data; - case VSpanCategory::FullArray: - return POINTER_OFFSET(this->data_.full_array.data, index * type_->size()); - case VSpanCategory::FullPointerArray: - return this->data_.full_pointer_array.data[index]; - } - BLI_assert(false); - return this->data_.single.data; - } - - template<typename T> VSpan<T> typed() const - { - BLI_assert(type_->is<T>()); - return VSpan<T>(*this); - } - - const void *as_single_element() const - { - BLI_assert(this->is_single_element()); - return (*this)[0]; - } - - GSpan as_full_array() const - { - BLI_assert(this->is_full_array()); - if (this->virtual_size_ == 0) { - return GSpan(*this->type_); - } - const void *data = (*this)[0]; - return GSpan(*this->type_, data, this->virtual_size_); - } - - void materialize_to_uninitialized(void *dst) const - { - this->materialize_to_uninitialized(IndexRange(virtual_size_), dst); - } - - void materialize_to_uninitialized(IndexMask mask, void *dst) const - { - BLI_assert(this->size() >= mask.min_array_size()); - - int64_t element_size = type_->size(); - for (int64_t i : mask) { - type_->copy_to_uninitialized((*this)[i], POINTER_OFFSET(dst, element_size * i)); - } - } -}; - -} // namespace blender::fn diff --git a/source/blender/functions/intern/generic_vector_array.cc b/source/blender/functions/intern/generic_vector_array.cc new file mode 100644 index 00000000000..b3c5517cc43 --- /dev/null +++ b/source/blender/functions/intern/generic_vector_array.cc @@ -0,0 +1,104 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "FN_generic_vector_array.hh" +#include "FN_multi_function_params.hh" +#include "FN_multi_function_signature.hh" + +namespace blender::fn { + +GVectorArray::GVectorArray(const CPPType &type, const int64_t array_size) + : type_(type), element_size_(type.size()), items_(array_size) +{ +} + +GVectorArray::~GVectorArray() +{ + if (type_.is_trivially_destructible()) { + return; + } + for (Item &item : items_) { + type_.destruct_n(item.start, item.length); + } +} + +void GVectorArray::append(const int64_t index, const void *value) +{ + Item &item = items_[index]; + if (item.length == item.capacity) { + this->realloc_to_at_least(item, item.capacity + 1); + } + + void *dst = POINTER_OFFSET(item.start, element_size_ * item.length); + type_.copy_to_uninitialized(value, dst); + item.length++; +} + +void GVectorArray::extend(const int64_t index, const GVArray &values) +{ + BLI_assert(values.type() == type_); + for (const int i : IndexRange(values.size())) { + BUFFER_FOR_CPP_TYPE_VALUE(type_, buffer); + values.get(i, buffer); + this->append(index, buffer); + type_.destruct(buffer); + } +} + +void GVectorArray::extend(const int64_t index, const GSpan values) +{ + GVArrayForGSpan varray{values}; + this->extend(index, varray); +} + +void GVectorArray::extend(IndexMask mask, const GVVectorArray &values) +{ + for (const int i : mask) { + GVArrayForGVVectorArrayIndex array{values, i}; + this->extend(i, array); + } +} + +void GVectorArray::extend(IndexMask mask, const GVectorArray &values) +{ + GVVectorArrayForGVectorArray virtual_values{values}; + this->extend(mask, virtual_values); +} + +GMutableSpan GVectorArray::operator[](const int64_t index) +{ + Item &item = items_[index]; + return GMutableSpan{type_, item.start, item.length}; +} + +GSpan GVectorArray::operator[](const int64_t index) const +{ + const Item &item = items_[index]; + return GSpan{type_, item.start, item.length}; +} + +void GVectorArray::realloc_to_at_least(Item &item, int64_t min_capacity) +{ + const int64_t new_capacity = std::max(min_capacity, item.length * 2); + + void *new_buffer = allocator_.allocate(element_size_ * new_capacity, type_.alignment()); + type_.relocate_to_initialized_n(item.start, new_buffer, item.length); + + item.start = new_buffer; + item.capacity = new_capacity; +} + +} // namespace blender::fn diff --git a/source/blender/functions/intern/generic_virtual_array.cc b/source/blender/functions/intern/generic_virtual_array.cc new file mode 100644 index 00000000000..9380eb257b2 --- /dev/null +++ b/source/blender/functions/intern/generic_virtual_array.cc @@ -0,0 +1,107 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "FN_generic_virtual_array.hh" + +namespace blender::fn { + +void GVArray::materialize_to_uninitialized(const IndexMask mask, void *dst) const +{ + for (const int64_t i : mask) { + void *elem_dst = POINTER_OFFSET(dst, type_->size() * i); + this->get_to_uninitialized(i, elem_dst); + } +} + +void GVArray::get_impl(const int64_t index, void *r_value) const +{ + type_->destruct(r_value); + this->get_to_uninitialized_impl(index, r_value); +} + +bool GVArray::is_span_impl() const +{ + return false; +} + +GSpan GVArray::get_span_impl() const +{ + BLI_assert(false); + return GSpan(*type_); +} + +bool GVArray::is_single_impl() const +{ + return false; +} + +void GVArray::get_single_impl(void *UNUSED(r_value)) const +{ + BLI_assert(false); +} + +void GVArrayForGSpan::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 +{ + type_->copy_to_uninitialized(POINTER_OFFSET(data_, element_size_ * index), r_value); +} + +bool GVArrayForGSpan::is_span_impl() const +{ + return true; +} + +GSpan GVArrayForGSpan::get_span_impl() const +{ + return GSpan(*type_, data_, size_); +} + +void GVArrayForSingleValueRef::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 +{ + type_->copy_to_uninitialized(value_, r_value); +} + +bool GVArrayForSingleValueRef::is_span_impl() const +{ + return size_ == 1; +} + +GSpan GVArrayForSingleValueRef::get_span_impl() const +{ + return GSpan{*type_, value_, 1}; +} + +bool GVArrayForSingleValueRef::is_single_impl() const +{ + return true; +} + +void GVArrayForSingleValueRef::get_single_impl(void *r_value) const +{ + type_->copy_to_initialized(value_, r_value); +} + +} // 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 new file mode 100644 index 00000000000..f6504cee41e --- /dev/null +++ b/source/blender/functions/intern/generic_virtual_vector_array.cc @@ -0,0 +1,67 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "FN_generic_virtual_vector_array.hh" + +namespace blender::fn { + +void GVArrayForGVVectorArrayIndex::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 +{ + 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 +{ + return array_.size(); +} + +void GVVectorArrayForSingleGVArray::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 +{ + return true; +} + +int64_t GVVectorArrayForSingleGSpan::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 +{ + type_->copy_to_initialized(span_[index_in_vector], r_value); +} + +bool GVVectorArrayForSingleGSpan::is_single_vector_impl() const +{ + return true; +} + +} // namespace blender::fn diff --git a/source/blender/functions/intern/multi_function_network_evaluation.cc b/source/blender/functions/intern/multi_function_network_evaluation.cc index 5a37a45908f..98500a350ae 100644 --- a/source/blender/functions/intern/multi_function_network_evaluation.cc +++ b/source/blender/functions/intern/multi_function_network_evaluation.cc @@ -37,6 +37,7 @@ #include "FN_multi_function_network_evaluation.hh" +#include "BLI_resource_collector.hh" #include "BLI_stack.hh" namespace blender::fn { @@ -62,16 +63,20 @@ class MFNetworkEvaluationStorage { ~MFNetworkEvaluationStorage(); /* Add the values that have been provided by the caller of the multi-function network. */ - void add_single_input_from_caller(const MFOutputSocket &socket, GVSpan virtual_span); - void add_vector_input_from_caller(const MFOutputSocket &socket, GVArraySpan virtual_array_span); + void add_single_input_from_caller(const MFOutputSocket &socket, const GVArray &virtual_array); + void add_vector_input_from_caller(const MFOutputSocket &socket, + const GVVectorArray &virtual_vector_array); void add_single_output_from_caller(const MFOutputSocket &socket, GMutableSpan span); void add_vector_output_from_caller(const MFOutputSocket &socket, GVectorArray &vector_array); /* Get input buffers for function node evaluations. */ - GVSpan get_single_input__full(const MFInputSocket &socket); - GVSpan get_single_input__single(const MFInputSocket &socket); - GVArraySpan get_vector_input__full(const MFInputSocket &socket); - GVArraySpan get_vector_input__single(const MFInputSocket &socket); + const GVArray &get_single_input__full(const MFInputSocket &socket, ResourceCollector &resources); + const GVArray &get_single_input__single(const MFInputSocket &socket, + ResourceCollector &resources); + const GVVectorArray &get_vector_input__full(const MFInputSocket &socket, + ResourceCollector &resources); + const GVVectorArray &get_vector_input__single(const MFInputSocket &socket, + ResourceCollector &resources); /* Get output buffers for function node evaluations. */ GMutableSpan get_single_output__full(const MFOutputSocket &socket); @@ -80,12 +85,18 @@ class MFNetworkEvaluationStorage { GVectorArray &get_vector_output__single(const MFOutputSocket &socket); /* Get mutable buffers for function node evaluations. */ - GMutableSpan get_mutable_single__full(const MFInputSocket &input, const MFOutputSocket &output); + GMutableSpan get_mutable_single__full(const MFInputSocket &input, + const MFOutputSocket &output, + ResourceCollector &resources); GMutableSpan get_mutable_single__single(const MFInputSocket &input, - const MFOutputSocket &output); - GVectorArray &get_mutable_vector__full(const MFInputSocket &input, const MFOutputSocket &output); + const MFOutputSocket &output, + ResourceCollector &resources); + GVectorArray &get_mutable_vector__full(const MFInputSocket &input, + const MFOutputSocket &output, + ResourceCollector &resources); GVectorArray &get_mutable_vector__single(const MFInputSocket &input, - const MFOutputSocket &output); + const MFOutputSocket &output, + ResourceCollector &resources); /* Mark a node as being done with evaluation. This might free temporary buffers that are no * longer needed. */ @@ -160,12 +171,12 @@ BLI_NOINLINE void MFNetworkEvaluator::copy_inputs_to_storage(MFParams params, const MFOutputSocket &socket = *inputs_[input_index]; switch (socket.data_type().category()) { case MFDataType::Single: { - GVSpan input_list = params.readonly_single_input(param_index); + const GVArray &input_list = params.readonly_single_input(param_index); storage.add_single_input_from_caller(socket, input_list); break; } case MFDataType::Vector: { - GVArraySpan input_list_list = params.readonly_vector_input(param_index); + const GVVectorArray &input_list_list = params.readonly_vector_input(param_index); storage.add_vector_input_from_caller(socket, input_list_list); break; } @@ -255,6 +266,7 @@ BLI_NOINLINE void MFNetworkEvaluator::evaluate_function(MFContext &global_contex const MFFunctionNode &function_node, Storage &storage) const { + const MultiFunction &function = function_node.function(); // std::cout << "Function: " << function.name() << "\n"; @@ -262,19 +274,20 @@ BLI_NOINLINE void MFNetworkEvaluator::evaluate_function(MFContext &global_contex /* The function output would be the same for all elements. Therefore, it is enough to call the * function only on a single element. This can avoid many duplicate computations. */ MFParamsBuilder params{function, 1}; + ResourceCollector &resources = params.resources(); for (int param_index : function.param_indices()) { MFParamType param_type = function.param_type(param_index); switch (param_type.category()) { case MFParamType::SingleInput: { const MFInputSocket &socket = function_node.input_for_param(param_index); - GVSpan values = storage.get_single_input__single(socket); + const GVArray &values = storage.get_single_input__single(socket, resources); params.add_readonly_single_input(values); break; } case MFParamType::VectorInput: { const MFInputSocket &socket = function_node.input_for_param(param_index); - GVArraySpan values = storage.get_vector_input__single(socket); + const GVVectorArray &values = storage.get_vector_input__single(socket, resources); params.add_readonly_vector_input(values); break; } @@ -293,14 +306,14 @@ BLI_NOINLINE void MFNetworkEvaluator::evaluate_function(MFContext &global_contex case MFParamType::SingleMutable: { const MFInputSocket &input = function_node.input_for_param(param_index); const MFOutputSocket &output = function_node.output_for_param(param_index); - GMutableSpan values = storage.get_mutable_single__single(input, output); + GMutableSpan values = storage.get_mutable_single__single(input, output, resources); params.add_single_mutable(values); break; } case MFParamType::VectorMutable: { const MFInputSocket &input = function_node.input_for_param(param_index); const MFOutputSocket &output = function_node.output_for_param(param_index); - GVectorArray &values = storage.get_mutable_vector__single(input, output); + GVectorArray &values = storage.get_mutable_vector__single(input, output, resources); params.add_vector_mutable(values); break; } @@ -311,19 +324,20 @@ BLI_NOINLINE void MFNetworkEvaluator::evaluate_function(MFContext &global_contex } else { MFParamsBuilder params{function, storage.mask().min_array_size()}; + ResourceCollector &resources = params.resources(); for (int param_index : function.param_indices()) { MFParamType param_type = function.param_type(param_index); switch (param_type.category()) { case MFParamType::SingleInput: { const MFInputSocket &socket = function_node.input_for_param(param_index); - GVSpan values = storage.get_single_input__full(socket); + const GVArray &values = storage.get_single_input__full(socket, resources); params.add_readonly_single_input(values); break; } case MFParamType::VectorInput: { const MFInputSocket &socket = function_node.input_for_param(param_index); - GVArraySpan values = storage.get_vector_input__full(socket); + const GVVectorArray &values = storage.get_vector_input__full(socket, resources); params.add_readonly_vector_input(values); break; } @@ -342,14 +356,14 @@ BLI_NOINLINE void MFNetworkEvaluator::evaluate_function(MFContext &global_contex case MFParamType::SingleMutable: { const MFInputSocket &input = function_node.input_for_param(param_index); const MFOutputSocket &output = function_node.output_for_param(param_index); - GMutableSpan values = storage.get_mutable_single__full(input, output); + GMutableSpan values = storage.get_mutable_single__full(input, output, resources); params.add_single_mutable(values); break; } case MFParamType::VectorMutable: { const MFInputSocket &input = function_node.input_for_param(param_index); const MFOutputSocket &output = function_node.output_for_param(param_index); - GVectorArray &values = storage.get_mutable_vector__full(input, output); + GVectorArray &values = storage.get_mutable_vector__full(input, output, resources); params.add_vector_mutable(values); break; } @@ -383,18 +397,19 @@ bool MFNetworkEvaluator::can_do_single_value_evaluation(const MFFunctionNode &fu BLI_NOINLINE void MFNetworkEvaluator::initialize_remaining_outputs( MFParams params, Storage &storage, Span<const MFInputSocket *> remaining_outputs) const { + ResourceCollector resources; for (const MFInputSocket *socket : remaining_outputs) { int param_index = inputs_.size() + outputs_.first_index_of(socket); switch (socket->data_type().category()) { case MFDataType::Single: { - GVSpan values = storage.get_single_input__full(*socket); + const GVArray &values = storage.get_single_input__full(*socket, resources); GMutableSpan output_values = params.uninitialized_single_output(param_index); values.materialize_to_uninitialized(storage.mask(), output_values.data()); break; } case MFDataType::Vector: { - GVArraySpan values = storage.get_vector_input__full(*socket); + const GVVectorArray &values = storage.get_vector_input__full(*socket, resources); GVectorArray &output_values = params.vector_output(param_index); output_values.extend(storage.mask(), values); break; @@ -425,20 +440,21 @@ struct Value { }; struct InputSingleValue : public Value { - /** This span has been provided by the code that called the multi-function network. */ - GVSpan virtual_span; + /** This virtual array has been provided by the code that called the multi-function network. */ + const GVArray &virtual_array; - InputSingleValue(GVSpan virtual_span) : Value(ValueType::InputSingle), virtual_span(virtual_span) + InputSingleValue(const GVArray &virtual_array) + : Value(ValueType::InputSingle), virtual_array(virtual_array) { } }; struct InputVectorValue : public Value { - /** This span has been provided by the code that called the multi-function network. */ - GVArraySpan virtual_array_span; + /** This virtual vector has been provided by the code that called the multi-function network. */ + const GVVectorArray &virtual_vector_array; - InputVectorValue(GVArraySpan virtual_array_span) - : Value(ValueType::InputVector), virtual_array_span(virtual_array_span) + InputVectorValue(const GVVectorArray &virtual_vector_array) + : Value(ValueType::InputVector), virtual_vector_array(virtual_vector_array) { } }; @@ -564,9 +580,9 @@ bool MFNetworkEvaluationStorage::is_same_value_for_every_index(const MFOutputSoc case ValueType::OwnVector: return static_cast<OwnVectorValue *>(any_value)->vector_array->size() == 1; case ValueType::InputSingle: - return static_cast<InputSingleValue *>(any_value)->virtual_span.is_single_element(); + return static_cast<InputSingleValue *>(any_value)->virtual_array.is_single(); case ValueType::InputVector: - return static_cast<InputVectorValue *>(any_value)->virtual_array_span.is_single_array(); + return static_cast<InputVectorValue *>(any_value)->virtual_vector_array.is_single_vector(); case ValueType::OutputSingle: return static_cast<OutputSingleValue *>(any_value)->span.size() == 1; case ValueType::OutputVector: @@ -658,22 +674,22 @@ void MFNetworkEvaluationStorage::finish_input_socket(const MFInputSocket &socket } void MFNetworkEvaluationStorage::add_single_input_from_caller(const MFOutputSocket &socket, - GVSpan virtual_span) + const GVArray &virtual_array) { BLI_assert(value_per_output_id_[socket.id()] == nullptr); - BLI_assert(virtual_span.size() >= min_array_size_); + BLI_assert(virtual_array.size() >= min_array_size_); - auto *value = allocator_.construct<InputSingleValue>(virtual_span).release(); + auto *value = allocator_.construct<InputSingleValue>(virtual_array).release(); value_per_output_id_[socket.id()] = value; } -void MFNetworkEvaluationStorage::add_vector_input_from_caller(const MFOutputSocket &socket, - GVArraySpan virtual_array_span) +void MFNetworkEvaluationStorage::add_vector_input_from_caller( + const MFOutputSocket &socket, const GVVectorArray &virtual_vector_array) { BLI_assert(value_per_output_id_[socket.id()] == nullptr); - BLI_assert(virtual_array_span.size() >= min_array_size_); + BLI_assert(virtual_vector_array.size() >= min_array_size_); - auto *value = allocator_.construct<InputVectorValue>(virtual_array_span).release(); + auto *value = allocator_.construct<InputVectorValue>(virtual_vector_array).release(); value_per_output_id_[socket.id()] = value; } @@ -776,7 +792,8 @@ GVectorArray &MFNetworkEvaluationStorage::get_vector_output__single(const MFOutp } GMutableSpan MFNetworkEvaluationStorage::get_mutable_single__full(const MFInputSocket &input, - const MFOutputSocket &output) + const MFOutputSocket &output, + ResourceCollector &resources) { const MFOutputSocket &from = *input.origin(); const MFOutputSocket &to = output; @@ -790,8 +807,8 @@ GMutableSpan MFNetworkEvaluationStorage::get_mutable_single__full(const MFInputS if (to_any_value != nullptr) { BLI_assert(to_any_value->type == ValueType::OutputSingle); GMutableSpan span = static_cast<OutputSingleValue *>(to_any_value)->span; - GVSpan virtual_span = this->get_single_input__full(input); - virtual_span.materialize_to_uninitialized(mask_, span.data()); + const GVArray &virtual_array = this->get_single_input__full(input, resources); + virtual_array.materialize_to_uninitialized(mask_, span.data()); return span; } @@ -805,10 +822,10 @@ GMutableSpan MFNetworkEvaluationStorage::get_mutable_single__full(const MFInputS } } - GVSpan virtual_span = this->get_single_input__full(input); + const GVArray &virtual_array = this->get_single_input__full(input, resources); void *new_buffer = MEM_mallocN_aligned(min_array_size_ * type.size(), type.alignment(), AT); GMutableSpan new_array_ref(type, new_buffer, min_array_size_); - virtual_span.materialize_to_uninitialized(mask_, new_array_ref.data()); + virtual_array.materialize_to_uninitialized(mask_, new_array_ref.data()); OwnSingleValue *new_value = allocator_.construct<OwnSingleValue>(new_array_ref, to.targets().size(), false).release(); @@ -817,7 +834,8 @@ GMutableSpan MFNetworkEvaluationStorage::get_mutable_single__full(const MFInputS } GMutableSpan MFNetworkEvaluationStorage::get_mutable_single__single(const MFInputSocket &input, - const MFOutputSocket &output) + const MFOutputSocket &output, + ResourceCollector &resources) { const MFOutputSocket &from = *input.origin(); const MFOutputSocket &to = output; @@ -832,8 +850,8 @@ GMutableSpan MFNetworkEvaluationStorage::get_mutable_single__single(const MFInpu BLI_assert(to_any_value->type == ValueType::OutputSingle); GMutableSpan span = static_cast<OutputSingleValue *>(to_any_value)->span; BLI_assert(span.size() == 1); - GVSpan virtual_span = this->get_single_input__single(input); - type.copy_to_uninitialized(virtual_span.as_single_element(), span[0]); + const GVArray &virtual_array = this->get_single_input__single(input, resources); + virtual_array.get_single_to_uninitialized(span[0]); return span; } @@ -848,10 +866,10 @@ GMutableSpan MFNetworkEvaluationStorage::get_mutable_single__single(const MFInpu } } - GVSpan virtual_span = this->get_single_input__single(input); + const GVArray &virtual_array = this->get_single_input__single(input, resources); void *new_buffer = allocator_.allocate(type.size(), type.alignment()); - type.copy_to_uninitialized(virtual_span.as_single_element(), new_buffer); + virtual_array.get_single_to_uninitialized(new_buffer); GMutableSpan new_array_ref(type, new_buffer, 1); OwnSingleValue *new_value = @@ -861,7 +879,8 @@ GMutableSpan MFNetworkEvaluationStorage::get_mutable_single__single(const MFInpu } GVectorArray &MFNetworkEvaluationStorage::get_mutable_vector__full(const MFInputSocket &input, - const MFOutputSocket &output) + const MFOutputSocket &output, + ResourceCollector &resources) { const MFOutputSocket &from = *input.origin(); const MFOutputSocket &to = output; @@ -875,8 +894,8 @@ GVectorArray &MFNetworkEvaluationStorage::get_mutable_vector__full(const MFInput if (to_any_value != nullptr) { BLI_assert(to_any_value->type == ValueType::OutputVector); GVectorArray &vector_array = *static_cast<OutputVectorValue *>(to_any_value)->vector_array; - GVArraySpan virtual_array_span = this->get_vector_input__full(input); - vector_array.extend(mask_, virtual_array_span); + const GVVectorArray &virtual_vector_array = this->get_vector_input__full(input, resources); + vector_array.extend(mask_, virtual_vector_array); return vector_array; } @@ -890,10 +909,10 @@ GVectorArray &MFNetworkEvaluationStorage::get_mutable_vector__full(const MFInput } } - GVArraySpan virtual_array_span = this->get_vector_input__full(input); + const GVVectorArray &virtual_vector_array = this->get_vector_input__full(input, resources); GVectorArray *new_vector_array = new GVectorArray(base_type, min_array_size_); - new_vector_array->extend(mask_, virtual_array_span); + new_vector_array->extend(mask_, virtual_vector_array); OwnVectorValue *new_value = allocator_.construct<OwnVectorValue>(*new_vector_array, to.targets().size()).release(); @@ -903,7 +922,8 @@ GVectorArray &MFNetworkEvaluationStorage::get_mutable_vector__full(const MFInput } GVectorArray &MFNetworkEvaluationStorage::get_mutable_vector__single(const MFInputSocket &input, - const MFOutputSocket &output) + const MFOutputSocket &output, + ResourceCollector &resources) { const MFOutputSocket &from = *input.origin(); const MFOutputSocket &to = output; @@ -918,8 +938,8 @@ GVectorArray &MFNetworkEvaluationStorage::get_mutable_vector__single(const MFInp BLI_assert(to_any_value->type == ValueType::OutputVector); GVectorArray &vector_array = *static_cast<OutputVectorValue *>(to_any_value)->vector_array; BLI_assert(vector_array.size() == 1); - GVArraySpan virtual_array_span = this->get_vector_input__single(input); - vector_array.extend(0, virtual_array_span[0]); + const GVVectorArray &virtual_vector_array = this->get_vector_input__single(input, resources); + vector_array.extend({0}, virtual_vector_array); return vector_array; } @@ -933,10 +953,10 @@ GVectorArray &MFNetworkEvaluationStorage::get_mutable_vector__single(const MFInp } } - GVArraySpan virtual_array_span = this->get_vector_input__single(input); + const GVVectorArray &virtual_vector_array = this->get_vector_input__single(input, resources); GVectorArray *new_vector_array = new GVectorArray(base_type, 1); - new_vector_array->extend(0, virtual_array_span[0]); + new_vector_array->extend({0}, virtual_vector_array); OwnVectorValue *new_value = allocator_.construct<OwnVectorValue>(*new_vector_array, to.targets().size()).release(); @@ -944,7 +964,8 @@ GVectorArray &MFNetworkEvaluationStorage::get_mutable_vector__single(const MFInp return *new_vector_array; } -GVSpan MFNetworkEvaluationStorage::get_single_input__full(const MFInputSocket &socket) +const GVArray &MFNetworkEvaluationStorage::get_single_input__full(const MFInputSocket &socket, + ResourceCollector &resources) { const MFOutputSocket &origin = *socket.origin(); Value *any_value = value_per_output_id_[origin.id()]; @@ -953,26 +974,28 @@ GVSpan MFNetworkEvaluationStorage::get_single_input__full(const MFInputSocket &s if (any_value->type == ValueType::OwnSingle) { OwnSingleValue *value = static_cast<OwnSingleValue *>(any_value); if (value->is_single_allocated) { - return GVSpan::FromSingle(value->span.type(), value->span.data(), min_array_size_); + return resources.construct<GVArrayForSingleValueRef>( + __func__, value->span.type(), min_array_size_, value->span.data()); } - return value->span; + return resources.construct<GVArrayForGSpan>(__func__, value->span); } if (any_value->type == ValueType::InputSingle) { InputSingleValue *value = static_cast<InputSingleValue *>(any_value); - return value->virtual_span; + return value->virtual_array; } if (any_value->type == ValueType::OutputSingle) { OutputSingleValue *value = static_cast<OutputSingleValue *>(any_value); BLI_assert(value->is_computed); - return value->span; + return resources.construct<GVArrayForGSpan>(__func__, value->span); } BLI_assert(false); - return GVSpan(CPPType::get<float>()); + return resources.construct<GVArrayForEmpty>(__func__, CPPType::get<float>()); } -GVSpan MFNetworkEvaluationStorage::get_single_input__single(const MFInputSocket &socket) +const GVArray &MFNetworkEvaluationStorage::get_single_input__single(const MFInputSocket &socket, + ResourceCollector &resources) { const MFOutputSocket &origin = *socket.origin(); Value *any_value = value_per_output_id_[origin.id()]; @@ -981,25 +1004,26 @@ GVSpan MFNetworkEvaluationStorage::get_single_input__single(const MFInputSocket if (any_value->type == ValueType::OwnSingle) { OwnSingleValue *value = static_cast<OwnSingleValue *>(any_value); BLI_assert(value->span.size() == 1); - return value->span; + return resources.construct<GVArrayForGSpan>(__func__, value->span); } if (any_value->type == ValueType::InputSingle) { InputSingleValue *value = static_cast<InputSingleValue *>(any_value); - BLI_assert(value->virtual_span.is_single_element()); - return value->virtual_span; + BLI_assert(value->virtual_array.is_single()); + return value->virtual_array; } if (any_value->type == ValueType::OutputSingle) { OutputSingleValue *value = static_cast<OutputSingleValue *>(any_value); BLI_assert(value->is_computed); BLI_assert(value->span.size() == 1); - return value->span; + return resources.construct<GVArrayForGSpan>(__func__, value->span); } BLI_assert(false); - return GVSpan(CPPType::get<float>()); + return resources.construct<GVArrayForEmpty>(__func__, CPPType::get<float>()); } -GVArraySpan MFNetworkEvaluationStorage::get_vector_input__full(const MFInputSocket &socket) +const GVVectorArray &MFNetworkEvaluationStorage::get_vector_input__full( + const MFInputSocket &socket, ResourceCollector &resources) { const MFOutputSocket &origin = *socket.origin(); Value *any_value = value_per_output_id_[origin.id()]; @@ -1009,25 +1033,27 @@ GVArraySpan MFNetworkEvaluationStorage::get_vector_input__full(const MFInputSock OwnVectorValue *value = static_cast<OwnVectorValue *>(any_value); if (value->vector_array->size() == 1) { GSpan span = (*value->vector_array)[0]; - return GVArraySpan(span, min_array_size_); + return resources.construct<GVVectorArrayForSingleGSpan>(__func__, span, min_array_size_); } - return *value->vector_array; + return resources.construct<GVVectorArrayForGVectorArray>(__func__, *value->vector_array); } if (any_value->type == ValueType::InputVector) { InputVectorValue *value = static_cast<InputVectorValue *>(any_value); - return value->virtual_array_span; + return value->virtual_vector_array; } if (any_value->type == ValueType::OutputVector) { OutputVectorValue *value = static_cast<OutputVectorValue *>(any_value); - return *value->vector_array; + return resources.construct<GVVectorArrayForGVectorArray>(__func__, *value->vector_array); } BLI_assert(false); - return GVArraySpan(CPPType::get<float>()); + return resources.construct<GVVectorArrayForSingleGSpan>( + __func__, GSpan(CPPType::get<float>()), 0); } -GVArraySpan MFNetworkEvaluationStorage::get_vector_input__single(const MFInputSocket &socket) +const GVVectorArray &MFNetworkEvaluationStorage::get_vector_input__single( + const MFInputSocket &socket, ResourceCollector &resources) { const MFOutputSocket &origin = *socket.origin(); Value *any_value = value_per_output_id_[origin.id()]; @@ -1036,21 +1062,22 @@ GVArraySpan MFNetworkEvaluationStorage::get_vector_input__single(const MFInputSo if (any_value->type == ValueType::OwnVector) { OwnVectorValue *value = static_cast<OwnVectorValue *>(any_value); BLI_assert(value->vector_array->size() == 1); - return *value->vector_array; + return resources.construct<GVVectorArrayForGVectorArray>(__func__, *value->vector_array); } if (any_value->type == ValueType::InputVector) { InputVectorValue *value = static_cast<InputVectorValue *>(any_value); - BLI_assert(value->virtual_array_span.is_single_array()); - return value->virtual_array_span; + BLI_assert(value->virtual_vector_array.is_single_vector()); + return value->virtual_vector_array; } if (any_value->type == ValueType::OutputVector) { OutputVectorValue *value = static_cast<OutputVectorValue *>(any_value); BLI_assert(value->vector_array->size() == 1); - return *value->vector_array; + return resources.construct<GVVectorArrayForGVectorArray>(__func__, *value->vector_array); } BLI_assert(false); - return GVArraySpan(CPPType::get<float>()); + return resources.construct<GVVectorArrayForSingleGSpan>( + __func__, GSpan(CPPType::get<float>()), 0); } /** \} */ diff --git a/source/blender/functions/tests/FN_array_spans_test.cc b/source/blender/functions/tests/FN_array_spans_test.cc deleted file mode 100644 index af2bc0aad91..00000000000 --- a/source/blender/functions/tests/FN_array_spans_test.cc +++ /dev/null @@ -1,134 +0,0 @@ -/* Apache License, Version 2.0 */ - -#include "testing/testing.h" - -#include "FN_array_spans.hh" -#include "FN_generic_vector_array.hh" - -#include "BLI_array.hh" - -namespace blender::fn::tests { - -TEST(virtual_array_span, EmptyConstructor) -{ - VArraySpan<int> span; - EXPECT_EQ(span.size(), 0); - EXPECT_TRUE(span.is_empty()); - - GVArraySpan converted(span); - EXPECT_EQ(converted.type(), CPPType::get<int>()); - EXPECT_EQ(converted.size(), 0); -} - -TEST(virtual_array_span, SingleArrayConstructor) -{ - std::array<int, 4> values = {3, 4, 5, 6}; - VArraySpan<int> span{Span<int>(values), 3}; - EXPECT_EQ(span.size(), 3); - EXPECT_FALSE(span.is_empty()); - EXPECT_EQ(span[0].size(), 4); - EXPECT_EQ(span[1].size(), 4); - EXPECT_EQ(span[2].size(), 4); - EXPECT_EQ(span[0][0], 3); - EXPECT_EQ(span[0][1], 4); - EXPECT_EQ(span[0][2], 5); - EXPECT_EQ(span[0][3], 6); - EXPECT_EQ(span[1][3], 6); - EXPECT_EQ(span[2][1], 4); - - GVArraySpan converted(span); - EXPECT_EQ(converted.type(), CPPType::get<int>()); - EXPECT_EQ(converted.size(), 3); - EXPECT_EQ(converted[0].size(), 4); - EXPECT_EQ(converted[1].size(), 4); - EXPECT_EQ(converted[1][2], &values[2]); -} - -TEST(virtual_array_span, MultipleArrayConstructor) -{ - std::array<int, 4> values0 = {1, 2, 3, 4}; - std::array<int, 2> values1 = {6, 7}; - std::array<int, 1> values2 = {8}; - std::array<const int *, 3> starts = {values0.data(), values1.data(), values2.data()}; - std::array<int64_t, 3> sizes{static_cast<int64_t>(values0.size()), - static_cast<int64_t>(values1.size()), - static_cast<int64_t>(values2.size())}; - - VArraySpan<int> span{starts, sizes}; - EXPECT_EQ(span.size(), 3); - EXPECT_FALSE(span.is_empty()); - EXPECT_EQ(span[0].size(), 4); - EXPECT_EQ(span[1].size(), 2); - EXPECT_EQ(span[2].size(), 1); - EXPECT_EQ(&span[0][0], values0.data()); - EXPECT_EQ(&span[1][0], values1.data()); - EXPECT_EQ(&span[2][0], values2.data()); - EXPECT_EQ(span[2][0], 8); - EXPECT_EQ(span[1][1], 7); - - GVArraySpan converted(span); - EXPECT_EQ(converted.type(), CPPType::get<int>()); - EXPECT_EQ(converted.size(), 3); - EXPECT_EQ(converted[0].size(), 4); - EXPECT_EQ(converted[1].size(), 2); - EXPECT_EQ(converted[2].size(), 1); - EXPECT_EQ(converted[0][0], values0.data()); - EXPECT_EQ(converted[1][1], values1.data() + 1); -} - -TEST(generic_virtual_array_span, TypeConstructor) -{ - GVArraySpan span{CPPType::get<int32_t>()}; - EXPECT_EQ(span.size(), 0); - EXPECT_TRUE(span.is_empty()); - - VArraySpan converted = span.typed<int>(); - EXPECT_EQ(converted.size(), 0); -} - -TEST(generic_virtual_array_span, GSpanConstructor) -{ - std::array<std::string, 3> values = {"hello", "world", "test"}; - GVArraySpan span{GSpan(CPPType::get<std::string>(), values.data(), 3), 5}; - EXPECT_EQ(span.size(), 5); - EXPECT_FALSE(span.is_empty()); - EXPECT_EQ(span[0][0], values.data()); - EXPECT_EQ(span[1][0], values.data()); - EXPECT_EQ(span[4][0], values.data()); - EXPECT_EQ(span[0].size(), 3); - EXPECT_EQ(span[2].size(), 3); - EXPECT_EQ(*(std::string *)span[3][1], "world"); - - VArraySpan converted = span.typed<std::string>(); - EXPECT_EQ(converted.size(), 5); - EXPECT_EQ(converted[0][0], "hello"); - EXPECT_EQ(converted[1][0], "hello"); - EXPECT_EQ(converted[4][0], "hello"); - EXPECT_EQ(converted[0].size(), 3); - EXPECT_EQ(converted[2].size(), 3); -} - -TEST(generic_virtual_array_span, IsSingleArray1) -{ - Array<int> values = {5, 6, 7}; - GVArraySpan span{GSpan(values.as_span()), 4}; - EXPECT_TRUE(span.is_single_array()); - - VArraySpan converted = span.typed<int>(); - EXPECT_TRUE(converted.is_single_array()); -} - -TEST(generic_virtual_array_span, IsSingleArray2) -{ - GVectorArray vectors{CPPType::get<int32_t>(), 3}; - GVectorArrayRef<int> vectors_ref = vectors; - vectors_ref.append(1, 4); - - GVArraySpan span = vectors; - EXPECT_FALSE(span.is_single_array()); - - VArraySpan converted = span.typed<int>(); - EXPECT_FALSE(converted.is_single_array()); -} - -} // namespace blender::fn::tests diff --git a/source/blender/functions/tests/FN_generic_span_test.cc b/source/blender/functions/tests/FN_generic_span_test.cc new file mode 100644 index 00000000000..81057ee9c4f --- /dev/null +++ b/source/blender/functions/tests/FN_generic_span_test.cc @@ -0,0 +1,53 @@ +/* Apache License, Version 2.0 */ + +#include "testing/testing.h" + +#include "FN_generic_span.hh" + +namespace blender::fn::tests { + +TEST(generic_span, TypeConstructor) +{ + GSpan span(CPPType::get<float>()); + EXPECT_EQ(span.size(), 0); + EXPECT_EQ(span.typed<float>().size(), 0); + EXPECT_TRUE(span.is_empty()); +} + +TEST(generic_span, BufferAndSizeConstructor) +{ + int values[4] = {6, 7, 3, 2}; + void *buffer = (void *)values; + GSpan span(CPPType::get<int32_t>(), buffer, 4); + EXPECT_EQ(span.size(), 4); + EXPECT_FALSE(span.is_empty()); + EXPECT_EQ(span.typed<int>().size(), 4); + EXPECT_EQ(span[0], &values[0]); + EXPECT_EQ(span[1], &values[1]); + EXPECT_EQ(span[2], &values[2]); + EXPECT_EQ(span[3], &values[3]); +} + +TEST(generic_mutable_span, TypeConstructor) +{ + GMutableSpan span(CPPType::get<int32_t>()); + EXPECT_EQ(span.size(), 0); + EXPECT_TRUE(span.is_empty()); +} + +TEST(generic_mutable_span, BufferAndSizeConstructor) +{ + int values[4] = {4, 7, 3, 5}; + void *buffer = (void *)values; + GMutableSpan span(CPPType::get<int32_t>(), buffer, 4); + EXPECT_EQ(span.size(), 4); + EXPECT_FALSE(span.is_empty()); + EXPECT_EQ(span.typed<int>().size(), 4); + EXPECT_EQ(values[2], 3); + *(int *)span[2] = 10; + EXPECT_EQ(values[2], 10); + span.typed<int>()[2] = 20; + EXPECT_EQ(values[2], 20); +} + +} // namespace blender::fn::tests diff --git a/source/blender/functions/tests/FN_generic_vector_array_test.cc b/source/blender/functions/tests/FN_generic_vector_array_test.cc index 77ec05f12dc..3e78aef4841 100644 --- a/source/blender/functions/tests/FN_generic_vector_array_test.cc +++ b/source/blender/functions/tests/FN_generic_vector_array_test.cc @@ -1,101 +1,43 @@ /* Apache License, Version 2.0 */ -#include "FN_generic_vector_array.hh" - #include "testing/testing.h" +#include "FN_generic_vector_array.hh" + namespace blender::fn::tests { -TEST(generic_vector_array, Constructor) +TEST(generic_vector_array, Construct) { - GVectorArray vectors{CPPType::get<int32_t>(), 3}; - EXPECT_EQ(vectors.size(), 3); - EXPECT_EQ(vectors.lengths().size(), 3); - EXPECT_EQ(vectors.starts().size(), 3); - EXPECT_EQ(vectors.lengths()[0], 0); - EXPECT_EQ(vectors.lengths()[1], 0); - EXPECT_EQ(vectors.lengths()[2], 0); - EXPECT_EQ(vectors.type(), CPPType::get<int32_t>()); + GVectorArray vector_array{CPPType::get<int>(), 4}; + EXPECT_EQ(vector_array.size(), 4); + EXPECT_FALSE(vector_array.is_empty()); } TEST(generic_vector_array, Append) { - GVectorArray vectors{CPPType::get<std::string>(), 3}; - std::string value = "hello"; - vectors.append(0, &value); - value = "world"; - vectors.append(0, &value); - vectors.append(2, &value); - - EXPECT_EQ(vectors.lengths()[0], 2); - EXPECT_EQ(vectors.lengths()[1], 0); - EXPECT_EQ(vectors.lengths()[2], 1); - EXPECT_EQ(vectors[0].size(), 2); - EXPECT_EQ(vectors[0].typed<std::string>()[0], "hello"); - EXPECT_EQ(vectors[0].typed<std::string>()[1], "world"); - EXPECT_EQ(vectors[2].typed<std::string>()[0], "world"); -} - -TEST(generic_vector_array, AsArraySpan) -{ - GVectorArray vectors{CPPType::get<int32_t>(), 3}; - int value = 3; - vectors.append(0, &value); - vectors.append(0, &value); - value = 5; - vectors.append(2, &value); - vectors.append(2, &value); - vectors.append(2, &value); - - GVArraySpan span = vectors; - EXPECT_EQ(span.type(), CPPType::get<int32_t>()); - EXPECT_EQ(span.size(), 3); - EXPECT_EQ(span[0].size(), 2); - EXPECT_EQ(span[1].size(), 0); - EXPECT_EQ(span[2].size(), 3); - EXPECT_EQ(span[0].typed<int>()[1], 3); - EXPECT_EQ(span[2].typed<int>()[0], 5); -} - -TEST(generic_vector_array, TypedRef) -{ - GVectorArray vectors{CPPType::get<int32_t>(), 4}; - GVectorArrayRef<int> ref = vectors.typed<int>(); - ref.append(0, 2); - ref.append(0, 6); - ref.append(0, 7); - ref.append(2, 1); - ref.append(2, 1); - ref.append(3, 5); - ref.append(3, 6); - - EXPECT_EQ(ref[0].size(), 3); - EXPECT_EQ(vectors[0].size(), 3); - EXPECT_EQ(ref[0][0], 2); - EXPECT_EQ(ref[0][1], 6); - EXPECT_EQ(ref[0][2], 7); - EXPECT_EQ(ref[1].size(), 0); - EXPECT_EQ(ref[2][0], 1); - EXPECT_EQ(ref[2][1], 1); - EXPECT_EQ(ref[3][0], 5); - EXPECT_EQ(ref[3][1], 6); + GVectorArray vector_array{CPPType::get<int>(), 3}; + int value1 = 2; + vector_array.append(1, &value1); + vector_array.append(1, &value1); + int value2 = 3; + vector_array.append(0, &value2); + vector_array.append(1, &value2); + + EXPECT_EQ(vector_array[0].size(), 1); + EXPECT_EQ(vector_array[1].size(), 3); + EXPECT_EQ(vector_array[2].size(), 0); } TEST(generic_vector_array, Extend) { - GVectorArray vectors{CPPType::get<int32_t>(), 3}; - GVectorArrayRef<int> ref = vectors; - - ref.extend(1, {5, 6, 7}); - ref.extend(0, {3}); - - EXPECT_EQ(vectors[0].size(), 1); - EXPECT_EQ(vectors[1].size(), 3); - EXPECT_EQ(vectors[2].size(), 0); - EXPECT_EQ(ref[1][0], 5); - EXPECT_EQ(ref[1][1], 6); - EXPECT_EQ(ref[1][2], 7); - EXPECT_EQ(ref[0][0], 3); + GVectorArray vector_array{CPPType::get<int>(), 3}; + vector_array.extend(0, Span<int>({1, 4, 6, 4})); + vector_array.extend(1, Span<int>()); + vector_array.extend(0, Span<int>({10, 20, 30})); + + EXPECT_EQ(vector_array[0].size(), 7); + EXPECT_EQ(vector_array[1].size(), 0); + EXPECT_EQ(vector_array[2].size(), 0); } } // namespace blender::fn::tests 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 f226e0eac2e..70da0315b93 100644 --- a/source/blender/functions/tests/FN_multi_function_network_test.cc +++ b/source/blender/functions/tests/FN_multi_function_network_test.cc @@ -76,12 +76,9 @@ class ConcatVectorsFunction : public MultiFunction { void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override { - GVectorArrayRef<int> a = params.vector_mutable<int>(0); - VArraySpan<int> b = params.readonly_vector_input<int>(1); - - for (int64_t i : mask) { - a.extend(i, b[i]); - } + GVectorArray &a = params.vector_mutable(0); + const GVVectorArray &b = params.readonly_vector_input(1); + a.extend(mask, b); } }; @@ -96,8 +93,8 @@ class AppendFunction : public MultiFunction { void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override { - GVectorArrayRef<int> vectors = params.vector_mutable<int>(0); - VSpan<int> values = params.readonly_single_input<int>(1); + GVectorArray_TypedMutableRef<int> vectors = params.vector_mutable<int>(0); + const VArray<int> &values = params.readonly_single_input<int>(1); for (int64_t i : mask) { vectors.append(i, values[i]); @@ -116,14 +113,13 @@ class SumVectorFunction : public MultiFunction { void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override { - VArraySpan<int> vectors = params.readonly_vector_input<int>(0); + const VVectorArray<int> &vectors = params.readonly_vector_input<int>(0); MutableSpan<int> sums = params.uninitialized_single_output<int>(1); for (int64_t i : mask) { int sum = 0; - VSpan<int> vector = vectors[i]; - for (int j = 0; j < vector.size(); j++) { - sum += vector[j]; + for (int j : IndexRange(vectors.get_vector_size(i))) { + sum += vectors.get_vector_element(i, j); } sums[i] = sum; } @@ -141,8 +137,8 @@ class CreateRangeFunction : public MultiFunction { void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override { - VSpan<int> sizes = params.readonly_single_input<int>(0, "Size"); - GVectorArrayRef<int> ranges = params.vector_output<int>(1, "Range"); + const VArray<int> &sizes = params.readonly_single_input<int>(0, "Size"); + GVectorArray_TypedMutableRef<int> ranges = params.vector_output<int>(1, "Range"); for (int64_t i : mask) { int size = sizes[i]; @@ -199,7 +195,8 @@ TEST(multi_function_network, Test2) Array<int> output_value_2(5, -1); MFParamsBuilder params(network_fn, 5); - params.add_readonly_vector_input(GVArraySpan(input_value_1.as_span(), 5)); + GVVectorArrayForSingleGSpan 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); params.add_uninitialized_single_output(output_value_2.as_mutable_span()); @@ -222,9 +219,9 @@ TEST(multi_function_network, Test2) } { GVectorArray input_value_1(CPPType::get<int32_t>(), 3); - GVectorArrayRef<int> input_value_ref_1 = input_value_1; - input_value_ref_1.extend(0, {3, 4, 5}); - input_value_ref_1.extend(1, {1, 2}); + GVectorArray_TypedMutableRef<int> input_value_1_ref{input_value_1}; + input_value_1_ref.extend(0, {3, 4, 5}); + input_value_1_ref.extend(1, {1, 2}); Array<int> input_value_2 = {4, 2, 3}; diff --git a/source/blender/functions/tests/FN_multi_function_test.cc b/source/blender/functions/tests/FN_multi_function_test.cc index cc023bce597..3ed60665149 100644 --- a/source/blender/functions/tests/FN_multi_function_test.cc +++ b/source/blender/functions/tests/FN_multi_function_test.cc @@ -20,8 +20,8 @@ class AddFunction : public MultiFunction { void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override { - VSpan<int> a = params.readonly_single_input<int>(0, "A"); - VSpan<int> b = params.readonly_single_input<int>(1, "B"); + const VArray<int> &a = params.readonly_single_input<int>(0, "A"); + const VArray<int> &b = params.readonly_single_input<int>(1, "B"); MutableSpan<int> result = params.uninitialized_single_output<int>(2, "Result"); for (int64_t i : mask) { @@ -63,7 +63,7 @@ class AddPrefixFunction : public MultiFunction { void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override { - VSpan<std::string> prefixes = params.readonly_single_input<std::string>(0, "Prefix"); + const VArray<std::string> &prefixes = params.readonly_single_input<std::string>(0, "Prefix"); MutableSpan<std::string> strings = params.single_mutable<std::string>(1, "Strings"); for (int64_t i : mask) { @@ -110,13 +110,13 @@ class CreateRangeFunction : public MultiFunction { void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override { - VSpan<uint> sizes = params.readonly_single_input<uint>(0, "Size"); - GVectorArrayRef<uint> ranges = params.vector_output<uint>(1, "Range"); + const VArray<uint> &sizes = params.readonly_single_input<uint>(0, "Size"); + GVectorArray &ranges = params.vector_output(1, "Range"); for (int64_t i : mask) { uint size = sizes[i]; for (uint j : IndexRange(size)) { - ranges.append(i, j); + ranges.append(i, &j); } } } @@ -127,7 +127,7 @@ TEST(multi_function, CreateRangeFunction) CreateRangeFunction fn; GVectorArray ranges(CPPType::get<uint>(), 5); - GVectorArrayRef<uint> ranges_ref(ranges); + GVectorArray_TypedMutableRef<uint> ranges_ref{ranges}; Array<uint> sizes = {3, 0, 6, 1, 4}; MFParamsBuilder params(fn, ranges.size()); @@ -138,11 +138,11 @@ TEST(multi_function, CreateRangeFunction) fn.call({0, 1, 2, 3}, params, context); - EXPECT_EQ(ranges_ref[0].size(), 3); - EXPECT_EQ(ranges_ref[1].size(), 0); - EXPECT_EQ(ranges_ref[2].size(), 6); - EXPECT_EQ(ranges_ref[3].size(), 1); - EXPECT_EQ(ranges_ref[4].size(), 0); + EXPECT_EQ(ranges[0].size(), 3); + EXPECT_EQ(ranges[1].size(), 0); + EXPECT_EQ(ranges[2].size(), 6); + EXPECT_EQ(ranges[3].size(), 1); + EXPECT_EQ(ranges[4].size(), 0); EXPECT_EQ(ranges_ref[0][0], 0); EXPECT_EQ(ranges_ref[0][1], 1); @@ -163,10 +163,13 @@ class GenericAppendFunction : public MultiFunction { void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override { GVectorArray &vectors = params.vector_mutable(0, "Vector"); - GVSpan values = params.readonly_single_input(1, "Value"); + const GVArray &values = params.readonly_single_input(1, "Value"); for (int64_t i : mask) { - vectors.append(i, values[i]); + BUFFER_FOR_CPP_TYPE_VALUE(values.type(), buffer); + values.get(i, buffer); + vectors.append(i, buffer); + values.type().destruct(buffer); } } }; @@ -176,7 +179,7 @@ TEST(multi_function, GenericAppendFunction) GenericAppendFunction fn(CPPType::get<int32_t>()); GVectorArray vectors(CPPType::get<int32_t>(), 4); - GVectorArrayRef<int> vectors_ref(vectors); + GVectorArray_TypedMutableRef<int> vectors_ref{vectors}; vectors_ref.append(0, 1); vectors_ref.append(0, 2); vectors_ref.append(2, 6); @@ -190,10 +193,10 @@ TEST(multi_function, GenericAppendFunction) fn.call(IndexRange(vectors.size()), params, context); - EXPECT_EQ(vectors_ref[0].size(), 3); - EXPECT_EQ(vectors_ref[1].size(), 1); - EXPECT_EQ(vectors_ref[2].size(), 2); - EXPECT_EQ(vectors_ref[3].size(), 1); + EXPECT_EQ(vectors[0].size(), 3); + EXPECT_EQ(vectors[1].size(), 1); + EXPECT_EQ(vectors[2].size(), 2); + EXPECT_EQ(vectors[3].size(), 1); EXPECT_EQ(vectors_ref[0][0], 1); EXPECT_EQ(vectors_ref[0][1], 2); @@ -342,11 +345,11 @@ TEST(multi_function, CustomMF_GenericConstantArray) CustomMF_GenericConstantArray fn{GSpan(Span(values))}; EXPECT_EQ(fn.param_name(0), "[3, 4, 5, 6, ]"); - GVectorArray g_vector_array{CPPType::get<int32_t>(), 4}; - GVectorArrayRef<int> vector_array = g_vector_array; + GVectorArray vector_array{CPPType::get<int32_t>(), 4}; + GVectorArray_TypedMutableRef<int> vector_array_ref{vector_array}; - MFParamsBuilder params(fn, g_vector_array.size()); - params.add_vector_output(g_vector_array); + MFParamsBuilder params(fn, vector_array.size()); + params.add_vector_output(vector_array); MFContextBuilder context; @@ -357,10 +360,10 @@ TEST(multi_function, CustomMF_GenericConstantArray) EXPECT_EQ(vector_array[2].size(), 4); EXPECT_EQ(vector_array[3].size(), 4); for (int i = 1; i < 4; i++) { - EXPECT_EQ(vector_array[i][0], 3); - EXPECT_EQ(vector_array[i][1], 4); - EXPECT_EQ(vector_array[i][2], 5); - EXPECT_EQ(vector_array[i][3], 6); + EXPECT_EQ(vector_array_ref[i][0], 3); + EXPECT_EQ(vector_array_ref[i][1], 4); + EXPECT_EQ(vector_array_ref[i][2], 5); + EXPECT_EQ(vector_array_ref[i][3], 6); } } diff --git a/source/blender/functions/tests/FN_spans_test.cc b/source/blender/functions/tests/FN_spans_test.cc deleted file mode 100644 index fbcf1fda71e..00000000000 --- a/source/blender/functions/tests/FN_spans_test.cc +++ /dev/null @@ -1,222 +0,0 @@ -/* Apache License, Version 2.0 */ - -#include "testing/testing.h" - -#include "FN_spans.hh" - -namespace blender::fn::tests { - -TEST(generic_span, TypeConstructor) -{ - GSpan span(CPPType::get<float>()); - EXPECT_EQ(span.size(), 0); - EXPECT_EQ(span.typed<float>().size(), 0); - EXPECT_TRUE(span.is_empty()); -} - -TEST(generic_span, BufferAndSizeConstructor) -{ - int values[4] = {6, 7, 3, 2}; - void *buffer = (void *)values; - GSpan span(CPPType::get<int32_t>(), buffer, 4); - EXPECT_EQ(span.size(), 4); - EXPECT_FALSE(span.is_empty()); - EXPECT_EQ(span.typed<int>().size(), 4); - EXPECT_EQ(span[0], &values[0]); - EXPECT_EQ(span[1], &values[1]); - EXPECT_EQ(span[2], &values[2]); - EXPECT_EQ(span[3], &values[3]); -} - -TEST(generic_mutable_span, TypeConstructor) -{ - GMutableSpan span(CPPType::get<int32_t>()); - EXPECT_EQ(span.size(), 0); - EXPECT_TRUE(span.is_empty()); -} - -TEST(generic_mutable_span, BufferAndSizeConstructor) -{ - int values[4] = {4, 7, 3, 5}; - void *buffer = (void *)values; - GMutableSpan span(CPPType::get<int32_t>(), buffer, 4); - EXPECT_EQ(span.size(), 4); - EXPECT_FALSE(span.is_empty()); - EXPECT_EQ(span.typed<int>().size(), 4); - EXPECT_EQ(values[2], 3); - *(int *)span[2] = 10; - EXPECT_EQ(values[2], 10); - span.typed<int>()[2] = 20; - EXPECT_EQ(values[2], 20); -} - -TEST(virtual_span, EmptyConstructor) -{ - VSpan<int> span; - EXPECT_EQ(span.size(), 0); - EXPECT_TRUE(span.is_empty()); - EXPECT_FALSE(span.is_single_element()); - EXPECT_TRUE(span.is_full_array()); - - GVSpan converted(span); - EXPECT_EQ(converted.type(), CPPType::get<int>()); - EXPECT_EQ(converted.size(), 0); -} - -TEST(virtual_span, SpanConstructor) -{ - std::array<int, 5> values = {7, 3, 8, 6, 4}; - Span<int> span = values; - VSpan<int> virtual_span = span; - EXPECT_EQ(virtual_span.size(), 5); - EXPECT_FALSE(virtual_span.is_empty()); - EXPECT_EQ(virtual_span[0], 7); - EXPECT_EQ(virtual_span[2], 8); - EXPECT_EQ(virtual_span[3], 6); - EXPECT_FALSE(virtual_span.is_single_element()); - EXPECT_TRUE(virtual_span.is_full_array()); - - GVSpan converted(span); - EXPECT_EQ(converted.type(), CPPType::get<int>()); - EXPECT_EQ(converted.size(), 5); -} - -TEST(virtual_span, PointerSpanConstructor) -{ - int x0 = 3; - int x1 = 6; - int x2 = 7; - std::array<const int *, 3> pointers = {&x0, &x2, &x1}; - VSpan<int> span = Span<const int *>(pointers); - EXPECT_EQ(span.size(), 3); - EXPECT_FALSE(span.is_empty()); - EXPECT_EQ(span[0], 3); - EXPECT_EQ(span[1], 7); - EXPECT_EQ(span[2], 6); - EXPECT_EQ(&span[1], &x2); - EXPECT_FALSE(span.is_single_element()); - EXPECT_FALSE(span.is_full_array()); - - GVSpan converted(span); - EXPECT_EQ(converted.type(), CPPType::get<int>()); - EXPECT_EQ(converted.size(), 3); - EXPECT_EQ(converted[0], &x0); - EXPECT_EQ(converted[1], &x2); - EXPECT_EQ(converted[2], &x1); -} - -TEST(virtual_span, SingleConstructor) -{ - int value = 5; - VSpan<int> span = VSpan<int>::FromSingle(&value, 3); - EXPECT_EQ(span.size(), 3); - EXPECT_FALSE(span.is_empty()); - EXPECT_EQ(span[0], 5); - EXPECT_EQ(span[1], 5); - EXPECT_EQ(span[2], 5); - EXPECT_EQ(&span[0], &value); - EXPECT_EQ(&span[1], &value); - EXPECT_EQ(&span[2], &value); - EXPECT_TRUE(span.is_single_element()); - EXPECT_FALSE(span.is_full_array()); - - GVSpan converted(span); - EXPECT_EQ(converted.type(), CPPType::get<int>()); - EXPECT_EQ(converted.size(), 3); - EXPECT_EQ(converted[0], &value); - EXPECT_EQ(converted[1], &value); - EXPECT_EQ(converted[2], &value); -} - -TEST(generic_virtual_span, TypeConstructor) -{ - GVSpan span(CPPType::get<int32_t>()); - EXPECT_EQ(span.size(), 0); - EXPECT_TRUE(span.is_empty()); - EXPECT_FALSE(span.is_single_element()); - EXPECT_TRUE(span.is_full_array()); - - VSpan<int> converted = span.typed<int>(); - EXPECT_EQ(converted.size(), 0); -} - -TEST(generic_virtual_span, GenericSpanConstructor) -{ - int values[4] = {3, 4, 5, 6}; - GVSpan span{GSpan(CPPType::get<int32_t>(), values, 4)}; - EXPECT_EQ(span.size(), 4); - EXPECT_FALSE(span.is_empty()); - EXPECT_EQ(span[0], &values[0]); - EXPECT_EQ(span[1], &values[1]); - EXPECT_EQ(span[2], &values[2]); - EXPECT_EQ(span[3], &values[3]); - EXPECT_FALSE(span.is_single_element()); - EXPECT_TRUE(span.is_full_array()); - - int materialized[4] = {0}; - span.materialize_to_uninitialized(materialized); - EXPECT_EQ(materialized[0], 3); - EXPECT_EQ(materialized[1], 4); - EXPECT_EQ(materialized[2], 5); - EXPECT_EQ(materialized[3], 6); - - VSpan<int> converted = span.typed<int>(); - EXPECT_EQ(converted.size(), 4); - EXPECT_EQ(converted[0], 3); - EXPECT_EQ(converted[1], 4); - EXPECT_EQ(converted[2], 5); - EXPECT_EQ(converted[3], 6); -} - -TEST(generic_virtual_span, SpanConstructor) -{ - std::array<int, 3> values = {6, 7, 8}; - GVSpan span{Span<int>(values)}; - EXPECT_EQ(span.type(), CPPType::get<int32_t>()); - EXPECT_EQ(span.size(), 3); - EXPECT_EQ(span[0], &values[0]); - EXPECT_EQ(span[1], &values[1]); - EXPECT_EQ(span[2], &values[2]); - EXPECT_FALSE(span.is_single_element()); - EXPECT_TRUE(span.is_full_array()); - - int materialized[3] = {0}; - span.materialize_to_uninitialized(materialized); - EXPECT_EQ(materialized[0], 6); - EXPECT_EQ(materialized[1], 7); - EXPECT_EQ(materialized[2], 8); - - VSpan<int> converted = span.typed<int>(); - EXPECT_EQ(converted.size(), 3); - EXPECT_EQ(converted[0], 6); - EXPECT_EQ(converted[1], 7); - EXPECT_EQ(converted[2], 8); -} - -TEST(generic_virtual_span, SingleConstructor) -{ - int value = 5; - GVSpan span = GVSpan::FromSingle(CPPType::get<int32_t>(), &value, 3); - EXPECT_EQ(span.size(), 3); - EXPECT_FALSE(span.is_empty()); - EXPECT_EQ(span[0], &value); - EXPECT_EQ(span[1], &value); - EXPECT_EQ(span[2], &value); - EXPECT_TRUE(span.is_single_element()); - EXPECT_EQ(span.as_single_element(), &value); - EXPECT_FALSE(span.is_full_array()); - - int materialized[3] = {0}; - span.materialize_to_uninitialized({1, 2}, materialized); - EXPECT_EQ(materialized[0], 0); - EXPECT_EQ(materialized[1], 5); - EXPECT_EQ(materialized[2], 5); - - VSpan<int> converted = span.typed<int>(); - EXPECT_EQ(converted.size(), 3); - EXPECT_EQ(converted[0], 5); - EXPECT_EQ(converted[1], 5); - EXPECT_EQ(converted[2], 5); -} - -} // namespace blender::fn::tests diff --git a/source/blender/nodes/function/nodes/node_fn_object_transforms.cc b/source/blender/nodes/function/nodes/node_fn_object_transforms.cc index 26b25406590..55f592d7879 100644 --- a/source/blender/nodes/function/nodes/node_fn_object_transforms.cc +++ b/source/blender/nodes/function/nodes/node_fn_object_transforms.cc @@ -42,7 +42,7 @@ class ObjectTransformsFunction : public blender::fn::MultiFunction { blender::fn::MFParams params, blender::fn::MFContext context) const override { - blender::fn::VSpan handles = + const blender::VArray<blender::bke::PersistentObjectHandle> &handles = params.readonly_single_input<blender::bke::PersistentObjectHandle>(0, "Object"); blender::MutableSpan locations = params.uninitialized_single_output<blender::float3>( 1, "Location"); diff --git a/source/blender/nodes/function/nodes/node_fn_random_float.cc b/source/blender/nodes/function/nodes/node_fn_random_float.cc index d0ecb5592da..84ded73c601 100644 --- a/source/blender/nodes/function/nodes/node_fn_random_float.cc +++ b/source/blender/nodes/function/nodes/node_fn_random_float.cc @@ -48,9 +48,9 @@ class RandomFloatFunction : public blender::fn::MultiFunction { blender::fn::MFParams params, blender::fn::MFContext UNUSED(context)) const override { - blender::fn::VSpan<float> min_values = params.readonly_single_input<float>(0, "Min"); - blender::fn::VSpan<float> max_values = params.readonly_single_input<float>(1, "Max"); - blender::fn::VSpan<int> seeds = params.readonly_single_input<int>(2, "Seed"); + const blender::VArray<float> &min_values = params.readonly_single_input<float>(0, "Min"); + const blender::VArray<float> &max_values = params.readonly_single_input<float>(1, "Max"); + const blender::VArray<int> &seeds = params.readonly_single_input<int>(2, "Seed"); blender::MutableSpan<float> values = params.uninitialized_single_output<float>(3, "Value"); for (int64_t i : mask) { diff --git a/source/blender/nodes/shader/nodes/node_shader_map_range.cc b/source/blender/nodes/shader/nodes/node_shader_map_range.cc index c01e390c513..3aa533599cf 100644 --- a/source/blender/nodes/shader/nodes/node_shader_map_range.cc +++ b/source/blender/nodes/shader/nodes/node_shader_map_range.cc @@ -108,11 +108,11 @@ class MapRangeFunction : public blender::fn::MultiFunction { blender::fn::MFParams params, blender::fn::MFContext UNUSED(context)) const override { - blender::fn::VSpan<float> values = params.readonly_single_input<float>(0, "Value"); - blender::fn::VSpan<float> from_min = params.readonly_single_input<float>(1, "From Min"); - blender::fn::VSpan<float> from_max = params.readonly_single_input<float>(2, "From Max"); - blender::fn::VSpan<float> to_min = params.readonly_single_input<float>(3, "To Min"); - blender::fn::VSpan<float> to_max = params.readonly_single_input<float>(4, "To Max"); + const blender::VArray<float> &values = params.readonly_single_input<float>(0, "Value"); + const blender::VArray<float> &from_min = params.readonly_single_input<float>(1, "From Min"); + const blender::VArray<float> &from_max = params.readonly_single_input<float>(2, "From Max"); + const blender::VArray<float> &to_min = params.readonly_single_input<float>(3, "To Min"); + const blender::VArray<float> &to_max = params.readonly_single_input<float>(4, "To Max"); blender::MutableSpan<float> results = params.uninitialized_single_output<float>(5, "Result"); for (int64_t i : mask) { diff --git a/source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc b/source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc index 59b89c46fc7..a9057428981 100644 --- a/source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc +++ b/source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc @@ -74,8 +74,8 @@ class SeparateRGBFunction : public blender::fn::MultiFunction { blender::fn::MFParams params, blender::fn::MFContext UNUSED(context)) const override { - blender::fn::VSpan<blender::Color4f> colors = params.readonly_single_input<blender::Color4f>( - 0, "Color"); + const blender::VArray<blender::Color4f> &colors = + params.readonly_single_input<blender::Color4f>(0, "Color"); blender::MutableSpan<float> rs = params.uninitialized_single_output<float>(1, "R"); blender::MutableSpan<float> gs = params.uninitialized_single_output<float>(2, "G"); blender::MutableSpan<float> bs = params.uninitialized_single_output<float>(3, "B"); diff --git a/source/blender/nodes/shader/nodes/node_shader_sepcombXYZ.cc b/source/blender/nodes/shader/nodes/node_shader_sepcombXYZ.cc index 8b23327460f..c0dc66b3342 100644 --- a/source/blender/nodes/shader/nodes/node_shader_sepcombXYZ.cc +++ b/source/blender/nodes/shader/nodes/node_shader_sepcombXYZ.cc @@ -59,8 +59,8 @@ class MF_SeparateXYZ : public blender::fn::MultiFunction { blender::fn::MFParams params, blender::fn::MFContext UNUSED(context)) const override { - blender::fn::VSpan<blender::float3> vectors = params.readonly_single_input<blender::float3>( - 0, "XYZ"); + const blender::VArray<blender::float3> &vectors = + params.readonly_single_input<blender::float3>(0, "XYZ"); blender::MutableSpan<float> xs = params.uninitialized_single_output<float>(1, "X"); blender::MutableSpan<float> ys = params.uninitialized_single_output<float>(2, "Y"); blender::MutableSpan<float> zs = params.uninitialized_single_output<float>(3, "Z"); diff --git a/source/blender/nodes/shader/nodes/node_shader_valToRgb.cc b/source/blender/nodes/shader/nodes/node_shader_valToRgb.cc index a86a8bb8935..0d50582e23a 100644 --- a/source/blender/nodes/shader/nodes/node_shader_valToRgb.cc +++ b/source/blender/nodes/shader/nodes/node_shader_valToRgb.cc @@ -142,7 +142,7 @@ class ColorBandFunction : public blender::fn::MultiFunction { blender::fn::MFParams params, blender::fn::MFContext UNUSED(context)) const override { - blender::fn::VSpan<float> values = params.readonly_single_input<float>(0, "Value"); + const blender::VArray<float> &values = params.readonly_single_input<float>(0, "Value"); blender::MutableSpan<blender::Color4f> colors = params.uninitialized_single_output<blender::Color4f>(1, "Color"); blender::MutableSpan<float> alphas = params.uninitialized_single_output<float>(2, "Alpha"); |