From 36088912828b0b511ce0f293f39187b5d8d770cf Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Sat, 17 Apr 2021 15:13:20 +0200 Subject: Functions: extend virtual array functionality This adds support for mutable virtual arrays and provides many utilities for creating virtual arrays for various kinds of data. This commit is preparation for D10994. --- .../blender/functions/FN_generic_virtual_array.hh | 681 +++++++++++++++++++-- 1 file changed, 636 insertions(+), 45 deletions(-) (limited to 'source/blender/functions/FN_generic_virtual_array.hh') diff --git a/source/blender/functions/FN_generic_virtual_array.hh b/source/blender/functions/FN_generic_virtual_array.hh index c6230730a8d..00f6cacd2e1 100644 --- a/source/blender/functions/FN_generic_virtual_array.hh +++ b/source/blender/functions/FN_generic_virtual_array.hh @@ -23,12 +23,17 @@ * the data type is only known at runtime. */ +#include + #include "BLI_virtual_array.hh" #include "FN_generic_span.hh" namespace blender::fn { +template class GVArray_Typed; +template class GVMutableArray_Typed; + /* A generically typed version of `VArray`. */ class GVArray { protected: @@ -86,13 +91,13 @@ class GVArray { /* Returns the internally used span of the virtual array. This invokes undefined behavior is the * virtual array is not stored as a span internally. */ - GSpan get_span() const + GSpan get_internal_span() const { BLI_assert(this->is_span()); if (size_ == 0) { return GSpan(*type_); } - return this->get_span_impl(); + return this->get_internal_span_impl(); } /* Returns true when the virtual array returns the same value for every index. */ @@ -107,57 +112,133 @@ class GVArray { /* Copies the value that is used for every element into `r_value`, which is expected to point to * initialized memory. This invokes undefined behavior if the virtual array would not return the * same value for every index. */ - void get_single(void *r_value) const + void get_internal_single(void *r_value) const { BLI_assert(this->is_single()); if (size_ == 1) { this->get(0, r_value); } - this->get_single_impl(r_value); + this->get_internal_single_impl(r_value); } - /* Same as `get_single`, but `r_value` points to initialized memory. */ + /* Same as `get_internal_single`, but `r_value` points to initialized memory. */ void get_single_to_uninitialized(void *r_value) const { type_->construct_default(r_value); - this->get_single(r_value); + this->get_internal_single(r_value); } void materialize_to_uninitialized(const IndexMask mask, void *dst) const; + template const VArray *try_get_internal_varray() const + { + BLI_assert(type_->is()); + return (const VArray *)this->try_get_internal_varray_impl(); + } + + /* Create a typed virtual array for this generic virtual array. */ + template GVArray_Typed typed() const + { + return GVArray_Typed(*this); + } + protected: virtual void get_impl(const int64_t index, void *r_value) const; virtual void get_to_uninitialized_impl(const int64_t index, void *r_value) const = 0; virtual bool is_span_impl() const; - virtual GSpan get_span_impl() const; + virtual GSpan get_internal_span_impl() const; virtual bool is_single_impl() const; - virtual void get_single_impl(void *UNUSED(r_value)) const; + virtual void get_internal_single_impl(void *UNUSED(r_value)) const; + + virtual const void *try_get_internal_varray_impl() const; +}; + +/* Similar to GVArray, but supports changing the elements in the virtual array. */ +class GVMutableArray : public GVArray { + public: + GVMutableArray(const CPPType &type, const int64_t size) : GVArray(type, size) + { + } + + void set_by_copy(const int64_t index, const void *value) + { + BLI_assert(index >= 0); + BLI_assert(index < size_); + this->set_by_copy_impl(index, value); + } + + void set_by_move(const int64_t index, void *value) + { + BLI_assert(index >= 0); + BLI_assert(index < size_); + this->set_by_move_impl(index, value); + } + + void set_by_relocate(const int64_t index, void *value) + { + BLI_assert(index >= 0); + BLI_assert(index < size_); + this->set_by_relocate_impl(index, value); + } + + GMutableSpan get_internal_span() + { + BLI_assert(this->is_span()); + GSpan span = static_cast(this)->get_internal_span(); + return GMutableSpan(span.type(), const_cast(span.data()), span.size()); + } + + template VMutableArray *try_get_internal_mutable_varray() + { + BLI_assert(type_->is()); + return (VMutableArray *)this->try_get_internal_mutable_varray_impl(); + } + + /* Create a typed virtual array for this generic virtual array. */ + template GVMutableArray_Typed typed() + { + return GVMutableArray_Typed(*this); + } + + void fill(const void *value); + + protected: + virtual void set_by_copy_impl(const int64_t index, const void *value); + virtual void set_by_relocate_impl(const int64_t index, void *value); + virtual void set_by_move_impl(const int64_t index, void *value) = 0; + + virtual void *try_get_internal_mutable_varray_impl(); }; -class GVArrayForGSpan : public GVArray { +class GVArray_For_GSpan : public GVArray { protected: - const void *data_; + const void *data_ = nullptr; const int64_t element_size_; public: - GVArrayForGSpan(const GSpan span) + GVArray_For_GSpan(const GSpan span) : GVArray(span.type(), span.size()), data_(span.data()), element_size_(span.type().size()) { } protected: + GVArray_For_GSpan(const CPPType &type, const int64_t size) + : GVArray(type, size), element_size_(type.size()) + { + } + void get_impl(const int64_t index, void *r_value) const override; void get_to_uninitialized_impl(const int64_t index, void *r_value) const override; bool is_span_impl() const override; - GSpan get_span_impl() const override; + GSpan get_internal_span_impl() const override; }; -class GVArrayForEmpty : public GVArray { +class GVArray_For_Empty : public GVArray { public: - GVArrayForEmpty(const CPPType &type) : GVArray(type, 0) + GVArray_For_Empty(const CPPType &type) : GVArray(type, 0) { } @@ -168,108 +249,618 @@ class GVArrayForEmpty : public GVArray { } }; -class GVArrayForSingleValueRef : public GVArray { - private: - const void *value_; +class GVMutableArray_For_GMutableSpan : public GVMutableArray { + protected: + void *data_ = nullptr; + const int64_t element_size_; + + public: + GVMutableArray_For_GMutableSpan(const GMutableSpan span) + : GVMutableArray(span.type(), span.size()), + data_(span.data()), + element_size_(span.type().size()) + { + } + + protected: + GVMutableArray_For_GMutableSpan(const CPPType &type, const int64_t size) + : GVMutableArray(type, size), element_size_(type.size()) + { + } + + void get_impl(const int64_t index, void *r_value) const override; + void get_to_uninitialized_impl(const int64_t index, void *r_value) const override; + + void set_by_copy_impl(const int64_t index, const void *value) override; + void set_by_move_impl(const int64_t index, void *value) override; + void set_by_relocate_impl(const int64_t index, void *value) override; + + bool is_span_impl() const override; + GSpan get_internal_span_impl() const override; +}; + +/* Generic virtual array where each element has the same value. The value is not owned. */ +class GVArray_For_SingleValueRef : public GVArray { + protected: + const void *value_ = nullptr; public: - GVArrayForSingleValueRef(const CPPType &type, const int64_t size, const void *value) + GVArray_For_SingleValueRef(const CPPType &type, const int64_t size, const void *value) : GVArray(type, size), value_(value) { } protected: + GVArray_For_SingleValueRef(const CPPType &type, const int64_t size) : GVArray(type, size) + { + } + void get_impl(const int64_t index, void *r_value) const override; void get_to_uninitialized_impl(const int64_t index, void *r_value) const override; bool is_span_impl() const override; - GSpan get_span_impl() const override; + GSpan get_internal_span_impl() const override; bool is_single_impl() const override; - void get_single_impl(void *r_value) const override; + void get_internal_single_impl(void *r_value) const override; }; -template class GVArrayForVArray : public GVArray { - private: - const VArray &array_; +/* Same as GVArray_For_SingleValueRef, but the value is owned. */ +class GVArray_For_SingleValue : public GVArray_For_SingleValueRef { + public: + GVArray_For_SingleValue(const CPPType &type, const int64_t size, const void *value); + ~GVArray_For_SingleValue(); +}; + +/* Used to convert a typed virtual array into a generic one. */ +template class GVArray_For_VArray : public GVArray { + protected: + const VArray *varray_ = nullptr; public: - GVArrayForVArray(const VArray &array) - : GVArray(CPPType::get(), array.size()), array_(array) + GVArray_For_VArray(const VArray &varray) + : GVArray(CPPType::get(), varray.size()), varray_(&varray) { } protected: + GVArray_For_VArray(const int64_t size) : GVArray(CPPType::get(), size) + { + } + void get_impl(const int64_t index, void *r_value) const override { - *(T *)r_value = array_.get(index); + *(T *)r_value = varray_->get(index); } void get_to_uninitialized_impl(const int64_t index, void *r_value) const override { - new (r_value) T(array_.get(index)); + new (r_value) T(varray_->get(index)); } bool is_span_impl() const override { - return array_.is_span(); + return varray_->is_span(); } - GSpan get_span_impl() const override + GSpan get_internal_span_impl() const override { - return GSpan(array_.get_span()); + return GSpan(varray_->get_internal_span()); } bool is_single_impl() const override { - return array_.is_single(); + return varray_->is_single(); + } + + void get_internal_single_impl(void *r_value) const override + { + *(T *)r_value = varray_->get_internal_single(); } - void get_single_impl(void *r_value) const override + const void *try_get_internal_varray_impl() const override { - *(T *)r_value = array_.get_single(); + return varray_; } }; -template class VArrayForGVArray : public VArray { - private: - const GVArray &array_; +/* Used to convert any generic virtual array into a typed one. */ +template class VArray_For_GVArray : public VArray { + protected: + const GVArray *varray_ = nullptr; public: - VArrayForGVArray(const GVArray &array) : VArray(array.size()), array_(array) + VArray_For_GVArray(const GVArray &varray) : VArray(varray.size()), varray_(&varray) { - BLI_assert(array_.type().template is()); + BLI_assert(varray_->type().template is()); } protected: + VArray_For_GVArray(const int64_t size) : VArray(size) + { + } + T get_impl(const int64_t index) const override { T value; - array_.get(index, &value); + varray_->get(index, &value); return value; } bool is_span_impl() const override { - return array_.is_span(); + return varray_->is_span(); } - Span get_span_impl() const override + Span get_internal_span_impl() const override { - return array_.get_span().template typed(); + return varray_->get_internal_span().template typed(); } bool is_single_impl() const override { - return array_.is_single(); + return varray_->is_single(); + } + + T get_internal_single_impl() const override + { + T value; + varray_->get_internal_single(&value); + return value; + } +}; + +/* Used to convert an generic mutable virtual array into a typed one. */ +template class VMutableArray_For_GVMutableArray : public VMutableArray { + protected: + GVMutableArray *varray_ = nullptr; + + public: + VMutableArray_For_GVMutableArray(GVMutableArray &varray) + : VMutableArray(varray.size()), varray_(&varray) + { + BLI_assert(varray.type().template is()); + } + + VMutableArray_For_GVMutableArray(const int64_t size) : VMutableArray(size) + { } - T get_single_impl() const override + private: + T get_impl(const int64_t index) const override { T value; - array_.get_single(&value); + varray_->get(index, &value); return value; } + + void set_impl(const int64_t index, T value) override + { + varray_->set_by_relocate(index, &value); + } + + bool is_span_impl() const override + { + return varray_->is_span(); + } + + Span get_internal_span_impl() const override + { + return varray_->get_internal_span().template typed(); + } + + bool is_single_impl() const override + { + return varray_->is_single(); + } + + T get_internal_single_impl() const override + { + T value; + varray_->get_internal_single(&value); + return value; + } +}; + +/* Used to convert any typed virtual mutable array into a generic one. */ +template class GVMutableArray_For_VMutableArray : public GVMutableArray { + protected: + VMutableArray *varray_ = nullptr; + + public: + GVMutableArray_For_VMutableArray(VMutableArray &varray) + : GVMutableArray(CPPType::get(), varray.size()), varray_(&varray) + { + } + + protected: + GVMutableArray_For_VMutableArray(const int64_t size) : GVMutableArray(CPPType::get(), size) + { + } + + void get_impl(const int64_t index, void *r_value) const override + { + *(T *)r_value = varray_->get(index); + } + + void get_to_uninitialized_impl(const int64_t index, void *r_value) const override + { + new (r_value) T(varray_->get(index)); + } + + bool is_span_impl() const override + { + return varray_->is_span(); + } + + GSpan get_internal_span_impl() const override + { + Span span = varray_->get_internal_span(); + return span; + } + + bool is_single_impl() const override + { + return varray_->is_single(); + } + + void get_internal_single_impl(void *r_value) const override + { + *(T *)r_value = varray_->get_internal_single(); + } + + void set_by_copy_impl(const int64_t index, const void *value) override + { + const T &value_ = *(const T *)value; + varray_->set(index, value_); + } + + void set_by_relocate_impl(const int64_t index, void *value) override + { + T &value_ = *(T *)value; + varray_->set(index, std::move(value_)); + value_.~T(); + } + + void set_by_move_impl(const int64_t index, void *value) override + { + T &value_ = *(T *)value; + varray_->set(index, std::move(value_)); + } + + const void *try_get_internal_varray_impl() const override + { + return (const VArray *)varray_; + } + + void *try_get_internal_mutable_varray_impl() override + { + return varray_; + } +}; + +/* A generic version of VArray_Span. */ +class GVArray_GSpan : public GSpan { + private: + const GVArray &varray_; + void *owned_data_ = nullptr; + + public: + GVArray_GSpan(const GVArray &varray); + ~GVArray_GSpan(); +}; + +/* A generic version of VMutableArray_Span. */ +class GVMutableArray_GSpan : public GMutableSpan { + private: + GVMutableArray &varray_; + void *owned_data_ = nullptr; + bool save_has_been_called_ = false; + bool show_not_saved_warning_ = true; + + public: + GVMutableArray_GSpan(GVMutableArray &varray, bool copy_values_to_span = true); + ~GVMutableArray_GSpan(); + + void save(); + void disable_not_applied_warning(); +}; + +/* Similar to GVArray_GSpan, but the resulting span is typed. */ +template class GVArray_Span : public Span { + private: + GVArray_GSpan varray_gspan_; + + public: + GVArray_Span(const GVArray &varray) : varray_gspan_(varray) + { + BLI_assert(varray.type().is()); + this->data_ = (const T *)varray_gspan_.data(); + this->size_ = varray_gspan_.size(); + } +}; + +template class GVArray_For_OwnedVArray : public GVArray_For_VArray { + private: + std::unique_ptr> owned_varray_; + + public: + /* Takes ownership of varray and passes a reference to the base class. */ + GVArray_For_OwnedVArray(std::unique_ptr> varray) + : GVArray_For_VArray(*varray), owned_varray_(std::move(varray)) + { + } +}; + +template class VArray_For_OwnedGVArray : public VArray_For_GVArray { + private: + std::unique_ptr> owned_varray_; + + public: + /* Takes ownership of varray and passes a reference to the base class. */ + VArray_For_OwnedGVArray(std::unique_ptr varray) + : VArray_For_GVArray(*varray), owned_varray_(std::move(varray)) + { + } +}; + +template +class GVMutableArray_For_OwnedVMutableArray : public GVMutableArray_For_VMutableArray { + private: + std::unique_ptr> owned_varray_; + + public: + /* Takes ownership of varray and passes a reference to the base class. */ + GVMutableArray_For_OwnedVMutableArray(std::unique_ptr> varray) + : GVMutableArray_For_VMutableArray(*varray), owned_varray_(std::move(varray)) + { + } +}; + +template +class VMutableArray_For_OwnedGVMutableArray : public VMutableArray_For_GVMutableArray { + private: + std::unique_ptr owned_varray_; + + public: + /* Takes ownership of varray and passes a reference to the base class. */ + VMutableArray_For_OwnedGVMutableArray(std::unique_ptr varray) + : VMutableArray_For_GVMutableArray(*varray), owned_varray_(std::move(varray)) + { + } +}; + +/* Utility to embed a typed virtual array into a generic one. This avoids one allocation and give + * the compiler more opportunity to optimize the generic virtual array. */ +template +class GVArray_For_EmbeddedVArray : public GVArray_For_VArray { + private: + VArrayT embedded_varray_; + + public: + template + GVArray_For_EmbeddedVArray(const int64_t size, Args &&... args) + : GVArray_For_VArray(size), embedded_varray_(std::forward(args)...) + { + this->varray_ = &embedded_varray_; + } +}; + +/* Same as GVArray_For_EmbeddedVArray, but for mutable virtual arrays. */ +template +class GVMutableArray_For_EmbeddedVMutableArray : public GVMutableArray_For_VMutableArray { + private: + VMutableArrayT embedded_varray_; + + public: + template + GVMutableArray_For_EmbeddedVMutableArray(const int64_t size, Args &&... args) + : GVMutableArray_For_VMutableArray(size), embedded_varray_(std::forward(args)...) + { + this->varray_ = &embedded_varray_; + } +}; + +/* Same as VArray_For_ArrayContainer, but for a generic virtual array. */ +template +class GVArray_For_ArrayContainer + : public GVArray_For_EmbeddedVArray> { + public: + GVArray_For_ArrayContainer(Container container) + : GVArray_For_EmbeddedVArray>( + container.size(), std::move(container)) + { + } +}; + +/* Same as VArray_For_DerivedSpan, but for a generic virtual array. */ +template +class GVArray_For_DerivedSpan + : public GVArray_For_EmbeddedVArray> { + public: + GVArray_For_DerivedSpan(const Span data) + : GVArray_For_EmbeddedVArray>( + data.size(), data) + { + } +}; + +/* Same as VMutableArray_For_DerivedSpan, but for a generic virtual array. */ +template +class GVMutableArray_For_DerivedSpan + : public GVMutableArray_For_EmbeddedVMutableArray< + ElemT, + VMutableArray_For_DerivedSpan> { + public: + GVMutableArray_For_DerivedSpan(const MutableSpan data) + : GVMutableArray_For_EmbeddedVMutableArray< + ElemT, + VMutableArray_For_DerivedSpan>(data.size(), data) + { + } +}; + +/* Same as VArray_For_Span, but for a generic virtual array. */ +template +class GVArray_For_Span : public GVArray_For_EmbeddedVArray> { + public: + GVArray_For_Span(const Span data) + : GVArray_For_EmbeddedVArray>(data.size(), data) + { + } +}; + +/* Same as VMutableArray_For_MutableSpan, but for a generic virtual array. */ +template +class GVMutableArray_For_MutableSpan + : public GVMutableArray_For_EmbeddedVMutableArray> { + public: + GVMutableArray_For_MutableSpan(const MutableSpan data) + : GVMutableArray_For_EmbeddedVMutableArray>(data.size(), + data) + { + } +}; + +/** + * Utility class to create the "best" typed virtual array for a given generic virtual array. + * In most cases we don't just want to use VArray_For_GVArray, because it adds an additional + * indirection on element-access that can be avoided in many cases (e.g. when the virtual array is + * just a span or single value). + * + * This is not a virtual array itself, but is used to get a virtual array. + */ +template class GVArray_Typed { + private: + const VArray *varray_; + /* Of these optional virtual arrays, at most one is constructed at any time. */ + std::optional> varray_span_; + std::optional> varray_single_; + std::optional> varray_any_; + std::unique_ptr owned_gvarray_; + + public: + explicit GVArray_Typed(const GVArray &gvarray) + { + BLI_assert(gvarray.type().is()); + if (gvarray.is_span()) { + const GSpan span = gvarray.get_internal_span(); + varray_span_.emplace(span.typed()); + varray_ = &*varray_span_; + } + else if (gvarray.is_single()) { + T single_value; + gvarray.get_internal_single(&single_value); + varray_single_.emplace(single_value, gvarray.size()); + varray_ = &*varray_single_; + } + else if (const VArray *internal_varray = gvarray.try_get_internal_varray()) { + varray_ = internal_varray; + } + else { + varray_any_.emplace(gvarray); + varray_ = &*varray_any_; + } + } + + /* Same as the constructor above, but also takes ownership of the passed in virtual array. */ + explicit GVArray_Typed(std::unique_ptr gvarray) : GVArray_Typed(*gvarray) + { + owned_gvarray_ = std::move(gvarray); + } + + const VArray &operator*() const + { + return *varray_; + } + + const VArray *operator->() const + { + return varray_; + } + + /* Support implicit cast to the typed virtual array for convenience when `varray->typed()` is + * used within an expression. */ + operator const VArray &() const + { + return *varray_; + } + + T operator[](const int64_t index) const + { + return varray_->get(index); + } + + int64_t size() const + { + return varray_->size(); + } + + IndexRange index_range() const + { + return IndexRange(this->size()); + } +}; + +/* Same as GVArray_Typed, but for mutable virtual arrays. */ +template class GVMutableArray_Typed { + private: + VMutableArray *varray_; + std::optional> varray_span_; + std::optional> varray_any_; + std::unique_ptr owned_gvarray_; + + public: + explicit GVMutableArray_Typed(GVMutableArray &gvarray) + { + BLI_assert(gvarray.type().is()); + if (gvarray.is_span()) { + const GMutableSpan span = gvarray.get_internal_span(); + varray_span_.emplace(span.typed()); + varray_ = &*varray_span_; + } + else if (VMutableArray *internal_varray = gvarray.try_get_internal_mutable_varray()) { + varray_ = internal_varray; + } + else { + varray_any_.emplace(gvarray); + varray_ = &*varray_any_; + } + } + + explicit GVMutableArray_Typed(std::unique_ptr gvarray) + : GVMutableArray_Typed(*gvarray) + { + owned_gvarray_ = std::move(gvarray); + } + + VMutableArray &operator*() + { + return *varray_; + } + + VMutableArray *operator->() + { + return varray_; + } + + operator VMutableArray &() + { + return *varray_; + } + + T operator[](const int64_t index) const + { + return varray_->get(index); + } + + int64_t size() const + { + return varray_->size(); + } }; } // namespace blender::fn -- cgit v1.2.3