Goals of this refactor: * Simplify creating virtual arrays. * Simplify passing virtual arrays around. * Simplify converting between typed and generic virtual arrays. * Reduce memory allocations. As a quick reminder, a virtual arrays is a data structure that behaves like an array (i.e. it can be accessed using an index). However, it may not actually be stored as array internally. The two most important implementations of virtual arrays are those that correspond to an actual plain array and those that have the same value for every index. However, many more implementations exist for various reasons (interfacing with legacy attributes, unified iterator over all points in multiple splines, ...). With this refactor the core types (`VArray`, `GVArray`, `VMutableArray` and `GVMutableArray`) can be used like "normal values". They typically live on the stack. Before, they were usually inside a `std::unique_ptr`. This makes passing them around much easier. Creation of new virtual arrays is also much simpler now due to some constructors. Memory allocations are reduced by making use of small object optimization inside the core types. Previously, `VArray` was a class with virtual methods that had to be overridden to change the behavior of a the virtual array. Now,`VArray` has a fixed size and has no virtual methods. Instead it contains a `VArrayImpl` that is similar to the old `VArray`. `VArrayImpl` should rarely ever be used directly, unless a new virtual array implementation is added. To support the small object optimization for many `VArrayImpl` classes, a new `blender::Any` type is added. It is similar to `std::any` with two additional features. It has an adjustable inline buffer size and alignment. The inline buffer size of `std::any` can't be relied on and is usually too small for our use case here. Furthermore, `blender::Any` can store additional user-defined type information without increasing the stack size. Differential Revision: https://developer.blender.org/D12986
163 lines
4.4 KiB
C++
163 lines
4.4 KiB
C++
/*
|
|
* 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`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 "BLI_array.hh"
|
|
#include "BLI_linear_allocator.hh"
|
|
|
|
#include "FN_generic_virtual_vector_array.hh"
|
|
|
|
namespace blender::fn {
|
|
|
|
/* An array of vectors containing elements of a generic type. */
|
|
class GVectorArray : NonCopyable, NonMovable {
|
|
private:
|
|
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_;
|
|
/* 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);
|
|
|
|
~GVectorArray();
|
|
|
|
int64_t size() const
|
|
{
|
|
return items_.size();
|
|
}
|
|
|
|
bool is_empty() const
|
|
{
|
|
return items_.is_empty();
|
|
}
|
|
|
|
const CPPType &type() const
|
|
{
|
|
return type_;
|
|
}
|
|
|
|
void append(int64_t index, const void *value);
|
|
|
|
/* Add multiple elements to a single vector. */
|
|
void extend(int64_t index, const GVArray &values);
|
|
void extend(int64_t index, GSpan values);
|
|
|
|
/* Add multiple elements to multiple vectors. */
|
|
void extend(IndexMask mask, const GVVectorArray &values);
|
|
void extend(IndexMask mask, const GVectorArray &values);
|
|
|
|
void clear(IndexMask mask);
|
|
|
|
GMutableSpan operator[](int64_t index);
|
|
GSpan operator[](int64_t index) const;
|
|
|
|
private:
|
|
void realloc_to_at_least(Item &item, int64_t min_capacity);
|
|
};
|
|
|
|
/* 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:
|
|
GVectorArray_TypedMutableRef(GVectorArray &vector_array) : vector_array_(&vector_array)
|
|
{
|
|
BLI_assert(vector_array_->type().is<T>());
|
|
}
|
|
|
|
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(const int64_t index, const Span<T> values)
|
|
{
|
|
vector_array_->extend(index, values);
|
|
}
|
|
|
|
void extend(const int64_t index, const VArray<T> &values)
|
|
{
|
|
vector_array_->extend(index, values);
|
|
}
|
|
|
|
MutableSpan<T> operator[](const int64_t index)
|
|
{
|
|
return (*vector_array_)[index].typed<T>();
|
|
}
|
|
};
|
|
|
|
/* A generic virtual vector array implementation for a `GVectorArray`. */
|
|
class GVVectorArray_For_GVectorArray : public GVVectorArray {
|
|
private:
|
|
const GVectorArray &vector_array_;
|
|
|
|
public:
|
|
GVVectorArray_For_GVectorArray(const GVectorArray &vector_array)
|
|
: GVVectorArray(vector_array.type(), vector_array.size()), vector_array_(vector_array)
|
|
{
|
|
}
|
|
|
|
protected:
|
|
int64_t get_vector_size_impl(const int64_t index) const override
|
|
{
|
|
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_assign(vector_array_[index][index_in_vector], r_value);
|
|
}
|
|
};
|
|
|
|
} // namespace blender::fn
|