2022-02-10 23:07:11 +01:00
|
|
|
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
2021-03-21 19:31:24 +01:00
|
|
|
|
|
|
|
#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,
|
2021-03-22 04:44:05 +01:00
|
|
|
* because it does not have to know exactly how the data is laid out in memory, or if it is stored
|
2021-03-21 19:31:24 +01:00
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
|
Geometry Nodes: refactor virtual array system
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
2021-11-16 10:15:51 +01:00
|
|
|
#include "BLI_any.hh"
|
2021-04-17 15:13:20 +02:00
|
|
|
#include "BLI_array.hh"
|
2021-04-29 12:59:44 +02:00
|
|
|
#include "BLI_index_mask.hh"
|
2021-03-21 19:31:24 +01:00
|
|
|
#include "BLI_span.hh"
|
|
|
|
|
|
|
|
namespace blender {
|
|
|
|
|
2021-12-20 09:01:14 +01:00
|
|
|
/** Forward declarations for generic virtual arrays. */
|
Geometry Nodes: refactor virtual array system
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
2021-11-16 10:15:51 +01:00
|
|
|
class GVArray;
|
|
|
|
class GVMutableArray;
|
|
|
|
|
2022-06-25 17:28:49 +02:00
|
|
|
/**
|
|
|
|
* 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;
|
|
|
|
|
2022-07-02 11:45:57 +02:00
|
|
|
/**
|
|
|
|
* 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.
|
|
|
|
*/
|
2022-06-25 17:28:49 +02:00
|
|
|
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)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
Geometry Nodes: refactor virtual array system
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
2021-11-16 10:15:51 +01:00
|
|
|
/**
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
template<typename T> class VArrayImpl {
|
2021-03-21 19:31:24 +01:00
|
|
|
protected:
|
Geometry Nodes: refactor virtual array system
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
2021-11-16 10:15:51 +01:00
|
|
|
/**
|
|
|
|
* Number of elements in the virtual array. All virtual arrays have a size, but in some cases it
|
|
|
|
* may make sense to set it to the max value.
|
|
|
|
*/
|
2021-03-21 19:31:24 +01:00
|
|
|
int64_t size_;
|
|
|
|
|
|
|
|
public:
|
Geometry Nodes: refactor virtual array system
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
2021-11-16 10:15:51 +01:00
|
|
|
VArrayImpl(const int64_t size) : size_(size)
|
2021-03-21 19:31:24 +01:00
|
|
|
{
|
|
|
|
BLI_assert(size_ >= 0);
|
|
|
|
}
|
|
|
|
|
Geometry Nodes: refactor virtual array system
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
2021-11-16 10:15:51 +01:00
|
|
|
virtual ~VArrayImpl() = default;
|
2021-03-21 19:31:24 +01:00
|
|
|
|
|
|
|
int64_t size() const
|
|
|
|
{
|
|
|
|
return size_;
|
|
|
|
}
|
|
|
|
|
Geometry Nodes: refactor virtual array system
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
2021-11-16 10:15:51 +01:00
|
|
|
/**
|
|
|
|
* Get the element at #index. This does not return a reference, because the value may be computed
|
|
|
|
* on the fly.
|
|
|
|
*/
|
2022-01-07 01:38:08 +01:00
|
|
|
virtual T get(int64_t index) const = 0;
|
2021-03-21 19:31:24 +01:00
|
|
|
|
2022-06-25 17:28:49 +02:00
|
|
|
virtual CommonVArrayInfo common_info() const
|
2021-03-21 19:31:24 +01:00
|
|
|
{
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
Geometry Nodes: refactor virtual array system
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
2021-11-16 10:15:51 +01:00
|
|
|
/**
|
|
|
|
* Copy values from the virtual array into the provided span. The index of the value in the
|
2022-04-07 10:02:34 +02:00
|
|
|
* virtual array is the same as the index in the span.
|
Geometry Nodes: refactor virtual array system
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
2021-11-16 10:15:51 +01:00
|
|
|
*/
|
|
|
|
virtual void materialize(IndexMask mask, MutableSpan<T> r_span) const
|
2021-04-17 15:13:20 +02:00
|
|
|
{
|
2021-04-29 12:59:44 +02:00
|
|
|
T *dst = r_span.data();
|
Geometry Nodes: refactor virtual array system
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
2021-11-16 10:15:51 +01:00
|
|
|
/* Optimize for a few different common cases. */
|
2022-06-25 17:28:49 +02:00
|
|
|
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;
|
|
|
|
}
|
2021-04-17 15:13:20 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Geometry Nodes: refactor virtual array system
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
2021-11-16 10:15:51 +01:00
|
|
|
/**
|
|
|
|
* Same as #materialize but #r_span is expected to be uninitialized.
|
|
|
|
*/
|
|
|
|
virtual void materialize_to_uninitialized(IndexMask mask, MutableSpan<T> r_span) const
|
2021-04-17 15:13:20 +02:00
|
|
|
{
|
2021-04-29 12:59:44 +02:00
|
|
|
T *dst = r_span.data();
|
Geometry Nodes: refactor virtual array system
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
2021-11-16 10:15:51 +01:00
|
|
|
/* Optimize for a few different common cases. */
|
2022-06-25 17:28:49 +02:00
|
|
|
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;
|
|
|
|
}
|
2021-04-17 15:13:20 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-04-07 10:02:34 +02:00
|
|
|
/**
|
|
|
|
* Copy values from the virtual array into the provided span. Contrary to #materialize, the index
|
|
|
|
* in virtual array is not the same as the index in the output span. Instead, the span is filled
|
|
|
|
* without gaps.
|
|
|
|
*/
|
|
|
|
virtual void materialize_compressed(IndexMask mask, MutableSpan<T> r_span) const
|
|
|
|
{
|
|
|
|
BLI_assert(mask.size() == r_span.size());
|
|
|
|
mask.to_best_mask_type([&](auto best_mask) {
|
|
|
|
for (const int64_t i : IndexRange(best_mask.size())) {
|
|
|
|
r_span[i] = this->get(best_mask[i]);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Same as #materialize_compressed but #r_span is expected to be uninitialized.
|
|
|
|
*/
|
|
|
|
virtual void materialize_compressed_to_uninitialized(IndexMask mask, MutableSpan<T> r_span) const
|
|
|
|
{
|
|
|
|
BLI_assert(mask.size() == r_span.size());
|
|
|
|
T *dst = r_span.data();
|
|
|
|
mask.to_best_mask_type([&](auto best_mask) {
|
|
|
|
for (const int64_t i : IndexRange(best_mask.size())) {
|
|
|
|
new (dst + i) T(this->get(best_mask[i]));
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
Geometry Nodes: refactor virtual array system
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
2021-11-16 10:15:51 +01:00
|
|
|
/**
|
|
|
|
* If this virtual wraps another #GVArray, this method should assign the wrapped array to the
|
|
|
|
* provided reference. This allows losslessly converting between generic and typed virtual
|
|
|
|
* arrays in all cases.
|
|
|
|
* Return true when the virtual array was assigned and false when nothing was done.
|
|
|
|
*/
|
2022-03-19 08:26:29 +01:00
|
|
|
virtual bool try_assign_GVArray(GVArray &UNUSED(varray)) const
|
2021-04-17 15:13:20 +02:00
|
|
|
{
|
Geometry Nodes: refactor virtual array system
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
2021-11-16 10:15:51 +01:00
|
|
|
return false;
|
2021-04-17 15:13:20 +02:00
|
|
|
}
|
|
|
|
|
2021-11-26 15:33:21 +01:00
|
|
|
/**
|
|
|
|
* Return true when the other virtual array should be considered to be the same, e.g. because it
|
|
|
|
* shares the same underlying memory.
|
|
|
|
*/
|
|
|
|
virtual bool is_same(const VArrayImpl<T> &UNUSED(other)) const
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
Geometry Nodes: refactor virtual array system
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
2021-11-16 10:15:51 +01:00
|
|
|
};
|
2021-04-17 15:13:20 +02:00
|
|
|
|
2021-12-20 09:01:14 +01:00
|
|
|
/** Similar to #VArrayImpl, but adds methods that allow modifying the referenced elements. */
|
Geometry Nodes: refactor virtual array system
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
2021-11-16 10:15:51 +01:00
|
|
|
template<typename T> class VMutableArrayImpl : public VArrayImpl<T> {
|
|
|
|
public:
|
|
|
|
using VArrayImpl<T>::VArrayImpl;
|
2021-04-17 15:13:20 +02:00
|
|
|
|
Geometry Nodes: refactor virtual array system
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
2021-11-16 10:15:51 +01:00
|
|
|
/**
|
|
|
|
* Assign the provided #value to the #index.
|
|
|
|
*/
|
2022-01-07 01:38:08 +01:00
|
|
|
virtual void set(int64_t index, T value) = 0;
|
2021-04-17 15:13:20 +02:00
|
|
|
|
Geometry Nodes: refactor virtual array system
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
2021-11-16 10:15:51 +01:00
|
|
|
/**
|
|
|
|
* Copy all elements from the provided span into the virtual array.
|
|
|
|
*/
|
|
|
|
virtual void set_all(Span<T> src)
|
2021-04-17 15:13:20 +02:00
|
|
|
{
|
2022-06-25 17:28:49 +02:00
|
|
|
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)));
|
2021-04-17 15:13:20 +02:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
const int64_t size = this->size_;
|
|
|
|
for (int64_t i = 0; i < size; i++) {
|
|
|
|
this->set(i, src[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-03-21 19:31:24 +01:00
|
|
|
|
Geometry Nodes: refactor virtual array system
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
2021-11-16 10:15:51 +01:00
|
|
|
/**
|
|
|
|
* Similar to #VArrayImpl::try_assign_GVArray but for mutable virtual arrays.
|
|
|
|
*/
|
2022-03-19 08:26:29 +01:00
|
|
|
virtual bool try_assign_GVMutableArray(GVMutableArray &UNUSED(varray)) const
|
Geometry Nodes: refactor virtual array system
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
2021-11-16 10:15:51 +01:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
};
|
Geometry Nodes: use virtual arrays in internal attribute api
A virtual array is a data structure that is similar to a normal array
in that its elements can be accessed by an index. However, a virtual
array does not have to be a contiguous array internally. Instead, its
elements can be layed out arbitrarily while element access happens
through a virtual function call. However, the virtual array data
structures are designed so that the virtual function call can be avoided
in cases where it could become a bottleneck.
Most commonly, a virtual array is backed by an actual array/span or
is a single value internally, that is the same for every index.
Besides those, there are many more specialized virtual arrays like the
ones that provides vertex positions based on the `MVert` struct or
vertex group weights.
Not all attributes used by geometry nodes are stored in simple contiguous
arrays. To provide uniform access to all kinds of attributes, the attribute
API has to provide virtual array functionality that hides the implementation
details of attributes.
Before this refactor, the attribute API provided its own virtual array
implementation as part of the `ReadAttribute` and `WriteAttribute` types.
That resulted in unnecessary code duplication with the virtual array system.
Even worse, it bound many algorithms used by geometry nodes to the specifics
of the attribute API, even though they could also use different data sources
(such as data from sockets, default values, later results of expressions, ...).
This refactor removes the `ReadAttribute` and `WriteAttribute` types and
replaces them with `GVArray` and `GVMutableArray` respectively. The `GV`
stands for "generic virtual". The "generic" means that the data type contained
in those virtual arrays is only known at run-time. There are the corresponding
statically typed types `VArray<T>` and `VMutableArray<T>` as well.
No regressions are expected from this refactor. It does come with one
improvement for users. The attribute API can convert the data type
on write now. This is especially useful when writing to builtin attributes
like `material_index` with e.g. the Attribute Math node (which usually
just writes to float attributes, while `material_index` is an integer attribute).
Differential Revision: https://developer.blender.org/D10994
2021-04-17 16:41:03 +02:00
|
|
|
|
2021-03-22 17:06:02 +01:00
|
|
|
/**
|
2021-11-26 14:47:02 +01:00
|
|
|
* A virtual array implementation that references that wraps a span. This implementation is used by
|
|
|
|
* mutable and immutable spans to avoid code duplication.
|
2021-03-22 17:06:02 +01:00
|
|
|
*/
|
2021-11-26 14:47:02 +01:00
|
|
|
template<typename T> class VArrayImpl_For_Span : public VMutableArrayImpl<T> {
|
2021-04-17 15:13:20 +02:00
|
|
|
protected:
|
|
|
|
T *data_ = nullptr;
|
|
|
|
|
|
|
|
public:
|
2021-11-26 14:47:02 +01:00
|
|
|
VArrayImpl_For_Span(const MutableSpan<T> data)
|
Geometry Nodes: refactor virtual array system
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
2021-11-16 10:15:51 +01:00
|
|
|
: VMutableArrayImpl<T>(data.size()), data_(data.data())
|
2021-04-17 15:13:20 +02:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
protected:
|
2021-11-26 14:47:02 +01:00
|
|
|
VArrayImpl_For_Span(const int64_t size) : VMutableArrayImpl<T>(size)
|
2021-04-17 15:13:20 +02:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
Geometry Nodes: refactor virtual array system
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
2021-11-16 10:15:51 +01:00
|
|
|
T get(const int64_t index) const final
|
2021-03-21 19:31:24 +01:00
|
|
|
{
|
|
|
|
return data_[index];
|
|
|
|
}
|
|
|
|
|
Geometry Nodes: refactor virtual array system
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
2021-11-16 10:15:51 +01:00
|
|
|
void set(const int64_t index, T value) final
|
2021-04-17 15:13:20 +02:00
|
|
|
{
|
|
|
|
data_[index] = value;
|
|
|
|
}
|
|
|
|
|
2022-06-25 17:28:49 +02:00
|
|
|
CommonVArrayInfo common_info() const override
|
2021-03-21 19:31:24 +01:00
|
|
|
{
|
2022-06-25 17:28:49 +02:00
|
|
|
return CommonVArrayInfo(CommonVArrayInfo::Type::Span, true, data_);
|
2021-03-21 19:31:24 +01:00
|
|
|
}
|
2021-11-26 15:33:21 +01:00
|
|
|
|
|
|
|
bool is_same(const VArrayImpl<T> &other) const final
|
|
|
|
{
|
|
|
|
if (other.size() != this->size_) {
|
|
|
|
return false;
|
|
|
|
}
|
2022-06-25 17:28:49 +02:00
|
|
|
const CommonVArrayInfo other_info = other.common_info();
|
|
|
|
if (other_info.type != CommonVArrayInfo::Type::Span) {
|
2021-11-26 15:33:21 +01:00
|
|
|
return false;
|
|
|
|
}
|
2022-06-25 17:28:49 +02:00
|
|
|
return data_ == static_cast<const T *>(other_info.data);
|
2021-11-26 15:33:21 +01:00
|
|
|
}
|
2022-04-07 10:02:34 +02:00
|
|
|
|
2022-04-07 17:14:47 +02:00
|
|
|
void materialize_compressed(IndexMask mask, MutableSpan<T> r_span) const override
|
2022-04-07 10:02:34 +02:00
|
|
|
{
|
|
|
|
mask.to_best_mask_type([&](auto best_mask) {
|
|
|
|
for (const int64_t i : IndexRange(best_mask.size())) {
|
|
|
|
r_span[i] = data_[best_mask[i]];
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2022-04-07 17:14:47 +02:00
|
|
|
void materialize_compressed_to_uninitialized(IndexMask mask,
|
|
|
|
MutableSpan<T> r_span) const override
|
2022-04-07 10:02:34 +02:00
|
|
|
{
|
|
|
|
T *dst = r_span.data();
|
|
|
|
mask.to_best_mask_type([&](auto best_mask) {
|
|
|
|
for (const int64_t i : IndexRange(best_mask.size())) {
|
|
|
|
new (dst + i) T(data_[best_mask[i]]);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
2021-03-21 19:31:24 +01:00
|
|
|
};
|
|
|
|
|
2021-04-17 15:13:20 +02:00
|
|
|
/**
|
2021-11-26 14:47:02 +01:00
|
|
|
* A version of #VArrayImpl_For_Span that can not be subclassed. This allows safely overwriting the
|
|
|
|
* #may_have_ownership method.
|
Geometry Nodes: refactor virtual array system
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
2021-11-16 10:15:51 +01:00
|
|
|
*/
|
2021-11-26 14:47:02 +01:00
|
|
|
template<typename T> class VArrayImpl_For_Span_final final : public VArrayImpl_For_Span<T> {
|
Geometry Nodes: refactor virtual array system
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
2021-11-16 10:15:51 +01:00
|
|
|
public:
|
2021-11-26 14:47:02 +01:00
|
|
|
using VArrayImpl_For_Span<T>::VArrayImpl_For_Span;
|
Geometry Nodes: refactor virtual array system
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
2021-11-16 10:15:51 +01:00
|
|
|
|
2022-08-02 20:44:07 +02:00
|
|
|
VArrayImpl_For_Span_final(const Span<T> data)
|
|
|
|
/* Cast const away, because the implementation for const and non const spans is shared. */
|
|
|
|
: VArrayImpl_For_Span<T>({const_cast<T *>(data.data()), data.size()})
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
Geometry Nodes: refactor virtual array system
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
2021-11-16 10:15:51 +01:00
|
|
|
private:
|
2022-06-25 17:28:49 +02:00
|
|
|
CommonVArrayInfo common_info() const final
|
Geometry Nodes: refactor virtual array system
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
2021-11-16 10:15:51 +01:00
|
|
|
{
|
2022-06-25 17:28:49 +02:00
|
|
|
return CommonVArrayInfo(CommonVArrayInfo::Type::Span, false, this->data_);
|
Geometry Nodes: refactor virtual array system
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
2021-11-16 10:15:51 +01:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2022-06-25 19:27:33 +02:00
|
|
|
template<typename T>
|
|
|
|
inline constexpr bool is_trivial_extended_v<VArrayImpl_For_Span_final<T>> = true;
|
|
|
|
|
Geometry Nodes: refactor virtual array system
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
2021-11-16 10:15:51 +01:00
|
|
|
/**
|
|
|
|
* A variant of `VArrayImpl_For_Span` that owns the underlying data.
|
2021-04-17 15:13:20 +02:00
|
|
|
* The `Container` type has to implement a `size()` and `data()` method.
|
|
|
|
* The `data()` method has to return a pointer to the first element in the continuous array of
|
|
|
|
* elements.
|
|
|
|
*/
|
|
|
|
template<typename Container, typename T = typename Container::value_type>
|
Geometry Nodes: refactor virtual array system
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
2021-11-16 10:15:51 +01:00
|
|
|
class VArrayImpl_For_ArrayContainer : public VArrayImpl_For_Span<T> {
|
2021-04-17 15:13:20 +02:00
|
|
|
private:
|
|
|
|
Container container_;
|
|
|
|
|
|
|
|
public:
|
Geometry Nodes: refactor virtual array system
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
2021-11-16 10:15:51 +01:00
|
|
|
VArrayImpl_For_ArrayContainer(Container container)
|
|
|
|
: VArrayImpl_For_Span<T>((int64_t)container.size()), container_(std::move(container))
|
2021-04-17 15:13:20 +02:00
|
|
|
{
|
2021-11-26 14:47:02 +01:00
|
|
|
this->data_ = const_cast<T *>(container_.data());
|
2021-04-17 15:13:20 +02:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2021-03-22 17:06:02 +01:00
|
|
|
/**
|
|
|
|
* A virtual array implementation that returns the same value for every index. This class is final
|
|
|
|
* so that it can be devirtualized by the compiler in some cases (e.g. when #devirtualize_varray is
|
|
|
|
* used).
|
|
|
|
*/
|
Geometry Nodes: refactor virtual array system
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
2021-11-16 10:15:51 +01:00
|
|
|
template<typename T> class VArrayImpl_For_Single final : public VArrayImpl<T> {
|
2021-03-21 19:31:24 +01:00
|
|
|
private:
|
|
|
|
T value_;
|
|
|
|
|
|
|
|
public:
|
Geometry Nodes: refactor virtual array system
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
2021-11-16 10:15:51 +01:00
|
|
|
VArrayImpl_For_Single(T value, const int64_t size)
|
|
|
|
: VArrayImpl<T>(size), value_(std::move(value))
|
2021-03-21 19:31:24 +01:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
protected:
|
Geometry Nodes: refactor virtual array system
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
2021-11-16 10:15:51 +01:00
|
|
|
T get(const int64_t UNUSED(index)) const override
|
2021-03-21 19:31:24 +01:00
|
|
|
{
|
|
|
|
return value_;
|
|
|
|
}
|
|
|
|
|
2022-06-25 17:28:49 +02:00
|
|
|
CommonVArrayInfo common_info() const override
|
2021-03-21 19:31:24 +01:00
|
|
|
{
|
2022-06-25 17:28:49 +02:00
|
|
|
return CommonVArrayInfo(CommonVArrayInfo::Type::Single, true, &value_);
|
2021-03-21 19:31:24 +01:00
|
|
|
}
|
2022-04-07 10:02:34 +02:00
|
|
|
|
|
|
|
void materialize_compressed(IndexMask mask, MutableSpan<T> r_span) const override
|
|
|
|
{
|
|
|
|
BLI_assert(mask.size() == r_span.size());
|
|
|
|
UNUSED_VARS_NDEBUG(mask);
|
|
|
|
r_span.fill(value_);
|
|
|
|
}
|
|
|
|
|
|
|
|
void materialize_compressed_to_uninitialized(IndexMask mask,
|
|
|
|
MutableSpan<T> r_span) const override
|
|
|
|
{
|
|
|
|
BLI_assert(mask.size() == r_span.size());
|
|
|
|
uninitialized_fill_n(r_span.data(), mask.size(), value_);
|
|
|
|
}
|
2021-03-21 19:31:24 +01:00
|
|
|
};
|
|
|
|
|
2022-06-25 19:27:33 +02:00
|
|
|
template<typename T>
|
|
|
|
inline constexpr bool is_trivial_extended_v<VArrayImpl_For_Single<T>> = is_trivial_extended_v<T>;
|
|
|
|
|
Geometry Nodes: refactor virtual array system
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
2021-11-16 10:15:51 +01:00
|
|
|
/**
|
|
|
|
* This class makes it easy to create a virtual array for an existing function or lambda. The
|
|
|
|
* `GetFunc` should take a single `index` argument and return the value at that index.
|
|
|
|
*/
|
|
|
|
template<typename T, typename GetFunc> class VArrayImpl_For_Func final : public VArrayImpl<T> {
|
|
|
|
private:
|
|
|
|
GetFunc get_func_;
|
|
|
|
|
|
|
|
public:
|
|
|
|
VArrayImpl_For_Func(const int64_t size, GetFunc get_func)
|
|
|
|
: VArrayImpl<T>(size), get_func_(std::move(get_func))
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
T get(const int64_t index) const override
|
|
|
|
{
|
|
|
|
return get_func_(index);
|
|
|
|
}
|
|
|
|
|
|
|
|
void materialize(IndexMask mask, MutableSpan<T> r_span) const override
|
|
|
|
{
|
|
|
|
T *dst = r_span.data();
|
|
|
|
mask.foreach_index([&](const int64_t i) { dst[i] = get_func_(i); });
|
|
|
|
}
|
|
|
|
|
|
|
|
void materialize_to_uninitialized(IndexMask mask, MutableSpan<T> r_span) const override
|
|
|
|
{
|
|
|
|
T *dst = r_span.data();
|
|
|
|
mask.foreach_index([&](const int64_t i) { new (dst + i) T(get_func_(i)); });
|
|
|
|
}
|
2022-04-07 10:02:34 +02:00
|
|
|
|
|
|
|
void materialize_compressed(IndexMask mask, MutableSpan<T> r_span) const override
|
|
|
|
{
|
|
|
|
BLI_assert(mask.size() == r_span.size());
|
|
|
|
T *dst = r_span.data();
|
|
|
|
mask.to_best_mask_type([&](auto best_mask) {
|
|
|
|
for (const int64_t i : IndexRange(best_mask.size())) {
|
|
|
|
dst[i] = get_func_(best_mask[i]);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
void materialize_compressed_to_uninitialized(IndexMask mask,
|
|
|
|
MutableSpan<T> r_span) const override
|
|
|
|
{
|
|
|
|
BLI_assert(mask.size() == r_span.size());
|
|
|
|
T *dst = r_span.data();
|
|
|
|
mask.to_best_mask_type([&](auto best_mask) {
|
|
|
|
for (const int64_t i : IndexRange(best_mask.size())) {
|
|
|
|
new (dst + i) T(get_func_(best_mask[i]));
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
Geometry Nodes: refactor virtual array system
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
2021-11-16 10:15:51 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
2022-05-13 01:24:28 +02:00
|
|
|
* \note This is `final` so that #may_have_ownership can be implemented reliably.
|
Geometry Nodes: refactor virtual array system
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
2021-11-16 10:15:51 +01:00
|
|
|
*/
|
|
|
|
template<typename StructT,
|
|
|
|
typename ElemT,
|
|
|
|
ElemT (*GetFunc)(const StructT &),
|
2021-11-26 14:47:02 +01:00
|
|
|
void (*SetFunc)(StructT &, ElemT) = nullptr>
|
|
|
|
class VArrayImpl_For_DerivedSpan final : public VMutableArrayImpl<ElemT> {
|
Geometry Nodes: refactor virtual array system
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
2021-11-16 10:15:51 +01:00
|
|
|
private:
|
|
|
|
StructT *data_;
|
|
|
|
|
|
|
|
public:
|
2021-11-26 14:47:02 +01:00
|
|
|
VArrayImpl_For_DerivedSpan(const MutableSpan<StructT> data)
|
Geometry Nodes: refactor virtual array system
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
2021-11-16 10:15:51 +01:00
|
|
|
: VMutableArrayImpl<ElemT>(data.size()), data_(data.data())
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2021-11-26 15:33:21 +01:00
|
|
|
template<typename OtherStructT,
|
|
|
|
typename OtherElemT,
|
|
|
|
OtherElemT (*OtherGetFunc)(const OtherStructT &),
|
|
|
|
void (*OtherSetFunc)(OtherStructT &, OtherElemT)>
|
|
|
|
friend class VArrayImpl_For_DerivedSpan;
|
|
|
|
|
Geometry Nodes: refactor virtual array system
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
2021-11-16 10:15:51 +01:00
|
|
|
private:
|
|
|
|
ElemT get(const int64_t index) const override
|
|
|
|
{
|
|
|
|
return GetFunc(data_[index]);
|
|
|
|
}
|
|
|
|
|
|
|
|
void set(const int64_t index, ElemT value) override
|
|
|
|
{
|
|
|
|
SetFunc(data_[index], std::move(value));
|
|
|
|
}
|
|
|
|
|
|
|
|
void materialize(IndexMask mask, MutableSpan<ElemT> r_span) const override
|
|
|
|
{
|
|
|
|
ElemT *dst = r_span.data();
|
|
|
|
mask.foreach_index([&](const int64_t i) { dst[i] = GetFunc(data_[i]); });
|
|
|
|
}
|
|
|
|
|
|
|
|
void materialize_to_uninitialized(IndexMask mask, MutableSpan<ElemT> r_span) const override
|
|
|
|
{
|
|
|
|
ElemT *dst = r_span.data();
|
|
|
|
mask.foreach_index([&](const int64_t i) { new (dst + i) ElemT(GetFunc(data_[i])); });
|
|
|
|
}
|
|
|
|
|
2022-04-07 10:02:34 +02:00
|
|
|
void materialize_compressed(IndexMask mask, MutableSpan<ElemT> r_span) const override
|
|
|
|
{
|
|
|
|
BLI_assert(mask.size() == r_span.size());
|
|
|
|
ElemT *dst = r_span.data();
|
|
|
|
mask.to_best_mask_type([&](auto best_mask) {
|
|
|
|
for (const int64_t i : IndexRange(best_mask.size())) {
|
|
|
|
dst[i] = GetFunc(data_[best_mask[i]]);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
void materialize_compressed_to_uninitialized(IndexMask mask,
|
|
|
|
MutableSpan<ElemT> r_span) const override
|
|
|
|
{
|
|
|
|
BLI_assert(mask.size() == r_span.size());
|
|
|
|
ElemT *dst = r_span.data();
|
|
|
|
mask.to_best_mask_type([&](auto best_mask) {
|
|
|
|
for (const int64_t i : IndexRange(best_mask.size())) {
|
|
|
|
new (dst + i) ElemT(GetFunc(data_[best_mask[i]]));
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2021-11-26 15:33:21 +01:00
|
|
|
bool is_same(const VArrayImpl<ElemT> &other) const override
|
|
|
|
{
|
|
|
|
if (other.size() != this->size_) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (const VArrayImpl_For_DerivedSpan<StructT, ElemT, GetFunc> *other_typed =
|
|
|
|
dynamic_cast<const VArrayImpl_For_DerivedSpan<StructT, ElemT, GetFunc> *>(&other)) {
|
|
|
|
return other_typed->data_ == data_;
|
|
|
|
}
|
|
|
|
if (const VArrayImpl_For_DerivedSpan<StructT, ElemT, GetFunc, SetFunc> *other_typed =
|
|
|
|
dynamic_cast<const VArrayImpl_For_DerivedSpan<StructT, ElemT, GetFunc, SetFunc> *>(
|
|
|
|
&other)) {
|
|
|
|
return other_typed->data_ == data_;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
Geometry Nodes: refactor virtual array system
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
2021-11-16 10:15:51 +01:00
|
|
|
};
|
|
|
|
|
2022-06-25 19:27:33 +02:00
|
|
|
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;
|
|
|
|
|
Geometry Nodes: refactor virtual array system
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
2021-11-16 10:15:51 +01:00
|
|
|
namespace detail {
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Struct that can be passed as `ExtraInfo` into an #Any.
|
|
|
|
* This struct is only intended to be used by #VArrayCommon.
|
|
|
|
*/
|
|
|
|
template<typename T> struct VArrayAnyExtraInfo {
|
|
|
|
/**
|
|
|
|
* Gets the virtual array that is stored at the given pointer.
|
|
|
|
*/
|
2022-04-25 11:51:22 +02:00
|
|
|
const VArrayImpl<T> *(*get_varray)(const void *buffer);
|
Geometry Nodes: refactor virtual array system
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
2021-11-16 10:15:51 +01:00
|
|
|
|
2022-04-25 11:51:22 +02:00
|
|
|
template<typename StorageT> static constexpr VArrayAnyExtraInfo get()
|
Geometry Nodes: refactor virtual array system
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
2021-11-16 10:15:51 +01:00
|
|
|
{
|
|
|
|
/* These are the only allowed types in the #Any. */
|
2021-12-27 16:08:11 +01:00
|
|
|
static_assert(
|
|
|
|
std::is_base_of_v<VArrayImpl<T>, StorageT> ||
|
|
|
|
is_same_any_v<StorageT, const VArrayImpl<T> *, std::shared_ptr<const VArrayImpl<T>>>);
|
Geometry Nodes: refactor virtual array system
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
2021-11-16 10:15:51 +01:00
|
|
|
|
|
|
|
/* Depending on how the virtual array implementation is stored in the #Any, a different
|
|
|
|
* #get_varray function is required. */
|
|
|
|
if constexpr (std::is_base_of_v<VArrayImpl<T>, StorageT>) {
|
|
|
|
return {[](const void *buffer) {
|
|
|
|
return static_cast<const VArrayImpl<T> *>((const StorageT *)buffer);
|
|
|
|
}};
|
|
|
|
}
|
|
|
|
else if constexpr (std::is_same_v<StorageT, const VArrayImpl<T> *>) {
|
|
|
|
return {[](const void *buffer) { return *(const StorageT *)buffer; }};
|
|
|
|
}
|
|
|
|
else if constexpr (std::is_same_v<StorageT, std::shared_ptr<const VArrayImpl<T>>>) {
|
|
|
|
return {[](const void *buffer) { return ((const StorageT *)buffer)->get(); }};
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
BLI_assert_unreachable();
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
} // namespace detail
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Utility class to reduce code duplication for methods available on #VArray and #VMutableArray.
|
|
|
|
* Deriving #VMutableArray from #VArray would have some issues:
|
|
|
|
* - Static methods on #VArray would also be available on #VMutableArray.
|
|
|
|
* - It would allow assigning a #VArray to a #VMutableArray under some circumstances which is not
|
|
|
|
* allowed and could result in hard to find bugs.
|
|
|
|
*/
|
|
|
|
template<typename T> class VArrayCommon {
|
|
|
|
protected:
|
|
|
|
/**
|
|
|
|
* Store the virtual array implementation in an #Any. This makes it easy to avoid a memory
|
|
|
|
* allocation if the implementation is small enough and is copyable. This is the case for the
|
|
|
|
* most common virtual arrays.
|
|
|
|
* Other virtual array implementations are typically stored as #std::shared_ptr. That works even
|
|
|
|
* when the implementation itself is not copyable and makes copying #VArrayCommon cheaper.
|
|
|
|
*/
|
|
|
|
using Storage = Any<detail::VArrayAnyExtraInfo<T>, 24, 8>;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Pointer to the currently contained virtual array implementation. This is allowed to be null.
|
|
|
|
*/
|
|
|
|
const VArrayImpl<T> *impl_ = nullptr;
|
|
|
|
/**
|
|
|
|
* Does the memory management for the virtual array implementation. It contains one of the
|
|
|
|
* following:
|
|
|
|
* - Inlined subclass of #VArrayImpl.
|
|
|
|
* - Non-owning pointer to a #VArrayImpl.
|
|
|
|
* - Shared pointer to a #VArrayImpl.
|
|
|
|
*/
|
|
|
|
Storage storage_;
|
|
|
|
|
|
|
|
protected:
|
|
|
|
VArrayCommon() = default;
|
|
|
|
|
|
|
|
/** Copy constructor. */
|
|
|
|
VArrayCommon(const VArrayCommon &other) : storage_(other.storage_)
|
|
|
|
{
|
|
|
|
impl_ = this->impl_from_storage();
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Move constructor. */
|
|
|
|
VArrayCommon(VArrayCommon &&other) noexcept : storage_(std::move(other.storage_))
|
|
|
|
{
|
|
|
|
impl_ = this->impl_from_storage();
|
|
|
|
other.storage_.reset();
|
|
|
|
other.impl_ = nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Wrap an existing #VArrayImpl and don't take ownership of it. This should rarely be used in
|
|
|
|
* practice.
|
|
|
|
*/
|
|
|
|
VArrayCommon(const VArrayImpl<T> *impl) : impl_(impl)
|
|
|
|
{
|
|
|
|
storage_ = impl_;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Wrap an existing #VArrayImpl that is contained in a #std::shared_ptr. This takes ownership.
|
|
|
|
*/
|
|
|
|
VArrayCommon(std::shared_ptr<const VArrayImpl<T>> impl) : impl_(impl.get())
|
|
|
|
{
|
|
|
|
if (impl) {
|
|
|
|
storage_ = std::move(impl);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Replace the contained #VArrayImpl.
|
|
|
|
*/
|
|
|
|
template<typename ImplT, typename... Args> void emplace(Args &&...args)
|
|
|
|
{
|
|
|
|
/* Make sure we are actually constructing a #VArrayImpl. */
|
|
|
|
static_assert(std::is_base_of_v<VArrayImpl<T>, ImplT>);
|
|
|
|
if constexpr (std::is_copy_constructible_v<ImplT> && Storage::template is_inline_v<ImplT>) {
|
2021-11-29 23:04:50 +01:00
|
|
|
/* Only inline the implementation when it is copyable and when it fits into the inline
|
Geometry Nodes: refactor virtual array system
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
2021-11-16 10:15:51 +01:00
|
|
|
* buffer of the storage. */
|
|
|
|
impl_ = &storage_.template emplace<ImplT>(std::forward<Args>(args)...);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* If it can't be inlined, create a new #std::shared_ptr instead and store that in the
|
|
|
|
* storage. */
|
|
|
|
std::shared_ptr<const VArrayImpl<T>> ptr = std::make_shared<ImplT>(
|
|
|
|
std::forward<Args>(args)...);
|
|
|
|
impl_ = &*ptr;
|
|
|
|
storage_ = std::move(ptr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Utility to implement a copy assignment operator in a subclass. */
|
|
|
|
void copy_from(const VArrayCommon &other)
|
|
|
|
{
|
|
|
|
if (this == &other) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
storage_ = other.storage_;
|
|
|
|
impl_ = this->impl_from_storage();
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Utility to implement a move assignment operator in a subclass. */
|
|
|
|
void move_from(VArrayCommon &&other) noexcept
|
|
|
|
{
|
|
|
|
if (this == &other) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
storage_ = std::move(other.storage_);
|
|
|
|
impl_ = this->impl_from_storage();
|
|
|
|
other.storage_.reset();
|
|
|
|
other.impl_ = nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Get a pointer to the virtual array implementation that is currently stored in #storage_, or
|
|
|
|
* null. */
|
|
|
|
const VArrayImpl<T> *impl_from_storage() const
|
|
|
|
{
|
2022-04-25 11:51:22 +02:00
|
|
|
if (!storage_.has_value()) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
Geometry Nodes: refactor virtual array system
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
2021-11-16 10:15:51 +01:00
|
|
|
return storage_.extra_info().get_varray(storage_.get());
|
|
|
|
}
|
|
|
|
|
|
|
|
public:
|
|
|
|
/** Return false when there is no virtual array implementation currently. */
|
|
|
|
operator bool() const
|
|
|
|
{
|
|
|
|
return impl_ != nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the element at a specific index.
|
2022-05-13 01:24:28 +02:00
|
|
|
* \note This can't return a reference because the value may be computed on the fly. This also
|
Geometry Nodes: refactor virtual array system
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
2021-11-16 10:15:51 +01:00
|
|
|
* implies that one can not use this method for assignments.
|
|
|
|
*/
|
|
|
|
T operator[](const int64_t index) const
|
|
|
|
{
|
|
|
|
BLI_assert(*this);
|
|
|
|
BLI_assert(index >= 0);
|
|
|
|
BLI_assert(index < this->size());
|
|
|
|
return impl_->get(index);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Same as the #operator[] but is sometimes easier to use when one has a pointer to a virtual
|
|
|
|
* array.
|
|
|
|
*/
|
|
|
|
T get(const int64_t index) const
|
|
|
|
{
|
|
|
|
return (*this)[index];
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Return the size of the virtual array. It's allowed to call this method even when there is no
|
|
|
|
* virtual array. In this case 0 is returned.
|
|
|
|
*/
|
|
|
|
int64_t size() const
|
|
|
|
{
|
|
|
|
if (impl_ == nullptr) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return impl_->size();
|
|
|
|
}
|
|
|
|
|
|
|
|
/** True when the size is zero or when there is no virtual array. */
|
|
|
|
bool is_empty() const
|
|
|
|
{
|
|
|
|
return this->size() == 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
IndexRange index_range() const
|
|
|
|
{
|
|
|
|
return IndexRange(this->size());
|
|
|
|
}
|
|
|
|
|
2022-06-25 17:28:49 +02:00
|
|
|
CommonVArrayInfo common_info() const
|
|
|
|
{
|
|
|
|
BLI_assert(*this);
|
|
|
|
return impl_->common_info();
|
|
|
|
}
|
|
|
|
|
Geometry Nodes: refactor virtual array system
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
2021-11-16 10:15:51 +01:00
|
|
|
/** Return true when the virtual array is stored as a span internally. */
|
|
|
|
bool is_span() const
|
|
|
|
{
|
|
|
|
BLI_assert(*this);
|
2022-06-25 17:28:49 +02:00
|
|
|
const CommonVArrayInfo info = impl_->common_info();
|
|
|
|
return info.type == CommonVArrayInfo::Type::Span;
|
Geometry Nodes: refactor virtual array system
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
2021-11-16 10:15:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2022-02-24 18:01:22 +01:00
|
|
|
* Returns the internally used span of the virtual array. This invokes undefined behavior if the
|
Geometry Nodes: refactor virtual array system
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
2021-11-16 10:15:51 +01:00
|
|
|
* virtual array is not stored as a span internally.
|
|
|
|
*/
|
|
|
|
Span<T> get_internal_span() const
|
|
|
|
{
|
|
|
|
BLI_assert(this->is_span());
|
2022-06-25 17:28:49 +02:00
|
|
|
const CommonVArrayInfo info = impl_->common_info();
|
|
|
|
return Span<T>(static_cast<const T *>(info.data), this->size());
|
Geometry Nodes: refactor virtual array system
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
2021-11-16 10:15:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/** Return true when the virtual array returns the same value for every index. */
|
|
|
|
bool is_single() const
|
|
|
|
{
|
|
|
|
BLI_assert(*this);
|
2022-06-25 17:28:49 +02:00
|
|
|
const CommonVArrayInfo info = impl_->common_info();
|
|
|
|
return info.type == CommonVArrayInfo::Type::Single;
|
Geometry Nodes: refactor virtual array system
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
2021-11-16 10:15:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Return 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_internal_single() const
|
|
|
|
{
|
|
|
|
BLI_assert(this->is_single());
|
2022-06-25 17:28:49 +02:00
|
|
|
const CommonVArrayInfo info = impl_->common_info();
|
|
|
|
return *static_cast<const T *>(info.data);
|
Geometry Nodes: refactor virtual array system
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
2021-11-16 10:15:51 +01:00
|
|
|
}
|
|
|
|
|
2021-11-26 15:33:21 +01:00
|
|
|
/**
|
|
|
|
* Return true when the other virtual references the same underlying memory.
|
|
|
|
*/
|
|
|
|
bool is_same(const VArrayCommon<T> &other) const
|
|
|
|
{
|
|
|
|
if (!*this || !other) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
/* Check in both directions in case one does not know how to compare to the other
|
|
|
|
* implementation. */
|
|
|
|
if (impl_->is_same(*other.impl_)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if (other.impl_->is_same(*impl_)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
Geometry Nodes: refactor virtual array system
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
2021-11-16 10:15:51 +01:00
|
|
|
/** Copy the entire virtual array into a span. */
|
|
|
|
void materialize(MutableSpan<T> r_span) const
|
|
|
|
{
|
|
|
|
this->materialize(IndexMask(this->size()), r_span);
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Copy some indices of the virtual array into a span. */
|
|
|
|
void materialize(IndexMask mask, MutableSpan<T> r_span) const
|
|
|
|
{
|
|
|
|
BLI_assert(mask.min_array_size() <= this->size());
|
|
|
|
impl_->materialize(mask, r_span);
|
|
|
|
}
|
|
|
|
|
|
|
|
void materialize_to_uninitialized(MutableSpan<T> r_span) const
|
|
|
|
{
|
|
|
|
this->materialize_to_uninitialized(IndexMask(this->size()), r_span);
|
|
|
|
}
|
|
|
|
|
|
|
|
void materialize_to_uninitialized(IndexMask mask, MutableSpan<T> r_span) const
|
|
|
|
{
|
|
|
|
BLI_assert(mask.min_array_size() <= this->size());
|
|
|
|
impl_->materialize_to_uninitialized(mask, r_span);
|
|
|
|
}
|
|
|
|
|
2022-04-07 10:02:34 +02:00
|
|
|
/** Copy some elements of the virtual array into a span. */
|
|
|
|
void materialize_compressed(IndexMask mask, MutableSpan<T> r_span) const
|
|
|
|
{
|
|
|
|
impl_->materialize_compressed(mask, r_span);
|
|
|
|
}
|
|
|
|
|
|
|
|
void materialize_compressed_to_uninitialized(IndexMask mask, MutableSpan<T> r_span) const
|
|
|
|
{
|
|
|
|
impl_->materialize_compressed_to_uninitialized(mask, r_span);
|
|
|
|
}
|
|
|
|
|
Geometry Nodes: refactor virtual array system
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
2021-11-16 10:15:51 +01:00
|
|
|
/** See #GVArrayImpl::try_assign_GVArray. */
|
2022-03-19 08:26:29 +01:00
|
|
|
bool try_assign_GVArray(GVArray &varray) const
|
Geometry Nodes: refactor virtual array system
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
2021-11-16 10:15:51 +01:00
|
|
|
{
|
|
|
|
return impl_->try_assign_GVArray(varray);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
template<typename T> class VMutableArray;
|
|
|
|
|
2022-05-31 20:41:01 +02:00
|
|
|
/**
|
|
|
|
* 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
|
|
|
|
|
Geometry Nodes: refactor virtual array system
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
2021-11-16 10:15:51 +01:00
|
|
|
/**
|
|
|
|
* 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).
|
|
|
|
*/
|
|
|
|
template<typename T> class VArray : public VArrayCommon<T> {
|
|
|
|
friend VMutableArray<T>;
|
|
|
|
|
|
|
|
public:
|
|
|
|
VArray() = default;
|
|
|
|
VArray(const VArray &other) = default;
|
|
|
|
VArray(VArray &&other) noexcept = default;
|
|
|
|
|
|
|
|
VArray(const VArrayImpl<T> *impl) : VArrayCommon<T>(impl)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
VArray(std::shared_ptr<const VArrayImpl<T>> impl) : VArrayCommon<T>(std::move(impl))
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2022-05-31 20:41:01 +02:00
|
|
|
VArray(varray_tag::span /* tag */, Span<T> span)
|
|
|
|
{
|
2022-08-02 20:44:07 +02:00
|
|
|
this->template emplace<VArrayImpl_For_Span_final<T>>(span);
|
2022-05-31 20:41:01 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
VArray(varray_tag::single /* tag */, T value, const int64_t size)
|
|
|
|
{
|
|
|
|
this->template emplace<VArrayImpl_For_Single<T>>(std::move(value), size);
|
|
|
|
}
|
|
|
|
|
Geometry Nodes: refactor virtual array system
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
2021-11-16 10:15:51 +01:00
|
|
|
/**
|
|
|
|
* Construct a new virtual array for a custom #VArrayImpl.
|
|
|
|
*/
|
|
|
|
template<typename ImplT, typename... Args> static VArray For(Args &&...args)
|
|
|
|
{
|
|
|
|
static_assert(std::is_base_of_v<VArrayImpl<T>, ImplT>);
|
|
|
|
VArray varray;
|
|
|
|
varray.template emplace<ImplT>(std::forward<Args>(args)...);
|
|
|
|
return varray;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Construct a new virtual array that has the same value at every index.
|
|
|
|
*/
|
|
|
|
static VArray ForSingle(T value, const int64_t size)
|
|
|
|
{
|
2022-05-31 20:41:01 +02:00
|
|
|
return VArray(varray_tag::single{}, std::move(value), size);
|
Geometry Nodes: refactor virtual array system
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
2021-11-16 10:15:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Construct a new virtual array for an existing span. This does not take ownership of the
|
|
|
|
* underlying memory.
|
|
|
|
*/
|
|
|
|
static VArray ForSpan(Span<T> values)
|
|
|
|
{
|
2022-05-31 20:41:01 +02:00
|
|
|
return VArray(varray_tag::span{}, values);
|
Geometry Nodes: refactor virtual array system
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
2021-11-16 10:15:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Construct a new virtual that will invoke the provided function whenever an element is
|
|
|
|
* accessed.
|
|
|
|
*/
|
|
|
|
template<typename GetFunc> static VArray ForFunc(const int64_t size, GetFunc get_func)
|
|
|
|
{
|
|
|
|
return VArray::For<VArrayImpl_For_Func<T, decltype(get_func)>>(size, std::move(get_func));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Construct a new virtual array for an existing span with a mapping function. This does not take
|
|
|
|
* ownership of the span.
|
|
|
|
*/
|
|
|
|
template<typename StructT, T (*GetFunc)(const StructT &)>
|
|
|
|
static VArray ForDerivedSpan(Span<StructT> values)
|
|
|
|
{
|
2021-11-26 14:47:02 +01:00
|
|
|
/* Cast const away, because the virtual array implementation for const and non const derived
|
|
|
|
* spans is shared. */
|
|
|
|
MutableSpan<StructT> span{const_cast<StructT *>(values.data()), values.size()};
|
|
|
|
return VArray::For<VArrayImpl_For_DerivedSpan<StructT, T, GetFunc>>(span);
|
Geometry Nodes: refactor virtual array system
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
2021-11-16 10:15:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Construct a new virtual array for an existing container. Every container that lays out the
|
|
|
|
* elements in a plain array works. This takes ownership of the passed in container. If that is
|
|
|
|
* not desired, use #ForSpan instead.
|
|
|
|
*/
|
|
|
|
template<typename ContainerT> static VArray ForContainer(ContainerT container)
|
|
|
|
{
|
|
|
|
return VArray::For<VArrayImpl_For_ArrayContainer<ContainerT>>(std::move(container));
|
|
|
|
}
|
|
|
|
|
|
|
|
VArray &operator=(const VArray &other)
|
|
|
|
{
|
|
|
|
this->copy_from(other);
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
VArray &operator=(VArray &&other) noexcept
|
|
|
|
{
|
|
|
|
this->move_from(std::move(other));
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Similar to #VArray but references a virtual array that can be modified.
|
|
|
|
*/
|
|
|
|
template<typename T> class VMutableArray : public VArrayCommon<T> {
|
|
|
|
public:
|
|
|
|
VMutableArray() = default;
|
|
|
|
VMutableArray(const VMutableArray &other) = default;
|
|
|
|
VMutableArray(VMutableArray &&other) noexcept = default;
|
|
|
|
|
|
|
|
VMutableArray(const VMutableArrayImpl<T> *impl) : VArrayCommon<T>(impl)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
VMutableArray(std::shared_ptr<const VMutableArrayImpl<T>> impl)
|
|
|
|
: VArrayCommon<T>(std::move(impl))
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Construct a new virtual array for a custom #VMutableArrayImpl.
|
|
|
|
*/
|
|
|
|
template<typename ImplT, typename... Args> static VMutableArray For(Args &&...args)
|
|
|
|
{
|
|
|
|
static_assert(std::is_base_of_v<VMutableArrayImpl<T>, ImplT>);
|
|
|
|
VMutableArray varray;
|
|
|
|
varray.template emplace<ImplT>(std::forward<Args>(args)...);
|
|
|
|
return varray;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Construct a new virtual array for an existing span. This does not take ownership of the span.
|
|
|
|
*/
|
|
|
|
static VMutableArray ForSpan(MutableSpan<T> values)
|
|
|
|
{
|
2021-11-26 14:47:02 +01:00
|
|
|
return VMutableArray::For<VArrayImpl_For_Span_final<T>>(values);
|
Geometry Nodes: refactor virtual array system
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
2021-11-16 10:15:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Construct a new virtual array for an existing span with a mapping function. This does not take
|
|
|
|
* ownership of the span.
|
|
|
|
*/
|
|
|
|
template<typename StructT, T (*GetFunc)(const StructT &), void (*SetFunc)(StructT &, T)>
|
|
|
|
static VMutableArray ForDerivedSpan(MutableSpan<StructT> values)
|
|
|
|
{
|
2021-11-26 14:47:02 +01:00
|
|
|
return VMutableArray::For<VArrayImpl_For_DerivedSpan<StructT, T, GetFunc, SetFunc>>(values);
|
Geometry Nodes: refactor virtual array system
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
2021-11-16 10:15:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/** Convert to a #VArray by copying. */
|
|
|
|
operator VArray<T>() const &
|
|
|
|
{
|
|
|
|
VArray<T> varray;
|
|
|
|
varray.copy_from(*this);
|
|
|
|
return varray;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Convert to a #VArray by moving. */
|
|
|
|
operator VArray<T>() &&noexcept
|
|
|
|
{
|
|
|
|
VArray<T> varray;
|
|
|
|
varray.move_from(std::move(*this));
|
|
|
|
return varray;
|
|
|
|
}
|
|
|
|
|
|
|
|
VMutableArray &operator=(const VMutableArray &other)
|
|
|
|
{
|
|
|
|
this->copy_from(other);
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
VMutableArray &operator=(VMutableArray &&other) noexcept
|
|
|
|
{
|
|
|
|
this->move_from(std::move(other));
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get access to the internal span. This invokes undefined behavior if the #is_span returned
|
|
|
|
* false.
|
|
|
|
*/
|
|
|
|
MutableSpan<T> get_internal_span() const
|
|
|
|
{
|
|
|
|
BLI_assert(this->is_span());
|
2022-06-25 17:28:49 +02:00
|
|
|
const CommonVArrayInfo info = this->get_impl()->common_info();
|
|
|
|
return MutableSpan<T>(const_cast<T *>(static_cast<const T *>(info.data)), this->size());
|
Geometry Nodes: refactor virtual array system
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
2021-11-16 10:15:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set the value at the given index.
|
|
|
|
*/
|
|
|
|
void set(const int64_t index, T value)
|
|
|
|
{
|
|
|
|
BLI_assert(index >= 0);
|
|
|
|
BLI_assert(index < this->size());
|
|
|
|
this->get_impl()->set(index, std::move(value));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Copy the values from the source span to all elements in the virtual array.
|
|
|
|
*/
|
|
|
|
void set_all(Span<T> src)
|
|
|
|
{
|
|
|
|
BLI_assert(src.size() == this->size());
|
|
|
|
this->get_impl()->set_all(src);
|
|
|
|
}
|
|
|
|
|
|
|
|
/** See #GVMutableArrayImpl::try_assign_GVMutableArray. */
|
2022-03-19 08:26:29 +01:00
|
|
|
bool try_assign_GVMutableArray(GVMutableArray &varray) const
|
Geometry Nodes: refactor virtual array system
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
2021-11-16 10:15:51 +01:00
|
|
|
{
|
|
|
|
return this->get_impl()->try_assign_GVMutableArray(varray);
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
/** Utility to get the pointer to the wrapped #VMutableArrayImpl. */
|
|
|
|
VMutableArrayImpl<T> *get_impl() const
|
|
|
|
{
|
|
|
|
/* This cast is valid by the invariant that a #VMutableArray->impl_ is always a
|
|
|
|
* #VMutableArrayImpl. */
|
|
|
|
return (VMutableArrayImpl<T> *)this->impl_;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2022-04-26 17:12:34 +02:00
|
|
|
template<typename T> static constexpr bool is_VArray_v = false;
|
|
|
|
template<typename T> static constexpr bool is_VArray_v<VArray<T>> = true;
|
|
|
|
|
|
|
|
template<typename T> static constexpr bool is_VMutableArray_v = false;
|
|
|
|
template<typename T> static constexpr bool is_VMutableArray_v<VMutableArray<T>> = true;
|
|
|
|
|
2021-04-17 15:13:20 +02:00
|
|
|
/**
|
|
|
|
* In many cases a virtual array is a span internally. In those cases, access to individual could
|
|
|
|
* be much more efficient than calling a virtual method. When the underlying virtual array is not a
|
|
|
|
* span, this class allocates a new array and copies the values over.
|
|
|
|
*
|
|
|
|
* This should be used in those cases:
|
|
|
|
* - All elements in the virtual array are accessed multiple times.
|
|
|
|
* - In most cases, the underlying virtual array is a span, so no copy is necessary to benefit
|
|
|
|
* from faster access.
|
|
|
|
* - An API is called, that does not accept virtual arrays, but only spans.
|
|
|
|
*/
|
2022-07-02 11:45:57 +02:00
|
|
|
template<typename T> class VArraySpan final : public Span<T> {
|
2021-04-17 15:13:20 +02:00
|
|
|
private:
|
Geometry Nodes: refactor virtual array system
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
2021-11-16 10:15:51 +01:00
|
|
|
VArray<T> varray_;
|
2021-04-17 15:13:20 +02:00
|
|
|
Array<T> owned_data_;
|
|
|
|
|
|
|
|
public:
|
2022-07-02 11:45:57 +02:00
|
|
|
VArraySpan() = default;
|
2022-06-05 12:14:13 +02:00
|
|
|
|
2022-07-02 11:45:57 +02:00
|
|
|
VArraySpan(VArray<T> varray) : Span<T>(), varray_(std::move(varray))
|
2021-04-17 15:13:20 +02:00
|
|
|
{
|
2022-07-07 19:19:01 +02:00
|
|
|
if (!varray_) {
|
|
|
|
return;
|
|
|
|
}
|
2021-04-17 15:13:20 +02:00
|
|
|
this->size_ = varray_.size();
|
2022-06-25 17:28:49 +02:00
|
|
|
const CommonVArrayInfo info = varray_.common_info();
|
|
|
|
if (info.type == CommonVArrayInfo::Type::Span) {
|
|
|
|
this->data_ = static_cast<const T *>(info.data);
|
2021-04-17 15:13:20 +02:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
owned_data_.~Array();
|
|
|
|
new (&owned_data_) Array<T>(varray_.size(), NoInitialization{});
|
|
|
|
varray_.materialize_to_uninitialized(owned_data_);
|
|
|
|
this->data_ = owned_data_.data();
|
|
|
|
}
|
|
|
|
}
|
2022-06-06 14:01:25 +02:00
|
|
|
|
2022-07-02 11:45:57 +02:00
|
|
|
VArraySpan(VArraySpan &&other)
|
2022-06-06 14:01:25 +02:00
|
|
|
: varray_(std::move(other.varray_)), owned_data_(std::move(other.owned_data_))
|
|
|
|
{
|
2022-07-07 19:19:01 +02:00
|
|
|
if (!varray_) {
|
|
|
|
return;
|
|
|
|
}
|
2022-06-06 14:01:25 +02:00
|
|
|
this->size_ = varray_.size();
|
2022-06-25 17:28:49 +02:00
|
|
|
const CommonVArrayInfo info = varray_.common_info();
|
|
|
|
if (info.type == CommonVArrayInfo::Type::Span) {
|
|
|
|
this->data_ = static_cast<const T *>(info.data);
|
2022-06-06 14:01:25 +02:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
this->data_ = owned_data_.data();
|
|
|
|
}
|
|
|
|
other.data_ = nullptr;
|
|
|
|
other.size_ = 0;
|
|
|
|
}
|
|
|
|
|
2022-07-02 11:45:57 +02:00
|
|
|
VArraySpan &operator=(VArraySpan &&other)
|
2022-06-06 14:01:25 +02:00
|
|
|
{
|
|
|
|
if (this == &other) {
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
std::destroy_at(this);
|
2022-07-02 11:45:57 +02:00
|
|
|
new (this) VArraySpan(std::move(other));
|
2022-06-06 14:01:25 +02:00
|
|
|
return *this;
|
|
|
|
}
|
2021-04-17 15:13:20 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
2022-07-02 11:45:57 +02:00
|
|
|
* Same as #VArraySpan, but for a mutable span.
|
2021-04-17 15:13:20 +02:00
|
|
|
* 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.
|
|
|
|
*/
|
2022-07-02 11:45:57 +02:00
|
|
|
template<typename T> class MutableVArraySpan final : public MutableSpan<T> {
|
2021-04-17 15:13:20 +02:00
|
|
|
private:
|
Geometry Nodes: refactor virtual array system
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
2021-11-16 10:15:51 +01:00
|
|
|
VMutableArray<T> varray_;
|
2021-04-17 15:13:20 +02:00
|
|
|
Array<T> owned_data_;
|
|
|
|
bool save_has_been_called_ = false;
|
|
|
|
bool show_not_saved_warning_ = true;
|
|
|
|
|
|
|
|
public:
|
2022-07-07 19:19:01 +02:00
|
|
|
MutableVArraySpan() = default;
|
|
|
|
|
2021-04-17 15:13:20 +02:00
|
|
|
/* 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. */
|
2022-07-02 11:45:57 +02:00
|
|
|
MutableVArraySpan(VMutableArray<T> varray, const bool copy_values_to_span = true)
|
Geometry Nodes: refactor virtual array system
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
2021-11-16 10:15:51 +01:00
|
|
|
: MutableSpan<T>(), varray_(std::move(varray))
|
2021-04-17 15:13:20 +02:00
|
|
|
{
|
2022-07-07 19:19:01 +02:00
|
|
|
if (!varray_) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-04-17 15:13:20 +02:00
|
|
|
this->size_ = varray_.size();
|
2022-06-25 17:28:49 +02:00
|
|
|
const CommonVArrayInfo info = varray_.common_info();
|
|
|
|
if (info.type == CommonVArrayInfo::Type::Span) {
|
|
|
|
this->data_ = const_cast<T *>(static_cast<const T *>(info.data));
|
2021-04-17 15:13:20 +02:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
if (copy_values_to_span) {
|
|
|
|
owned_data_.~Array();
|
|
|
|
new (&owned_data_) Array<T>(varray_.size(), NoInitialization{});
|
|
|
|
varray_.materialize_to_uninitialized(owned_data_);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
owned_data_.reinitialize(varray_.size());
|
|
|
|
}
|
|
|
|
this->data_ = owned_data_.data();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-02 11:45:57 +02:00
|
|
|
MutableVArraySpan(MutableVArraySpan &&other)
|
|
|
|
: varray_(std::move(other.varray_)),
|
2022-07-08 16:26:13 +02:00
|
|
|
owned_data_(std::move(other.owned_data_)),
|
2022-07-02 11:45:57 +02:00
|
|
|
show_not_saved_warning_(other.show_not_saved_warning_)
|
|
|
|
{
|
2022-07-07 19:19:01 +02:00
|
|
|
if (!varray_) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-07-02 11:45:57 +02:00
|
|
|
this->size_ = varray_.size();
|
|
|
|
const CommonVArrayInfo info = varray_.common_info();
|
|
|
|
if (info.type == CommonVArrayInfo::Type::Span) {
|
2022-07-07 19:19:01 +02:00
|
|
|
this->data_ = static_cast<T *>(const_cast<void *>(info.data));
|
2022-07-02 11:45:57 +02:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
this->data_ = owned_data_.data();
|
|
|
|
}
|
|
|
|
other.data_ = nullptr;
|
|
|
|
other.size_ = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
~MutableVArraySpan()
|
2021-04-17 15:13:20 +02:00
|
|
|
{
|
2022-07-02 11:45:57 +02:00
|
|
|
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";
|
|
|
|
}
|
2021-04-17 15:13:20 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-02 11:45:57 +02:00
|
|
|
MutableVArraySpan &operator=(MutableVArraySpan &&other)
|
|
|
|
{
|
|
|
|
if (this == &other) {
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
std::destroy_at(this);
|
|
|
|
new (this) MutableVArraySpan(std::move(other));
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
2022-07-07 19:19:01 +02:00
|
|
|
const VMutableArray<T> &varray() const
|
|
|
|
{
|
|
|
|
return varray_;
|
|
|
|
}
|
|
|
|
|
2021-04-17 15:13:20 +02:00
|
|
|
/* Write back all values from a temporary allocated array to the underlying virtual array. */
|
|
|
|
void save()
|
|
|
|
{
|
|
|
|
save_has_been_called_ = true;
|
|
|
|
if (this->data_ != owned_data_.data()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
varray_.set_all(owned_data_);
|
|
|
|
}
|
|
|
|
|
|
|
|
void disable_not_applied_warning()
|
|
|
|
{
|
|
|
|
show_not_saved_warning_ = false;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2021-11-24 17:46:00 +01:00
|
|
|
template<typename T> class SingleAsSpan {
|
|
|
|
private:
|
|
|
|
T value_;
|
|
|
|
int64_t size_;
|
|
|
|
|
|
|
|
public:
|
|
|
|
SingleAsSpan(T value, int64_t size) : value_(std::move(value)), size_(size)
|
|
|
|
{
|
|
|
|
BLI_assert(size_ >= 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
SingleAsSpan(const VArray<T> &varray) : SingleAsSpan(varray.get_internal_single(), varray.size())
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
const T &operator[](const int64_t index) const
|
|
|
|
{
|
|
|
|
BLI_assert(index >= 0);
|
|
|
|
BLI_assert(index < size_);
|
|
|
|
UNUSED_VARS_NDEBUG(index);
|
|
|
|
return value_;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2021-03-21 19:31:24 +01:00
|
|
|
} // namespace blender
|