2022-02-11 09:07:11 +11:00
|
|
|
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
2020-12-02 13:25:25 +01:00
|
|
|
|
|
|
|
|
#include <utility>
|
|
|
|
|
|
|
|
|
|
#include "BKE_attribute_access.hh"
|
2021-02-09 11:44:58 +01:00
|
|
|
#include "BKE_attribute_math.hh"
|
2020-12-02 13:25:25 +01:00
|
|
|
#include "BKE_customdata.h"
|
|
|
|
|
#include "BKE_deform.h"
|
|
|
|
|
#include "BKE_geometry_set.hh"
|
|
|
|
|
#include "BKE_mesh.h"
|
|
|
|
|
#include "BKE_pointcloud.h"
|
2021-12-07 15:21:59 +01:00
|
|
|
#include "BKE_type_conversions.hh"
|
2020-12-02 13:25:25 +01:00
|
|
|
|
|
|
|
|
#include "DNA_mesh_types.h"
|
|
|
|
|
#include "DNA_meshdata_types.h"
|
|
|
|
|
#include "DNA_pointcloud_types.h"
|
|
|
|
|
|
|
|
|
|
#include "BLI_color.hh"
|
BLI: Refactor vector types & functions to use templates
This patch implements the vector types (i.e:`float2`) by making heavy
usage of templating. All vector functions are now outside of the vector
classes (inside the `blender::math` namespace) and are not vector size
dependent for the most part.
In the ongoing effort to make shaders less GL centric, we are aiming
to share more code between GLSL and C++ to avoid code duplication.
####Motivations:
- We are aiming to share UBO and SSBO structures between GLSL and C++.
This means we will use many of the existing vector types and others
we currently don't have (uintX, intX). All these variations were
asking for many more code duplication.
- Deduplicate existing code which is duplicated for each vector size.
- We also want to share small functions. Which means that vector
functions should be static and not in the class namespace.
- Reduce friction to use these types in new projects due to their
incompleteness.
- The current state of the `BLI_(float|double|mpq)(2|3|4).hh` is a
bit of a let down. Most clases are incomplete, out of sync with each
others with different codestyles, and some functions that should be
static are not (i.e: `float3::reflect()`).
####Upsides:
- Still support `.x, .y, .z, .w` for readability.
- Compact, readable and easilly extendable.
- All of the vector functions are available for all the vectors types
and can be restricted to certain types. Also template specialization
let us define exception for special class (like mpq).
- With optimization ON, the compiler unroll the loops and performance
is the same.
####Downsides:
- Might impact debugability. Though I would arge that the bugs are
rarelly caused by the vector class itself (since the operations are
quite trivial) but by the type conversions.
- Might impact compile time. I did not saw a significant impact since
the usage is not really widespread.
- Functions needs to be rewritten to support arbitrary vector length.
For instance, one can't call `len_squared_v3v3` in
`math::length_squared()` and call it a day.
- Type cast does not work with the template version of the `math::`
vector functions. Meaning you need to manually cast `float *` and
`(float *)[3]` to `float3` for the function calls.
i.e: `math::distance_squared(float3(nearest.co), positions[i]);`
- Some parts might loose in readability:
`float3::dot(v1.normalized(), v2.normalized())`
becoming
`math::dot(math::normalize(v1), math::normalize(v2))`
But I propose, when appropriate, to use
`using namespace blender::math;` on function local or file scope to
increase readability.
`dot(normalize(v1), normalize(v2))`
####Consideration:
- Include back `.length()` method. It is quite handy and is more C++
oriented.
- I considered the GLM library as a candidate for replacement. It felt
like too much for what we need and would be difficult to extend / modify
to our needs.
- I used Macros to reduce code in operators declaration and potential
copy paste bugs. This could reduce debugability and could be reverted.
- This touches `delaunay_2d.cc` and the intersection code. I would like
to know @howardt opinion on the matter.
- The `noexcept` on the copy constructor of `mpq(2|3)` is being removed.
But according to @JacquesLucke it is not a real problem for now.
I would like to give a huge thanks to @JacquesLucke who helped during this
and pushed me to reduce the duplication further.
Reviewed By: brecht, sergey, JacquesLucke
Differential Revision: https://developer.blender.org/D13791
2022-01-12 12:57:07 +01:00
|
|
|
#include "BLI_math_vec_types.hh"
|
2020-12-02 13:25:25 +01:00
|
|
|
#include "BLI_span.hh"
|
|
|
|
|
|
2021-09-11 13:05:20 +02:00
|
|
|
#include "BLT_translation.h"
|
|
|
|
|
|
2020-12-02 13:25:25 +01:00
|
|
|
#include "CLG_log.h"
|
|
|
|
|
|
2021-03-08 16:31:51 -05:00
|
|
|
#include "attribute_access_intern.hh"
|
|
|
|
|
|
2020-12-02 13:25:25 +01:00
|
|
|
static CLG_LogRef LOG = {"bke.attribute_access"};
|
|
|
|
|
|
|
|
|
|
using blender::float3;
|
|
|
|
|
using blender::Set;
|
|
|
|
|
using blender::StringRef;
|
2021-01-14 15:52:08 +01:00
|
|
|
using blender::StringRefNull;
|
2021-09-09 12:54:20 +02:00
|
|
|
using blender::bke::AttributeIDRef;
|
|
|
|
|
using blender::bke::OutputAttribute;
|
2021-01-14 15:52:08 +01:00
|
|
|
using blender::fn::GMutableSpan;
|
2021-05-19 13:22:09 -04:00
|
|
|
using blender::fn::GSpan;
|
2021-11-26 14:47:02 +01:00
|
|
|
using blender::fn::GVArrayImpl_For_GSpan;
|
2020-12-02 13:25:25 +01:00
|
|
|
|
|
|
|
|
namespace blender::bke {
|
|
|
|
|
|
2021-09-23 13:46:13 +02:00
|
|
|
std::ostream &operator<<(std::ostream &stream, const AttributeIDRef &attribute_id)
|
|
|
|
|
{
|
|
|
|
|
if (attribute_id.is_named()) {
|
|
|
|
|
stream << attribute_id.name();
|
|
|
|
|
}
|
|
|
|
|
else if (attribute_id.is_anonymous()) {
|
|
|
|
|
const AnonymousAttributeID &anonymous_id = attribute_id.anonymous_id();
|
|
|
|
|
stream << "<" << BKE_anonymous_attribute_id_debug_name(&anonymous_id) << ">";
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
stream << "<none>";
|
|
|
|
|
}
|
|
|
|
|
return stream;
|
|
|
|
|
}
|
|
|
|
|
|
2020-12-02 13:25:25 +01:00
|
|
|
const blender::fn::CPPType *custom_data_type_to_cpp_type(const CustomDataType type)
|
|
|
|
|
{
|
|
|
|
|
switch (type) {
|
|
|
|
|
case CD_PROP_FLOAT:
|
|
|
|
|
return &CPPType::get<float>();
|
|
|
|
|
case CD_PROP_FLOAT2:
|
|
|
|
|
return &CPPType::get<float2>();
|
|
|
|
|
case CD_PROP_FLOAT3:
|
|
|
|
|
return &CPPType::get<float3>();
|
|
|
|
|
case CD_PROP_INT32:
|
|
|
|
|
return &CPPType::get<int>();
|
|
|
|
|
case CD_PROP_COLOR:
|
2021-05-25 17:16:35 +02:00
|
|
|
return &CPPType::get<ColorGeometry4f>();
|
2020-12-16 12:33:13 -06:00
|
|
|
case CD_PROP_BOOL:
|
|
|
|
|
return &CPPType::get<bool>();
|
2022-02-04 10:29:11 -06:00
|
|
|
case CD_PROP_INT8:
|
|
|
|
|
return &CPPType::get<int8_t>();
|
2020-12-02 13:25:25 +01:00
|
|
|
default:
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CustomDataType cpp_type_to_custom_data_type(const blender::fn::CPPType &type)
|
|
|
|
|
{
|
|
|
|
|
if (type.is<float>()) {
|
|
|
|
|
return CD_PROP_FLOAT;
|
|
|
|
|
}
|
|
|
|
|
if (type.is<float2>()) {
|
|
|
|
|
return CD_PROP_FLOAT2;
|
|
|
|
|
}
|
|
|
|
|
if (type.is<float3>()) {
|
|
|
|
|
return CD_PROP_FLOAT3;
|
|
|
|
|
}
|
|
|
|
|
if (type.is<int>()) {
|
|
|
|
|
return CD_PROP_INT32;
|
|
|
|
|
}
|
2021-05-25 17:16:35 +02:00
|
|
|
if (type.is<ColorGeometry4f>()) {
|
2020-12-02 13:25:25 +01:00
|
|
|
return CD_PROP_COLOR;
|
|
|
|
|
}
|
2020-12-16 12:33:13 -06:00
|
|
|
if (type.is<bool>()) {
|
|
|
|
|
return CD_PROP_BOOL;
|
|
|
|
|
}
|
2022-02-04 10:29:11 -06:00
|
|
|
if (type.is<int8_t>()) {
|
|
|
|
|
return CD_PROP_INT8;
|
|
|
|
|
}
|
2021-03-08 16:31:51 -05:00
|
|
|
return static_cast<CustomDataType>(-1);
|
2021-02-09 11:24:28 +01:00
|
|
|
}
|
|
|
|
|
|
2021-03-08 16:31:51 -05:00
|
|
|
static int attribute_data_type_complexity(const CustomDataType data_type)
|
2021-02-09 11:24:28 +01:00
|
|
|
{
|
2021-03-08 16:31:51 -05:00
|
|
|
switch (data_type) {
|
|
|
|
|
case CD_PROP_BOOL:
|
|
|
|
|
return 0;
|
2022-02-04 10:29:11 -06:00
|
|
|
case CD_PROP_INT8:
|
2021-03-08 16:31:51 -05:00
|
|
|
return 1;
|
2022-02-04 10:29:11 -06:00
|
|
|
case CD_PROP_INT32:
|
2021-03-08 16:31:51 -05:00
|
|
|
return 2;
|
2022-02-04 10:29:11 -06:00
|
|
|
case CD_PROP_FLOAT:
|
2021-03-08 16:31:51 -05:00
|
|
|
return 3;
|
2022-02-04 10:29:11 -06:00
|
|
|
case CD_PROP_FLOAT2:
|
2021-03-08 16:31:51 -05:00
|
|
|
return 4;
|
2022-02-04 10:29:11 -06:00
|
|
|
case CD_PROP_FLOAT3:
|
2021-03-08 16:31:51 -05:00
|
|
|
return 5;
|
2022-02-04 10:29:11 -06:00
|
|
|
case CD_PROP_COLOR:
|
|
|
|
|
return 6;
|
2021-03-08 16:31:51 -05:00
|
|
|
#if 0 /* These attribute types are not supported yet. */
|
|
|
|
|
case CD_MLOOPCOL:
|
|
|
|
|
return 3;
|
|
|
|
|
case CD_PROP_STRING:
|
|
|
|
|
return 6;
|
|
|
|
|
#endif
|
|
|
|
|
default:
|
|
|
|
|
/* Only accept "generic" custom data types used by the attribute system. */
|
2021-03-23 16:49:47 +01:00
|
|
|
BLI_assert_unreachable();
|
2021-03-08 16:31:51 -05:00
|
|
|
return 0;
|
|
|
|
|
}
|
2020-12-02 13:25:25 +01:00
|
|
|
}
|
|
|
|
|
|
2021-03-08 16:31:51 -05:00
|
|
|
CustomDataType attribute_data_type_highest_complexity(Span<CustomDataType> data_types)
|
2020-12-02 13:25:25 +01:00
|
|
|
{
|
2021-03-08 16:31:51 -05:00
|
|
|
int highest_complexity = INT_MIN;
|
|
|
|
|
CustomDataType most_complex_type = CD_PROP_COLOR;
|
2020-12-02 13:25:25 +01:00
|
|
|
|
2021-03-08 16:31:51 -05:00
|
|
|
for (const CustomDataType data_type : data_types) {
|
|
|
|
|
const int complexity = attribute_data_type_complexity(data_type);
|
|
|
|
|
if (complexity > highest_complexity) {
|
|
|
|
|
highest_complexity = complexity;
|
|
|
|
|
most_complex_type = data_type;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return most_complex_type;
|
2020-12-02 13:25:25 +01:00
|
|
|
}
|
|
|
|
|
|
2021-03-08 16:31:51 -05:00
|
|
|
/**
|
|
|
|
|
* \note Generally the order should mirror the order of the domains
|
|
|
|
|
* established in each component's ComponentAttributeProviders.
|
|
|
|
|
*/
|
|
|
|
|
static int attribute_domain_priority(const AttributeDomain domain)
|
2021-02-12 11:31:15 +01:00
|
|
|
{
|
2021-03-08 16:31:51 -05:00
|
|
|
switch (domain) {
|
2021-11-19 17:53:48 +01:00
|
|
|
case ATTR_DOMAIN_INSTANCE:
|
2021-03-08 16:31:51 -05:00
|
|
|
return 0;
|
2021-11-19 17:53:48 +01:00
|
|
|
case ATTR_DOMAIN_CURVE:
|
2021-03-08 16:31:51 -05:00
|
|
|
return 1;
|
2021-11-19 17:53:48 +01:00
|
|
|
case ATTR_DOMAIN_FACE:
|
2021-03-08 16:31:51 -05:00
|
|
|
return 2;
|
2021-11-19 17:53:48 +01:00
|
|
|
case ATTR_DOMAIN_EDGE:
|
2021-03-08 16:31:51 -05:00
|
|
|
return 3;
|
2021-11-19 17:53:48 +01:00
|
|
|
case ATTR_DOMAIN_POINT:
|
2021-03-08 16:31:51 -05:00
|
|
|
return 4;
|
2021-11-19 17:53:48 +01:00
|
|
|
case ATTR_DOMAIN_CORNER:
|
|
|
|
|
return 5;
|
2021-03-08 16:31:51 -05:00
|
|
|
default:
|
|
|
|
|
/* Domain not supported in nodes yet. */
|
2021-03-23 16:49:47 +01:00
|
|
|
BLI_assert_unreachable();
|
2021-03-08 16:31:51 -05:00
|
|
|
return 0;
|
2021-02-12 11:31:15 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-08 16:31:51 -05:00
|
|
|
AttributeDomain attribute_domain_highest_priority(Span<AttributeDomain> domains)
|
2021-02-17 08:30:15 -06:00
|
|
|
{
|
2021-03-08 16:31:51 -05:00
|
|
|
int highest_priority = INT_MIN;
|
|
|
|
|
AttributeDomain highest_priority_domain = ATTR_DOMAIN_CORNER;
|
2021-02-17 08:30:15 -06:00
|
|
|
|
2021-03-08 16:31:51 -05:00
|
|
|
for (const AttributeDomain domain : domains) {
|
|
|
|
|
const int priority = attribute_domain_priority(domain);
|
|
|
|
|
if (priority > highest_priority) {
|
|
|
|
|
highest_priority = priority;
|
|
|
|
|
highest_priority_domain = domain;
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-02-17 08:30:15 -06:00
|
|
|
|
2021-03-08 16:31:51 -05:00
|
|
|
return highest_priority_domain;
|
2021-02-17 08:30:15 -06:00
|
|
|
}
|
|
|
|
|
|
2021-10-03 17:07:26 +02:00
|
|
|
fn::GMutableSpan OutputAttribute::as_span()
|
|
|
|
|
{
|
|
|
|
|
if (!optional_span_varray_) {
|
|
|
|
|
const bool materialize_old_values = !ignore_old_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
|
|
|
optional_span_varray_ = std::make_unique<fn::GVMutableArray_GSpan>(varray_,
|
2021-10-03 17:07:26 +02:00
|
|
|
materialize_old_values);
|
|
|
|
|
}
|
|
|
|
|
fn::GVMutableArray_GSpan &span_varray = *optional_span_varray_;
|
|
|
|
|
return span_varray;
|
|
|
|
|
}
|
|
|
|
|
|
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
|
|
|
void OutputAttribute::save()
|
|
|
|
|
{
|
2021-04-23 12:44:59 +02:00
|
|
|
save_has_been_called_ = true;
|
2021-09-16 12:03:32 -05:00
|
|
|
if (optional_span_varray_) {
|
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
|
|
|
optional_span_varray_->save();
|
|
|
|
|
}
|
|
|
|
|
if (save_) {
|
|
|
|
|
save_(*this);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-23 12:44:59 +02:00
|
|
|
OutputAttribute::~OutputAttribute()
|
|
|
|
|
{
|
|
|
|
|
if (!save_has_been_called_) {
|
|
|
|
|
if (varray_) {
|
|
|
|
|
std::cout << "Warning: Call `save()` to make sure that changes persist in all cases.\n";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-02 13:43:54 -05:00
|
|
|
static AttributeIDRef attribute_id_from_custom_data_layer(const CustomDataLayer &layer)
|
|
|
|
|
{
|
|
|
|
|
if (layer.anonymous_id != nullptr) {
|
|
|
|
|
return layer.anonymous_id;
|
|
|
|
|
}
|
|
|
|
|
return layer.name;
|
|
|
|
|
}
|
|
|
|
|
|
2021-10-18 12:22:44 -05:00
|
|
|
static bool add_builtin_type_custom_data_layer_from_init(CustomData &custom_data,
|
|
|
|
|
const CustomDataType data_type,
|
|
|
|
|
const int domain_size,
|
|
|
|
|
const AttributeInit &initializer)
|
|
|
|
|
{
|
|
|
|
|
switch (initializer.type) {
|
|
|
|
|
case AttributeInit::Type::Default: {
|
|
|
|
|
void *data = CustomData_add_layer(&custom_data, data_type, CD_DEFAULT, nullptr, domain_size);
|
|
|
|
|
return data != nullptr;
|
|
|
|
|
}
|
|
|
|
|
case AttributeInit::Type::VArray: {
|
|
|
|
|
void *data = CustomData_add_layer(&custom_data, data_type, CD_DEFAULT, nullptr, domain_size);
|
|
|
|
|
if (data == nullptr) {
|
|
|
|
|
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
|
|
|
const GVArray &varray = static_cast<const AttributeInitVArray &>(initializer).varray;
|
|
|
|
|
varray.materialize_to_uninitialized(varray.index_range(), data);
|
2021-10-18 12:22:44 -05:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
case AttributeInit::Type::MoveArray: {
|
|
|
|
|
void *source_data = static_cast<const AttributeInitMove &>(initializer).data;
|
|
|
|
|
void *data = CustomData_add_layer(
|
|
|
|
|
&custom_data, data_type, CD_ASSIGN, source_data, domain_size);
|
|
|
|
|
if (data == nullptr) {
|
|
|
|
|
MEM_freeN(source_data);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BLI_assert_unreachable();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void *add_generic_custom_data_layer(CustomData &custom_data,
|
|
|
|
|
const CustomDataType data_type,
|
|
|
|
|
const eCDAllocType alloctype,
|
|
|
|
|
void *layer_data,
|
|
|
|
|
const int domain_size,
|
|
|
|
|
const AttributeIDRef &attribute_id)
|
|
|
|
|
{
|
|
|
|
|
if (attribute_id.is_named()) {
|
|
|
|
|
char attribute_name_c[MAX_NAME];
|
|
|
|
|
attribute_id.name().copy(attribute_name_c);
|
|
|
|
|
return CustomData_add_layer_named(
|
2021-12-02 15:32:04 +01:00
|
|
|
&custom_data, data_type, alloctype, layer_data, domain_size, attribute_name_c);
|
2021-10-18 12:22:44 -05:00
|
|
|
}
|
|
|
|
|
const AnonymousAttributeID &anonymous_id = attribute_id.anonymous_id();
|
|
|
|
|
return CustomData_add_layer_anonymous(
|
|
|
|
|
&custom_data, data_type, alloctype, layer_data, domain_size, &anonymous_id);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool add_custom_data_layer_from_attribute_init(const AttributeIDRef &attribute_id,
|
|
|
|
|
CustomData &custom_data,
|
|
|
|
|
const CustomDataType data_type,
|
|
|
|
|
const int domain_size,
|
|
|
|
|
const AttributeInit &initializer)
|
|
|
|
|
{
|
|
|
|
|
switch (initializer.type) {
|
|
|
|
|
case AttributeInit::Type::Default: {
|
|
|
|
|
void *data = add_generic_custom_data_layer(
|
|
|
|
|
custom_data, data_type, CD_DEFAULT, nullptr, domain_size, attribute_id);
|
|
|
|
|
return data != nullptr;
|
|
|
|
|
}
|
|
|
|
|
case AttributeInit::Type::VArray: {
|
|
|
|
|
void *data = add_generic_custom_data_layer(
|
|
|
|
|
custom_data, data_type, CD_DEFAULT, nullptr, domain_size, attribute_id);
|
|
|
|
|
if (data == nullptr) {
|
|
|
|
|
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
|
|
|
const GVArray &varray = static_cast<const AttributeInitVArray &>(initializer).varray;
|
|
|
|
|
varray.materialize_to_uninitialized(varray.index_range(), data);
|
2021-10-18 12:22:44 -05:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
case AttributeInit::Type::MoveArray: {
|
|
|
|
|
void *source_data = static_cast<const AttributeInitMove &>(initializer).data;
|
|
|
|
|
void *data = add_generic_custom_data_layer(
|
|
|
|
|
custom_data, data_type, CD_ASSIGN, source_data, domain_size, attribute_id);
|
|
|
|
|
if (data == nullptr) {
|
|
|
|
|
MEM_freeN(source_data);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BLI_assert_unreachable();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool custom_data_layer_matches_attribute_id(const CustomDataLayer &layer,
|
|
|
|
|
const AttributeIDRef &attribute_id)
|
|
|
|
|
{
|
|
|
|
|
if (!attribute_id) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if (attribute_id.is_anonymous()) {
|
|
|
|
|
return layer.anonymous_id == &attribute_id.anonymous_id();
|
|
|
|
|
}
|
|
|
|
|
return layer.name == attribute_id.name();
|
|
|
|
|
}
|
|
|
|
|
|
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
|
|
|
GVArray BuiltinCustomDataLayerProvider::try_get_for_read(const GeometryComponent &component) const
|
2021-02-17 08:30:15 -06:00
|
|
|
{
|
2021-03-08 16:31:51 -05:00
|
|
|
const CustomData *custom_data = custom_data_access_.get_const_custom_data(component);
|
|
|
|
|
if (custom_data == nullptr) {
|
|
|
|
|
return {};
|
|
|
|
|
}
|
2021-02-17 08:30:15 -06:00
|
|
|
|
2021-10-20 10:54:54 -05:00
|
|
|
const void *data;
|
|
|
|
|
if (stored_as_named_attribute_) {
|
|
|
|
|
data = CustomData_get_layer_named(custom_data, stored_type_, name_.c_str());
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
data = CustomData_get_layer(custom_data, stored_type_);
|
|
|
|
|
}
|
2021-03-08 16:31:51 -05:00
|
|
|
if (data == nullptr) {
|
|
|
|
|
return {};
|
|
|
|
|
}
|
2021-10-20 10:54:54 -05:00
|
|
|
|
|
|
|
|
const int domain_size = component.attribute_domain_size(domain_);
|
2021-03-08 16:31:51 -05:00
|
|
|
return as_read_attribute_(data, domain_size);
|
2021-03-05 15:16:25 -06:00
|
|
|
}
|
|
|
|
|
|
2021-10-29 09:28:31 +02:00
|
|
|
WriteAttributeLookup BuiltinCustomDataLayerProvider::try_get_for_write(
|
2021-03-08 16:31:51 -05:00
|
|
|
GeometryComponent &component) const
|
2021-03-05 15:16:25 -06:00
|
|
|
{
|
2021-03-08 16:31:51 -05:00
|
|
|
if (writable_ != Writable) {
|
|
|
|
|
return {};
|
|
|
|
|
}
|
|
|
|
|
CustomData *custom_data = custom_data_access_.get_custom_data(component);
|
|
|
|
|
if (custom_data == nullptr) {
|
|
|
|
|
return {};
|
|
|
|
|
}
|
|
|
|
|
const int domain_size = component.attribute_domain_size(domain_);
|
2021-10-20 13:15:37 -05:00
|
|
|
|
|
|
|
|
void *data;
|
|
|
|
|
if (stored_as_named_attribute_) {
|
|
|
|
|
data = CustomData_get_layer_named(custom_data, stored_type_, name_.c_str());
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
data = CustomData_get_layer(custom_data, stored_type_);
|
|
|
|
|
}
|
2021-03-08 16:31:51 -05:00
|
|
|
if (data == nullptr) {
|
|
|
|
|
return {};
|
|
|
|
|
}
|
2021-10-20 10:54:54 -05:00
|
|
|
|
|
|
|
|
void *new_data;
|
|
|
|
|
if (stored_as_named_attribute_) {
|
|
|
|
|
new_data = CustomData_duplicate_referenced_layer_named(
|
|
|
|
|
custom_data, stored_type_, name_.c_str(), domain_size);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
new_data = CustomData_duplicate_referenced_layer(custom_data, stored_type_, domain_size);
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-08 16:31:51 -05:00
|
|
|
if (data != new_data) {
|
2021-12-01 09:27:27 -05:00
|
|
|
if (custom_data_access_.update_custom_data_pointers) {
|
|
|
|
|
custom_data_access_.update_custom_data_pointers(component);
|
|
|
|
|
}
|
2021-03-08 16:31:51 -05:00
|
|
|
data = new_data;
|
|
|
|
|
}
|
2021-10-20 10:54:54 -05:00
|
|
|
|
2021-10-29 09:28:31 +02:00
|
|
|
std::function<void()> tag_modified_fn;
|
2021-03-08 16:31:51 -05:00
|
|
|
if (update_on_write_ != nullptr) {
|
2021-10-29 09:28:31 +02:00
|
|
|
tag_modified_fn = [component = &component, update = update_on_write_]() {
|
|
|
|
|
update(*component);
|
|
|
|
|
};
|
2021-03-08 16:31:51 -05:00
|
|
|
}
|
2021-10-29 09:28:31 +02:00
|
|
|
|
|
|
|
|
return {as_write_attribute_(data, domain_size), domain_, std::move(tag_modified_fn)};
|
2021-03-05 15:16:25 -06:00
|
|
|
}
|
|
|
|
|
|
2021-03-08 16:31:51 -05:00
|
|
|
bool BuiltinCustomDataLayerProvider::try_delete(GeometryComponent &component) const
|
2021-03-05 15:16:25 -06:00
|
|
|
{
|
2021-03-08 16:31:51 -05:00
|
|
|
if (deletable_ != Deletable) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
CustomData *custom_data = custom_data_access_.get_custom_data(component);
|
|
|
|
|
if (custom_data == nullptr) {
|
|
|
|
|
return {};
|
2021-03-05 15:16:25 -06:00
|
|
|
}
|
|
|
|
|
|
2021-03-08 16:31:51 -05:00
|
|
|
const int domain_size = component.attribute_domain_size(domain_);
|
2021-10-20 10:54:54 -05:00
|
|
|
int layer_index;
|
|
|
|
|
if (stored_as_named_attribute_) {
|
|
|
|
|
for (const int i : IndexRange(custom_data->totlayer)) {
|
|
|
|
|
if (custom_data_layer_matches_attribute_id(custom_data->layers[i], name_)) {
|
|
|
|
|
layer_index = i;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
layer_index = CustomData_get_layer_index(custom_data, stored_type_);
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-08 16:31:51 -05:00
|
|
|
const bool delete_success = CustomData_free_layer(
|
|
|
|
|
custom_data, stored_type_, domain_size, layer_index);
|
|
|
|
|
if (delete_success) {
|
2021-12-01 09:27:27 -05:00
|
|
|
if (custom_data_access_.update_custom_data_pointers) {
|
|
|
|
|
custom_data_access_.update_custom_data_pointers(component);
|
|
|
|
|
}
|
2021-03-05 15:16:25 -06:00
|
|
|
}
|
2021-03-08 16:31:51 -05:00
|
|
|
return delete_success;
|
2021-03-05 15:16:25 -06:00
|
|
|
}
|
|
|
|
|
|
2021-04-22 09:20:03 -05:00
|
|
|
bool BuiltinCustomDataLayerProvider::try_create(GeometryComponent &component,
|
|
|
|
|
const AttributeInit &initializer) const
|
2021-02-18 10:24:02 +01:00
|
|
|
{
|
2021-03-08 16:31:51 -05:00
|
|
|
if (createable_ != Creatable) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
CustomData *custom_data = custom_data_access_.get_custom_data(component);
|
|
|
|
|
if (custom_data == nullptr) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2021-04-22 09:20:03 -05:00
|
|
|
|
2021-03-08 16:31:51 -05:00
|
|
|
const int domain_size = component.attribute_domain_size(domain_);
|
2021-10-20 10:54:54 -05:00
|
|
|
bool success;
|
|
|
|
|
if (stored_as_named_attribute_) {
|
|
|
|
|
if (CustomData_get_layer_named(custom_data, data_type_, name_.c_str())) {
|
|
|
|
|
/* Exists already. */
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
success = add_custom_data_layer_from_attribute_init(
|
|
|
|
|
name_, *custom_data, stored_type_, domain_size, initializer);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
if (CustomData_get_layer(custom_data, stored_type_) != nullptr) {
|
|
|
|
|
/* Exists already. */
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
success = add_builtin_type_custom_data_layer_from_init(
|
|
|
|
|
*custom_data, stored_type_, domain_size, initializer);
|
|
|
|
|
}
|
2021-03-08 16:31:51 -05:00
|
|
|
if (success) {
|
2021-12-01 09:27:27 -05:00
|
|
|
if (custom_data_access_.update_custom_data_pointers) {
|
|
|
|
|
custom_data_access_.update_custom_data_pointers(component);
|
|
|
|
|
}
|
2021-03-08 16:31:51 -05:00
|
|
|
}
|
|
|
|
|
return success;
|
2021-02-18 10:24:02 +01:00
|
|
|
}
|
|
|
|
|
|
2021-03-08 16:31:51 -05:00
|
|
|
bool BuiltinCustomDataLayerProvider::exists(const GeometryComponent &component) const
|
2021-02-18 10:24:02 +01:00
|
|
|
{
|
2021-03-08 16:31:51 -05:00
|
|
|
const CustomData *custom_data = custom_data_access_.get_const_custom_data(component);
|
|
|
|
|
if (custom_data == nullptr) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2021-10-20 10:54:54 -05:00
|
|
|
if (stored_as_named_attribute_) {
|
|
|
|
|
return CustomData_get_layer_named(custom_data, stored_type_, name_.c_str()) != nullptr;
|
|
|
|
|
}
|
|
|
|
|
return CustomData_get_layer(custom_data, stored_type_) != nullptr;
|
2021-02-18 10:24:02 +01:00
|
|
|
}
|
|
|
|
|
|
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
|
|
|
ReadAttributeLookup CustomDataAttributeProvider::try_get_for_read(
|
2021-09-09 12:54:20 +02:00
|
|
|
const GeometryComponent &component, const AttributeIDRef &attribute_id) const
|
2021-02-18 10:24:02 +01:00
|
|
|
{
|
2021-03-08 16:31:51 -05:00
|
|
|
const CustomData *custom_data = custom_data_access_.get_const_custom_data(component);
|
|
|
|
|
if (custom_data == nullptr) {
|
|
|
|
|
return {};
|
|
|
|
|
}
|
|
|
|
|
const int domain_size = component.attribute_domain_size(domain_);
|
|
|
|
|
for (const CustomDataLayer &layer : Span(custom_data->layers, custom_data->totlayer)) {
|
2021-09-09 12:54:20 +02:00
|
|
|
if (!custom_data_layer_matches_attribute_id(layer, attribute_id)) {
|
2021-03-08 16:31:51 -05:00
|
|
|
continue;
|
|
|
|
|
}
|
2021-10-18 10:08:57 -05:00
|
|
|
const CPPType *type = custom_data_type_to_cpp_type((CustomDataType)layer.type);
|
|
|
|
|
if (type == nullptr) {
|
|
|
|
|
continue;
|
2021-03-08 16:31:51 -05:00
|
|
|
}
|
2021-10-18 10:08:57 -05:00
|
|
|
GSpan data{*type, layer.data, domain_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 {GVArray::ForSpan(data), domain_};
|
2021-03-08 16:31:51 -05:00
|
|
|
}
|
|
|
|
|
return {};
|
2021-02-18 10:24:02 +01:00
|
|
|
}
|
|
|
|
|
|
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
|
|
|
WriteAttributeLookup CustomDataAttributeProvider::try_get_for_write(
|
2021-09-09 12:54:20 +02:00
|
|
|
GeometryComponent &component, const AttributeIDRef &attribute_id) const
|
2021-02-18 10:24:02 +01:00
|
|
|
{
|
2021-03-08 16:31:51 -05:00
|
|
|
CustomData *custom_data = custom_data_access_.get_custom_data(component);
|
|
|
|
|
if (custom_data == nullptr) {
|
|
|
|
|
return {};
|
|
|
|
|
}
|
|
|
|
|
const int domain_size = component.attribute_domain_size(domain_);
|
|
|
|
|
for (CustomDataLayer &layer : MutableSpan(custom_data->layers, custom_data->totlayer)) {
|
2021-09-09 12:54:20 +02:00
|
|
|
if (!custom_data_layer_matches_attribute_id(layer, attribute_id)) {
|
2021-03-08 16:31:51 -05:00
|
|
|
continue;
|
|
|
|
|
}
|
2021-09-09 12:54:20 +02:00
|
|
|
if (attribute_id.is_named()) {
|
|
|
|
|
CustomData_duplicate_referenced_layer_named(
|
|
|
|
|
custom_data, layer.type, layer.name, domain_size);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
CustomData_duplicate_referenced_layer_anonymous(
|
|
|
|
|
custom_data, layer.type, &attribute_id.anonymous_id(), domain_size);
|
|
|
|
|
}
|
2021-10-18 10:08:57 -05:00
|
|
|
const CPPType *type = custom_data_type_to_cpp_type((CustomDataType)layer.type);
|
|
|
|
|
if (type == nullptr) {
|
|
|
|
|
continue;
|
2021-03-08 16:31:51 -05:00
|
|
|
}
|
2021-10-18 10:08:57 -05:00
|
|
|
GMutableSpan data{*type, layer.data, domain_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 {GVMutableArray::ForSpan(data), domain_};
|
2021-03-08 16:31:51 -05:00
|
|
|
}
|
|
|
|
|
return {};
|
2021-02-18 10:24:02 +01:00
|
|
|
}
|
|
|
|
|
|
2021-03-08 16:31:51 -05:00
|
|
|
bool CustomDataAttributeProvider::try_delete(GeometryComponent &component,
|
2021-09-09 12:54:20 +02:00
|
|
|
const AttributeIDRef &attribute_id) const
|
2021-02-18 10:24:02 +01:00
|
|
|
{
|
2021-03-08 16:31:51 -05:00
|
|
|
CustomData *custom_data = custom_data_access_.get_custom_data(component);
|
|
|
|
|
if (custom_data == nullptr) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
const int domain_size = component.attribute_domain_size(domain_);
|
|
|
|
|
for (const int i : IndexRange(custom_data->totlayer)) {
|
|
|
|
|
const CustomDataLayer &layer = custom_data->layers[i];
|
2021-09-09 12:54:20 +02:00
|
|
|
if (this->type_is_supported((CustomDataType)layer.type) &&
|
|
|
|
|
custom_data_layer_matches_attribute_id(layer, attribute_id)) {
|
2021-03-08 16:31:51 -05:00
|
|
|
CustomData_free_layer(custom_data, layer.type, domain_size, i);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false;
|
2021-02-18 10:24:02 +01:00
|
|
|
}
|
|
|
|
|
|
2021-03-08 16:31:51 -05:00
|
|
|
bool CustomDataAttributeProvider::try_create(GeometryComponent &component,
|
2021-09-09 12:54:20 +02:00
|
|
|
const AttributeIDRef &attribute_id,
|
2021-03-08 16:31:51 -05:00
|
|
|
const AttributeDomain domain,
|
2021-04-22 09:20:03 -05:00
|
|
|
const CustomDataType data_type,
|
|
|
|
|
const AttributeInit &initializer) const
|
2021-02-18 10:24:02 +01:00
|
|
|
{
|
2021-03-08 16:31:51 -05:00
|
|
|
if (domain_ != domain) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if (!this->type_is_supported(data_type)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
CustomData *custom_data = custom_data_access_.get_custom_data(component);
|
|
|
|
|
if (custom_data == nullptr) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
for (const CustomDataLayer &layer : Span(custom_data->layers, custom_data->totlayer)) {
|
2021-09-09 12:54:20 +02:00
|
|
|
if (custom_data_layer_matches_attribute_id(layer, attribute_id)) {
|
2021-03-08 16:31:51 -05:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
const int domain_size = component.attribute_domain_size(domain_);
|
2021-09-09 12:54:20 +02:00
|
|
|
add_custom_data_layer_from_attribute_init(
|
|
|
|
|
attribute_id, *custom_data, data_type, domain_size, initializer);
|
2021-03-08 16:31:51 -05:00
|
|
|
return true;
|
2021-02-18 10:24:02 +01:00
|
|
|
}
|
|
|
|
|
|
2021-03-08 16:31:51 -05:00
|
|
|
bool CustomDataAttributeProvider::foreach_attribute(const GeometryComponent &component,
|
|
|
|
|
const AttributeForeachCallback callback) const
|
2021-02-18 10:24:02 +01:00
|
|
|
{
|
2021-03-08 16:31:51 -05:00
|
|
|
const CustomData *custom_data = custom_data_access_.get_const_custom_data(component);
|
|
|
|
|
if (custom_data == nullptr) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
for (const CustomDataLayer &layer : Span(custom_data->layers, custom_data->totlayer)) {
|
|
|
|
|
const CustomDataType data_type = (CustomDataType)layer.type;
|
|
|
|
|
if (this->type_is_supported(data_type)) {
|
|
|
|
|
AttributeMetaData meta_data{domain_, data_type};
|
2021-11-02 13:43:54 -05:00
|
|
|
const AttributeIDRef attribute_id = attribute_id_from_custom_data_layer(layer);
|
2021-09-09 12:54:20 +02:00
|
|
|
if (!callback(attribute_id, meta_data)) {
|
2021-03-08 16:31:51 -05:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return true;
|
2021-02-18 10:24:02 +01:00
|
|
|
}
|
|
|
|
|
|
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
|
|
|
ReadAttributeLookup NamedLegacyCustomDataProvider::try_get_for_read(
|
2021-09-09 12:54:20 +02:00
|
|
|
const GeometryComponent &component, const AttributeIDRef &attribute_id) const
|
2021-02-18 10:24:02 +01:00
|
|
|
{
|
2021-03-08 16:31:51 -05:00
|
|
|
const CustomData *custom_data = custom_data_access_.get_const_custom_data(component);
|
|
|
|
|
if (custom_data == nullptr) {
|
|
|
|
|
return {};
|
|
|
|
|
}
|
|
|
|
|
for (const CustomDataLayer &layer : Span(custom_data->layers, custom_data->totlayer)) {
|
|
|
|
|
if (layer.type == stored_type_) {
|
2021-09-09 12:54:20 +02:00
|
|
|
if (custom_data_layer_matches_attribute_id(layer, attribute_id)) {
|
2021-03-08 16:31:51 -05:00
|
|
|
const int domain_size = component.attribute_domain_size(domain_);
|
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
|
|
|
return {as_read_attribute_(layer.data, domain_size), domain_};
|
2021-03-08 16:31:51 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return {};
|
2021-02-18 10:24:02 +01:00
|
|
|
}
|
|
|
|
|
|
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
|
|
|
WriteAttributeLookup NamedLegacyCustomDataProvider::try_get_for_write(
|
2021-09-09 12:54:20 +02:00
|
|
|
GeometryComponent &component, const AttributeIDRef &attribute_id) const
|
2020-12-02 13:25:25 +01:00
|
|
|
{
|
2021-03-08 16:31:51 -05:00
|
|
|
CustomData *custom_data = custom_data_access_.get_custom_data(component);
|
|
|
|
|
if (custom_data == nullptr) {
|
|
|
|
|
return {};
|
|
|
|
|
}
|
|
|
|
|
for (CustomDataLayer &layer : MutableSpan(custom_data->layers, custom_data->totlayer)) {
|
|
|
|
|
if (layer.type == stored_type_) {
|
2021-09-09 12:54:20 +02:00
|
|
|
if (custom_data_layer_matches_attribute_id(layer, attribute_id)) {
|
2021-03-08 16:31:51 -05:00
|
|
|
const int domain_size = component.attribute_domain_size(domain_);
|
|
|
|
|
void *data_old = layer.data;
|
|
|
|
|
void *data_new = CustomData_duplicate_referenced_layer_named(
|
|
|
|
|
custom_data, stored_type_, layer.name, domain_size);
|
|
|
|
|
if (data_old != data_new) {
|
2021-12-01 09:27:27 -05:00
|
|
|
if (custom_data_access_.update_custom_data_pointers) {
|
|
|
|
|
custom_data_access_.update_custom_data_pointers(component);
|
|
|
|
|
}
|
2021-03-08 16:31:51 -05:00
|
|
|
}
|
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
|
|
|
return {as_write_attribute_(layer.data, domain_size), domain_};
|
2021-03-08 16:31:51 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return {};
|
2021-02-09 11:24:28 +01:00
|
|
|
}
|
|
|
|
|
|
2021-03-08 16:31:51 -05:00
|
|
|
bool NamedLegacyCustomDataProvider::try_delete(GeometryComponent &component,
|
2021-09-09 12:54:20 +02:00
|
|
|
const AttributeIDRef &attribute_id) const
|
2021-02-09 11:24:28 +01:00
|
|
|
{
|
2021-03-08 16:31:51 -05:00
|
|
|
CustomData *custom_data = custom_data_access_.get_custom_data(component);
|
|
|
|
|
if (custom_data == nullptr) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
for (const int i : IndexRange(custom_data->totlayer)) {
|
|
|
|
|
const CustomDataLayer &layer = custom_data->layers[i];
|
|
|
|
|
if (layer.type == stored_type_) {
|
2021-09-09 12:54:20 +02:00
|
|
|
if (custom_data_layer_matches_attribute_id(layer, attribute_id)) {
|
2021-03-08 16:31:51 -05:00
|
|
|
const int domain_size = component.attribute_domain_size(domain_);
|
|
|
|
|
CustomData_free_layer(custom_data, stored_type_, domain_size, i);
|
2021-12-01 09:27:27 -05:00
|
|
|
if (custom_data_access_.update_custom_data_pointers) {
|
|
|
|
|
custom_data_access_.update_custom_data_pointers(component);
|
|
|
|
|
}
|
2021-03-08 16:31:51 -05:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false;
|
2021-02-09 11:24:28 +01:00
|
|
|
}
|
|
|
|
|
|
2021-03-08 16:31:51 -05:00
|
|
|
bool NamedLegacyCustomDataProvider::foreach_attribute(
|
|
|
|
|
const GeometryComponent &component, const AttributeForeachCallback callback) const
|
2021-02-09 11:24:28 +01:00
|
|
|
{
|
2021-03-08 16:31:51 -05:00
|
|
|
const CustomData *custom_data = custom_data_access_.get_const_custom_data(component);
|
|
|
|
|
if (custom_data == nullptr) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
for (const CustomDataLayer &layer : Span(custom_data->layers, custom_data->totlayer)) {
|
|
|
|
|
if (layer.type == stored_type_) {
|
|
|
|
|
AttributeMetaData meta_data{domain_, attribute_type_};
|
|
|
|
|
if (!callback(layer.name, meta_data)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2020-12-02 13:25:25 +01:00
|
|
|
}
|
2021-03-08 16:31:51 -05:00
|
|
|
}
|
|
|
|
|
return true;
|
2020-12-02 13:25:25 +01:00
|
|
|
}
|
|
|
|
|
|
2021-03-17 11:50:13 +01:00
|
|
|
void NamedLegacyCustomDataProvider::foreach_domain(
|
|
|
|
|
const FunctionRef<void(AttributeDomain)> callback) const
|
2020-12-02 13:25:25 +01:00
|
|
|
{
|
2021-03-17 11:50:13 +01:00
|
|
|
callback(domain_);
|
2020-12-02 13:25:25 +01:00
|
|
|
}
|
|
|
|
|
|
2021-05-19 13:22:09 -04:00
|
|
|
CustomDataAttributes::CustomDataAttributes()
|
|
|
|
|
{
|
|
|
|
|
CustomData_reset(&data);
|
|
|
|
|
size_ = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CustomDataAttributes::~CustomDataAttributes()
|
|
|
|
|
{
|
|
|
|
|
CustomData_free(&data, size_);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CustomDataAttributes::CustomDataAttributes(const CustomDataAttributes &other)
|
|
|
|
|
{
|
|
|
|
|
size_ = other.size_;
|
|
|
|
|
CustomData_copy(&other.data, &data, CD_MASK_ALL, CD_DUPLICATE, size_);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CustomDataAttributes::CustomDataAttributes(CustomDataAttributes &&other)
|
|
|
|
|
{
|
|
|
|
|
size_ = other.size_;
|
|
|
|
|
data = other.data;
|
|
|
|
|
CustomData_reset(&other.data);
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-09 15:54:26 -05:00
|
|
|
CustomDataAttributes &CustomDataAttributes::operator=(const CustomDataAttributes &other)
|
|
|
|
|
{
|
|
|
|
|
if (this != &other) {
|
|
|
|
|
CustomData_copy(&other.data, &data, CD_MASK_ALL, CD_DUPLICATE, other.size_);
|
|
|
|
|
size_ = other.size_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return *this;
|
|
|
|
|
}
|
|
|
|
|
|
2021-09-09 12:54:20 +02:00
|
|
|
std::optional<GSpan> CustomDataAttributes::get_for_read(const AttributeIDRef &attribute_id) const
|
2021-05-19 13:22:09 -04:00
|
|
|
{
|
|
|
|
|
for (const CustomDataLayer &layer : Span(data.layers, data.totlayer)) {
|
2021-09-09 12:54:20 +02:00
|
|
|
if (custom_data_layer_matches_attribute_id(layer, attribute_id)) {
|
2021-05-19 13:22:09 -04:00
|
|
|
const CPPType *cpp_type = custom_data_type_to_cpp_type((CustomDataType)layer.type);
|
|
|
|
|
BLI_assert(cpp_type != nullptr);
|
|
|
|
|
return GSpan(*cpp_type, layer.data, size_);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
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
|
|
|
GVArray CustomDataAttributes::get_for_read(const AttributeIDRef &attribute_id,
|
|
|
|
|
const CustomDataType data_type,
|
|
|
|
|
const void *default_value) const
|
2021-06-02 08:24:42 -04:00
|
|
|
{
|
|
|
|
|
const CPPType *type = blender::bke::custom_data_type_to_cpp_type(data_type);
|
|
|
|
|
|
2021-09-09 12:54:20 +02:00
|
|
|
std::optional<GSpan> attribute = this->get_for_read(attribute_id);
|
2021-06-02 08:24:42 -04:00
|
|
|
if (!attribute) {
|
|
|
|
|
const int domain_size = 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 GVArray::ForSingle(
|
2021-06-02 08:24:42 -04:00
|
|
|
*type, domain_size, (default_value == nullptr) ? type->default_value() : default_value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (attribute->type() == *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
|
|
|
return GVArray::ForSpan(*attribute);
|
2021-06-02 08:24:42 -04:00
|
|
|
}
|
2021-12-07 15:21:59 +01:00
|
|
|
const blender::bke::DataTypeConversions &conversions =
|
|
|
|
|
blender::bke::get_implicit_type_conversions();
|
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 conversions.try_convert(GVArray::ForSpan(*attribute), *type);
|
2021-06-02 08:24:42 -04:00
|
|
|
}
|
|
|
|
|
|
2021-09-09 12:54:20 +02:00
|
|
|
std::optional<GMutableSpan> CustomDataAttributes::get_for_write(const AttributeIDRef &attribute_id)
|
2021-05-19 13:22:09 -04:00
|
|
|
{
|
|
|
|
|
for (CustomDataLayer &layer : MutableSpan(data.layers, data.totlayer)) {
|
2021-09-09 12:54:20 +02:00
|
|
|
if (custom_data_layer_matches_attribute_id(layer, attribute_id)) {
|
2021-05-19 13:22:09 -04:00
|
|
|
const CPPType *cpp_type = custom_data_type_to_cpp_type((CustomDataType)layer.type);
|
|
|
|
|
BLI_assert(cpp_type != nullptr);
|
|
|
|
|
return GMutableSpan(*cpp_type, layer.data, size_);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return {};
|
|
|
|
|
}
|
|
|
|
|
|
2021-09-09 12:54:20 +02:00
|
|
|
bool CustomDataAttributes::create(const AttributeIDRef &attribute_id,
|
|
|
|
|
const CustomDataType data_type)
|
2021-05-19 13:22:09 -04:00
|
|
|
{
|
2021-09-09 12:54:20 +02:00
|
|
|
void *result = add_generic_custom_data_layer(
|
|
|
|
|
data, data_type, CD_DEFAULT, nullptr, size_, attribute_id);
|
2021-05-19 13:22:09 -04:00
|
|
|
return result != nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
2021-09-09 12:54:20 +02:00
|
|
|
bool CustomDataAttributes::create_by_move(const AttributeIDRef &attribute_id,
|
2021-05-19 13:22:09 -04:00
|
|
|
const CustomDataType data_type,
|
|
|
|
|
void *buffer)
|
|
|
|
|
{
|
2021-09-09 12:54:20 +02:00
|
|
|
void *result = add_generic_custom_data_layer(
|
|
|
|
|
data, data_type, CD_ASSIGN, buffer, size_, attribute_id);
|
2021-05-19 13:22:09 -04:00
|
|
|
return result != nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
2021-09-09 12:54:20 +02:00
|
|
|
bool CustomDataAttributes::remove(const AttributeIDRef &attribute_id)
|
2021-05-19 13:22:09 -04:00
|
|
|
{
|
|
|
|
|
bool result = false;
|
|
|
|
|
for (const int i : IndexRange(data.totlayer)) {
|
|
|
|
|
const CustomDataLayer &layer = data.layers[i];
|
2021-09-09 12:54:20 +02:00
|
|
|
if (custom_data_layer_matches_attribute_id(layer, attribute_id)) {
|
2021-05-19 13:22:09 -04:00
|
|
|
CustomData_free_layer(&data, layer.type, size_, i);
|
|
|
|
|
result = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CustomDataAttributes::reallocate(const int size)
|
|
|
|
|
{
|
|
|
|
|
size_ = size;
|
|
|
|
|
CustomData_realloc(&data, size);
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-30 10:59:11 -05:00
|
|
|
void CustomDataAttributes::clear()
|
|
|
|
|
{
|
|
|
|
|
CustomData_free(&data, size_);
|
|
|
|
|
size_ = 0;
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-19 13:22:09 -04:00
|
|
|
bool CustomDataAttributes::foreach_attribute(const AttributeForeachCallback callback,
|
|
|
|
|
const AttributeDomain domain) const
|
|
|
|
|
{
|
|
|
|
|
for (const CustomDataLayer &layer : Span(data.layers, data.totlayer)) {
|
|
|
|
|
AttributeMetaData meta_data{domain, (CustomDataType)layer.type};
|
2021-11-02 13:43:54 -05:00
|
|
|
const AttributeIDRef attribute_id = attribute_id_from_custom_data_layer(layer);
|
2021-09-09 12:54:20 +02:00
|
|
|
if (!callback(attribute_id, meta_data)) {
|
2021-05-19 13:22:09 -04:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-02 15:16:52 -05:00
|
|
|
void CustomDataAttributes::reorder(Span<AttributeIDRef> new_order)
|
|
|
|
|
{
|
|
|
|
|
BLI_assert(new_order.size() == data.totlayer);
|
|
|
|
|
|
|
|
|
|
Map<AttributeIDRef, int> old_order;
|
|
|
|
|
old_order.reserve(data.totlayer);
|
|
|
|
|
Array<CustomDataLayer> old_layers(Span(data.layers, data.totlayer));
|
|
|
|
|
for (const int i : old_layers.index_range()) {
|
|
|
|
|
old_order.add_new(attribute_id_from_custom_data_layer(old_layers[i]), i);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
MutableSpan layers(data.layers, data.totlayer);
|
|
|
|
|
for (const int i : layers.index_range()) {
|
|
|
|
|
const int old_index = old_order.lookup(new_order[i]);
|
|
|
|
|
layers[i] = old_layers[old_index];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CustomData_update_typemap(&data);
|
|
|
|
|
}
|
|
|
|
|
|
2021-02-09 11:24:28 +01:00
|
|
|
} // namespace blender::bke
|
2020-12-02 13:25:25 +01:00
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
/** \name Geometry Component
|
|
|
|
|
* \{ */
|
|
|
|
|
|
2021-02-09 11:24:28 +01:00
|
|
|
const blender::bke::ComponentAttributeProviders *GeometryComponent::get_attribute_providers() const
|
2020-12-02 13:25:25 +01:00
|
|
|
{
|
2021-02-09 11:24:28 +01:00
|
|
|
return nullptr;
|
2020-12-02 13:25:25 +01:00
|
|
|
}
|
|
|
|
|
|
2021-02-09 11:24:28 +01:00
|
|
|
bool GeometryComponent::attribute_domain_supported(const AttributeDomain domain) const
|
2020-12-02 13:25:25 +01:00
|
|
|
{
|
2021-02-09 11:24:28 +01:00
|
|
|
using namespace blender::bke;
|
|
|
|
|
const ComponentAttributeProviders *providers = this->get_attribute_providers();
|
|
|
|
|
if (providers == nullptr) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return providers->supported_domains().contains(domain);
|
2020-12-02 13:25:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int GeometryComponent::attribute_domain_size(const AttributeDomain UNUSED(domain)) const
|
|
|
|
|
{
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
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
|
|
|
bool GeometryComponent::attribute_is_builtin(const blender::StringRef attribute_name) const
|
|
|
|
|
{
|
|
|
|
|
using namespace blender::bke;
|
|
|
|
|
const ComponentAttributeProviders *providers = this->get_attribute_providers();
|
|
|
|
|
if (providers == nullptr) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return providers->builtin_attribute_providers().contains_as(attribute_name);
|
|
|
|
|
}
|
|
|
|
|
|
2021-09-16 11:56:31 -05:00
|
|
|
bool GeometryComponent::attribute_is_builtin(const AttributeIDRef &attribute_id) const
|
|
|
|
|
{
|
|
|
|
|
/* Anonymous attributes cannot be built-in. */
|
|
|
|
|
return attribute_id.is_named() && this->attribute_is_builtin(attribute_id.name());
|
|
|
|
|
}
|
|
|
|
|
|
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
|
|
|
blender::bke::ReadAttributeLookup GeometryComponent::attribute_try_get_for_read(
|
2021-09-09 12:54:20 +02:00
|
|
|
const AttributeIDRef &attribute_id) const
|
2020-12-02 13:25:25 +01:00
|
|
|
{
|
2021-02-09 11:24:28 +01:00
|
|
|
using namespace blender::bke;
|
|
|
|
|
const ComponentAttributeProviders *providers = this->get_attribute_providers();
|
|
|
|
|
if (providers == nullptr) {
|
|
|
|
|
return {};
|
|
|
|
|
}
|
2021-09-09 12:54:20 +02:00
|
|
|
if (attribute_id.is_named()) {
|
|
|
|
|
const BuiltinAttributeProvider *builtin_provider =
|
|
|
|
|
providers->builtin_attribute_providers().lookup_default_as(attribute_id.name(), nullptr);
|
|
|
|
|
if (builtin_provider != nullptr) {
|
|
|
|
|
return {builtin_provider->try_get_for_read(*this), builtin_provider->domain()};
|
|
|
|
|
}
|
2021-02-09 11:24:28 +01:00
|
|
|
}
|
|
|
|
|
for (const DynamicAttributesProvider *dynamic_provider :
|
|
|
|
|
providers->dynamic_attribute_providers()) {
|
2021-09-09 12:54:20 +02:00
|
|
|
ReadAttributeLookup attribute = dynamic_provider->try_get_for_read(*this, attribute_id);
|
2021-02-09 11:24:28 +01:00
|
|
|
if (attribute) {
|
|
|
|
|
return attribute;
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-12-02 13:25:25 +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
|
|
|
blender::fn::GVArray GeometryComponent::attribute_try_adapt_domain_impl(
|
|
|
|
|
const blender::fn::GVArray &varray,
|
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
|
|
|
const AttributeDomain from_domain,
|
|
|
|
|
const AttributeDomain to_domain) const
|
2020-12-02 13:25:25 +01:00
|
|
|
{
|
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
|
|
|
if (from_domain == to_domain) {
|
|
|
|
|
return varray;
|
2020-12-02 13:25:25 +01:00
|
|
|
}
|
|
|
|
|
return {};
|
|
|
|
|
}
|
|
|
|
|
|
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
|
|
|
blender::bke::WriteAttributeLookup GeometryComponent::attribute_try_get_for_write(
|
2021-09-09 12:54:20 +02:00
|
|
|
const AttributeIDRef &attribute_id)
|
2020-12-02 13:25:25 +01:00
|
|
|
{
|
2021-02-09 11:24:28 +01:00
|
|
|
using namespace blender::bke;
|
|
|
|
|
const ComponentAttributeProviders *providers = this->get_attribute_providers();
|
|
|
|
|
if (providers == nullptr) {
|
|
|
|
|
return {};
|
|
|
|
|
}
|
2021-09-09 12:54:20 +02:00
|
|
|
if (attribute_id.is_named()) {
|
|
|
|
|
const BuiltinAttributeProvider *builtin_provider =
|
|
|
|
|
providers->builtin_attribute_providers().lookup_default_as(attribute_id.name(), nullptr);
|
|
|
|
|
if (builtin_provider != nullptr) {
|
2021-10-29 09:28:31 +02:00
|
|
|
return builtin_provider->try_get_for_write(*this);
|
2021-09-09 12:54:20 +02:00
|
|
|
}
|
2021-02-09 11:24:28 +01:00
|
|
|
}
|
|
|
|
|
for (const DynamicAttributesProvider *dynamic_provider :
|
|
|
|
|
providers->dynamic_attribute_providers()) {
|
2021-09-09 12:54:20 +02:00
|
|
|
WriteAttributeLookup attribute = dynamic_provider->try_get_for_write(*this, attribute_id);
|
2021-02-09 11:24:28 +01:00
|
|
|
if (attribute) {
|
|
|
|
|
return attribute;
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-12-02 13:25:25 +01:00
|
|
|
return {};
|
|
|
|
|
}
|
|
|
|
|
|
2021-09-09 12:54:20 +02:00
|
|
|
bool GeometryComponent::attribute_try_delete(const AttributeIDRef &attribute_id)
|
2020-12-02 13:25:25 +01:00
|
|
|
{
|
2021-02-09 11:24:28 +01:00
|
|
|
using namespace blender::bke;
|
|
|
|
|
const ComponentAttributeProviders *providers = this->get_attribute_providers();
|
|
|
|
|
if (providers == nullptr) {
|
|
|
|
|
return {};
|
|
|
|
|
}
|
2021-09-09 12:54:20 +02:00
|
|
|
if (attribute_id.is_named()) {
|
|
|
|
|
const BuiltinAttributeProvider *builtin_provider =
|
|
|
|
|
providers->builtin_attribute_providers().lookup_default_as(attribute_id.name(), nullptr);
|
|
|
|
|
if (builtin_provider != nullptr) {
|
|
|
|
|
return builtin_provider->try_delete(*this);
|
|
|
|
|
}
|
2021-02-09 11:24:28 +01:00
|
|
|
}
|
|
|
|
|
bool success = false;
|
|
|
|
|
for (const DynamicAttributesProvider *dynamic_provider :
|
|
|
|
|
providers->dynamic_attribute_providers()) {
|
2021-09-09 12:54:20 +02:00
|
|
|
success = dynamic_provider->try_delete(*this, attribute_id) || success;
|
2021-02-09 11:24:28 +01:00
|
|
|
}
|
|
|
|
|
return success;
|
2020-12-02 13:25:25 +01:00
|
|
|
}
|
|
|
|
|
|
2021-09-09 12:54:20 +02:00
|
|
|
bool GeometryComponent::attribute_try_create(const AttributeIDRef &attribute_id,
|
2021-02-09 11:24:28 +01:00
|
|
|
const AttributeDomain domain,
|
2021-04-22 09:20:03 -05:00
|
|
|
const CustomDataType data_type,
|
|
|
|
|
const AttributeInit &initializer)
|
2020-12-02 13:25:25 +01:00
|
|
|
{
|
2021-02-09 11:24:28 +01:00
|
|
|
using namespace blender::bke;
|
2021-09-09 12:54:20 +02:00
|
|
|
if (!attribute_id) {
|
2021-02-19 18:08:40 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
2021-02-09 11:24:28 +01:00
|
|
|
const ComponentAttributeProviders *providers = this->get_attribute_providers();
|
|
|
|
|
if (providers == nullptr) {
|
2021-02-19 18:08:40 +01:00
|
|
|
return false;
|
2021-02-09 11:24:28 +01:00
|
|
|
}
|
2021-09-09 12:54:20 +02:00
|
|
|
if (attribute_id.is_named()) {
|
|
|
|
|
const BuiltinAttributeProvider *builtin_provider =
|
|
|
|
|
providers->builtin_attribute_providers().lookup_default_as(attribute_id.name(), nullptr);
|
|
|
|
|
if (builtin_provider != nullptr) {
|
|
|
|
|
if (builtin_provider->domain() != domain) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if (builtin_provider->data_type() != data_type) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return builtin_provider->try_create(*this, initializer);
|
2021-02-09 11:24:28 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for (const DynamicAttributesProvider *dynamic_provider :
|
|
|
|
|
providers->dynamic_attribute_providers()) {
|
2021-09-09 12:54:20 +02:00
|
|
|
if (dynamic_provider->try_create(*this, attribute_id, domain, data_type, initializer)) {
|
2021-02-09 11:24:28 +01:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-12-02 13:25:25 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-22 09:20:03 -05:00
|
|
|
bool GeometryComponent::attribute_try_create_builtin(const blender::StringRef attribute_name,
|
|
|
|
|
const AttributeInit &initializer)
|
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
|
|
|
{
|
|
|
|
|
using namespace blender::bke;
|
|
|
|
|
if (attribute_name.is_empty()) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
const ComponentAttributeProviders *providers = this->get_attribute_providers();
|
|
|
|
|
if (providers == nullptr) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
const BuiltinAttributeProvider *builtin_provider =
|
|
|
|
|
providers->builtin_attribute_providers().lookup_default_as(attribute_name, nullptr);
|
|
|
|
|
if (builtin_provider == nullptr) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2021-04-22 09:20:03 -05:00
|
|
|
return builtin_provider->try_create(*this, initializer);
|
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-09-09 12:54:20 +02:00
|
|
|
Set<AttributeIDRef> GeometryComponent::attribute_ids() const
|
2021-02-23 15:15:23 +01:00
|
|
|
{
|
2021-09-09 12:54:20 +02:00
|
|
|
Set<AttributeIDRef> attributes;
|
|
|
|
|
this->attribute_foreach(
|
|
|
|
|
[&](const AttributeIDRef &attribute_id, const AttributeMetaData &UNUSED(meta_data)) {
|
|
|
|
|
attributes.add(attribute_id);
|
|
|
|
|
return true;
|
|
|
|
|
});
|
2021-02-23 15:15:23 +01:00
|
|
|
return attributes;
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-08 12:19:09 -05:00
|
|
|
bool GeometryComponent::attribute_foreach(const AttributeForeachCallback callback) const
|
2020-12-02 13:25:25 +01:00
|
|
|
{
|
2021-02-09 11:24:28 +01:00
|
|
|
using namespace blender::bke;
|
|
|
|
|
const ComponentAttributeProviders *providers = this->get_attribute_providers();
|
|
|
|
|
if (providers == nullptr) {
|
2021-04-08 12:19:09 -05:00
|
|
|
return true;
|
2021-02-09 11:24:28 +01:00
|
|
|
}
|
2021-02-23 15:15:23 +01:00
|
|
|
|
|
|
|
|
/* Keep track handled attribute names to make sure that we do not return the same name twice. */
|
|
|
|
|
Set<std::string> handled_attribute_names;
|
|
|
|
|
|
2021-02-09 11:24:28 +01:00
|
|
|
for (const BuiltinAttributeProvider *provider :
|
|
|
|
|
providers->builtin_attribute_providers().values()) {
|
|
|
|
|
if (provider->exists(*this)) {
|
2021-02-23 15:15:23 +01:00
|
|
|
AttributeMetaData meta_data{provider->domain(), provider->data_type()};
|
|
|
|
|
if (!callback(provider->name(), meta_data)) {
|
2021-04-08 12:19:09 -05:00
|
|
|
return false;
|
2021-02-23 15:15:23 +01:00
|
|
|
}
|
|
|
|
|
handled_attribute_names.add_new(provider->name());
|
2021-02-09 11:24:28 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for (const DynamicAttributesProvider *provider : providers->dynamic_attribute_providers()) {
|
2021-02-23 15:15:23 +01:00
|
|
|
const bool continue_loop = provider->foreach_attribute(
|
2021-09-09 12:54:20 +02:00
|
|
|
*this, [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) {
|
|
|
|
|
if (attribute_id.is_anonymous() || handled_attribute_names.add(attribute_id.name())) {
|
|
|
|
|
return callback(attribute_id, meta_data);
|
2021-02-23 15:15:23 +01:00
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
});
|
|
|
|
|
if (!continue_loop) {
|
2021-04-08 12:19:09 -05:00
|
|
|
return false;
|
2021-02-23 15:15:23 +01:00
|
|
|
}
|
2021-02-09 11:24:28 +01:00
|
|
|
}
|
2021-04-08 12:19:09 -05:00
|
|
|
|
|
|
|
|
return true;
|
2020-12-02 13:25:25 +01:00
|
|
|
}
|
|
|
|
|
|
2021-09-09 12:54:20 +02:00
|
|
|
bool GeometryComponent::attribute_exists(const AttributeIDRef &attribute_id) const
|
2020-12-10 10:50:37 -06:00
|
|
|
{
|
2021-09-09 12:54:20 +02:00
|
|
|
blender::bke::ReadAttributeLookup attribute = this->attribute_try_get_for_read(attribute_id);
|
2020-12-10 10:50:37 -06:00
|
|
|
if (attribute) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-22 08:05:02 -05:00
|
|
|
std::optional<AttributeMetaData> GeometryComponent::attribute_get_meta_data(
|
2021-09-09 12:54:20 +02:00
|
|
|
const AttributeIDRef &attribute_id) const
|
2021-04-22 08:05:02 -05:00
|
|
|
{
|
|
|
|
|
std::optional<AttributeMetaData> result{std::nullopt};
|
2021-09-09 12:54:20 +02:00
|
|
|
this->attribute_foreach(
|
|
|
|
|
[&](const AttributeIDRef ¤t_attribute_id, const AttributeMetaData &meta_data) {
|
|
|
|
|
if (attribute_id == current_attribute_id) {
|
|
|
|
|
result = meta_data;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
});
|
2021-04-22 08:05:02 -05:00
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
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
|
|
|
static blender::fn::GVArray try_adapt_data_type(blender::fn::GVArray varray,
|
|
|
|
|
const blender::fn::CPPType &to_type)
|
2020-12-02 13:25:25 +01:00
|
|
|
{
|
2021-12-07 15:21:59 +01:00
|
|
|
const blender::bke::DataTypeConversions &conversions =
|
|
|
|
|
blender::bke::get_implicit_type_conversions();
|
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
|
|
|
return conversions.try_convert(std::move(varray), to_type);
|
2020-12-02 13:25:25 +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
|
|
|
blender::fn::GVArray GeometryComponent::attribute_try_get_for_read(
|
2021-09-09 12:54:20 +02:00
|
|
|
const AttributeIDRef &attribute_id,
|
2020-12-02 13:25:25 +01:00
|
|
|
const AttributeDomain domain,
|
|
|
|
|
const CustomDataType data_type) const
|
|
|
|
|
{
|
2021-09-09 12:54:20 +02:00
|
|
|
blender::bke::ReadAttributeLookup attribute = this->attribute_try_get_for_read(attribute_id);
|
2020-12-02 13:25:25 +01:00
|
|
|
if (!attribute) {
|
|
|
|
|
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
|
|
|
blender::fn::GVArray varray = std::move(attribute.varray);
|
2021-10-20 10:45:59 +11:00
|
|
|
if (!ELEM(domain, ATTR_DOMAIN_AUTO, attribute.domain)) {
|
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
|
|
|
varray = this->attribute_try_adapt_domain(std::move(varray), attribute.domain, domain);
|
|
|
|
|
if (!varray) {
|
2020-12-02 13:25:25 +01:00
|
|
|
return {};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const blender::fn::CPPType *cpp_type = blender::bke::custom_data_type_to_cpp_type(data_type);
|
|
|
|
|
BLI_assert(cpp_type != 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
|
|
|
if (varray.type() != *cpp_type) {
|
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
|
|
|
varray = try_adapt_data_type(std::move(varray), *cpp_type);
|
|
|
|
|
if (!varray) {
|
2020-12-02 13:25:25 +01:00
|
|
|
return {};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
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
|
|
|
return varray;
|
2020-12-02 13:25:25 +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
|
|
|
blender::fn::GVArray GeometryComponent::attribute_try_get_for_read(
|
2021-09-09 12:54:20 +02:00
|
|
|
const AttributeIDRef &attribute_id, const AttributeDomain domain) const
|
2020-12-17 07:43:31 -06:00
|
|
|
{
|
|
|
|
|
if (!this->attribute_domain_supported(domain)) {
|
|
|
|
|
return {};
|
|
|
|
|
}
|
|
|
|
|
|
2021-09-09 12:54:20 +02:00
|
|
|
blender::bke::ReadAttributeLookup attribute = this->attribute_try_get_for_read(attribute_id);
|
2020-12-17 07:43:31 -06:00
|
|
|
if (!attribute) {
|
|
|
|
|
return {};
|
|
|
|
|
}
|
|
|
|
|
|
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
|
|
|
if (attribute.domain != domain) {
|
|
|
|
|
return this->attribute_try_adapt_domain(std::move(attribute.varray), attribute.domain, domain);
|
2020-12-17 07:43:31 -06:00
|
|
|
}
|
|
|
|
|
|
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
|
|
|
return std::move(attribute.varray);
|
2020-12-17 07:43:31 -06:00
|
|
|
}
|
|
|
|
|
|
2021-04-21 17:07:00 +02:00
|
|
|
blender::bke::ReadAttributeLookup GeometryComponent::attribute_try_get_for_read(
|
2021-09-09 12:54:20 +02:00
|
|
|
const AttributeIDRef &attribute_id, const CustomDataType data_type) const
|
2021-04-21 17:07:00 +02:00
|
|
|
{
|
2021-09-09 12:54:20 +02:00
|
|
|
blender::bke::ReadAttributeLookup attribute = this->attribute_try_get_for_read(attribute_id);
|
2021-04-21 17:07:00 +02:00
|
|
|
if (!attribute) {
|
|
|
|
|
return {};
|
|
|
|
|
}
|
|
|
|
|
const blender::fn::CPPType *type = blender::bke::custom_data_type_to_cpp_type(data_type);
|
|
|
|
|
BLI_assert(type != 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
|
|
|
if (attribute.varray.type() == *type) {
|
2021-04-21 17:07:00 +02:00
|
|
|
return attribute;
|
|
|
|
|
}
|
2021-12-07 15:21:59 +01:00
|
|
|
const blender::bke::DataTypeConversions &conversions =
|
|
|
|
|
blender::bke::get_implicit_type_conversions();
|
2021-04-21 17:07:00 +02:00
|
|
|
return {conversions.try_convert(std::move(attribute.varray), *type), attribute.domain};
|
|
|
|
|
}
|
|
|
|
|
|
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
|
|
|
blender::fn::GVArray GeometryComponent::attribute_get_for_read(const AttributeIDRef &attribute_id,
|
|
|
|
|
const AttributeDomain domain,
|
|
|
|
|
const CustomDataType data_type,
|
|
|
|
|
const void *default_value) const
|
2020-12-09 16:20:48 +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
|
|
|
blender::fn::GVArray varray = this->attribute_try_get_for_read(attribute_id, domain, data_type);
|
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
|
|
|
if (varray) {
|
|
|
|
|
return varray;
|
|
|
|
|
}
|
|
|
|
|
const blender::fn::CPPType *type = blender::bke::custom_data_type_to_cpp_type(data_type);
|
|
|
|
|
if (default_value == nullptr) {
|
|
|
|
|
default_value = type->default_value();
|
2020-12-09 16:20:48 +01:00
|
|
|
}
|
|
|
|
|
const int domain_size = this->attribute_domain_size(domain);
|
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 blender::fn::GVArray::ForSingle(*type, domain_size, default_value);
|
2020-12-09 16:20:48 +01:00
|
|
|
}
|
|
|
|
|
|
2021-11-26 14:47:02 +01:00
|
|
|
class GVMutableAttribute_For_OutputAttribute : public blender::fn::GVArrayImpl_For_GSpan {
|
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
|
|
|
public:
|
|
|
|
|
GeometryComponent *component;
|
2021-09-09 12:54:20 +02:00
|
|
|
std::string attribute_name;
|
|
|
|
|
blender::bke::WeakAnonymousAttributeID anonymous_attribute_id;
|
2021-01-14 15:52:08 +01:00
|
|
|
|
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
|
|
|
GVMutableAttribute_For_OutputAttribute(GMutableSpan data,
|
|
|
|
|
GeometryComponent &component,
|
2021-09-09 12:54:20 +02:00
|
|
|
const AttributeIDRef &attribute_id)
|
2021-11-26 14:47:02 +01:00
|
|
|
: blender::fn::GVArrayImpl_For_GSpan(data), component(&component)
|
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-09-09 12:54:20 +02:00
|
|
|
if (attribute_id.is_named()) {
|
|
|
|
|
this->attribute_name = attribute_id.name();
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
const AnonymousAttributeID *anonymous_id = &attribute_id.anonymous_id();
|
|
|
|
|
BKE_anonymous_attribute_id_increment_weak(anonymous_id);
|
|
|
|
|
this->anonymous_attribute_id = blender::bke::WeakAnonymousAttributeID{anonymous_id};
|
|
|
|
|
}
|
2020-12-02 13:25:25 +01:00
|
|
|
}
|
|
|
|
|
|
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
|
|
|
~GVMutableAttribute_For_OutputAttribute() override
|
|
|
|
|
{
|
|
|
|
|
type_->destruct_n(data_, size_);
|
|
|
|
|
MEM_freeN(data_);
|
2020-12-02 13:25:25 +01:00
|
|
|
}
|
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-01-14 15:52:08 +01:00
|
|
|
|
2021-09-09 12:54:20 +02:00
|
|
|
static void save_output_attribute(OutputAttribute &output_attribute)
|
2021-01-14 15:52:08 +01:00
|
|
|
{
|
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
|
|
|
using namespace blender;
|
|
|
|
|
using namespace blender::fn;
|
|
|
|
|
using namespace blender::bke;
|
2021-01-14 15:52:08 +01:00
|
|
|
|
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
|
|
|
GVMutableAttribute_For_OutputAttribute &varray =
|
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
|
|
|
dynamic_cast<GVMutableAttribute_For_OutputAttribute &>(
|
|
|
|
|
*output_attribute.varray().get_implementation());
|
2021-01-14 15:52:08 +01:00
|
|
|
|
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
|
|
|
GeometryComponent &component = *varray.component;
|
2021-09-09 12:54:20 +02:00
|
|
|
AttributeIDRef attribute_id;
|
|
|
|
|
if (!varray.attribute_name.empty()) {
|
|
|
|
|
attribute_id = varray.attribute_name;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
attribute_id = varray.anonymous_attribute_id.extract();
|
|
|
|
|
}
|
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
|
|
|
const AttributeDomain domain = output_attribute.domain();
|
|
|
|
|
const CustomDataType data_type = output_attribute.custom_data_type();
|
|
|
|
|
const CPPType &cpp_type = output_attribute.cpp_type();
|
2021-01-21 16:55:44 +01:00
|
|
|
|
2021-09-09 12:54:20 +02:00
|
|
|
component.attribute_try_delete(attribute_id);
|
|
|
|
|
if (!component.attribute_try_create(attribute_id, domain, data_type, AttributeInitDefault())) {
|
|
|
|
|
if (!varray.attribute_name.empty()) {
|
|
|
|
|
CLOG_WARN(&LOG,
|
|
|
|
|
"Could not create the '%s' attribute with type '%s'.",
|
|
|
|
|
varray.attribute_name.c_str(),
|
|
|
|
|
cpp_type.name().c_str());
|
|
|
|
|
}
|
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
|
|
|
return;
|
|
|
|
|
}
|
2021-09-09 12:54:20 +02:00
|
|
|
WriteAttributeLookup write_attribute = component.attribute_try_get_for_write(attribute_id);
|
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
|
|
|
BUFFER_FOR_CPP_TYPE_VALUE(varray.type(), buffer);
|
|
|
|
|
for (const int i : IndexRange(varray.size())) {
|
|
|
|
|
varray.get(i, 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
|
|
|
write_attribute.varray.set_by_relocate(i, buffer);
|
2021-01-21 16:55:44 +01:00
|
|
|
}
|
2021-10-29 09:28:31 +02:00
|
|
|
if (write_attribute.tag_modified_fn) {
|
|
|
|
|
write_attribute.tag_modified_fn();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static std::function<void(OutputAttribute &)> get_simple_output_attribute_save_method(
|
|
|
|
|
const blender::bke::WriteAttributeLookup &attribute)
|
|
|
|
|
{
|
|
|
|
|
if (!attribute.tag_modified_fn) {
|
|
|
|
|
return {};
|
|
|
|
|
}
|
|
|
|
|
return [tag_modified_fn = attribute.tag_modified_fn](OutputAttribute &UNUSED(attribute)) {
|
|
|
|
|
tag_modified_fn();
|
|
|
|
|
};
|
2021-01-14 15:52:08 +01:00
|
|
|
}
|
|
|
|
|
|
2021-09-09 12:54:20 +02:00
|
|
|
static OutputAttribute create_output_attribute(GeometryComponent &component,
|
|
|
|
|
const AttributeIDRef &attribute_id,
|
|
|
|
|
const AttributeDomain domain,
|
|
|
|
|
const CustomDataType data_type,
|
|
|
|
|
const bool ignore_old_values,
|
|
|
|
|
const void *default_value)
|
2021-01-14 15:52:08 +01:00
|
|
|
{
|
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
|
|
|
using namespace blender;
|
|
|
|
|
using namespace blender::fn;
|
|
|
|
|
using namespace blender::bke;
|
2021-01-14 15:52:08 +01:00
|
|
|
|
2021-09-09 12:54:20 +02:00
|
|
|
if (!attribute_id) {
|
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
|
|
|
return {};
|
2020-12-02 13:25:25 +01:00
|
|
|
}
|
2021-01-14 15:52:08 +01:00
|
|
|
|
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
|
|
|
const CPPType *cpp_type = custom_data_type_to_cpp_type(data_type);
|
|
|
|
|
BLI_assert(cpp_type != nullptr);
|
2021-12-07 15:21:59 +01:00
|
|
|
const DataTypeConversions &conversions = get_implicit_type_conversions();
|
2021-01-14 15:52:08 +01:00
|
|
|
|
2021-09-16 11:56:31 -05:00
|
|
|
if (component.attribute_is_builtin(attribute_id)) {
|
2021-09-09 12:54:20 +02:00
|
|
|
const StringRef attribute_name = attribute_id.name();
|
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
|
|
|
WriteAttributeLookup attribute = component.attribute_try_get_for_write(attribute_name);
|
|
|
|
|
if (!attribute) {
|
2021-04-22 09:20:03 -05:00
|
|
|
if (default_value) {
|
|
|
|
|
const int64_t domain_size = component.attribute_domain_size(domain);
|
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
|
|
|
component.attribute_try_create_builtin(
|
|
|
|
|
attribute_name,
|
|
|
|
|
AttributeInitVArray(GVArray::ForSingleRef(*cpp_type, domain_size, default_value)));
|
2021-04-22 09:20:03 -05:00
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
component.attribute_try_create_builtin(attribute_name, AttributeInitDefault());
|
|
|
|
|
}
|
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
|
|
|
attribute = component.attribute_try_get_for_write(attribute_name);
|
|
|
|
|
if (!attribute) {
|
|
|
|
|
/* Builtin attribute does not exist and can't be created. */
|
|
|
|
|
return {};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (attribute.domain != domain) {
|
|
|
|
|
/* Builtin attribute is on different domain. */
|
|
|
|
|
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
|
|
|
GVMutableArray varray = std::move(attribute.varray);
|
|
|
|
|
if (varray.type() == *cpp_type) {
|
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
|
|
|
/* Builtin attribute matches exactly. */
|
2021-10-29 09:28:31 +02:00
|
|
|
return OutputAttribute(std::move(varray),
|
|
|
|
|
domain,
|
|
|
|
|
get_simple_output_attribute_save_method(attribute),
|
|
|
|
|
ignore_old_values);
|
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
|
|
|
}
|
|
|
|
|
/* Builtin attribute is on the same domain but has a different data type. */
|
|
|
|
|
varray = conversions.try_convert(std::move(varray), *cpp_type);
|
2021-10-29 09:28:31 +02:00
|
|
|
return OutputAttribute(std::move(varray),
|
|
|
|
|
domain,
|
|
|
|
|
get_simple_output_attribute_save_method(attribute),
|
|
|
|
|
ignore_old_values);
|
2021-01-14 15:52:08 +01:00
|
|
|
}
|
|
|
|
|
|
2021-04-22 09:20:03 -05:00
|
|
|
const int domain_size = component.attribute_domain_size(domain);
|
|
|
|
|
|
2021-09-09 12:54:20 +02:00
|
|
|
WriteAttributeLookup attribute = component.attribute_try_get_for_write(attribute_id);
|
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
|
|
|
if (!attribute) {
|
2021-04-22 09:20:03 -05:00
|
|
|
if (default_value) {
|
|
|
|
|
component.attribute_try_create(
|
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
|
|
|
attribute_id,
|
|
|
|
|
domain,
|
|
|
|
|
data_type,
|
|
|
|
|
AttributeInitVArray(GVArray::ForSingleRef(*cpp_type, domain_size, default_value)));
|
2021-04-22 09:20:03 -05:00
|
|
|
}
|
|
|
|
|
else {
|
2021-09-09 12:54:20 +02:00
|
|
|
component.attribute_try_create(attribute_id, domain, data_type, AttributeInitDefault());
|
2021-04-22 09:20:03 -05:00
|
|
|
}
|
|
|
|
|
|
2021-09-09 12:54:20 +02:00
|
|
|
attribute = component.attribute_try_get_for_write(attribute_id);
|
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
|
|
|
if (!attribute) {
|
|
|
|
|
/* Can't create the attribute. */
|
|
|
|
|
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
|
|
|
if (attribute.domain == domain && attribute.varray.type() == *cpp_type) {
|
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
|
|
|
/* Existing generic attribute matches exactly. */
|
2021-10-29 09:28:31 +02:00
|
|
|
|
|
|
|
|
return OutputAttribute(std::move(attribute.varray),
|
|
|
|
|
domain,
|
|
|
|
|
get_simple_output_attribute_save_method(attribute),
|
|
|
|
|
ignore_old_values);
|
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-01-14 15:52:08 +01:00
|
|
|
|
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
|
|
|
/* Allocate a new array that lives next to the existing attribute. It will overwrite the existing
|
|
|
|
|
* attribute after processing is done. */
|
|
|
|
|
void *data = MEM_mallocN_aligned(
|
|
|
|
|
cpp_type->size() * domain_size, cpp_type->alignment(), __func__);
|
|
|
|
|
if (ignore_old_values) {
|
|
|
|
|
/* This does nothing for trivially constructible types, but is necessary for correctness. */
|
2021-06-28 13:13:52 +02:00
|
|
|
cpp_type->default_construct_n(data, domain);
|
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
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
/* Fill the temporary array with values from the existing attribute. */
|
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
|
|
|
GVArray old_varray = component.attribute_get_for_read(
|
2021-09-09 12:54:20 +02:00
|
|
|
attribute_id, domain, data_type, default_value);
|
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
|
|
|
old_varray.materialize_to_uninitialized(IndexRange(domain_size), data);
|
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
|
|
|
}
|
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
|
|
|
GVMutableArray varray = GVMutableArray::For<GVMutableAttribute_For_OutputAttribute>(
|
2021-09-09 12:54:20 +02:00
|
|
|
GMutableSpan{*cpp_type, data, domain_size}, component, attribute_id);
|
2021-01-14 15:52:08 +01:00
|
|
|
|
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
|
|
|
return OutputAttribute(std::move(varray), domain, save_output_attribute, true);
|
2021-01-14 15:52:08 +01:00
|
|
|
}
|
|
|
|
|
|
2021-09-09 12:54:20 +02:00
|
|
|
OutputAttribute GeometryComponent::attribute_try_get_for_output(const AttributeIDRef &attribute_id,
|
|
|
|
|
const AttributeDomain domain,
|
|
|
|
|
const CustomDataType data_type,
|
|
|
|
|
const void *default_value)
|
2021-01-14 15:52:08 +01:00
|
|
|
{
|
2021-09-09 12:54:20 +02:00
|
|
|
return create_output_attribute(*this, attribute_id, domain, data_type, false, default_value);
|
2021-01-14 15:52:08 +01:00
|
|
|
}
|
|
|
|
|
|
2021-09-09 12:54:20 +02:00
|
|
|
OutputAttribute GeometryComponent::attribute_try_get_for_output_only(
|
|
|
|
|
const AttributeIDRef &attribute_id,
|
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
|
|
|
const AttributeDomain domain,
|
|
|
|
|
const CustomDataType data_type)
|
2021-01-14 15:52:08 +01:00
|
|
|
{
|
2021-09-09 12:54:20 +02:00
|
|
|
return create_output_attribute(*this, attribute_id, domain, data_type, true, nullptr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
namespace blender::bke {
|
|
|
|
|
|
2021-12-06 19:05:29 +01:00
|
|
|
GVArray GeometryFieldInput::get_varray_for_context(const fn::FieldContext &context,
|
|
|
|
|
IndexMask mask,
|
|
|
|
|
ResourceScope &UNUSED(scope)) const
|
2021-09-09 12:54:20 +02:00
|
|
|
{
|
|
|
|
|
if (const GeometryComponentFieldContext *geometry_context =
|
|
|
|
|
dynamic_cast<const GeometryComponentFieldContext *>(&context)) {
|
|
|
|
|
const GeometryComponent &component = geometry_context->geometry_component();
|
|
|
|
|
const AttributeDomain domain = geometry_context->domain();
|
2021-12-06 19:05:29 +01:00
|
|
|
return this->get_varray_for_context(component, domain, mask);
|
2021-09-09 12:54: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 {};
|
2021-09-09 12:54:20 +02:00
|
|
|
}
|
|
|
|
|
|
2021-12-06 19:05:29 +01:00
|
|
|
GVArray AttributeFieldInput::get_varray_for_context(const GeometryComponent &component,
|
|
|
|
|
const AttributeDomain domain,
|
|
|
|
|
IndexMask UNUSED(mask)) const
|
|
|
|
|
{
|
|
|
|
|
const CustomDataType data_type = cpp_type_to_custom_data_type(*type_);
|
|
|
|
|
return component.attribute_try_get_for_read(name_, domain, data_type);
|
|
|
|
|
}
|
|
|
|
|
|
2021-09-11 13:05:20 +02:00
|
|
|
std::string AttributeFieldInput::socket_inspection_name() const
|
|
|
|
|
{
|
|
|
|
|
std::stringstream ss;
|
2021-10-26 15:32:01 +02:00
|
|
|
ss << '"' << name_ << '"' << TIP_(" attribute from geometry");
|
2021-09-11 13:05:20 +02:00
|
|
|
return ss.str();
|
|
|
|
|
}
|
|
|
|
|
|
2021-09-09 12:54:20 +02:00
|
|
|
uint64_t AttributeFieldInput::hash() const
|
|
|
|
|
{
|
|
|
|
|
return get_default_hash_2(name_, type_);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool AttributeFieldInput::is_equal_to(const fn::FieldNode &other) const
|
|
|
|
|
{
|
|
|
|
|
if (const AttributeFieldInput *other_typed = dynamic_cast<const AttributeFieldInput *>(&other)) {
|
|
|
|
|
return name_ == other_typed->name_ && type_ == other_typed->type_;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2021-10-20 10:54:54 -05:00
|
|
|
static StringRef get_random_id_attribute_name(const AttributeDomain domain)
|
|
|
|
|
{
|
|
|
|
|
switch (domain) {
|
|
|
|
|
case ATTR_DOMAIN_POINT:
|
2021-11-19 17:53:48 +01:00
|
|
|
case ATTR_DOMAIN_INSTANCE:
|
2021-10-20 10:54:54 -05:00
|
|
|
return "id";
|
|
|
|
|
default:
|
|
|
|
|
return "";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-06 19:05:29 +01:00
|
|
|
GVArray IDAttributeFieldInput::get_varray_for_context(const GeometryComponent &component,
|
|
|
|
|
const AttributeDomain domain,
|
|
|
|
|
IndexMask mask) const
|
2021-10-20 10:54:54 -05:00
|
|
|
{
|
|
|
|
|
|
2021-12-06 19:05:29 +01:00
|
|
|
const StringRef name = get_random_id_attribute_name(domain);
|
|
|
|
|
GVArray attribute = component.attribute_try_get_for_read(name, domain, CD_PROP_INT32);
|
|
|
|
|
if (attribute) {
|
|
|
|
|
BLI_assert(attribute.size() == component.attribute_domain_size(domain));
|
|
|
|
|
return attribute;
|
2021-10-20 10:54:54 -05:00
|
|
|
}
|
2021-12-06 19:05:29 +01:00
|
|
|
|
|
|
|
|
/* Use the index as the fallback if no random ID attribute exists. */
|
|
|
|
|
return fn::IndexFieldInput::get_index_varray(mask);
|
2021-10-20 10:54:54 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::string IDAttributeFieldInput::socket_inspection_name() const
|
|
|
|
|
{
|
|
|
|
|
return TIP_("ID / Index");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint64_t IDAttributeFieldInput::hash() const
|
|
|
|
|
{
|
|
|
|
|
/* All random ID attribute inputs are the same within the same evaluation context. */
|
|
|
|
|
return 92386459827;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool IDAttributeFieldInput::is_equal_to(const fn::FieldNode &other) const
|
|
|
|
|
{
|
|
|
|
|
/* All random ID attribute inputs are the same within the same evaluation context. */
|
|
|
|
|
return dynamic_cast<const IDAttributeFieldInput *>(&other) != nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-06 19:05:29 +01:00
|
|
|
GVArray AnonymousAttributeFieldInput::get_varray_for_context(const GeometryComponent &component,
|
|
|
|
|
const AttributeDomain domain,
|
|
|
|
|
IndexMask UNUSED(mask)) const
|
2021-09-09 12:54:20 +02:00
|
|
|
{
|
2021-12-06 19:05:29 +01:00
|
|
|
const CustomDataType data_type = cpp_type_to_custom_data_type(*type_);
|
|
|
|
|
return component.attribute_try_get_for_read(anonymous_id_.get(), domain, data_type);
|
2021-09-09 12:54:20 +02:00
|
|
|
}
|
|
|
|
|
|
2021-09-11 13:05:20 +02:00
|
|
|
std::string AnonymousAttributeFieldInput::socket_inspection_name() const
|
|
|
|
|
{
|
|
|
|
|
std::stringstream ss;
|
2021-10-26 15:32:01 +02:00
|
|
|
ss << '"' << debug_name_ << '"' << TIP_(" from ") << producer_name_;
|
2021-09-11 13:05:20 +02:00
|
|
|
return ss.str();
|
|
|
|
|
}
|
|
|
|
|
|
2021-09-09 12:54:20 +02:00
|
|
|
uint64_t AnonymousAttributeFieldInput::hash() const
|
|
|
|
|
{
|
|
|
|
|
return get_default_hash_2(anonymous_id_.get(), type_);
|
2020-12-02 13:25:25 +01:00
|
|
|
}
|
2021-09-09 12:54:20 +02:00
|
|
|
|
|
|
|
|
bool AnonymousAttributeFieldInput::is_equal_to(const fn::FieldNode &other) const
|
|
|
|
|
{
|
|
|
|
|
if (const AnonymousAttributeFieldInput *other_typed =
|
|
|
|
|
dynamic_cast<const AnonymousAttributeFieldInput *>(&other)) {
|
|
|
|
|
return anonymous_id_.get() == other_typed->anonymous_id_.get() && type_ == other_typed->type_;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace blender::bke
|
2021-12-14 15:49:31 +11:00
|
|
|
|
|
|
|
|
/** \} */
|