Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/blenlib/BLI_virtual_array.hh')
-rw-r--r--source/blender/blenlib/BLI_virtual_array.hh363
1 files changed, 228 insertions, 135 deletions
diff --git a/source/blender/blenlib/BLI_virtual_array.hh b/source/blender/blenlib/BLI_virtual_array.hh
index 6efd1d6d769..438fcc4b8f7 100644
--- a/source/blender/blenlib/BLI_virtual_array.hh
+++ b/source/blender/blenlib/BLI_virtual_array.hh
@@ -35,6 +35,36 @@ class GVArray;
class GVMutableArray;
/**
+ * Is used to quickly check if a varray is a span or single value. This struct also allows
+ * retrieving multiple pieces of data with a single virtual method call.
+ */
+struct CommonVArrayInfo {
+ enum class Type : uint8_t {
+ /* Is not one of the common special types below. */
+ Any,
+ Span,
+ Single,
+ };
+
+ Type type = Type::Any;
+
+ /** True when the #data becomes a dangling pointer when the virtual array is destructed. */
+ bool may_have_ownership = true;
+
+ /**
+ * Points either to nothing, a single value or array of values, depending on #type.
+ * If this is a span of a mutable virtual array, it is safe to cast away const.
+ */
+ const void *data;
+
+ CommonVArrayInfo() = default;
+ CommonVArrayInfo(const Type _type, const bool _may_have_ownership, const void *_data)
+ : type(_type), may_have_ownership(_may_have_ownership), data(_data)
+ {
+ }
+};
+
+/**
* Implements the specifics of how the elements of a virtual array are accessed. It contains a
* bunch of virtual methods that are wrapped by #VArray.
*/
@@ -65,47 +95,12 @@ template<typename T> class VArrayImpl {
*/
virtual T get(int64_t index) const = 0;
- /**
- * Return true when the virtual array is a plain array internally.
- */
- virtual bool is_span() const
+ virtual CommonVArrayInfo common_info() const
{
- return false;
- }
-
- /**
- * Return the span of the virtual array.
- * This invokes undefined behavior when #is_span returned false.
- */
- virtual Span<T> get_internal_span() const
- {
- /* Provide a default implementation, so that subclasses don't have to provide it. This method
- * should never be called because #is_span returns false by default. */
- BLI_assert_unreachable();
return {};
}
/**
- * Return true when the virtual array has the same value at every index.
- */
- virtual bool is_single() const
- {
- return false;
- }
-
- /**
- * Return the value that is used at every index.
- * This invokes undefined behavior when #is_single returned false.
- */
- virtual T get_internal_single() const
- {
- /* Provide a default implementation, so that subclasses don't have to provide it. This method
- * should never be called because #is_single returns false by default. */
- BLI_assert_unreachable();
- return T();
- }
-
- /**
* Copy values from the virtual array into the provided span. The index of the value in the
* virtual array is the same as the index in the span.
*/
@@ -113,16 +108,22 @@ template<typename T> class VArrayImpl {
{
T *dst = r_span.data();
/* Optimize for a few different common cases. */
- if (this->is_span()) {
- const T *src = this->get_internal_span().data();
- mask.foreach_index([&](const int64_t i) { dst[i] = src[i]; });
- }
- else if (this->is_single()) {
- const T single = this->get_internal_single();
- mask.foreach_index([&](const int64_t i) { dst[i] = single; });
- }
- else {
- mask.foreach_index([&](const int64_t i) { dst[i] = this->get(i); });
+ const CommonVArrayInfo info = this->common_info();
+ switch (info.type) {
+ case CommonVArrayInfo::Type::Any: {
+ mask.foreach_index([&](const int64_t i) { dst[i] = this->get(i); });
+ break;
+ }
+ case CommonVArrayInfo::Type::Span: {
+ const T *src = static_cast<const T *>(info.data);
+ mask.foreach_index([&](const int64_t i) { dst[i] = src[i]; });
+ break;
+ }
+ case CommonVArrayInfo::Type::Single: {
+ const T single = *static_cast<const T *>(info.data);
+ mask.foreach_index([&](const int64_t i) { dst[i] = single; });
+ break;
+ }
}
}
@@ -133,16 +134,22 @@ template<typename T> class VArrayImpl {
{
T *dst = r_span.data();
/* Optimize for a few different common cases. */
- if (this->is_span()) {
- const T *src = this->get_internal_span().data();
- mask.foreach_index([&](const int64_t i) { new (dst + i) T(src[i]); });
- }
- else if (this->is_single()) {
- const T single = this->get_internal_single();
- mask.foreach_index([&](const int64_t i) { new (dst + i) T(single); });
- }
- else {
- mask.foreach_index([&](const int64_t i) { new (dst + i) T(this->get(i)); });
+ const CommonVArrayInfo info = this->common_info();
+ switch (info.type) {
+ case CommonVArrayInfo::Type::Any: {
+ mask.foreach_index([&](const int64_t i) { new (dst + i) T(this->get(i)); });
+ break;
+ }
+ case CommonVArrayInfo::Type::Span: {
+ const T *src = static_cast<const T *>(info.data);
+ mask.foreach_index([&](const int64_t i) { new (dst + i) T(src[i]); });
+ break;
+ }
+ case CommonVArrayInfo::Type::Single: {
+ const T single = *static_cast<const T *>(info.data);
+ mask.foreach_index([&](const int64_t i) { new (dst + i) T(single); });
+ break;
+ }
}
}
@@ -187,17 +194,6 @@ template<typename T> class VArrayImpl {
}
/**
- * Return true when this virtual array may own any of the memory it references. This can be used
- * for optimization purposes when converting or copying the virtual array.
- */
- virtual bool may_have_ownership() const
- {
- /* Use true by default to be on the safe side. Subclasses that know for sure that they don't
- * own anything can overwrite this with false. */
- return true;
- }
-
- /**
* Return true when the other virtual array should be considered to be the same, e.g. because it
* shares the same underlying memory.
*/
@@ -222,10 +218,10 @@ template<typename T> class VMutableArrayImpl : public VArrayImpl<T> {
*/
virtual void set_all(Span<T> src)
{
- if (this->is_span()) {
- const Span<T> const_span = this->get_internal_span();
- const MutableSpan<T> span{(T *)const_span.data(), const_span.size()};
- initialized_copy_n(src.data(), this->size_, span.data());
+ const CommonVArrayInfo info = this->common_info();
+ if (info.type == CommonVArrayInfo::Type::Span) {
+ initialized_copy_n(
+ src.data(), this->size_, const_cast<T *>(static_cast<const T *>(info.data)));
}
else {
const int64_t size = this->size_;
@@ -273,14 +269,9 @@ template<typename T> class VArrayImpl_For_Span : public VMutableArrayImpl<T> {
data_[index] = value;
}
- bool is_span() const override
+ CommonVArrayInfo common_info() const override
{
- return true;
- }
-
- Span<T> get_internal_span() const override
- {
- return Span<T>(data_, this->size_);
+ return CommonVArrayInfo(CommonVArrayInfo::Type::Span, true, data_);
}
bool is_same(const VArrayImpl<T> &other) const final
@@ -288,11 +279,11 @@ template<typename T> class VArrayImpl_For_Span : public VMutableArrayImpl<T> {
if (other.size() != this->size_) {
return false;
}
- if (!other.is_span()) {
+ const CommonVArrayInfo other_info = other.common_info();
+ if (other_info.type != CommonVArrayInfo::Type::Span) {
return false;
}
- const Span<T> other_span = other.get_internal_span();
- return data_ == other_span.data();
+ return data_ == static_cast<const T *>(other_info.data);
}
void materialize_compressed(IndexMask mask, MutableSpan<T> r_span) const override
@@ -325,12 +316,15 @@ template<typename T> class VArrayImpl_For_Span_final final : public VArrayImpl_F
using VArrayImpl_For_Span<T>::VArrayImpl_For_Span;
private:
- bool may_have_ownership() const override
+ CommonVArrayInfo common_info() const final
{
- return false;
+ return CommonVArrayInfo(CommonVArrayInfo::Type::Span, false, this->data_);
}
};
+template<typename T>
+inline constexpr bool is_trivial_extended_v<VArrayImpl_For_Span_final<T>> = true;
+
/**
* A variant of `VArrayImpl_For_Span` that owns the underlying data.
* The `Container` type has to implement a `size()` and `data()` method.
@@ -371,24 +365,9 @@ template<typename T> class VArrayImpl_For_Single final : public VArrayImpl<T> {
return value_;
}
- bool is_span() const override
+ CommonVArrayInfo common_info() const override
{
- return this->size_ == 1;
- }
-
- Span<T> get_internal_span() const override
- {
- return Span<T>(&value_, 1);
- }
-
- bool is_single() const override
- {
- return true;
- }
-
- T get_internal_single() const override
- {
- return value_;
+ return CommonVArrayInfo(CommonVArrayInfo::Type::Single, true, &value_);
}
void materialize_compressed(IndexMask mask, MutableSpan<T> r_span) const override
@@ -406,6 +385,9 @@ template<typename T> class VArrayImpl_For_Single final : public VArrayImpl<T> {
}
};
+template<typename T>
+inline constexpr bool is_trivial_extended_v<VArrayImpl_For_Single<T>> = is_trivial_extended_v<T>;
+
/**
* This class makes it easy to create a virtual array for an existing function or lambda. The
* `GetFunc` should take a single `index` argument and return the value at that index.
@@ -531,11 +513,6 @@ class VArrayImpl_For_DerivedSpan final : public VMutableArrayImpl<ElemT> {
});
}
- bool may_have_ownership() const override
- {
- return false;
- }
-
bool is_same(const VArrayImpl<ElemT> &other) const override
{
if (other.size() != this->size_) {
@@ -554,6 +531,13 @@ class VArrayImpl_For_DerivedSpan final : public VMutableArrayImpl<ElemT> {
}
};
+template<typename StructT,
+ typename ElemT,
+ ElemT (*GetFunc)(const StructT &),
+ void (*SetFunc)(StructT &, ElemT)>
+inline constexpr bool
+ is_trivial_extended_v<VArrayImpl_For_DerivedSpan<StructT, ElemT, GetFunc, SetFunc>> = true;
+
namespace detail {
/**
@@ -768,11 +752,18 @@ template<typename T> class VArrayCommon {
return IndexRange(this->size());
}
+ CommonVArrayInfo common_info() const
+ {
+ BLI_assert(*this);
+ return impl_->common_info();
+ }
+
/** Return true when the virtual array is stored as a span internally. */
bool is_span() const
{
BLI_assert(*this);
- return impl_->is_span();
+ const CommonVArrayInfo info = impl_->common_info();
+ return info.type == CommonVArrayInfo::Type::Span;
}
/**
@@ -782,14 +773,16 @@ template<typename T> class VArrayCommon {
Span<T> get_internal_span() const
{
BLI_assert(this->is_span());
- return impl_->get_internal_span();
+ const CommonVArrayInfo info = impl_->common_info();
+ return Span<T>(static_cast<const T *>(info.data), this->size());
}
/** Return true when the virtual array returns the same value for every index. */
bool is_single() const
{
BLI_assert(*this);
- return impl_->is_single();
+ const CommonVArrayInfo info = impl_->common_info();
+ return info.type == CommonVArrayInfo::Type::Single;
}
/**
@@ -799,7 +792,8 @@ template<typename T> class VArrayCommon {
T get_internal_single() const
{
BLI_assert(this->is_single());
- return impl_->get_internal_single();
+ const CommonVArrayInfo info = impl_->common_info();
+ return *static_cast<const T *>(info.data);
}
/**
@@ -861,17 +855,27 @@ template<typename T> class VArrayCommon {
{
return impl_->try_assign_GVArray(varray);
}
-
- /** See #GVArrayImpl::may_have_ownership. */
- bool may_have_ownership() const
- {
- return impl_->may_have_ownership();
- }
};
template<typename T> class VMutableArray;
/**
+ * Various tags to disambiguate constructors of virtual arrays.
+ * Generally it is easier to use `VArray::For*` functions to construct virtual arrays, but
+ * sometimes being able to use the constructor can result in better performance For example, when
+ * constructing the virtual array directly in a vector. Without the constructor one would have to
+ * construct the virtual array first and then move it into the vector.
+ */
+namespace varray_tag {
+struct span {
+};
+struct single_ref {
+};
+struct single {
+};
+} // namespace varray_tag
+
+/**
* A #VArray wraps a virtual array implementation and provides easy access to its elements. It can
* be copied and moved. While it is relatively small, it should still be passed by reference if
* possible (other than e.g. #Span).
@@ -892,6 +896,19 @@ template<typename T> class VArray : public VArrayCommon<T> {
{
}
+ VArray(varray_tag::span /* tag */, Span<T> span)
+ {
+ /* Cast const away, because the virtual array implementation for const and non const spans is
+ * shared. */
+ MutableSpan<T> mutable_span{const_cast<T *>(span.data()), span.size()};
+ this->template emplace<VArrayImpl_For_Span_final<T>>(mutable_span);
+ }
+
+ VArray(varray_tag::single /* tag */, T value, const int64_t size)
+ {
+ this->template emplace<VArrayImpl_For_Single<T>>(std::move(value), size);
+ }
+
/**
* Construct a new virtual array for a custom #VArrayImpl.
*/
@@ -908,7 +925,7 @@ template<typename T> class VArray : public VArrayCommon<T> {
*/
static VArray ForSingle(T value, const int64_t size)
{
- return VArray::For<VArrayImpl_For_Single<T>>(std::move(value), size);
+ return VArray(varray_tag::single{}, std::move(value), size);
}
/**
@@ -917,10 +934,7 @@ template<typename T> class VArray : public VArrayCommon<T> {
*/
static VArray ForSpan(Span<T> values)
{
- /* Cast const away, because the virtual array implementation for const and non const spans is
- * shared. */
- MutableSpan<T> span{const_cast<T *>(values.data()), values.size()};
- return VArray::For<VArrayImpl_For_Span_final<T>>(span);
+ return VArray(varray_tag::span{}, values);
}
/**
@@ -1050,8 +1064,8 @@ template<typename T> class VMutableArray : public VArrayCommon<T> {
MutableSpan<T> get_internal_span() const
{
BLI_assert(this->is_span());
- const Span<T> span = this->impl_->get_internal_span();
- return MutableSpan<T>(const_cast<T *>(span.data()), span.size());
+ const CommonVArrayInfo info = this->get_impl()->common_info();
+ return MutableSpan<T>(const_cast<T *>(static_cast<const T *>(info.data)), this->size());
}
/**
@@ -1106,17 +1120,23 @@ template<typename T> static constexpr bool is_VMutableArray_v<VMutableArray<T>>
* from faster access.
* - An API is called, that does not accept virtual arrays, but only spans.
*/
-template<typename T> class VArray_Span final : public Span<T> {
+template<typename T> class VArraySpan final : public Span<T> {
private:
VArray<T> varray_;
Array<T> owned_data_;
public:
- VArray_Span(VArray<T> varray) : Span<T>(), varray_(std::move(varray))
+ VArraySpan() = default;
+
+ VArraySpan(VArray<T> varray) : Span<T>(), varray_(std::move(varray))
{
+ if (!varray_) {
+ return;
+ }
this->size_ = varray_.size();
- if (varray_.is_span()) {
- this->data_ = varray_.get_internal_span().data();
+ const CommonVArrayInfo info = varray_.common_info();
+ if (info.type == CommonVArrayInfo::Type::Span) {
+ this->data_ = static_cast<const T *>(info.data);
}
else {
owned_data_.~Array();
@@ -1125,16 +1145,44 @@ template<typename T> class VArray_Span final : public Span<T> {
this->data_ = owned_data_.data();
}
}
+
+ VArraySpan(VArraySpan &&other)
+ : varray_(std::move(other.varray_)), owned_data_(std::move(other.owned_data_))
+ {
+ if (!varray_) {
+ return;
+ }
+ this->size_ = varray_.size();
+ const CommonVArrayInfo info = varray_.common_info();
+ if (info.type == CommonVArrayInfo::Type::Span) {
+ this->data_ = static_cast<const T *>(info.data);
+ }
+ else {
+ this->data_ = owned_data_.data();
+ }
+ other.data_ = nullptr;
+ other.size_ = 0;
+ }
+
+ VArraySpan &operator=(VArraySpan &&other)
+ {
+ if (this == &other) {
+ return *this;
+ }
+ std::destroy_at(this);
+ new (this) VArraySpan(std::move(other));
+ return *this;
+ }
};
/**
- * Same as #VArray_Span, but for a mutable span.
+ * Same as #VArraySpan, but for a mutable span.
* The important thing to note is that when changing this span, the results might not be
* immediately reflected in the underlying virtual array (only when the virtual array is a span
* internally). The #save method can be used to write all changes to the underlying virtual array,
* if necessary.
*/
-template<typename T> class VMutableArray_Span final : public MutableSpan<T> {
+template<typename T> class MutableVArraySpan final : public MutableSpan<T> {
private:
VMutableArray<T> varray_;
Array<T> owned_data_;
@@ -1142,14 +1190,21 @@ template<typename T> class VMutableArray_Span final : public MutableSpan<T> {
bool show_not_saved_warning_ = true;
public:
+ MutableVArraySpan() = default;
+
/* Create a span for any virtual array. This is cheap when the virtual array is a span itself. If
* not, a new array has to be allocated as a wrapper for the underlying virtual array. */
- VMutableArray_Span(VMutableArray<T> varray, const bool copy_values_to_span = true)
+ MutableVArraySpan(VMutableArray<T> varray, const bool copy_values_to_span = true)
: MutableSpan<T>(), varray_(std::move(varray))
{
+ if (!varray_) {
+ return;
+ }
+
this->size_ = varray_.size();
- if (varray_.is_span()) {
- this->data_ = varray_.get_internal_span().data();
+ const CommonVArrayInfo info = varray_.common_info();
+ if (info.type == CommonVArrayInfo::Type::Span) {
+ this->data_ = const_cast<T *>(static_cast<const T *>(info.data));
}
else {
if (copy_values_to_span) {
@@ -1164,15 +1219,53 @@ template<typename T> class VMutableArray_Span final : public MutableSpan<T> {
}
}
- ~VMutableArray_Span()
+ MutableVArraySpan(MutableVArraySpan &&other)
+ : varray_(std::move(other.varray_)),
+ owned_data_(std::move(other.owned_data_)),
+ show_not_saved_warning_(other.show_not_saved_warning_)
+ {
+ if (!varray_) {
+ return;
+ }
+
+ this->size_ = varray_.size();
+ const CommonVArrayInfo info = varray_.common_info();
+ if (info.type == CommonVArrayInfo::Type::Span) {
+ this->data_ = static_cast<T *>(const_cast<void *>(info.data));
+ }
+ else {
+ this->data_ = owned_data_.data();
+ }
+ other.data_ = nullptr;
+ other.size_ = 0;
+ }
+
+ ~MutableVArraySpan()
{
- if (show_not_saved_warning_) {
- if (!save_has_been_called_) {
- std::cout << "Warning: Call `save()` to make sure that changes persist in all cases.\n";
+ if (varray_) {
+ if (show_not_saved_warning_) {
+ if (!save_has_been_called_) {
+ std::cout << "Warning: Call `save()` to make sure that changes persist in all cases.\n";
+ }
}
}
}
+ MutableVArraySpan &operator=(MutableVArraySpan &&other)
+ {
+ if (this == &other) {
+ return *this;
+ }
+ std::destroy_at(this);
+ new (this) MutableVArraySpan(std::move(other));
+ return *this;
+ }
+
+ const VMutableArray<T> &varray() const
+ {
+ return varray_;
+ }
+
/* Write back all values from a temporary allocated array to the underlying virtual array. */
void save()
{