1
1

Compare commits

...

90 Commits

Author SHA1 Message Date
fb0d5124f2 support anonymous attributes in foreach_attribute 2021-08-30 16:02:58 +02:00
6146a679c9 support creating anonymous attributes 2021-08-30 14:24:18 +02:00
65a1ec89ba Merge branch 'temp-geometry-nodes-fields' into temp-geometry-nodes-fields--anonymous-attributes 2021-08-24 17:56:36 +02:00
ef9fbf258b use attribute id in more places 2021-08-24 17:53:15 +02:00
a448949f25 more uses of attribute id 2021-08-24 17:30:56 +02:00
e3232f987a initial attribute id ref 2021-08-24 17:11:24 +02:00
7ebc3140bb Merge branch 'master' into temp-geometry-nodes-fields 2021-08-23 15:12:04 -05:00
c42ceef040 initial anonymous attributes implementation 2021-08-23 18:40:29 +02:00
acc2e8afa9 Merge branch 'master' into temp-geometry-nodes-fields 2021-08-23 15:58:16 +02:00
04bb1bda32 fix comment 2021-08-23 15:57:50 +02:00
eed93aaa07 make dot output more compact 2021-08-22 22:34:25 +02:00
fc0bb6cdee avoid allocating index array in some cases 2021-08-22 20:29:52 +02:00
10f2ad1556 add return instruction and initial procedure validation 2021-08-22 20:16:27 +02:00
6d77b87b13 support span buffer reuse 2021-08-22 15:07:23 +02:00
2101b46802 fix comment 2021-08-22 12:49:22 +02:00
34f6765630 Merge branch 'master' into temp-multi-function-procedure 2021-08-22 12:21:52 +02:00
c58d1acba8 improve naming 2021-08-20 14:29:01 +02:00
0ee79f304e cleanup 2021-08-20 13:38:38 +02:00
e642de3d6f Merge branch 'master' into temp-multi-function-procedure 2021-08-20 13:37:32 +02:00
eca5a8b695 Merge branch 'master' into temp-multi-function-procedure 2021-08-20 12:48:18 +02:00
79c79f3c70 cleanup 2021-08-20 11:37:23 +02:00
a9970d3cb9 bring back clamping in math node 2021-08-20 10:44:29 +02:00
b812f289f5 Merge branch 'master' into mf-procedure 2021-08-20 10:36:04 +02:00
215ce0fb57 fix 2021-08-19 18:19:15 +02:00
5154598845 Merge branch 'master' into mf-procedure 2021-08-19 18:11:51 +02:00
1ee80d792c cleanup 2021-08-19 18:09:52 +02:00
95284d2f1e bring back function nodes 2021-08-19 18:07:36 +02:00
7281f3eb56 start bringing back function nodes 2021-08-19 17:28:15 +02:00
607ef8f6c5 pull out multi function network 2021-08-19 16:49:00 +02:00
1ce640cc0b test vector processing 2021-08-19 16:24:32 +02:00
7bed18fdb1 support creating loops with builder 2021-08-19 15:33:35 +02:00
c827b50d40 add dummy instruction type 2021-08-19 14:06:43 +02:00
3c7e3c8e44 improve naming 2021-08-19 13:46:19 +02:00
98e38ce4f3 cleanup 2021-08-19 13:44:15 +02:00
132cf268c0 add comments 2021-08-19 13:36:52 +02:00
fd7edc9b05 cleanup 2021-08-19 13:28:15 +02:00
ecf7c90840 remove redundant utilties 2021-08-19 13:15:11 +02:00
d78a530af1 initial procedure builder 2021-08-19 13:09:41 +02:00
3ebe61db9f support evaluation on one 2021-08-19 11:22:57 +02:00
86c2f139c6 cleanup 2021-08-19 10:09:02 +02:00
3596c348eb cleanup 2021-08-19 10:07:51 +02:00
41a81474e4 refactor procedure executor 2021-08-18 20:19:12 +02:00
aa2822d137 cleanup 2021-08-18 20:18:19 +02:00
55b333d3e3 Merge branch 'master' into mf-procedure 2021-08-18 16:14:32 +02:00
00cfad8578 add utility method 2021-08-18 10:00:51 +02:00
1891c956e5 refactor variable store 2021-08-17 17:24:01 +02:00
249c050757 add single test 2021-08-17 15:27:46 +02:00
a0081046b6 cleanup instruction scheduling 2021-08-17 15:01:27 +02:00
635f73b7f1 fixes after merge 2021-08-17 14:00:06 +02:00
74fcd50e2f Merge branch 'master' into mf-procedure 2021-08-17 13:44:25 +02:00
b04a2a7be7 add utility 2021-06-13 14:42:22 +02:00
083671e8ac progress 2021-06-13 14:25:23 +02:00
78ea401e19 Merge branch 'master' into mf-procedure 2021-06-13 14:13:22 +02:00
f3ca987bce start constructing procedure from node tree 2021-06-11 13:39:38 +02:00
2245add9f8 Merge branch 'master' into mf-procedure 2021-06-11 12:59:12 +02:00
31004d7fac start with creating procedure for node tree 2021-05-31 10:51:34 +02:00
3d3f66ed41 fix merge conflicts 2021-05-29 12:14:48 +02:00
c9c0195da5 Merge branch 'master' into mf-procedure 2021-05-29 12:07:13 +02:00
70c0403858 fixes 2021-03-27 22:58:44 +01:00
8d4de82c7f initial network to procedure 2021-03-27 22:30:53 +01:00
22c51c2d51 cleanup 2021-03-27 21:22:28 +01:00
158bd7c6a0 cleanup 2021-03-27 21:18:48 +01:00
4a28d0b583 another check 2021-03-27 15:52:13 +01:00
0501e6e693 fix memory leak in test 2021-03-27 15:45:59 +01:00
8450ac09c1 comment containing things to check 2021-03-27 15:36:08 +01:00
1af00015e8 initial destruct support 2021-03-27 15:29:02 +01:00
b44c3a3125 count initializations 2021-03-27 15:12:36 +01:00
d729f1ca37 cleanup 2021-03-27 15:08:21 +01:00
0fc9f00c14 start extracting container 2021-03-27 14:53:39 +01:00
6c9b339af7 refactor variable store 2021-03-27 14:46:53 +01:00
b7a976af01 branch test 2021-03-27 14:07:23 +01:00
a689037917 initial branch instruction 2021-03-27 13:48:18 +01:00
313403c1f1 support mutable params 2021-03-27 13:33:42 +01:00
60409b8823 simplify 2021-03-27 13:19:46 +01:00
773dc2ec94 improve dot graph 2021-03-27 13:14:19 +01:00
2a98c5d06b initial execution 2021-03-27 13:06:50 +01:00
6d1b4ce3c6 simplify 2021-03-27 12:06:40 +01:00
0b2d961b70 move executor to separate file 2021-03-27 11:55:51 +01:00
d553b70470 Merge branch 'master' into mf-procedure 2021-03-27 11:52:12 +01:00
6954f2cdd7 dot export 2021-03-24 18:46:17 +01:00
8cc832110a more 2021-03-24 18:23:36 +01:00
7b8c54b5a1 more 2021-03-24 17:51:37 +01:00
e850d175b5 more 2021-03-24 17:42:18 +01:00
326f79d59b more 2021-03-24 17:39:22 +01:00
ec4954ece2 destructor 2021-03-24 17:34:14 +01:00
b30e782c82 more stuff 2021-03-24 17:30:53 +01:00
e34fe5d28e add destruct instruction 2021-03-24 16:49:52 +01:00
8581a062f1 Merge branch 'master' into mf-procedure 2021-03-24 16:47:48 +01:00
b43971e5e9 add executor class 2021-03-23 16:20:40 +01:00
855382170e initial mf procedure data structure 2021-03-23 16:18:23 +01:00
40 changed files with 4073 additions and 384 deletions

View File

@@ -0,0 +1,37 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
typedef struct AnonymousAttributeID AnonymousAttributeID;
AnonymousAttributeID *BKE_anonymous_attribute_id_new_weak(const char *debug_name);
AnonymousAttributeID *BKE_anonymous_attribute_id_new_strong(const char *debug_name);
bool BKE_anonymous_attribute_id_has_strong_references(const AnonymousAttributeID *anonymous_id);
void BKE_anonymous_attribute_id_increment_weak(const AnonymousAttributeID *anonymous_id);
void BKE_anonymous_attribute_id_increment_strong(const AnonymousAttributeID *anonymous_id);
void BKE_anonymous_attribute_id_decrement_weak(const AnonymousAttributeID *anonymous_id);
void BKE_anonymous_attribute_id_decrement_strong(const AnonymousAttributeID *anonymous_id);
const char *BKE_anonymous_attribute_id_debug_name(const AnonymousAttributeID *anonymous_id);
const char *BKE_anonymous_attribute_id_internal_name(const AnonymousAttributeID *anonymous_id);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,223 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#pragma once
#include <atomic>
#include <string>
#include "BLI_hash.hh"
#include "BLI_string_ref.hh"
#include "BKE_anonymous_attribute.h"
namespace blender::bke {
template<bool IsStrongReference> class OwnedAnonymousAttributeID {
private:
const AnonymousAttributeID *data_ = nullptr;
public:
OwnedAnonymousAttributeID() = default;
explicit OwnedAnonymousAttributeID(StringRefNull debug_name)
{
if constexpr (IsStrongReference) {
data_ = BKE_anonymous_attribute_id_new_strong(debug_name.c_str());
}
else {
data_ = BKE_anonymous_attribute_id_new_weak(debug_name.c_str());
}
}
/* This transfers ownership, so no incref is necessary. */
explicit OwnedAnonymousAttributeID(const AnonymousAttributeID *anonymous_id)
: data_(anonymous_id)
{
}
template<bool OtherIsStrong>
OwnedAnonymousAttributeID(const OwnedAnonymousAttributeID<OtherIsStrong> &other)
{
data_ = other.data_;
this->incref();
}
template<bool OtherIsStrong>
OwnedAnonymousAttributeID(OwnedAnonymousAttributeID<OtherIsStrong> &&other)
{
data_ = other.data_;
this->incref();
other.decref();
other.data_ = nullptr;
}
~OwnedAnonymousAttributeID()
{
this->decref();
}
template<bool OtherIsStrong>
OwnedAnonymousAttributeID &operator=(const OwnedAnonymousAttributeID<OtherIsStrong> &other)
{
if (this == &other) {
return *this;
}
this->~OwnedAnonymousAttributeID();
new (this) OwnedAnonymousAttributeID(other);
return *this;
}
template<bool OtherIsStrong>
OwnedAnonymousAttributeID &operator=(OwnedAnonymousAttributeID<OtherIsStrong> &&other)
{
if (this == &other) {
return *this;
}
this->~OwnedAnonymousAttributeID();
new (this) OwnedAnonymousAttributeID(std::move(other));
return *this;
}
operator bool() const
{
return data_ != nullptr;
}
StringRefNull debug_name() const
{
BLI_assert(data_ != nullptr);
return BKE_anonymous_attribute_id_debug_name(data_);
}
bool has_strong_references() const
{
BLI_assert(data_ != nullptr);
return BKE_anonymous_attribute_id_has_strong_references(data_);
}
const AnonymousAttributeID *extract()
{
const AnonymousAttributeID *extracted_data = data_;
/* Don't decref because the caller becomes the new owner. */
data_ = nullptr;
return extracted_data;
}
const AnonymousAttributeID *get()
{
return data_;
}
private:
void incref()
{
if (data_ == nullptr) {
return;
}
if constexpr (IsStrongReference) {
BKE_anonymous_attribute_id_increment_strong(data_);
}
else {
BKE_anonymous_attribute_id_increment_weak(data_);
}
}
void decref()
{
if (data_ == nullptr) {
return;
}
if constexpr (IsStrongReference) {
BKE_anonymous_attribute_id_decrement_strong(data_);
}
else {
BKE_anonymous_attribute_id_decrement_weak(data_);
}
}
};
using StrongAnonymousAttributeID = OwnedAnonymousAttributeID<true>;
using WeakAnonymousAttributeID = OwnedAnonymousAttributeID<false>;
class AttributeIDRef {
private:
StringRef name_;
const AnonymousAttributeID *anonymous_id_ = nullptr;
public:
AttributeIDRef() = default;
AttributeIDRef(StringRef name) : name_(name)
{
}
AttributeIDRef(StringRefNull name) : name_(name)
{
}
AttributeIDRef(const char *name) : name_(name)
{
}
AttributeIDRef(const std::string &name) : name_(name)
{
}
/* The anonymous id is only borrowed, the caller has to keep a reference to it. */
AttributeIDRef(const AnonymousAttributeID *anonymous_id) : anonymous_id_(anonymous_id)
{
}
operator bool() const
{
return this->is_named() || this->is_anonymous();
}
friend bool operator==(const AttributeIDRef &a, const AttributeIDRef &b)
{
return a.anonymous_id_ == b.anonymous_id_ && a.name_ == b.name_;
}
uint64_t hash() const
{
return get_default_hash_2(name_, anonymous_id_);
}
bool is_named() const
{
return !name_.is_empty();
}
bool is_anonymous() const
{
return anonymous_id_ != nullptr;
}
StringRef name() const
{
BLI_assert(this->is_named());
return name_;
}
const AnonymousAttributeID &anonymous_id() const
{
BLI_assert(this->is_anonymous());
return *anonymous_id_;
}
};
} // namespace blender::bke

View File

@@ -22,6 +22,7 @@
#include "FN_generic_span.hh"
#include "FN_generic_virtual_array.hh"
#include "BKE_anonymous_attribute.hh"
#include "BKE_attribute.h"
#include "BLI_color.hh"
@@ -104,8 +105,8 @@ struct AttributeInitMove : public AttributeInit {
};
/* Returns false when the iteration should be stopped. */
using AttributeForeachCallback = blender::FunctionRef<bool(blender::StringRefNull attribute_name,
const AttributeMetaData &meta_data)>;
using AttributeForeachCallback = blender::FunctionRef<bool(
const blender::bke::AttributeIDRef &attribute_id, const AttributeMetaData &meta_data)>;
namespace blender::bke {
@@ -333,26 +334,30 @@ class CustomDataAttributes {
void reallocate(const int size);
std::optional<blender::fn::GSpan> get_for_read(const blender::StringRef name) const;
std::optional<blender::fn::GSpan> get_for_read(
const blender::bke::AttributeIDRef &attribute_id) const;
blender::fn::GVArrayPtr get_for_read(const StringRef name,
blender::fn::GVArrayPtr get_for_read(const AttributeIDRef &attribute_id,
const CustomDataType data_type,
const void *default_value) const;
template<typename T>
blender::fn::GVArray_Typed<T> get_for_read(const blender::StringRef name,
blender::fn::GVArray_Typed<T> get_for_read(const blender::bke::AttributeIDRef &attribute_id,
const T &default_value) const
{
const blender::fn::CPPType &cpp_type = blender::fn::CPPType::get<T>();
const CustomDataType type = blender::bke::cpp_type_to_custom_data_type(cpp_type);
GVArrayPtr varray = this->get_for_read(name, type, &default_value);
GVArrayPtr varray = this->get_for_read(attribute_id, type, &default_value);
return blender::fn::GVArray_Typed<T>(std::move(varray));
}
std::optional<blender::fn::GMutableSpan> get_for_write(const blender::StringRef name);
bool create(const blender::StringRef name, const CustomDataType data_type);
bool create_by_move(const blender::StringRef name, const CustomDataType data_type, void *buffer);
bool remove(const blender::StringRef name);
std::optional<blender::fn::GMutableSpan> get_for_write(
const blender::bke::AttributeIDRef &attribute_id);
bool create(const blender::bke::AttributeIDRef &attribute_id, const CustomDataType data_type);
bool create_by_move(const blender::bke::AttributeIDRef &attribute_id,
const CustomDataType data_type,
void *buffer);
bool remove(const blender::bke::AttributeIDRef &attribute_id);
bool foreach_attribute(const AttributeForeachCallback callback,
const AttributeDomain domain) const;

View File

@@ -33,6 +33,7 @@
extern "C" {
#endif
struct AnonymousAttributeID;
struct BMesh;
struct BlendDataReader;
struct BlendWriter;
@@ -193,6 +194,12 @@ void *CustomData_add_layer_named(struct CustomData *data,
void *layer,
int totelem,
const char *name);
void *CustomData_add_layer_anonymous(struct CustomData *data,
int type,
eCDAllocType alloctype,
void *layer,
int totelem,
const struct AnonymousAttributeID *anonymous_id);
/* frees the active or first data layer with the give type.
* returns 1 on success, 0 if no layer with the given type is found
@@ -231,6 +238,11 @@ void *CustomData_duplicate_referenced_layer_named(struct CustomData *data,
const int type,
const char *name,
const int totelem);
void *CustomData_duplicate_referenced_layer_anonymous(
CustomData *data,
const int type,
const struct AnonymousAttributeID *anonymous_id,
const int totelem);
bool CustomData_is_referenced_layer(struct CustomData *data, int type);
/* Duplicate all the layers with flag NOFREE, and remove the flag from duplicated layers. */

View File

@@ -31,6 +31,7 @@
#include "BLI_user_counter.hh"
#include "BLI_vector_set.hh"
#include "BKE_anonymous_attribute.hh"
#include "BKE_attribute_access.hh"
#include "BKE_geometry_set.h"
@@ -88,11 +89,11 @@ class GeometryComponent {
GeometryComponentType type() const;
/* Return true when any attribute with this name exists, including built in attributes. */
bool attribute_exists(const blender::StringRef attribute_name) const;
bool attribute_exists(const blender::bke::AttributeIDRef &attribute_id) const;
/* Return the data type and domain of an attribute with the given name if it exists. */
std::optional<AttributeMetaData> attribute_get_meta_data(
const blender::StringRef attribute_name) const;
const blender::bke::AttributeIDRef &attribute_id) const;
/* Returns true when the geometry component supports this attribute domain. */
bool attribute_domain_supported(const AttributeDomain domain) const;
@@ -104,12 +105,12 @@ class GeometryComponent {
/* Get read-only access to the highest priority attribute with the given name.
* Returns null if the attribute does not exist. */
blender::bke::ReadAttributeLookup attribute_try_get_for_read(
const blender::StringRef attribute_name) const;
const blender::bke::AttributeIDRef &attribute_id) const;
/* Get read and write access to the highest priority attribute with the given name.
* Returns null if the attribute does not exist. */
blender::bke::WriteAttributeLookup attribute_try_get_for_write(
const blender::StringRef attribute_name);
const blender::bke::AttributeIDRef &attribute_id);
/* Get a read-only attribute for the domain based on the given attribute. This can be used to
* interpolate from one domain to another.
@@ -120,10 +121,10 @@ class GeometryComponent {
const AttributeDomain to_domain) const;
/* Returns true when the attribute has been deleted. */
bool attribute_try_delete(const blender::StringRef attribute_name);
bool attribute_try_delete(const blender::bke::AttributeIDRef &attribute_id);
/* Returns true when the attribute has been created. */
bool attribute_try_create(const blender::StringRef attribute_name,
bool attribute_try_create(const blender::bke::AttributeIDRef &attribute_id,
const AttributeDomain domain,
const CustomDataType data_type,
const AttributeInit &initializer);
@@ -133,7 +134,7 @@ class GeometryComponent {
bool attribute_try_create_builtin(const blender::StringRef attribute_name,
const AttributeInit &initializer);
blender::Set<std::string> attribute_names() const;
blender::Set<blender::bke::AttributeIDRef> attribute_ids() const;
bool attribute_foreach(const AttributeForeachCallback callback) const;
virtual bool is_empty() const;
@@ -142,7 +143,7 @@ class GeometryComponent {
* Returns null when the attribute does not exist or cannot be converted to the requested domain
* and data type. */
std::unique_ptr<blender::fn::GVArray> attribute_try_get_for_read(
const blender::StringRef attribute_name,
const blender::bke::AttributeIDRef &attribute_id,
const AttributeDomain domain,
const CustomDataType data_type) const;
@@ -150,18 +151,18 @@ class GeometryComponent {
* left unchanged. Returns null when the attribute does not exist or cannot be adapted to the
* requested domain. */
std::unique_ptr<blender::fn::GVArray> attribute_try_get_for_read(
const blender::StringRef attribute_name, const AttributeDomain domain) const;
const blender::bke::AttributeIDRef &attribute_id, const AttributeDomain domain) const;
/* Get a virtual array to read data of an attribute with the given data type. The domain is
* left unchanged. Returns null when the attribute does not exist or cannot be converted to the
* requested data type. */
blender::bke::ReadAttributeLookup attribute_try_get_for_read(
const blender::StringRef attribute_name, const CustomDataType data_type) const;
const blender::bke::AttributeIDRef &attribute_id, const CustomDataType data_type) const;
/* Get a virtual array to read the data of an attribute. If that is not possible, the returned
* virtual array will contain a default value. This never returns null. */
std::unique_ptr<blender::fn::GVArray> attribute_get_for_read(
const blender::StringRef attribute_name,
const blender::bke::AttributeIDRef &attribute_id,
const AttributeDomain domain,
const CustomDataType data_type,
const void *default_value = nullptr) const;
@@ -169,14 +170,15 @@ class GeometryComponent {
/* Should be used instead of the method above when the requested data type is known at compile
* time for better type safety. */
template<typename T>
blender::fn::GVArray_Typed<T> attribute_get_for_read(const blender::StringRef attribute_name,
const AttributeDomain domain,
const T &default_value) const
blender::fn::GVArray_Typed<T> attribute_get_for_read(
const blender::bke::AttributeIDRef &attribute_id,
const AttributeDomain domain,
const T &default_value) const
{
const blender::fn::CPPType &cpp_type = blender::fn::CPPType::get<T>();
const CustomDataType type = blender::bke::cpp_type_to_custom_data_type(cpp_type);
std::unique_ptr varray = this->attribute_get_for_read(
attribute_name, domain, type, &default_value);
attribute_id, domain, type, &default_value);
return blender::fn::GVArray_Typed<T>(std::move(varray));
}
@@ -191,7 +193,7 @@ class GeometryComponent {
* is created that will overwrite the existing attribute in the end.
*/
blender::bke::OutputAttribute attribute_try_get_for_output(
const blender::StringRef attribute_name,
const blender::bke::AttributeIDRef &attribute_id,
const AttributeDomain domain,
const CustomDataType data_type,
const void *default_value = nullptr);
@@ -200,28 +202,30 @@ class GeometryComponent {
* attributes are not read, i.e. the attribute is used only for output. Since values are not read
* from this attribute, no default value is necessary. */
blender::bke::OutputAttribute attribute_try_get_for_output_only(
const blender::StringRef attribute_name,
const blender::bke::AttributeIDRef &attribute_id,
const AttributeDomain domain,
const CustomDataType data_type);
/* Statically typed method corresponding to the equally named generic one. */
template<typename T>
blender::bke::OutputAttribute_Typed<T> attribute_try_get_for_output(
const blender::StringRef attribute_name, const AttributeDomain domain, const T default_value)
const blender::bke::AttributeIDRef &attribute_id,
const AttributeDomain domain,
const T default_value)
{
const blender::fn::CPPType &cpp_type = blender::fn::CPPType::get<T>();
const CustomDataType data_type = blender::bke::cpp_type_to_custom_data_type(cpp_type);
return this->attribute_try_get_for_output(attribute_name, domain, data_type, &default_value);
return this->attribute_try_get_for_output(attribute_id, domain, data_type, &default_value);
}
/* Statically typed method corresponding to the equally named generic one. */
template<typename T>
blender::bke::OutputAttribute_Typed<T> attribute_try_get_for_output_only(
const blender::StringRef attribute_name, const AttributeDomain domain)
const blender::bke::AttributeIDRef &attribute_id, const AttributeDomain domain)
{
const blender::fn::CPPType &cpp_type = blender::fn::CPPType::get<T>();
const CustomDataType data_type = blender::bke::cpp_type_to_custom_data_type(cpp_type);
return this->attribute_try_get_for_output_only(attribute_name, domain, data_type);
return this->attribute_try_get_for_output_only(attribute_id, domain, data_type);
}
private:

View File

@@ -59,9 +59,10 @@ struct AttributeKind {
* will contain the highest complexity data type and the highest priority domain among every
* attribute with the given name on all of the input components.
*/
void geometry_set_gather_instances_attribute_info(Span<GeometryInstanceGroup> set_groups,
Span<GeometryComponentType> component_types,
const Set<std::string> &ignored_attributes,
Map<std::string, AttributeKind> &r_attributes);
void geometry_set_gather_instances_attribute_info(
Span<GeometryInstanceGroup> set_groups,
Span<GeometryComponentType> component_types,
const Set<std::string> &ignored_attributes,
Map<AttributeIDRef, AttributeKind> &r_attributes);
} // namespace blender::bke

View File

@@ -76,6 +76,7 @@ set(SRC
intern/anim_path.c
intern/anim_sys.c
intern/anim_visualization.c
intern/anonymous_attribute.cc
intern/appdir.c
intern/armature.c
intern/armature_selection.cc
@@ -295,6 +296,8 @@ set(SRC
BKE_anim_path.h
BKE_anim_visualization.h
BKE_animsys.h
BKE_anonymous_attribute.h
BKE_anonymous_attribute.hh
BKE_appdir.h
BKE_armature.h
BKE_armature.hh

View File

@@ -0,0 +1,92 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "BKE_anonymous_attribute.hh"
using namespace blender::bke;
struct AnonymousAttributeID {
mutable std::atomic<int> refcount_weak = 0;
mutable std::atomic<int> refcount_strong = 0;
std::string debug_name;
std::string internal_name;
};
static std::string get_new_internal_name()
{
static std::atomic<int> index = 0;
const int next_index = index.fetch_add(1);
return "anonymous_attribute_" + std::to_string(next_index);
}
AnonymousAttributeID *BKE_anonymous_attribute_id_new_weak(const char *debug_name)
{
AnonymousAttributeID *anonymous_id = new AnonymousAttributeID();
anonymous_id->debug_name = debug_name;
anonymous_id->internal_name = get_new_internal_name();
anonymous_id->refcount_weak.store(1);
return anonymous_id;
}
AnonymousAttributeID *BKE_anonymous_attribute_id_new_strong(const char *debug_name)
{
AnonymousAttributeID *anonymous_id = new AnonymousAttributeID();
anonymous_id->debug_name = debug_name;
anonymous_id->internal_name = get_new_internal_name();
anonymous_id->refcount_weak.store(1);
anonymous_id->refcount_strong.store(1);
return anonymous_id;
}
bool BKE_anonymous_attribute_id_has_strong_references(const AnonymousAttributeID *anonymous_id)
{
return anonymous_id->refcount_strong.load() >= 1;
}
void BKE_anonymous_attribute_id_increment_weak(const AnonymousAttributeID *anonymous_id)
{
anonymous_id->refcount_weak.fetch_add(1);
}
void BKE_anonymous_attribute_id_increment_strong(const AnonymousAttributeID *anonymous_id)
{
anonymous_id->refcount_weak.fetch_add(1);
anonymous_id->refcount_strong.fetch_add(1);
}
void BKE_anonymous_attribute_id_decrement_weak(const AnonymousAttributeID *anonymous_id)
{
const int new_refcount = anonymous_id->refcount_weak.fetch_sub(1) - 1;
if (new_refcount == 0) {
delete anonymous_id;
}
}
void BKE_anonymous_attribute_id_decrement_strong(const AnonymousAttributeID *anonymous_id)
{
anonymous_id->refcount_strong.fetch_sub(1);
BKE_anonymous_attribute_id_decrement_weak(anonymous_id);
}
const char *BKE_anonymous_attribute_id_debug_name(const AnonymousAttributeID *anonymous_id)
{
return anonymous_id->debug_name.c_str();
}
const char *BKE_anonymous_attribute_id_internal_name(const AnonymousAttributeID *anonymous_id)
{
return anonymous_id->internal_name.c_str();
}

View File

@@ -334,8 +334,20 @@ bool BuiltinCustomDataLayerProvider::exists(const GeometryComponent &component)
return data != nullptr;
}
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();
}
ReadAttributeLookup CustomDataAttributeProvider::try_get_for_read(
const GeometryComponent &component, const StringRef attribute_name) const
const GeometryComponent &component, const AttributeIDRef &attribute_id) const
{
const CustomData *custom_data = custom_data_access_.get_const_custom_data(component);
if (custom_data == nullptr) {
@@ -343,7 +355,7 @@ ReadAttributeLookup CustomDataAttributeProvider::try_get_for_read(
}
const int domain_size = component.attribute_domain_size(domain_);
for (const CustomDataLayer &layer : Span(custom_data->layers, custom_data->totlayer)) {
if (layer.name != attribute_name) {
if (!custom_data_layer_matches_attribute_id(layer, attribute_id)) {
continue;
}
const CustomDataType data_type = (CustomDataType)layer.type;
@@ -368,7 +380,7 @@ ReadAttributeLookup CustomDataAttributeProvider::try_get_for_read(
}
WriteAttributeLookup CustomDataAttributeProvider::try_get_for_write(
GeometryComponent &component, const StringRef attribute_name) const
GeometryComponent &component, const AttributeIDRef &attribute_id) const
{
CustomData *custom_data = custom_data_access_.get_custom_data(component);
if (custom_data == nullptr) {
@@ -376,10 +388,17 @@ WriteAttributeLookup CustomDataAttributeProvider::try_get_for_write(
}
const int domain_size = component.attribute_domain_size(domain_);
for (CustomDataLayer &layer : MutableSpan(custom_data->layers, custom_data->totlayer)) {
if (layer.name != attribute_name) {
if (!custom_data_layer_matches_attribute_id(layer, attribute_id)) {
continue;
}
CustomData_duplicate_referenced_layer_named(custom_data, layer.type, layer.name, domain_size);
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);
}
const CustomDataType data_type = (CustomDataType)layer.type;
switch (data_type) {
case CD_PROP_FLOAT:
@@ -402,7 +421,7 @@ WriteAttributeLookup CustomDataAttributeProvider::try_get_for_write(
}
bool CustomDataAttributeProvider::try_delete(GeometryComponent &component,
const StringRef attribute_name) const
const AttributeIDRef &attribute_id) const
{
CustomData *custom_data = custom_data_access_.get_custom_data(component);
if (custom_data == nullptr) {
@@ -411,7 +430,8 @@ bool CustomDataAttributeProvider::try_delete(GeometryComponent &component,
const int domain_size = component.attribute_domain_size(domain_);
for (const int i : IndexRange(custom_data->totlayer)) {
const CustomDataLayer &layer = custom_data->layers[i];
if (this->type_is_supported((CustomDataType)layer.type) && layer.name == attribute_name) {
if (this->type_is_supported((CustomDataType)layer.type) &&
custom_data_layer_matches_attribute_id(layer, attribute_id)) {
CustomData_free_layer(custom_data, layer.type, domain_size, i);
return true;
}
@@ -419,24 +439,39 @@ bool CustomDataAttributeProvider::try_delete(GeometryComponent &component,
return false;
}
static bool add_named_custom_data_layer_from_attribute_init(const StringRef attribute_name,
CustomData &custom_data,
const CustomDataType data_type,
const int domain_size,
const AttributeInit &initializer)
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)
{
char attribute_name_c[MAX_NAME];
attribute_name.copy(attribute_name_c);
if (attribute_id.is_named()) {
char attribute_name_c[MAX_NAME];
attribute_id.name().copy(attribute_name_c);
return CustomData_add_layer_named(
&custom_data, data_type, CD_DEFAULT, nullptr, domain_size, attribute_name_c);
}
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 = CustomData_add_layer_named(
&custom_data, data_type, CD_DEFAULT, nullptr, domain_size, attribute_name_c);
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 = CustomData_add_layer_named(
&custom_data, data_type, CD_DEFAULT, nullptr, domain_size, attribute_name_c);
void *data = add_generic_custom_data_layer(
custom_data, data_type, CD_DEFAULT, nullptr, domain_size, attribute_id);
if (data == nullptr) {
return false;
}
@@ -446,8 +481,8 @@ static bool add_named_custom_data_layer_from_attribute_init(const StringRef attr
}
case AttributeInit::Type::MoveArray: {
void *source_data = static_cast<const AttributeInitMove &>(initializer).data;
void *data = CustomData_add_layer_named(
&custom_data, data_type, CD_ASSIGN, source_data, domain_size, attribute_name_c);
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;
@@ -461,7 +496,7 @@ static bool add_named_custom_data_layer_from_attribute_init(const StringRef attr
}
bool CustomDataAttributeProvider::try_create(GeometryComponent &component,
const StringRef attribute_name,
const AttributeIDRef &attribute_id,
const AttributeDomain domain,
const CustomDataType data_type,
const AttributeInit &initializer) const
@@ -477,13 +512,13 @@ bool CustomDataAttributeProvider::try_create(GeometryComponent &component,
return false;
}
for (const CustomDataLayer &layer : Span(custom_data->layers, custom_data->totlayer)) {
if (layer.name == attribute_name) {
if (custom_data_layer_matches_attribute_id(layer, attribute_id)) {
return false;
}
}
const int domain_size = component.attribute_domain_size(domain_);
add_named_custom_data_layer_from_attribute_init(
attribute_name, *custom_data, data_type, domain_size, initializer);
add_custom_data_layer_from_attribute_init(
attribute_id, *custom_data, data_type, domain_size, initializer);
return true;
}
@@ -498,7 +533,14 @@ bool CustomDataAttributeProvider::foreach_attribute(const GeometryComponent &com
const CustomDataType data_type = (CustomDataType)layer.type;
if (this->type_is_supported(data_type)) {
AttributeMetaData meta_data{domain_, data_type};
if (!callback(layer.name, meta_data)) {
AttributeIDRef attribute_id;
if (layer.anonymous_id != nullptr) {
attribute_id = layer.anonymous_id;
}
else {
attribute_id = layer.name;
}
if (!callback(attribute_id, meta_data)) {
return false;
}
}
@@ -507,7 +549,7 @@ bool CustomDataAttributeProvider::foreach_attribute(const GeometryComponent &com
}
ReadAttributeLookup NamedLegacyCustomDataProvider::try_get_for_read(
const GeometryComponent &component, const StringRef attribute_name) const
const GeometryComponent &component, const AttributeIDRef &attribute_id) const
{
const CustomData *custom_data = custom_data_access_.get_const_custom_data(component);
if (custom_data == nullptr) {
@@ -515,7 +557,7 @@ ReadAttributeLookup NamedLegacyCustomDataProvider::try_get_for_read(
}
for (const CustomDataLayer &layer : Span(custom_data->layers, custom_data->totlayer)) {
if (layer.type == stored_type_) {
if (layer.name == attribute_name) {
if (custom_data_layer_matches_attribute_id(layer, attribute_id)) {
const int domain_size = component.attribute_domain_size(domain_);
return {as_read_attribute_(layer.data, domain_size), domain_};
}
@@ -525,7 +567,7 @@ ReadAttributeLookup NamedLegacyCustomDataProvider::try_get_for_read(
}
WriteAttributeLookup NamedLegacyCustomDataProvider::try_get_for_write(
GeometryComponent &component, const StringRef attribute_name) const
GeometryComponent &component, const AttributeIDRef &attribute_id) const
{
CustomData *custom_data = custom_data_access_.get_custom_data(component);
if (custom_data == nullptr) {
@@ -533,7 +575,7 @@ WriteAttributeLookup NamedLegacyCustomDataProvider::try_get_for_write(
}
for (CustomDataLayer &layer : MutableSpan(custom_data->layers, custom_data->totlayer)) {
if (layer.type == stored_type_) {
if (layer.name == attribute_name) {
if (custom_data_layer_matches_attribute_id(layer, attribute_id)) {
const int domain_size = component.attribute_domain_size(domain_);
void *data_old = layer.data;
void *data_new = CustomData_duplicate_referenced_layer_named(
@@ -549,7 +591,7 @@ WriteAttributeLookup NamedLegacyCustomDataProvider::try_get_for_write(
}
bool NamedLegacyCustomDataProvider::try_delete(GeometryComponent &component,
const StringRef attribute_name) const
const AttributeIDRef &attribute_id) const
{
CustomData *custom_data = custom_data_access_.get_custom_data(component);
if (custom_data == nullptr) {
@@ -558,7 +600,7 @@ bool NamedLegacyCustomDataProvider::try_delete(GeometryComponent &component,
for (const int i : IndexRange(custom_data->totlayer)) {
const CustomDataLayer &layer = custom_data->layers[i];
if (layer.type == stored_type_) {
if (layer.name == attribute_name) {
if (custom_data_layer_matches_attribute_id(layer, attribute_id)) {
const int domain_size = component.attribute_domain_size(domain_);
CustomData_free_layer(custom_data, stored_type_, domain_size, i);
custom_data_access_.update_custom_data_pointers(component);
@@ -627,11 +669,11 @@ CustomDataAttributes &CustomDataAttributes::operator=(const CustomDataAttributes
return *this;
}
std::optional<GSpan> CustomDataAttributes::get_for_read(const StringRef name) const
std::optional<GSpan> CustomDataAttributes::get_for_read(const AttributeIDRef &attribute_id) const
{
BLI_assert(size_ != 0);
for (const CustomDataLayer &layer : Span(data.layers, data.totlayer)) {
if (layer.name == name) {
if (custom_data_layer_matches_attribute_id(layer, attribute_id)) {
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_);
@@ -645,13 +687,13 @@ std::optional<GSpan> CustomDataAttributes::get_for_read(const StringRef name) co
* value if the attribute doesn't exist. If no default value is provided, the default value for the
* type will be used.
*/
GVArrayPtr CustomDataAttributes::get_for_read(const StringRef name,
GVArrayPtr CustomDataAttributes::get_for_read(const AttributeIDRef &attribute_id,
const CustomDataType data_type,
const void *default_value) const
{
const CPPType *type = blender::bke::custom_data_type_to_cpp_type(data_type);
std::optional<GSpan> attribute = this->get_for_read(name);
std::optional<GSpan> attribute = this->get_for_read(attribute_id);
if (!attribute) {
const int domain_size = this->size_;
return std::make_unique<GVArray_For_SingleValue>(
@@ -666,12 +708,12 @@ GVArrayPtr CustomDataAttributes::get_for_read(const StringRef name,
return conversions.try_convert(std::make_unique<GVArray_For_GSpan>(*attribute), *type);
}
std::optional<GMutableSpan> CustomDataAttributes::get_for_write(const StringRef name)
std::optional<GMutableSpan> CustomDataAttributes::get_for_write(const AttributeIDRef &attribute_id)
{
/* If this assert hits, it most likely means that #reallocate was not called at some point. */
BLI_assert(size_ != 0);
for (CustomDataLayer &layer : MutableSpan(data.layers, data.totlayer)) {
if (layer.name == name) {
if (custom_data_layer_matches_attribute_id(layer, attribute_id)) {
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_);
@@ -680,30 +722,29 @@ std::optional<GMutableSpan> CustomDataAttributes::get_for_write(const StringRef
return {};
}
bool CustomDataAttributes::create(const StringRef name, const CustomDataType data_type)
bool CustomDataAttributes::create(const AttributeIDRef &attribute_id,
const CustomDataType data_type)
{
char name_c[MAX_NAME];
name.copy(name_c);
void *result = CustomData_add_layer_named(&data, data_type, CD_DEFAULT, nullptr, size_, name_c);
void *result = add_generic_custom_data_layer(
data, data_type, CD_DEFAULT, nullptr, size_, attribute_id);
return result != nullptr;
}
bool CustomDataAttributes::create_by_move(const blender::StringRef name,
bool CustomDataAttributes::create_by_move(const blender::bke::AttributeIDRef &attribute_id,
const CustomDataType data_type,
void *buffer)
{
char name_c[MAX_NAME];
name.copy(name_c);
void *result = CustomData_add_layer_named(&data, data_type, CD_ASSIGN, buffer, size_, name_c);
void *result = add_generic_custom_data_layer(
data, data_type, CD_ASSIGN, buffer, size_, attribute_id);
return result != nullptr;
}
bool CustomDataAttributes::remove(const blender::StringRef name)
bool CustomDataAttributes::remove(const blender::bke::AttributeIDRef &attribute_id)
{
bool result = false;
for (const int i : IndexRange(data.totlayer)) {
const CustomDataLayer &layer = data.layers[i];
if (layer.name == name) {
if (custom_data_layer_matches_attribute_id(layer, attribute_id)) {
CustomData_free_layer(&data, layer.type, size_, i);
result = true;
}
@@ -722,7 +763,14 @@ bool CustomDataAttributes::foreach_attribute(const AttributeForeachCallback call
{
for (const CustomDataLayer &layer : Span(data.layers, data.totlayer)) {
AttributeMetaData meta_data{domain, (CustomDataType)layer.type};
if (!callback(layer.name, meta_data)) {
AttributeIDRef attribute_id;
if (layer.anonymous_id != nullptr) {
attribute_id = layer.anonymous_id;
}
else {
attribute_id = layer.name;
}
if (!callback(attribute_id, meta_data)) {
return false;
}
}
@@ -766,21 +814,23 @@ bool GeometryComponent::attribute_is_builtin(const blender::StringRef attribute_
}
blender::bke::ReadAttributeLookup GeometryComponent::attribute_try_get_for_read(
const StringRef attribute_name) const
const blender::bke::AttributeIDRef &attribute_id) const
{
using namespace blender::bke;
const ComponentAttributeProviders *providers = this->get_attribute_providers();
if (providers == nullptr) {
return {};
}
const BuiltinAttributeProvider *builtin_provider =
providers->builtin_attribute_providers().lookup_default_as(attribute_name, nullptr);
if (builtin_provider != nullptr) {
return {builtin_provider->try_get_for_read(*this), builtin_provider->domain()};
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()};
}
}
for (const DynamicAttributesProvider *dynamic_provider :
providers->dynamic_attribute_providers()) {
ReadAttributeLookup attribute = dynamic_provider->try_get_for_read(*this, attribute_name);
ReadAttributeLookup attribute = dynamic_provider->try_get_for_read(*this, attribute_id);
if (attribute) {
return attribute;
}
@@ -800,21 +850,23 @@ std::unique_ptr<blender::fn::GVArray> GeometryComponent::attribute_try_adapt_dom
}
blender::bke::WriteAttributeLookup GeometryComponent::attribute_try_get_for_write(
const StringRef attribute_name)
const blender::bke::AttributeIDRef &attribute_id)
{
using namespace blender::bke;
const ComponentAttributeProviders *providers = this->get_attribute_providers();
if (providers == nullptr) {
return {};
}
const BuiltinAttributeProvider *builtin_provider =
providers->builtin_attribute_providers().lookup_default_as(attribute_name, nullptr);
if (builtin_provider != nullptr) {
return {builtin_provider->try_get_for_write(*this), builtin_provider->domain()};
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_write(*this), builtin_provider->domain()};
}
}
for (const DynamicAttributesProvider *dynamic_provider :
providers->dynamic_attribute_providers()) {
WriteAttributeLookup attribute = dynamic_provider->try_get_for_write(*this, attribute_name);
WriteAttributeLookup attribute = dynamic_provider->try_get_for_write(*this, attribute_id);
if (attribute) {
return attribute;
}
@@ -822,53 +874,57 @@ blender::bke::WriteAttributeLookup GeometryComponent::attribute_try_get_for_writ
return {};
}
bool GeometryComponent::attribute_try_delete(const StringRef attribute_name)
bool GeometryComponent::attribute_try_delete(const blender::bke::AttributeIDRef &attribute_id)
{
using namespace blender::bke;
const ComponentAttributeProviders *providers = this->get_attribute_providers();
if (providers == nullptr) {
return {};
}
const BuiltinAttributeProvider *builtin_provider =
providers->builtin_attribute_providers().lookup_default_as(attribute_name, nullptr);
if (builtin_provider != nullptr) {
return builtin_provider->try_delete(*this);
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);
}
}
bool success = false;
for (const DynamicAttributesProvider *dynamic_provider :
providers->dynamic_attribute_providers()) {
success = dynamic_provider->try_delete(*this, attribute_name) || success;
success = dynamic_provider->try_delete(*this, attribute_id) || success;
}
return success;
}
bool GeometryComponent::attribute_try_create(const StringRef attribute_name,
bool GeometryComponent::attribute_try_create(const blender::bke::AttributeIDRef &attribute_id,
const AttributeDomain domain,
const CustomDataType data_type,
const AttributeInit &initializer)
{
using namespace blender::bke;
if (attribute_name.is_empty()) {
if (!attribute_id) {
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) {
if (builtin_provider->domain() != domain) {
return false;
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);
}
if (builtin_provider->data_type() != data_type) {
return false;
}
return builtin_provider->try_create(*this, initializer);
}
for (const DynamicAttributesProvider *dynamic_provider :
providers->dynamic_attribute_providers()) {
if (dynamic_provider->try_create(*this, attribute_name, domain, data_type, initializer)) {
if (dynamic_provider->try_create(*this, attribute_id, domain, data_type, initializer)) {
return true;
}
}
@@ -894,11 +950,12 @@ bool GeometryComponent::attribute_try_create_builtin(const blender::StringRef at
return builtin_provider->try_create(*this, initializer);
}
Set<std::string> GeometryComponent::attribute_names() const
Set<blender::bke::AttributeIDRef> GeometryComponent::attribute_ids() const
{
Set<std::string> attributes;
this->attribute_foreach([&](StringRefNull name, const AttributeMetaData &UNUSED(meta_data)) {
attributes.add(name);
Set<blender::bke::AttributeIDRef> attributes;
this->attribute_foreach([&](const blender::bke::AttributeIDRef &attribute_id,
const AttributeMetaData &UNUSED(meta_data)) {
attributes.add(attribute_id);
return true;
});
return attributes;
@@ -931,9 +988,9 @@ bool GeometryComponent::attribute_foreach(const AttributeForeachCallback callbac
}
for (const DynamicAttributesProvider *provider : providers->dynamic_attribute_providers()) {
const bool continue_loop = provider->foreach_attribute(
*this, [&](StringRefNull name, const AttributeMetaData &meta_data) {
if (handled_attribute_names.add(name)) {
return callback(name, meta_data);
*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);
}
return true;
});
@@ -945,9 +1002,9 @@ bool GeometryComponent::attribute_foreach(const AttributeForeachCallback callbac
return true;
}
bool GeometryComponent::attribute_exists(const blender::StringRef attribute_name) const
bool GeometryComponent::attribute_exists(const blender::bke::AttributeIDRef &attribute_id) const
{
blender::bke::ReadAttributeLookup attribute = this->attribute_try_get_for_read(attribute_name);
blender::bke::ReadAttributeLookup attribute = this->attribute_try_get_for_read(attribute_id);
if (attribute) {
return true;
}
@@ -955,11 +1012,12 @@ bool GeometryComponent::attribute_exists(const blender::StringRef attribute_name
}
std::optional<AttributeMetaData> GeometryComponent::attribute_get_meta_data(
const StringRef attribute_name) const
const blender::bke::AttributeIDRef &attribute_id) const
{
std::optional<AttributeMetaData> result{std::nullopt};
this->attribute_foreach([&](StringRefNull name, const AttributeMetaData &meta_data) {
if (attribute_name == name) {
this->attribute_foreach([&](const blender::bke::AttributeIDRef &current_attribute_id,
const AttributeMetaData &meta_data) {
if (attribute_id == current_attribute_id) {
result = meta_data;
return false;
}
@@ -977,11 +1035,11 @@ static std::unique_ptr<blender::fn::GVArray> try_adapt_data_type(
}
std::unique_ptr<blender::fn::GVArray> GeometryComponent::attribute_try_get_for_read(
const StringRef attribute_name,
const blender::bke::AttributeIDRef &attribute_id,
const AttributeDomain domain,
const CustomDataType data_type) const
{
blender::bke::ReadAttributeLookup attribute = this->attribute_try_get_for_read(attribute_name);
blender::bke::ReadAttributeLookup attribute = this->attribute_try_get_for_read(attribute_id);
if (!attribute) {
return {};
}
@@ -1007,13 +1065,13 @@ std::unique_ptr<blender::fn::GVArray> GeometryComponent::attribute_try_get_for_r
}
std::unique_ptr<blender::bke::GVArray> GeometryComponent::attribute_try_get_for_read(
const StringRef attribute_name, const AttributeDomain domain) const
const blender::bke::AttributeIDRef &attribute_id, const AttributeDomain domain) const
{
if (!this->attribute_domain_supported(domain)) {
return {};
}
blender::bke::ReadAttributeLookup attribute = this->attribute_try_get_for_read(attribute_name);
blender::bke::ReadAttributeLookup attribute = this->attribute_try_get_for_read(attribute_id);
if (!attribute) {
return {};
}
@@ -1026,9 +1084,9 @@ std::unique_ptr<blender::bke::GVArray> GeometryComponent::attribute_try_get_for_
}
blender::bke::ReadAttributeLookup GeometryComponent::attribute_try_get_for_read(
const blender::StringRef attribute_name, const CustomDataType data_type) const
const blender::bke::AttributeIDRef &attribute_id, const CustomDataType data_type) const
{
blender::bke::ReadAttributeLookup attribute = this->attribute_try_get_for_read(attribute_name);
blender::bke::ReadAttributeLookup attribute = this->attribute_try_get_for_read(attribute_id);
if (!attribute) {
return {};
}
@@ -1043,13 +1101,13 @@ blender::bke::ReadAttributeLookup GeometryComponent::attribute_try_get_for_read(
}
std::unique_ptr<blender::bke::GVArray> GeometryComponent::attribute_get_for_read(
const StringRef attribute_name,
const blender::bke::AttributeIDRef &attribute_id,
const AttributeDomain domain,
const CustomDataType data_type,
const void *default_value) const
{
std::unique_ptr<blender::bke::GVArray> varray = this->attribute_try_get_for_read(
attribute_name, domain, data_type);
attribute_id, domain, data_type);
if (varray) {
return varray;
}
@@ -1065,15 +1123,22 @@ class GVMutableAttribute_For_OutputAttribute
: public blender::fn::GVMutableArray_For_GMutableSpan {
public:
GeometryComponent *component;
std::string final_name;
std::string attribute_name;
blender::bke::WeakAnonymousAttributeID anonymous_attribute_id;
GVMutableAttribute_For_OutputAttribute(GMutableSpan data,
GeometryComponent &component,
std::string final_name)
: blender::fn::GVMutableArray_For_GMutableSpan(data),
component(&component),
final_name(std::move(final_name))
const blender::bke::AttributeIDRef &attribute_id)
: blender::fn::GVMutableArray_For_GMutableSpan(data), component(&component)
{
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};
}
}
~GVMutableAttribute_For_OutputAttribute() override
@@ -1093,21 +1158,28 @@ static void save_output_attribute(blender::bke::OutputAttribute &output_attribut
dynamic_cast<GVMutableAttribute_For_OutputAttribute &>(output_attribute.varray());
GeometryComponent &component = *varray.component;
const StringRefNull name = varray.final_name;
AttributeIDRef attribute_id;
if (!varray.attribute_name.empty()) {
attribute_id = varray.attribute_name;
}
else {
attribute_id = varray.anonymous_attribute_id.extract();
}
const AttributeDomain domain = output_attribute.domain();
const CustomDataType data_type = output_attribute.custom_data_type();
const CPPType &cpp_type = output_attribute.cpp_type();
component.attribute_try_delete(name);
if (!component.attribute_try_create(
varray.final_name, domain, data_type, AttributeInitDefault())) {
CLOG_WARN(&LOG,
"Could not create the '%s' attribute with type '%s'.",
name.c_str(),
cpp_type.name().c_str());
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());
}
return;
}
WriteAttributeLookup write_attribute = component.attribute_try_get_for_write(name);
WriteAttributeLookup write_attribute = component.attribute_try_get_for_write(attribute_id);
BUFFER_FOR_CPP_TYPE_VALUE(varray.type(), buffer);
for (const int i : IndexRange(varray.size())) {
varray.get(i, buffer);
@@ -1117,7 +1189,7 @@ static void save_output_attribute(blender::bke::OutputAttribute &output_attribut
static blender::bke::OutputAttribute create_output_attribute(
GeometryComponent &component,
const blender::StringRef attribute_name,
const blender::bke::AttributeIDRef &attribute_id,
const AttributeDomain domain,
const CustomDataType data_type,
const bool ignore_old_values,
@@ -1127,7 +1199,7 @@ static blender::bke::OutputAttribute create_output_attribute(
using namespace blender::fn;
using namespace blender::bke;
if (attribute_name.is_empty()) {
if (!attribute_id) {
return {};
}
@@ -1135,7 +1207,8 @@ static blender::bke::OutputAttribute create_output_attribute(
BLI_assert(cpp_type != nullptr);
const nodes::DataTypeConversions &conversions = nodes::get_implicit_type_conversions();
if (component.attribute_is_builtin(attribute_name)) {
if (attribute_id.is_named() && component.attribute_is_builtin(attribute_id.name())) {
const StringRef attribute_name = attribute_id.name();
WriteAttributeLookup attribute = component.attribute_try_get_for_write(attribute_name);
if (!attribute) {
if (default_value) {
@@ -1169,18 +1242,18 @@ static blender::bke::OutputAttribute create_output_attribute(
const int domain_size = component.attribute_domain_size(domain);
WriteAttributeLookup attribute = component.attribute_try_get_for_write(attribute_name);
WriteAttributeLookup attribute = component.attribute_try_get_for_write(attribute_id);
if (!attribute) {
if (default_value) {
const GVArray_For_SingleValueRef default_varray{*cpp_type, domain_size, default_value};
component.attribute_try_create(
attribute_name, domain, data_type, AttributeInitVArray(&default_varray));
attribute_id, domain, data_type, AttributeInitVArray(&default_varray));
}
else {
component.attribute_try_create(attribute_name, domain, data_type, AttributeInitDefault());
component.attribute_try_create(attribute_id, domain, data_type, AttributeInitDefault());
}
attribute = component.attribute_try_get_for_write(attribute_name);
attribute = component.attribute_try_get_for_write(attribute_id);
if (!attribute) {
/* Can't create the attribute. */
return {};
@@ -1202,28 +1275,28 @@ static blender::bke::OutputAttribute create_output_attribute(
else {
/* Fill the temporary array with values from the existing attribute. */
GVArrayPtr old_varray = component.attribute_get_for_read(
attribute_name, domain, data_type, default_value);
attribute_id, domain, data_type, default_value);
old_varray->materialize_to_uninitialized(IndexRange(domain_size), data);
}
GVMutableArrayPtr varray = std::make_unique<GVMutableAttribute_For_OutputAttribute>(
GMutableSpan{*cpp_type, data, domain_size}, component, attribute_name);
GMutableSpan{*cpp_type, data, domain_size}, component, attribute_id);
return OutputAttribute(std::move(varray), domain, save_output_attribute, true);
}
blender::bke::OutputAttribute GeometryComponent::attribute_try_get_for_output(
const StringRef attribute_name,
const blender::bke::AttributeIDRef &attribute_id,
const AttributeDomain domain,
const CustomDataType data_type,
const void *default_value)
{
return create_output_attribute(*this, attribute_name, domain, data_type, false, default_value);
return create_output_attribute(*this, attribute_id, domain, data_type, false, default_value);
}
blender::bke::OutputAttribute GeometryComponent::attribute_try_get_for_output_only(
const blender::StringRef attribute_name,
const blender::bke::AttributeIDRef &attribute_id,
const AttributeDomain domain,
const CustomDataType data_type)
{
return create_output_attribute(*this, attribute_name, domain, data_type, true, nullptr);
return create_output_attribute(*this, attribute_id, domain, data_type, true, nullptr);
}

View File

@@ -116,12 +116,13 @@ class BuiltinAttributeProvider {
class DynamicAttributesProvider {
public:
virtual ReadAttributeLookup try_get_for_read(const GeometryComponent &component,
const StringRef attribute_name) const = 0;
const AttributeIDRef &attribute_id) const = 0;
virtual WriteAttributeLookup try_get_for_write(GeometryComponent &component,
const StringRef attribute_name) const = 0;
virtual bool try_delete(GeometryComponent &component, const StringRef attribute_name) const = 0;
const AttributeIDRef &attribute_id) const = 0;
virtual bool try_delete(GeometryComponent &component,
const AttributeIDRef &attribute_id) const = 0;
virtual bool try_create(GeometryComponent &UNUSED(component),
const StringRef UNUSED(attribute_name),
const AttributeIDRef &UNUSED(attribute_id),
const AttributeDomain UNUSED(domain),
const CustomDataType UNUSED(data_type),
const AttributeInit &UNUSED(initializer)) const
@@ -154,15 +155,15 @@ class CustomDataAttributeProvider final : public DynamicAttributesProvider {
}
ReadAttributeLookup try_get_for_read(const GeometryComponent &component,
const StringRef attribute_name) const final;
const AttributeIDRef &attribute_id) const final;
WriteAttributeLookup try_get_for_write(GeometryComponent &component,
const StringRef attribute_name) const final;
const AttributeIDRef &attribute_id) const final;
bool try_delete(GeometryComponent &component, const StringRef attribute_name) const final;
bool try_delete(GeometryComponent &component, const AttributeIDRef &attribute_id) const final;
bool try_create(GeometryComponent &component,
const StringRef attribute_name,
const AttributeIDRef &attribute_id,
const AttributeDomain domain,
const CustomDataType data_type,
const AttributeInit &initializer) const final;
@@ -231,10 +232,10 @@ class NamedLegacyCustomDataProvider final : public DynamicAttributesProvider {
}
ReadAttributeLookup try_get_for_read(const GeometryComponent &component,
const StringRef attribute_name) const final;
const AttributeIDRef &attribute_id) const final;
WriteAttributeLookup try_get_for_write(GeometryComponent &component,
const StringRef attribute_name) const final;
bool try_delete(GeometryComponent &component, const StringRef attribute_name) const final;
const AttributeIDRef &attribute_id) const final;
bool try_delete(GeometryComponent &component, const AttributeIDRef &attribute_id) const final;
bool foreach_attribute(const GeometryComponent &component,
const AttributeForeachCallback callback) const final;
void foreach_domain(const FunctionRef<void(AttributeDomain)> callback) const final;

View File

@@ -25,6 +25,7 @@
#include "DNA_curve_types.h"
#include "BKE_anonymous_attribute.hh"
#include "BKE_curve.h"
#include "BKE_spline.hh"
@@ -330,13 +331,13 @@ void CurveEval::assert_valid_point_attributes() const
return;
}
const int layer_len = splines_.first()->attributes.data.totlayer;
Map<StringRefNull, AttributeMetaData> map;
Map<blender::bke::AttributeIDRef, AttributeMetaData> map;
for (const SplinePtr &spline : splines_) {
BLI_assert(spline->attributes.data.totlayer == layer_len);
spline->attributes.foreach_attribute(
[&](StringRefNull name, const AttributeMetaData &meta_data) {
[&](const blender::bke::AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) {
map.add_or_modify(
name,
attribute_id,
[&](AttributeMetaData *map_data) {
/* All unique attribute names should be added on the first spline. */
BLI_assert(spline == splines_.first());

View File

@@ -46,6 +46,7 @@
#include "BLT_translation.h"
#include "BKE_anonymous_attribute.h"
#include "BKE_customdata.h"
#include "BKE_customdata_file.h"
#include "BKE_deform.h"
@@ -2127,6 +2128,10 @@ bool CustomData_merge(const struct CustomData *source,
if (flag & CD_FLAG_NOCOPY) {
continue;
}
if (layer->anonymous_id &&
!BKE_anonymous_attribute_id_has_strong_references(layer->anonymous_id)) {
continue;
}
if (!(mask & CD_TYPE_AS_MASK(type))) {
continue;
}
@@ -2166,6 +2171,11 @@ bool CustomData_merge(const struct CustomData *source,
newlayer->active_mask = lastmask;
newlayer->flag |= flag & (CD_FLAG_EXTERNAL | CD_FLAG_IN_MEMORY);
changed = true;
if (layer->anonymous_id != NULL) {
BKE_anonymous_attribute_id_increment_weak(layer->anonymous_id);
newlayer->anonymous_id = layer->anonymous_id;
}
}
}
@@ -2206,6 +2216,10 @@ static void customData_free_layer__internal(CustomDataLayer *layer, int totelem)
{
const LayerTypeInfo *typeInfo;
if (layer->anonymous_id != NULL) {
BKE_anonymous_attribute_id_decrement_weak(layer->anonymous_id);
layer->anonymous_id = NULL;
}
if (!(layer->flag & CD_FLAG_NOFREE) && layer->data) {
typeInfo = layerType_getInfo(layer->type);
@@ -2649,6 +2663,27 @@ void *CustomData_add_layer_named(CustomData *data,
return NULL;
}
void *CustomData_add_layer_anonymous(struct CustomData *data,
int type,
eCDAllocType alloctype,
void *layerdata,
int totelem,
const AnonymousAttributeID *anonymous_id)
{
const char *name = BKE_anonymous_attribute_id_internal_name(anonymous_id);
CustomDataLayer *layer = customData_add_layer__internal(
data, type, alloctype, layerdata, totelem, name);
CustomData_update_typemap(data);
if (layer == NULL) {
return NULL;
}
BKE_anonymous_attribute_id_increment_weak(anonymous_id);
layer->anonymous_id = anonymous_id;
return layer->data;
}
bool CustomData_free_layer(CustomData *data, int type, int totelem, int index)
{
const int index_first = CustomData_get_layer_index(data, type);
@@ -2812,6 +2847,20 @@ void *CustomData_duplicate_referenced_layer_named(CustomData *data,
return customData_duplicate_referenced_layer_index(data, layer_index, totelem);
}
void *CustomData_duplicate_referenced_layer_anonymous(CustomData *data,
const int type,
const AnonymousAttributeID *anonymous_id,
const int totelem)
{
for (int i = 0; i < data->totlayer; i++) {
if (data->layers[i].anonymous_id == anonymous_id) {
return customData_duplicate_referenced_layer_index(data, i, totelem);
}
}
BLI_assert_unreachable();
return NULL;
}
void CustomData_duplicate_referenced_layers(CustomData *data, int totelem)
{
for (int i = 0; i < data->totlayer; i++) {
@@ -4244,7 +4293,8 @@ void CustomData_blend_write_prepare(CustomData *data,
for (i = 0, j = 0; i < totlayer; i++) {
CustomDataLayer *layer = &data->layers[i];
if (layer->flag & CD_FLAG_NOCOPY) { /* Layers with this flag set are not written to file. */
/* Layers with this flag set are not written to file. */
if ((layer->flag & CD_FLAG_NOCOPY) || layer->anonymous_id != NULL) {
data->totlayer--;
// CLOG_WARN(&LOG, "skipping layer %p (%s)", layer, layer->name);
}

View File

@@ -892,7 +892,7 @@ class DynamicPointAttributeProvider final : public DynamicAttributesProvider {
public:
ReadAttributeLookup try_get_for_read(const GeometryComponent &component,
const StringRef attribute_name) const final
const AttributeIDRef &attribute_id) const final
{
const CurveEval *curve = get_curve_from_component_for_read(component);
if (curve == nullptr || curve->splines().size() == 0) {
@@ -902,13 +902,13 @@ class DynamicPointAttributeProvider final : public DynamicAttributesProvider {
Span<SplinePtr> splines = curve->splines();
Vector<GSpan> spans; /* GSpan has no default constructor. */
spans.reserve(splines.size());
std::optional<GSpan> first_span = splines[0]->attributes.get_for_read(attribute_name);
std::optional<GSpan> first_span = splines[0]->attributes.get_for_read(attribute_id);
if (!first_span) {
return {};
}
spans.append(*first_span);
for (const int i : IndexRange(1, splines.size() - 1)) {
std::optional<GSpan> span = splines[i]->attributes.get_for_read(attribute_name);
std::optional<GSpan> span = splines[i]->attributes.get_for_read(attribute_id);
if (!span) {
/* All splines should have the same set of data layers. It would be possible to recover
* here and return partial data instead, but that would add a lot of complexity for a
@@ -945,7 +945,7 @@ class DynamicPointAttributeProvider final : public DynamicAttributesProvider {
/* This function is almost the same as #try_get_for_read, but without const. */
WriteAttributeLookup try_get_for_write(GeometryComponent &component,
const StringRef attribute_name) const final
const AttributeIDRef &attribute_id) const final
{
CurveEval *curve = get_curve_from_component_for_write(component);
if (curve == nullptr || curve->splines().size() == 0) {
@@ -955,13 +955,13 @@ class DynamicPointAttributeProvider final : public DynamicAttributesProvider {
MutableSpan<SplinePtr> splines = curve->splines();
Vector<GMutableSpan> spans; /* GMutableSpan has no default constructor. */
spans.reserve(splines.size());
std::optional<GMutableSpan> first_span = splines[0]->attributes.get_for_write(attribute_name);
std::optional<GMutableSpan> first_span = splines[0]->attributes.get_for_write(attribute_id);
if (!first_span) {
return {};
}
spans.append(*first_span);
for (const int i : IndexRange(1, splines.size() - 1)) {
std::optional<GMutableSpan> span = splines[i]->attributes.get_for_write(attribute_name);
std::optional<GMutableSpan> span = splines[i]->attributes.get_for_write(attribute_id);
if (!span) {
/* All splines should have the same set of data layers. It would be possible to recover
* here and return partial data instead, but that would add a lot of complexity for a
@@ -996,7 +996,7 @@ class DynamicPointAttributeProvider final : public DynamicAttributesProvider {
return attribute;
}
bool try_delete(GeometryComponent &component, const StringRef attribute_name) const final
bool try_delete(GeometryComponent &component, const AttributeIDRef &attribute_id) const final
{
CurveEval *curve = get_curve_from_component_for_write(component);
if (curve == nullptr) {
@@ -1006,7 +1006,7 @@ class DynamicPointAttributeProvider final : public DynamicAttributesProvider {
/* Reuse the boolean for all splines; we expect all splines to have the same attributes. */
bool layer_freed = false;
for (SplinePtr &spline : curve->splines()) {
layer_freed = spline->attributes.remove(attribute_name);
layer_freed = spline->attributes.remove(attribute_id);
}
return layer_freed;
}
@@ -1034,7 +1034,7 @@ class DynamicPointAttributeProvider final : public DynamicAttributesProvider {
}
bool try_create(GeometryComponent &component,
const StringRef attribute_name,
const AttributeIDRef &attribute_id,
const AttributeDomain domain,
const CustomDataType data_type,
const AttributeInit &initializer) const final
@@ -1053,7 +1053,7 @@ class DynamicPointAttributeProvider final : public DynamicAttributesProvider {
/* First check the one case that allows us to avoid copying the input data. */
if (splines.size() == 1 && initializer.type == AttributeInit::Type::MoveArray) {
void *source_data = static_cast<const AttributeInitMove &>(initializer).data;
if (!splines[0]->attributes.create_by_move(attribute_name, data_type, source_data)) {
if (!splines[0]->attributes.create_by_move(attribute_id, data_type, source_data)) {
MEM_freeN(source_data);
return false;
}
@@ -1062,7 +1062,7 @@ class DynamicPointAttributeProvider final : public DynamicAttributesProvider {
/* Otherwise just create a custom data layer on each of the splines. */
for (const int i : splines.index_range()) {
if (!splines[i]->attributes.create(attribute_name, data_type)) {
if (!splines[i]->attributes.create(attribute_id, data_type)) {
/* If attribute creation fails on one of the splines, we cannot leave the custom data
* layers in the previous splines around, so delete them before returning. However,
* this is not an expected case. */
@@ -1076,7 +1076,7 @@ class DynamicPointAttributeProvider final : public DynamicAttributesProvider {
return true;
}
WriteAttributeLookup write_attribute = this->try_get_for_write(component, attribute_name);
WriteAttributeLookup write_attribute = this->try_get_for_write(component, attribute_id);
/* We just created the attribute, it should exist. */
BLI_assert(write_attribute);

View File

@@ -818,16 +818,20 @@ class VArray_For_VertexWeights final : public VArray<float> {
class VertexGroupsAttributeProvider final : public DynamicAttributesProvider {
public:
ReadAttributeLookup try_get_for_read(const GeometryComponent &component,
const StringRef attribute_name) const final
const AttributeIDRef &attribute_id) const final
{
BLI_assert(component.type() == GEO_COMPONENT_TYPE_MESH);
if (!attribute_id.is_named()) {
return {};
}
const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
const Mesh *mesh = mesh_component.get_for_read();
if (mesh == nullptr) {
return {};
}
std::string name = attribute_id.name();
const int vertex_group_index = BLI_findstringindex(
&mesh->vertex_group_names, attribute_name.data(), offsetof(bDeformGroup, name));
&mesh->vertex_group_names, name.c_str(), offsetof(bDeformGroup, name));
if (vertex_group_index < 0) {
return {};
}
@@ -843,17 +847,21 @@ class VertexGroupsAttributeProvider final : public DynamicAttributesProvider {
}
WriteAttributeLookup try_get_for_write(GeometryComponent &component,
const StringRef attribute_name) const final
const AttributeIDRef &attribute_id) const final
{
BLI_assert(component.type() == GEO_COMPONENT_TYPE_MESH);
if (!attribute_id.is_named()) {
return {};
}
MeshComponent &mesh_component = static_cast<MeshComponent &>(component);
Mesh *mesh = mesh_component.get_for_write();
if (mesh == nullptr) {
return {};
}
const std::string name = attribute_id.name();
const int vertex_group_index = BLI_findstringindex(
&mesh->vertex_group_names, attribute_name.data(), offsetof(bDeformGroup, name));
&mesh->vertex_group_names, name.c_str(), offsetof(bDeformGroup, name));
if (vertex_group_index < 0) {
return {};
}
@@ -872,17 +880,21 @@ class VertexGroupsAttributeProvider final : public DynamicAttributesProvider {
ATTR_DOMAIN_POINT};
}
bool try_delete(GeometryComponent &component, const StringRef attribute_name) const final
bool try_delete(GeometryComponent &component, const AttributeIDRef &attribute_id) const final
{
BLI_assert(component.type() == GEO_COMPONENT_TYPE_MESH);
if (!attribute_id.is_named()) {
return false;
}
MeshComponent &mesh_component = static_cast<MeshComponent &>(component);
Mesh *mesh = mesh_component.get_for_write();
if (mesh == nullptr) {
return true;
}
const std::string name = attribute_id.name();
const int vertex_group_index = BLI_findstringindex(
&mesh->vertex_group_names, attribute_name.data(), offsetof(bDeformGroup, name));
&mesh->vertex_group_names, name.c_str(), offsetof(bDeformGroup, name));
if (vertex_group_index < 0) {
return false;
}

View File

@@ -319,7 +319,7 @@ void geometry_set_instances_attribute_foreach(const GeometrySet &geometry_set,
void geometry_set_gather_instances_attribute_info(Span<GeometryInstanceGroup> set_groups,
Span<GeometryComponentType> component_types,
const Set<std::string> &ignored_attributes,
Map<std::string, AttributeKind> &r_attributes)
Map<AttributeIDRef, AttributeKind> &r_attributes)
{
for (const GeometryInstanceGroup &set_group : set_groups) {
const GeometrySet &set = set_group.geometry_set;
@@ -329,23 +329,24 @@ void geometry_set_gather_instances_attribute_info(Span<GeometryInstanceGroup> se
}
const GeometryComponent &component = *set.get_component_for_read(component_type);
component.attribute_foreach([&](StringRefNull name, const AttributeMetaData &meta_data) {
if (ignored_attributes.contains(name)) {
return true;
}
auto add_info = [&](AttributeKind *attribute_kind) {
attribute_kind->domain = meta_data.domain;
attribute_kind->data_type = meta_data.data_type;
};
auto modify_info = [&](AttributeKind *attribute_kind) {
attribute_kind->domain = meta_data.domain; /* TODO: Use highest priority domain. */
attribute_kind->data_type = bke::attribute_data_type_highest_complexity(
{attribute_kind->data_type, meta_data.data_type});
};
component.attribute_foreach(
[&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) {
if (attribute_id.is_named() && ignored_attributes.contains(attribute_id.name())) {
return true;
}
auto add_info = [&](AttributeKind *attribute_kind) {
attribute_kind->domain = meta_data.domain;
attribute_kind->data_type = meta_data.data_type;
};
auto modify_info = [&](AttributeKind *attribute_kind) {
attribute_kind->domain = meta_data.domain; /* TODO: Use highest priority domain. */
attribute_kind->data_type = bke::attribute_data_type_highest_complexity(
{attribute_kind->data_type, meta_data.data_type});
};
r_attributes.add_or_modify(name, add_info, modify_info);
return true;
});
r_attributes.add_or_modify(attribute_id, add_info, modify_info);
return true;
});
}
}
}
@@ -500,11 +501,11 @@ static Mesh *join_mesh_topology_and_builtin_attributes(Span<GeometryInstanceGrou
static void join_attributes(Span<GeometryInstanceGroup> set_groups,
Span<GeometryComponentType> component_types,
const Map<std::string, AttributeKind> &attribute_info,
const Map<AttributeIDRef, AttributeKind> &attribute_info,
GeometryComponent &result)
{
for (Map<std::string, AttributeKind>::Item entry : attribute_info.items()) {
StringRef name = entry.key;
for (Map<AttributeIDRef, AttributeKind>::Item entry : attribute_info.items()) {
const AttributeIDRef attribute_id = entry.key;
const AttributeDomain domain_output = entry.value.domain;
const CustomDataType data_type_output = entry.value.data_type;
const CPPType *cpp_type = bke::custom_data_type_to_cpp_type(data_type_output);
@@ -512,7 +513,7 @@ static void join_attributes(Span<GeometryInstanceGroup> set_groups,
result.attribute_try_create(
entry.key, domain_output, data_type_output, AttributeInitDefault());
WriteAttributeLookup write_attribute = result.attribute_try_get_for_write(name);
WriteAttributeLookup write_attribute = result.attribute_try_get_for_write(attribute_id);
if (!write_attribute || &write_attribute.varray->type() != cpp_type ||
write_attribute.domain != domain_output) {
continue;
@@ -531,7 +532,7 @@ static void join_attributes(Span<GeometryInstanceGroup> set_groups,
continue; /* Domain size is 0, so no need to increment the offset. */
}
GVArrayPtr source_attribute = component.attribute_try_get_for_read(
name, domain_output, data_type_output);
attribute_id, domain_output, data_type_output);
if (source_attribute) {
fn::GVArray_GSpan src_span{*source_attribute};
@@ -641,7 +642,7 @@ static void join_instance_groups_mesh(Span<GeometryInstanceGroup> set_groups,
}
/* Don't copy attributes that are stored directly in the mesh data structs. */
Map<std::string, AttributeKind> attributes;
Map<AttributeIDRef, AttributeKind> attributes;
geometry_set_gather_instances_attribute_info(
set_groups,
component_types,
@@ -662,7 +663,7 @@ static void join_instance_groups_pointcloud(Span<GeometryInstanceGroup> set_grou
PointCloudComponent &dst_component = result.get_component_for_write<PointCloudComponent>();
dst_component.replace(new_pointcloud);
Map<std::string, AttributeKind> attributes;
Map<AttributeIDRef, AttributeKind> attributes;
geometry_set_gather_instances_attribute_info(
set_groups, {GEO_COMPONENT_TYPE_POINT_CLOUD}, {"position"}, attributes);
join_attributes(set_groups,
@@ -696,7 +697,7 @@ static void join_instance_groups_curve(Span<GeometryInstanceGroup> set_groups, G
CurveComponent &dst_component = result.get_component_for_write<CurveComponent>();
dst_component.replace(curve);
Map<std::string, AttributeKind> attributes;
Map<AttributeIDRef, AttributeKind> attributes;
geometry_set_gather_instances_attribute_info(
set_groups,
{GEO_COMPONENT_TYPE_CURVE},

View File

@@ -45,15 +45,20 @@ namespace blender::ed::spreadsheet {
void GeometryDataSource::foreach_default_column_ids(
FunctionRef<void(const SpreadsheetColumnID &)> fn) const
{
component_->attribute_foreach([&](StringRefNull name, const AttributeMetaData &meta_data) {
if (meta_data.domain != domain_) {
return true;
}
SpreadsheetColumnID column_id;
column_id.name = (char *)name.c_str();
fn(column_id);
return true;
});
component_->attribute_foreach(
[&](const bke::AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) {
if (meta_data.domain != domain_) {
return true;
}
if (attribute_id.is_anonymous()) {
return true;
}
SpreadsheetColumnID column_id;
std::string name = attribute_id.name();
column_id.name = (char *)name.c_str();
fn(column_id);
return true;
});
}
std::unique_ptr<ColumnValues> GeometryDataSource::get_column_values(

View File

@@ -33,6 +33,9 @@ set(SRC
intern/generic_virtual_vector_array.cc
intern/multi_function.cc
intern/multi_function_builder.cc
intern/multi_function_procedure.cc
intern/multi_function_procedure_builder.cc
intern/multi_function_procedure_executor.cc
FN_cpp_type.hh
FN_cpp_type_make.hh
@@ -48,6 +51,9 @@ set(SRC
FN_multi_function_data_type.hh
FN_multi_function_param_type.hh
FN_multi_function_params.hh
FN_multi_function_procedure.hh
FN_multi_function_procedure_builder.hh
FN_multi_function_procedure_executor.hh
FN_multi_function_signature.hh
)
@@ -62,6 +68,7 @@ if(WITH_GTESTS)
tests/FN_cpp_type_test.cc
tests/FN_generic_span_test.cc
tests/FN_generic_vector_array_test.cc
tests/FN_multi_function_procedure_test.cc
tests/FN_multi_function_test.cc
)
set(TEST_LIB

View File

@@ -0,0 +1,408 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#pragma once
/** \file
* \ingroup fn
*/
#include "FN_multi_function.hh"
namespace blender::fn {
class MFVariable;
class MFInstruction;
class MFCallInstruction;
class MFBranchInstruction;
class MFDestructInstruction;
class MFDummyInstruction;
class MFReturnInstruction;
class MFProcedure;
enum class MFInstructionType {
Call,
Branch,
Destruct,
Dummy,
Return,
};
class MFVariable : NonCopyable, NonMovable {
private:
MFDataType data_type_;
Vector<MFInstruction *> users_;
std::string name_;
int id_;
friend MFProcedure;
friend MFCallInstruction;
friend MFBranchInstruction;
friend MFDestructInstruction;
public:
MFDataType data_type() const;
Span<MFInstruction *> users();
StringRefNull name() const;
void set_name(std::string name);
int id() const;
};
class MFInstruction : NonCopyable, NonMovable {
protected:
MFInstructionType type_;
Vector<MFInstruction *> prev_;
friend MFProcedure;
friend MFCallInstruction;
friend MFBranchInstruction;
friend MFDestructInstruction;
friend MFDummyInstruction;
friend MFReturnInstruction;
public:
MFInstructionType type() const;
Span<MFInstruction *> prev();
Span<const MFInstruction *> prev() const;
};
class MFCallInstruction : public MFInstruction {
private:
const MultiFunction *fn_ = nullptr;
MFInstruction *next_ = nullptr;
MutableSpan<MFVariable *> params_;
friend MFProcedure;
public:
const MultiFunction &fn() const;
MFInstruction *next();
const MFInstruction *next() const;
void set_next(MFInstruction *instruction);
void set_param_variable(int param_index, MFVariable *variable);
void set_params(Span<MFVariable *> variables);
Span<MFVariable *> params();
Span<const MFVariable *> params() const;
};
class MFBranchInstruction : public MFInstruction {
private:
MFVariable *condition_ = nullptr;
MFInstruction *branch_true_ = nullptr;
MFInstruction *branch_false_ = nullptr;
friend MFProcedure;
public:
MFVariable *condition();
const MFVariable *condition() const;
void set_condition(MFVariable *variable);
MFInstruction *branch_true();
const MFInstruction *branch_true() const;
void set_branch_true(MFInstruction *instruction);
MFInstruction *branch_false();
const MFInstruction *branch_false() const;
void set_branch_false(MFInstruction *instruction);
};
class MFDestructInstruction : public MFInstruction {
private:
MFVariable *variable_ = nullptr;
MFInstruction *next_ = nullptr;
friend MFProcedure;
public:
MFVariable *variable();
const MFVariable *variable() const;
void set_variable(MFVariable *variable);
MFInstruction *next();
const MFInstruction *next() const;
void set_next(MFInstruction *instruction);
};
class MFDummyInstruction : public MFInstruction {
private:
MFInstruction *next_ = nullptr;
friend MFProcedure;
public:
MFInstruction *next();
const MFInstruction *next() const;
void set_next(MFInstruction *instruction);
};
class MFReturnInstruction : public MFInstruction {
};
struct MFParameter {
MFParamType::InterfaceType type;
MFVariable *variable;
};
struct ConstMFParameter {
MFParamType::InterfaceType type;
const MFVariable *variable;
};
class MFProcedure : NonCopyable, NonMovable {
private:
LinearAllocator<> allocator_;
Vector<MFCallInstruction *> call_instructions_;
Vector<MFBranchInstruction *> branch_instructions_;
Vector<MFDestructInstruction *> destruct_instructions_;
Vector<MFDummyInstruction *> dummy_instructions_;
Vector<MFReturnInstruction *> return_instructions_;
Vector<MFVariable *> variables_;
Vector<MFParameter> params_;
MFInstruction *entry_ = nullptr;
public:
MFProcedure() = default;
~MFProcedure();
MFVariable &new_variable(MFDataType data_type, std::string name = "");
MFCallInstruction &new_call_instruction(const MultiFunction &fn);
MFBranchInstruction &new_branch_instruction();
MFDestructInstruction &new_destruct_instruction();
MFDummyInstruction &new_dummy_instruction();
MFReturnInstruction &new_return_instruction();
void add_parameter(MFParamType::InterfaceType interface_type, MFVariable &variable);
Span<ConstMFParameter> params() const;
MFInstruction *entry();
const MFInstruction *entry() const;
void set_entry(MFInstruction &entry);
Span<MFVariable *> variables();
Span<const MFVariable *> variables() const;
void assert_valid() const;
std::string to_dot() const;
bool validate() const;
private:
bool validate_all_instruction_pointers_set() const;
bool validate_all_params_provided() const;
bool validate_same_variables_in_one_call() const;
bool validate_parameters() const;
bool validate_initialization() const;
struct InitState {
bool can_be_initialized = false;
bool can_be_uninitialized = false;
};
InitState find_initialization_state_before_instruction(const MFInstruction &target_instruction,
const MFVariable &variable) const;
};
namespace multi_function_procedure_types {
using MFVariable = fn::MFVariable;
using MFInstruction = fn::MFInstruction;
using MFCallInstruction = fn::MFCallInstruction;
using MFBranchInstruction = fn::MFBranchInstruction;
using MFDestructInstruction = fn::MFDestructInstruction;
using MFProcedure = fn::MFProcedure;
} // namespace multi_function_procedure_types
/* --------------------------------------------------------------------
* MFVariable inline methods.
*/
inline MFDataType MFVariable::data_type() const
{
return data_type_;
}
inline Span<MFInstruction *> MFVariable::users()
{
return users_;
}
inline StringRefNull MFVariable::name() const
{
return name_;
}
inline int MFVariable::id() const
{
return id_;
}
/* --------------------------------------------------------------------
* MFInstruction inline methods.
*/
inline MFInstructionType MFInstruction::type() const
{
return type_;
}
inline Span<MFInstruction *> MFInstruction::prev()
{
return prev_;
}
inline Span<const MFInstruction *> MFInstruction::prev() const
{
return prev_;
}
/* --------------------------------------------------------------------
* MFCallInstruction inline methods.
*/
inline const MultiFunction &MFCallInstruction::fn() const
{
return *fn_;
}
inline MFInstruction *MFCallInstruction::next()
{
return next_;
}
inline const MFInstruction *MFCallInstruction::next() const
{
return next_;
}
inline Span<MFVariable *> MFCallInstruction::params()
{
return params_;
}
inline Span<const MFVariable *> MFCallInstruction::params() const
{
return params_;
}
/* --------------------------------------------------------------------
* MFBranchInstruction inline methods.
*/
inline MFVariable *MFBranchInstruction::condition()
{
return condition_;
}
inline const MFVariable *MFBranchInstruction::condition() const
{
return condition_;
}
inline MFInstruction *MFBranchInstruction::branch_true()
{
return branch_true_;
}
inline const MFInstruction *MFBranchInstruction::branch_true() const
{
return branch_true_;
}
inline MFInstruction *MFBranchInstruction::branch_false()
{
return branch_false_;
}
inline const MFInstruction *MFBranchInstruction::branch_false() const
{
return branch_false_;
}
/* --------------------------------------------------------------------
* MFDestructInstruction inline methods.
*/
inline MFVariable *MFDestructInstruction::variable()
{
return variable_;
}
inline const MFVariable *MFDestructInstruction::variable() const
{
return variable_;
}
inline MFInstruction *MFDestructInstruction::next()
{
return next_;
}
inline const MFInstruction *MFDestructInstruction::next() const
{
return next_;
}
/* --------------------------------------------------------------------
* MFDummyInstruction inline methods.
*/
inline MFInstruction *MFDummyInstruction::next()
{
return next_;
}
inline const MFInstruction *MFDummyInstruction::next() const
{
return next_;
}
/* --------------------------------------------------------------------
* MFProcedure inline methods.
*/
inline Span<ConstMFParameter> MFProcedure::params() const
{
static_assert(sizeof(MFParameter) == sizeof(ConstMFParameter));
return params_.as_span().cast<ConstMFParameter>();
}
inline MFInstruction *MFProcedure::entry()
{
return entry_;
}
inline const MFInstruction *MFProcedure::entry() const
{
return entry_;
}
inline Span<MFVariable *> MFProcedure::variables()
{
return variables_;
}
inline Span<const MFVariable *> MFProcedure::variables() const
{
return variables_;
}
} // namespace blender::fn

View File

@@ -0,0 +1,251 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#pragma once
/** \file
* \ingroup fn
*/
#include "FN_multi_function_procedure.hh"
namespace blender::fn {
class MFInstructionCursor {
private:
MFInstruction *instruction_ = nullptr;
/* Only used when it is a branch instruction. */
bool branch_output_ = false;
/* Only used when instruction is null. */
bool is_entry_ = false;
public:
MFInstructionCursor() = default;
MFInstructionCursor(MFCallInstruction &instruction);
MFInstructionCursor(MFDestructInstruction &instruction);
MFInstructionCursor(MFBranchInstruction &instruction, bool branch_output);
MFInstructionCursor(MFDummyInstruction &instruction);
static MFInstructionCursor Entry();
void insert(MFProcedure &procedure, MFInstruction *new_instruction);
};
class MFProcedureBuilder {
private:
MFProcedure *procedure_ = nullptr;
Vector<MFInstructionCursor> cursors_;
public:
struct Branch;
struct Loop;
MFProcedureBuilder(MFProcedure &procedure,
MFInstructionCursor initial_cursor = MFInstructionCursor::Entry());
MFProcedureBuilder(Span<MFProcedureBuilder *> builders);
MFProcedureBuilder(Branch &branch);
void set_cursor(const MFInstructionCursor &cursor);
void set_cursor(Span<MFInstructionCursor> cursors);
void set_cursor(Span<MFProcedureBuilder *> builders);
void set_cursor_after_branch(Branch &branch);
void set_cursor_after_loop(Loop &loop);
void add_destruct(MFVariable &variable);
void add_destruct(Span<MFVariable *> variables);
MFReturnInstruction &add_return();
Branch add_branch(MFVariable &condition);
Loop add_loop();
void add_loop_continue(Loop &loop);
void add_loop_break(Loop &loop);
MFCallInstruction &add_call_with_no_variables(const MultiFunction &fn);
MFCallInstruction &add_call_with_all_variables(const MultiFunction &fn,
Span<MFVariable *> param_variables);
Vector<MFVariable *> add_call(const MultiFunction &fn,
Span<MFVariable *> input_and_mutable_variables = {});
template<int OutputN>
std::array<MFVariable *, OutputN> add_call(const MultiFunction &fn,
Span<MFVariable *> input_and_mutable_variables = {});
void add_parameter(MFParamType::InterfaceType interface_type, MFVariable &variable);
MFVariable &add_parameter(MFParamType param_type, std::string name = "");
MFVariable &add_input_parameter(MFDataType data_type, std::string name = "");
template<typename T> MFVariable &add_single_input_parameter(std::string name = "");
template<typename T> MFVariable &add_single_mutable_parameter(std::string name = "");
void add_output_parameter(MFVariable &variable);
private:
void link_to_cursors(MFInstruction *instruction);
};
struct MFProcedureBuilder::Branch {
MFProcedureBuilder branch_true;
MFProcedureBuilder branch_false;
};
struct MFProcedureBuilder::Loop {
MFInstruction *begin = nullptr;
MFDummyInstruction *end = nullptr;
};
/* --------------------------------------------------------------------
* MFInstructionCursor inline methods.
*/
inline MFInstructionCursor::MFInstructionCursor(MFCallInstruction &instruction)
: instruction_(&instruction)
{
}
inline MFInstructionCursor::MFInstructionCursor(MFDestructInstruction &instruction)
: instruction_(&instruction)
{
}
inline MFInstructionCursor::MFInstructionCursor(MFBranchInstruction &instruction,
bool branch_output)
: instruction_(&instruction), branch_output_(branch_output)
{
}
inline MFInstructionCursor::MFInstructionCursor(MFDummyInstruction &instruction)
: instruction_(&instruction)
{
}
inline MFInstructionCursor MFInstructionCursor::Entry()
{
MFInstructionCursor cursor;
cursor.is_entry_ = true;
return cursor;
}
/* --------------------------------------------------------------------
* MFProcedureBuilder inline methods.
*/
inline MFProcedureBuilder::MFProcedureBuilder(Branch &branch)
: MFProcedureBuilder(*branch.branch_true.procedure_)
{
this->set_cursor_after_branch(branch);
}
inline MFProcedureBuilder::MFProcedureBuilder(MFProcedure &procedure,
MFInstructionCursor initial_cursor)
: procedure_(&procedure), cursors_({initial_cursor})
{
}
inline MFProcedureBuilder::MFProcedureBuilder(Span<MFProcedureBuilder *> builders)
: MFProcedureBuilder(*builders[0]->procedure_)
{
this->set_cursor(builders);
}
inline void MFProcedureBuilder::set_cursor(const MFInstructionCursor &cursor)
{
cursors_ = {cursor};
}
inline void MFProcedureBuilder::set_cursor(Span<MFInstructionCursor> cursors)
{
cursors_ = cursors;
}
inline void MFProcedureBuilder::set_cursor_after_branch(Branch &branch)
{
this->set_cursor({&branch.branch_false, &branch.branch_true});
}
inline void MFProcedureBuilder::set_cursor_after_loop(Loop &loop)
{
this->set_cursor(MFInstructionCursor{*loop.end});
}
inline void MFProcedureBuilder::set_cursor(Span<MFProcedureBuilder *> builders)
{
cursors_.clear();
for (MFProcedureBuilder *builder : builders) {
cursors_.extend(builder->cursors_);
}
}
template<int OutputN>
inline std::array<MFVariable *, OutputN> MFProcedureBuilder::add_call(
const MultiFunction &fn, Span<MFVariable *> input_and_mutable_variables)
{
Vector<MFVariable *> output_variables = this->add_call(fn, input_and_mutable_variables);
BLI_assert(output_variables.size() == OutputN);
std::array<MFVariable *, OutputN> output_array;
initialized_copy_n(output_variables.data(), OutputN, output_array.data());
return output_array;
}
inline void MFProcedureBuilder::add_parameter(MFParamType::InterfaceType interface_type,
MFVariable &variable)
{
procedure_->add_parameter(interface_type, variable);
}
inline MFVariable &MFProcedureBuilder::add_parameter(MFParamType param_type, std::string name)
{
MFVariable &variable = procedure_->new_variable(param_type.data_type(), std::move(name));
this->add_parameter(param_type.interface_type(), variable);
return variable;
}
inline MFVariable &MFProcedureBuilder::add_input_parameter(MFDataType data_type, std::string name)
{
return this->add_parameter(MFParamType(MFParamType::Input, data_type), std::move(name));
}
template<typename T>
inline MFVariable &MFProcedureBuilder::add_single_input_parameter(std::string name)
{
return this->add_parameter(MFParamType::ForSingleInput(CPPType::get<T>()), std::move(name));
}
template<typename T>
inline MFVariable &MFProcedureBuilder::add_single_mutable_parameter(std::string name)
{
return this->add_parameter(MFParamType::ForMutableSingle(CPPType::get<T>()), std::move(name));
}
inline void MFProcedureBuilder::add_output_parameter(MFVariable &variable)
{
this->add_parameter(MFParamType::Output, variable);
}
inline void MFProcedureBuilder::link_to_cursors(MFInstruction *instruction)
{
for (MFInstructionCursor &cursor : cursors_) {
cursor.insert(*procedure_, instruction);
}
}
} // namespace blender::fn

View File

@@ -0,0 +1,38 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#pragma once
/** \file
* \ingroup fn
*/
#include "FN_multi_function_procedure.hh"
namespace blender::fn {
class MFProcedureExecutor : public MultiFunction {
private:
MFSignature signature_;
const MFProcedure &procedure_;
public:
MFProcedureExecutor(std::string name, const MFProcedure &procedure);
void call(IndexMask mask, MFParams params, MFContext context) const override;
};
} // namespace blender::fn

View File

@@ -0,0 +1,743 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "FN_multi_function_procedure.hh"
#include "BLI_dot_export.hh"
#include "BLI_stack.hh"
namespace blender::fn {
void MFVariable::set_name(std::string name)
{
name_ = std::move(name);
}
void MFCallInstruction::set_next(MFInstruction *instruction)
{
if (next_ != nullptr) {
next_->prev_.remove_first_occurrence_and_reorder(this);
}
if (instruction != nullptr) {
instruction->prev_.append(this);
}
next_ = instruction;
}
void MFCallInstruction::set_param_variable(int param_index, MFVariable *variable)
{
if (params_[param_index] != nullptr) {
params_[param_index]->users_.remove_first_occurrence_and_reorder(this);
}
if (variable != nullptr) {
BLI_assert(fn_->param_type(param_index).data_type() == variable->data_type());
variable->users_.append(this);
}
params_[param_index] = variable;
}
void MFCallInstruction::set_params(Span<MFVariable *> variables)
{
BLI_assert(variables.size() == params_.size());
for (const int i : variables.index_range()) {
this->set_param_variable(i, variables[i]);
}
}
void MFBranchInstruction::set_condition(MFVariable *variable)
{
if (condition_ != nullptr) {
condition_->users_.remove_first_occurrence_and_reorder(this);
}
if (variable != nullptr) {
variable->users_.append(this);
}
condition_ = variable;
}
void MFBranchInstruction::set_branch_true(MFInstruction *instruction)
{
if (branch_true_ != nullptr) {
branch_true_->prev_.remove_first_occurrence_and_reorder(this);
}
if (instruction != nullptr) {
instruction->prev_.append(this);
}
branch_true_ = instruction;
}
void MFBranchInstruction::set_branch_false(MFInstruction *instruction)
{
if (branch_false_ != nullptr) {
branch_false_->prev_.remove_first_occurrence_and_reorder(this);
}
if (instruction != nullptr) {
instruction->prev_.append(this);
}
branch_false_ = instruction;
}
void MFDestructInstruction::set_variable(MFVariable *variable)
{
if (variable_ != nullptr) {
variable_->users_.remove_first_occurrence_and_reorder(this);
}
if (variable != nullptr) {
variable->users_.append(this);
}
variable_ = variable;
}
void MFDestructInstruction::set_next(MFInstruction *instruction)
{
if (next_ != nullptr) {
next_->prev_.remove_first_occurrence_and_reorder(this);
}
if (instruction != nullptr) {
instruction->prev_.append(this);
}
next_ = instruction;
}
void MFDummyInstruction::set_next(MFInstruction *instruction)
{
if (next_ != nullptr) {
next_->prev_.remove_first_occurrence_and_reorder(this);
}
if (instruction != nullptr) {
instruction->prev_.append(this);
}
next_ = instruction;
}
MFVariable &MFProcedure::new_variable(MFDataType data_type, std::string name)
{
MFVariable &variable = *allocator_.construct<MFVariable>().release();
variable.name_ = std::move(name);
variable.data_type_ = data_type;
variable.id_ = variables_.size();
variables_.append(&variable);
return variable;
}
MFCallInstruction &MFProcedure::new_call_instruction(const MultiFunction &fn)
{
MFCallInstruction &instruction = *allocator_.construct<MFCallInstruction>().release();
instruction.type_ = MFInstructionType::Call;
instruction.fn_ = &fn;
instruction.params_ = allocator_.allocate_array<MFVariable *>(fn.param_amount());
instruction.params_.fill(nullptr);
call_instructions_.append(&instruction);
return instruction;
}
MFBranchInstruction &MFProcedure::new_branch_instruction()
{
MFBranchInstruction &instruction = *allocator_.construct<MFBranchInstruction>().release();
instruction.type_ = MFInstructionType::Branch;
branch_instructions_.append(&instruction);
return instruction;
}
MFDestructInstruction &MFProcedure::new_destruct_instruction()
{
MFDestructInstruction &instruction = *allocator_.construct<MFDestructInstruction>().release();
instruction.type_ = MFInstructionType::Destruct;
destruct_instructions_.append(&instruction);
return instruction;
}
MFDummyInstruction &MFProcedure::new_dummy_instruction()
{
MFDummyInstruction &instruction = *allocator_.construct<MFDummyInstruction>().release();
instruction.type_ = MFInstructionType::Dummy;
dummy_instructions_.append(&instruction);
return instruction;
}
MFReturnInstruction &MFProcedure::new_return_instruction()
{
MFReturnInstruction &instruction = *allocator_.construct<MFReturnInstruction>().release();
instruction.type_ = MFInstructionType::Return;
return_instructions_.append(&instruction);
return instruction;
}
void MFProcedure::add_parameter(MFParamType::InterfaceType interface_type, MFVariable &variable)
{
params_.append({interface_type, &variable});
}
void MFProcedure::set_entry(MFInstruction &entry)
{
entry_ = &entry;
}
void MFProcedure::assert_valid() const
{
/**
* - Non parameter variables are destructed.
* - At every instruction, every variable is either initialized or uninitialized.
* - Input and mutable parameters of call instructions are initialized.
* - Condition of branch instruction is initialized.
* - Output parameters of call instructions are not initialized.
* - Input parameters are never destructed.
* - Mutable and output parameteres are initialized on every exit.
* - No aliasing issues in call instructions (can happen when variable is used more than once).
*/
}
MFProcedure::~MFProcedure()
{
for (MFCallInstruction *instruction : call_instructions_) {
instruction->~MFCallInstruction();
}
for (MFBranchInstruction *instruction : branch_instructions_) {
instruction->~MFBranchInstruction();
}
for (MFDestructInstruction *instruction : destruct_instructions_) {
instruction->~MFDestructInstruction();
}
for (MFDummyInstruction *instruction : dummy_instructions_) {
instruction->~MFDummyInstruction();
}
for (MFReturnInstruction *instruction : return_instructions_) {
instruction->~MFReturnInstruction();
}
for (MFVariable *variable : variables_) {
variable->~MFVariable();
}
}
bool MFProcedure::validate() const
{
if (entry_ == nullptr) {
return false;
}
if (!this->validate_all_instruction_pointers_set()) {
return false;
}
if (!this->validate_all_params_provided()) {
return false;
}
if (!this->validate_same_variables_in_one_call()) {
return false;
}
if (!this->validate_parameters()) {
return false;
}
if (!this->validate_initialization()) {
return false;
}
return true;
}
bool MFProcedure::validate_all_instruction_pointers_set() const
{
for (const MFCallInstruction *instruction : call_instructions_) {
if (instruction->next_ == nullptr) {
return false;
}
}
for (const MFDestructInstruction *instruction : destruct_instructions_) {
if (instruction->next_ == nullptr) {
return false;
}
}
for (const MFBranchInstruction *instruction : branch_instructions_) {
if (instruction->branch_true_ == nullptr) {
return false;
}
if (instruction->branch_false_ == nullptr) {
return false;
}
}
for (const MFDummyInstruction *instruction : dummy_instructions_) {
if (instruction->next_ == nullptr) {
return false;
}
}
return true;
}
bool MFProcedure::validate_all_params_provided() const
{
for (const MFCallInstruction *instruction : call_instructions_) {
for (const MFVariable *variable : instruction->params_) {
if (variable == nullptr) {
return false;
}
}
}
for (const MFBranchInstruction *instruction : branch_instructions_) {
if (instruction->condition_ == nullptr) {
return false;
}
}
for (const MFDestructInstruction *instruction : destruct_instructions_) {
if (instruction->variable_ == nullptr) {
return false;
}
}
return true;
}
bool MFProcedure::validate_same_variables_in_one_call() const
{
for (const MFCallInstruction *instruction : call_instructions_) {
const MultiFunction &fn = *instruction->fn_;
for (const int param_index : fn.param_indices()) {
const MFParamType param_type = fn.param_type(param_index);
const MFVariable *variable = instruction->params_[param_index];
for (const int other_param_index : fn.param_indices()) {
if (other_param_index == param_index) {
continue;
}
const MFVariable *other_variable = instruction->params_[other_param_index];
if (other_variable != variable) {
continue;
}
if (ELEM(param_type.interface_type(), MFParamType::Mutable, MFParamType::Output)) {
/* When a variable is used as mutable or output parameter, it can only be used once. */
return false;
}
const MFParamType other_param_type = fn.param_type(other_param_index);
/* A variable is allowed to be used as input more than once. */
if (other_param_type.interface_type() != MFParamType::Input) {
return false;
}
}
}
}
return true;
}
bool MFProcedure::validate_parameters() const
{
Set<const MFVariable *> variables;
for (const MFParameter &param : params_) {
/* One variable cannot be used as multiple parameters. */
if (!variables.add(param.variable)) {
return false;
}
}
return true;
}
bool MFProcedure::validate_initialization() const
{
/* TODO: Issue warning when it maybe wrongly initialized. */
for (const MFDestructInstruction *instruction : destruct_instructions_) {
const MFVariable &variable = *instruction->variable_;
const InitState state = this->find_initialization_state_before_instruction(*instruction,
variable);
if (!state.can_be_initialized) {
return false;
}
}
for (const MFBranchInstruction *instruction : branch_instructions_) {
const MFVariable &variable = *instruction->condition_;
const InitState state = this->find_initialization_state_before_instruction(*instruction,
variable);
if (!state.can_be_initialized) {
return false;
}
}
for (const MFCallInstruction *instruction : call_instructions_) {
const MultiFunction &fn = *instruction->fn_;
for (const int param_index : fn.param_indices()) {
const MFParamType param_type = fn.param_type(param_index);
const MFVariable &variable = *instruction->params_[param_index];
const InitState state = this->find_initialization_state_before_instruction(*instruction,
variable);
switch (param_type.interface_type()) {
case MFParamType::Input:
case MFParamType::Mutable: {
if (!state.can_be_initialized) {
return false;
}
break;
}
case MFParamType::Output: {
if (!state.can_be_uninitialized) {
return false;
}
break;
}
}
}
}
Set<const MFVariable *> variables_that_should_be_initialized_on_return;
for (const MFParameter &param : params_) {
if (ELEM(param.type, MFParamType::Mutable, MFParamType::Output)) {
variables_that_should_be_initialized_on_return.add_new(param.variable);
}
}
for (const MFReturnInstruction *instruction : return_instructions_) {
for (const MFVariable *variable : variables_) {
const InitState init_state = this->find_initialization_state_before_instruction(*instruction,
*variable);
if (variables_that_should_be_initialized_on_return.contains(variable)) {
if (!init_state.can_be_initialized) {
return false;
}
}
else {
if (!init_state.can_be_uninitialized) {
return false;
}
}
}
}
return true;
}
MFProcedure::InitState MFProcedure::find_initialization_state_before_instruction(
const MFInstruction &target_instruction, const MFVariable &target_variable) const
{
InitState state;
auto check_entry_instruction = [&]() {
bool caller_initialized_variable = false;
for (const MFParameter &param : params_) {
if (param.variable == &target_variable) {
if (ELEM(param.type, MFParamType::Input, MFParamType::Mutable)) {
caller_initialized_variable = true;
break;
}
}
}
if (caller_initialized_variable) {
state.can_be_initialized = true;
}
else {
state.can_be_uninitialized = true;
}
};
if (&target_instruction == entry_) {
check_entry_instruction();
}
Set<const MFInstruction *> checked_instructions;
Stack<const MFInstruction *> instructions_to_check;
instructions_to_check.push_multiple(target_instruction.prev_);
while (!instructions_to_check.is_empty()) {
const MFInstruction &instruction = *instructions_to_check.pop();
if (!checked_instructions.add(&instruction)) {
/* Skip if the instruction has been checked already. */
continue;
}
bool state_modified = false;
switch (instruction.type_) {
case MFInstructionType::Call: {
const MFCallInstruction &call_instruction = static_cast<const MFCallInstruction &>(
instruction);
const MultiFunction &fn = *call_instruction.fn_;
for (const int param_index : fn.param_indices()) {
if (call_instruction.params_[param_index] == &target_variable) {
const MFParamType param_type = fn.param_type(param_index);
if (param_type.interface_type() == MFParamType::Output) {
state.can_be_initialized = true;
state_modified = true;
break;
}
}
}
break;
}
case MFInstructionType::Destruct: {
const MFDestructInstruction &destruct_instruction =
static_cast<const MFDestructInstruction &>(instruction);
if (destruct_instruction.variable_ == &target_variable) {
state.can_be_uninitialized = true;
state_modified = true;
}
break;
}
case MFInstructionType::Branch:
case MFInstructionType::Dummy:
case MFInstructionType::Return: {
/* These instruction types don't change the initialization state of variables. */
break;
}
}
if (!state_modified) {
if (&instruction == entry_) {
check_entry_instruction();
}
instructions_to_check.push_multiple(instruction.prev_);
}
}
return state;
}
static bool has_to_be_block_begin(const MFProcedure &procedure, const MFInstruction &instruction)
{
if (procedure.entry() == &instruction) {
return true;
}
if (instruction.prev().size() != 1) {
return true;
}
if (instruction.prev()[0]->type() == MFInstructionType::Branch) {
return true;
}
return false;
}
static const MFInstruction &get_first_instruction_in_block(const MFProcedure &procedure,
const MFInstruction &representative)
{
const MFInstruction *current = &representative;
while (!has_to_be_block_begin(procedure, *current)) {
current = current->prev()[0];
if (current == &representative) {
/* There is a loop without entry or exit, just break it up here. */
break;
}
}
return *current;
}
static const MFInstruction *get_next_instruction_in_block(const MFProcedure &procedure,
const MFInstruction &instruction,
const MFInstruction &block_begin)
{
const MFInstruction *next = nullptr;
switch (instruction.type()) {
case MFInstructionType::Call: {
next = static_cast<const MFCallInstruction &>(instruction).next();
break;
}
case MFInstructionType::Destruct: {
next = static_cast<const MFDestructInstruction &>(instruction).next();
break;
}
case MFInstructionType::Dummy: {
next = static_cast<const MFDummyInstruction &>(instruction).next();
break;
}
case MFInstructionType::Return:
case MFInstructionType::Branch: {
break;
}
}
if (next == nullptr) {
return nullptr;
}
if (next == &block_begin) {
return nullptr;
}
if (has_to_be_block_begin(procedure, *next)) {
return nullptr;
}
return next;
}
static Vector<const MFInstruction *> get_instructions_in_block(const MFProcedure &procedure,
const MFInstruction &representative)
{
Vector<const MFInstruction *> instructions;
const MFInstruction &begin = get_first_instruction_in_block(procedure, representative);
for (const MFInstruction *current = &begin; current != nullptr;
current = get_next_instruction_in_block(procedure, *current, begin)) {
instructions.append(current);
}
return instructions;
}
static void variable_to_string(const MFVariable *variable, std::stringstream &ss)
{
if (variable == nullptr) {
ss << "<none>";
}
else {
ss << "$" << variable->id();
if (!variable->name().is_empty()) {
ss << "(" << variable->name() << ")";
}
}
}
static void instruction_to_string(const MFCallInstruction &instruction, std::stringstream &ss)
{
const MultiFunction &fn = instruction.fn();
ss << fn.name() << " - ";
for (const int param_index : fn.param_indices()) {
const MFParamType param_type = fn.param_type(param_index);
const MFVariable *variable = instruction.params()[param_index];
switch (param_type.interface_type()) {
case MFParamType::Input: {
ss << "in";
break;
}
case MFParamType::Mutable: {
ss << "mut";
break;
}
case MFParamType::Output: {
ss << "out";
break;
}
}
ss << " ";
variable_to_string(variable, ss);
if (param_index < fn.param_amount() - 1) {
ss << ", ";
}
}
}
static void instruction_to_string(const MFDestructInstruction &instruction, std::stringstream &ss)
{
ss << "Destruct ";
variable_to_string(instruction.variable(), ss);
}
static void instruction_to_string(const MFDummyInstruction &UNUSED(instruction),
std::stringstream &ss)
{
ss << "Dummy";
}
static void instruction_to_string(const MFReturnInstruction &UNUSED(instruction),
std::stringstream &ss)
{
ss << "Return";
}
static void instruction_to_string(const MFBranchInstruction &instruction, std::stringstream &ss)
{
ss << "Branch on ";
variable_to_string(instruction.condition(), ss);
}
std::string MFProcedure::to_dot() const
{
Vector<const MFInstruction *> all_instructions;
all_instructions.extend(call_instructions_.begin(), call_instructions_.end());
all_instructions.extend(branch_instructions_.begin(), branch_instructions_.end());
all_instructions.extend(destruct_instructions_.begin(), destruct_instructions_.end());
all_instructions.extend(dummy_instructions_.begin(), dummy_instructions_.end());
all_instructions.extend(return_instructions_.begin(), return_instructions_.end());
Set<const MFInstruction *> handled_instructions;
dot::DirectedGraph digraph;
Map<const MFInstruction *, dot::Node *> dot_nodes_by_begin;
Map<const MFInstruction *, dot::Node *> dot_nodes_by_end;
for (const MFInstruction *representative : all_instructions) {
if (handled_instructions.contains(representative)) {
continue;
}
Vector<const MFInstruction *> block_instructions = get_instructions_in_block(*this,
*representative);
std::stringstream ss;
for (const MFInstruction *current : block_instructions) {
handled_instructions.add_new(current);
switch (current->type()) {
case MFInstructionType::Call: {
instruction_to_string(*static_cast<const MFCallInstruction *>(current), ss);
break;
}
case MFInstructionType::Destruct: {
instruction_to_string(*static_cast<const MFDestructInstruction *>(current), ss);
break;
}
case MFInstructionType::Dummy: {
instruction_to_string(*static_cast<const MFDummyInstruction *>(current), ss);
break;
}
case MFInstructionType::Return: {
instruction_to_string(*static_cast<const MFReturnInstruction *>(current), ss);
break;
}
case MFInstructionType::Branch: {
instruction_to_string(*static_cast<const MFBranchInstruction *>(current), ss);
break;
}
}
ss << "\\l";
}
dot::Node &dot_node = digraph.new_node(ss.str());
dot_node.set_shape(dot::Attr_shape::Rectangle);
dot_nodes_by_begin.add_new(block_instructions.first(), &dot_node);
dot_nodes_by_end.add_new(block_instructions.last(), &dot_node);
}
auto create_edge = [&](dot::Node &from_node,
const MFInstruction *to_instruction) -> dot::DirectedEdge & {
if (to_instruction == nullptr) {
dot::Node &to_node = digraph.new_node("missing");
to_node.set_shape(dot::Attr_shape::Diamond);
return digraph.new_edge(from_node, to_node);
}
dot::Node &to_node = *dot_nodes_by_begin.lookup(to_instruction);
return digraph.new_edge(from_node, to_node);
};
for (auto item : dot_nodes_by_end.items()) {
const MFInstruction &from_instruction = *item.key;
dot::Node &from_node = *item.value;
switch (from_instruction.type()) {
case MFInstructionType::Call: {
const MFInstruction *to_instruction =
static_cast<const MFCallInstruction &>(from_instruction).next();
create_edge(from_node, to_instruction);
break;
}
case MFInstructionType::Destruct: {
const MFInstruction *to_instruction =
static_cast<const MFDestructInstruction &>(from_instruction).next();
create_edge(from_node, to_instruction);
break;
}
case MFInstructionType::Dummy: {
const MFInstruction *to_instruction =
static_cast<const MFDummyInstruction &>(from_instruction).next();
create_edge(from_node, to_instruction);
break;
}
case MFInstructionType::Return: {
break;
}
case MFInstructionType::Branch: {
const MFBranchInstruction &branch_instruction = static_cast<const MFBranchInstruction &>(
from_instruction);
const MFInstruction *to_true_instruction = branch_instruction.branch_true();
const MFInstruction *to_false_instruction = branch_instruction.branch_false();
create_edge(from_node, to_true_instruction).attributes.set("color", "#118811");
create_edge(from_node, to_false_instruction).attributes.set("color", "#881111");
break;
}
}
}
dot::Node &entry_node = digraph.new_node("Entry");
entry_node.set_shape(dot::Attr_shape::Circle);
create_edge(entry_node, entry_);
return digraph.to_dot_string();
}
} // namespace blender::fn

View File

@@ -0,0 +1,174 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "FN_multi_function_procedure_builder.hh"
namespace blender::fn {
void MFInstructionCursor::insert(MFProcedure &procedure, MFInstruction *new_instruction)
{
if (instruction_ == nullptr) {
if (is_entry_) {
procedure.set_entry(*new_instruction);
}
else {
/* The cursors points at nothing, nothing to do. */
}
}
else {
switch (instruction_->type()) {
case MFInstructionType::Call: {
static_cast<MFCallInstruction *>(instruction_)->set_next(new_instruction);
break;
}
case MFInstructionType::Branch: {
MFBranchInstruction &branch_instruction = *static_cast<MFBranchInstruction *>(
instruction_);
if (branch_output_) {
branch_instruction.set_branch_true(new_instruction);
}
else {
branch_instruction.set_branch_false(new_instruction);
}
break;
}
case MFInstructionType::Destruct: {
static_cast<MFDestructInstruction *>(instruction_)->set_next(new_instruction);
break;
}
case MFInstructionType::Dummy: {
static_cast<MFDummyInstruction *>(instruction_)->set_next(new_instruction);
break;
}
case MFInstructionType::Return: {
/* It shouldn't be possible to build a cursor that points to a return instruction. */
BLI_assert_unreachable();
break;
}
}
}
}
void MFProcedureBuilder::add_destruct(MFVariable &variable)
{
MFDestructInstruction &instruction = procedure_->new_destruct_instruction();
instruction.set_variable(&variable);
this->link_to_cursors(&instruction);
cursors_ = {MFInstructionCursor{instruction}};
}
void MFProcedureBuilder::add_destruct(Span<MFVariable *> variables)
{
for (MFVariable *variable : variables) {
this->add_destruct(*variable);
}
}
MFReturnInstruction &MFProcedureBuilder::add_return()
{
MFReturnInstruction &instruction = procedure_->new_return_instruction();
this->link_to_cursors(&instruction);
cursors_ = {};
return instruction;
}
MFCallInstruction &MFProcedureBuilder::add_call_with_no_variables(const MultiFunction &fn)
{
MFCallInstruction &instruction = procedure_->new_call_instruction(fn);
this->link_to_cursors(&instruction);
cursors_ = {MFInstructionCursor{instruction}};
return instruction;
}
MFCallInstruction &MFProcedureBuilder::add_call_with_all_variables(
const MultiFunction &fn, Span<MFVariable *> param_variables)
{
MFCallInstruction &instruction = this->add_call_with_no_variables(fn);
instruction.set_params(param_variables);
return instruction;
}
Vector<MFVariable *> MFProcedureBuilder::add_call(const MultiFunction &fn,
Span<MFVariable *> input_and_mutable_variables)
{
Vector<MFVariable *> output_variables;
MFCallInstruction &instruction = this->add_call_with_no_variables(fn);
for (const int param_index : fn.param_indices()) {
const MFParamType param_type = fn.param_type(param_index);
switch (param_type.interface_type()) {
case MFParamType::Input:
case MFParamType::Mutable: {
MFVariable *variable = input_and_mutable_variables.first();
instruction.set_param_variable(param_index, variable);
input_and_mutable_variables = input_and_mutable_variables.drop_front(1);
break;
}
case MFParamType::Output: {
MFVariable &variable = procedure_->new_variable(param_type.data_type());
instruction.set_param_variable(param_index, &variable);
output_variables.append(&variable);
break;
}
}
}
/* All passed in variables should have been dropped in the loop above. */
BLI_assert(input_and_mutable_variables.is_empty());
return output_variables;
}
MFProcedureBuilder::Branch MFProcedureBuilder::add_branch(MFVariable &condition)
{
MFBranchInstruction &instruction = procedure_->new_branch_instruction();
instruction.set_condition(&condition);
this->link_to_cursors(&instruction);
/* Clear cursors because this builder ends here. */
cursors_.clear();
Branch branch{*procedure_, *procedure_};
branch.branch_true.set_cursor(MFInstructionCursor{instruction, true});
branch.branch_false.set_cursor(MFInstructionCursor{instruction, false});
return branch;
}
MFProcedureBuilder::Loop MFProcedureBuilder::add_loop()
{
MFDummyInstruction &loop_begin = procedure_->new_dummy_instruction();
MFDummyInstruction &loop_end = procedure_->new_dummy_instruction();
this->link_to_cursors(&loop_begin);
cursors_ = {MFInstructionCursor{loop_begin}};
Loop loop;
loop.begin = &loop_begin;
loop.end = &loop_end;
return loop;
}
void MFProcedureBuilder::add_loop_continue(Loop &loop)
{
this->link_to_cursors(loop.begin);
/* Clear cursors because this builder ends here. */
cursors_.clear();
}
void MFProcedureBuilder::add_loop_break(Loop &loop)
{
this->link_to_cursors(loop.end);
/* Clear cursors because this builder ends here. */
cursors_.clear();
}
} // namespace blender::fn

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,344 @@
/* Apache License, Version 2.0 */
#include "testing/testing.h"
#include "FN_multi_function_builder.hh"
#include "FN_multi_function_procedure_builder.hh"
#include "FN_multi_function_procedure_executor.hh"
#include "FN_multi_function_test_common.hh"
namespace blender::fn::tests {
TEST(multi_function_procedure, SimpleTest)
{
/**
* procedure(int var1, int var2, int *var4) {
* int var3 = var1 + var2;
* var4 = var2 + var3;
* var4 += 10;
* }
*/
CustomMF_SI_SI_SO<int, int, int> add_fn{"add", [](int a, int b) { return a + b; }};
CustomMF_SM<int> add_10_fn{"add_10", [](int &a) { a += 10; }};
MFProcedure procedure;
MFProcedureBuilder builder{procedure};
MFVariable *var1 = &builder.add_single_input_parameter<int>();
MFVariable *var2 = &builder.add_single_input_parameter<int>();
auto [var3] = builder.add_call<1>(add_fn, {var1, var2});
auto [var4] = builder.add_call<1>(add_fn, {var2, var3});
builder.add_call(add_10_fn, {var4});
builder.add_destruct({var1, var2, var3});
builder.add_return();
builder.add_output_parameter(*var4);
EXPECT_TRUE(procedure.validate());
MFProcedureExecutor executor{"My Procedure", procedure};
MFParamsBuilder params{executor, 3};
MFContextBuilder context;
Array<int> input_array = {1, 2, 3};
params.add_readonly_single_input(input_array.as_span());
params.add_readonly_single_input_value(3);
Array<int> output_array(3);
params.add_uninitialized_single_output(output_array.as_mutable_span());
executor.call(IndexRange(3), params, context);
EXPECT_EQ(output_array[0], 17);
EXPECT_EQ(output_array[1], 18);
EXPECT_EQ(output_array[2], 19);
}
TEST(multi_function_procedure, BranchTest)
{
/**
* procedure(int &var1, bool var2) {
* if (var2) {
* var1 += 100;
* }
* else {
* var1 += 10;
* }
* var1 += 10;
* }
*/
CustomMF_SM<int> add_10_fn{"add_10", [](int &a) { a += 10; }};
CustomMF_SM<int> add_100_fn{"add_100", [](int &a) { a += 100; }};
MFProcedure procedure;
MFProcedureBuilder builder{procedure};
MFVariable *var1 = &builder.add_single_mutable_parameter<int>();
MFVariable *var2 = &builder.add_single_input_parameter<bool>();
MFProcedureBuilder::Branch branch = builder.add_branch(*var2);
branch.branch_false.add_call(add_10_fn, {var1});
branch.branch_true.add_call(add_100_fn, {var1});
builder.set_cursor_after_branch(branch);
builder.add_call(add_10_fn, {var1});
builder.add_destruct({var2});
builder.add_return();
EXPECT_TRUE(procedure.validate());
MFProcedureExecutor procedure_fn{"Condition Test", procedure};
MFParamsBuilder params(procedure_fn, 5);
Array<int> values_a = {1, 5, 3, 6, 2};
Array<bool> values_cond = {true, false, true, true, false};
params.add_single_mutable(values_a.as_mutable_span());
params.add_readonly_single_input(values_cond.as_span());
MFContextBuilder context;
procedure_fn.call({1, 2, 3, 4}, params, context);
EXPECT_EQ(values_a[0], 1);
EXPECT_EQ(values_a[1], 25);
EXPECT_EQ(values_a[2], 113);
EXPECT_EQ(values_a[3], 116);
EXPECT_EQ(values_a[4], 22);
}
TEST(multi_function_procedure, EvaluateOne)
{
/**
* procedure(int var1, int *var2) {
* var2 = var1 + 10;
* }
*/
int tot_evaluations = 0;
CustomMF_SI_SO<int, int> add_10_fn{"add_10", [&](int a) {
tot_evaluations++;
return a + 10;
}};
MFProcedure procedure;
MFProcedureBuilder builder{procedure};
MFVariable *var1 = &builder.add_single_input_parameter<int>();
auto [var2] = builder.add_call<1>(add_10_fn, {var1});
builder.add_destruct(*var1);
builder.add_return();
builder.add_output_parameter(*var2);
MFProcedureExecutor procedure_fn{"Evaluate One", procedure};
MFParamsBuilder params{procedure_fn, 5};
Array<int> values_out = {1, 2, 3, 4, 5};
params.add_readonly_single_input_value(1);
params.add_uninitialized_single_output(values_out.as_mutable_span());
MFContextBuilder context;
procedure_fn.call({0, 1, 3, 4}, params, context);
EXPECT_EQ(values_out[0], 11);
EXPECT_EQ(values_out[1], 11);
EXPECT_EQ(values_out[2], 3);
EXPECT_EQ(values_out[3], 11);
EXPECT_EQ(values_out[4], 11);
/* We expect only one evaluation, because the input is constant. */
EXPECT_EQ(tot_evaluations, 1);
}
TEST(multi_function_procedure, SimpleLoop)
{
/**
* procedure(int count, int *out) {
* out = 1;
* int index = 0'
* loop {
* if (index >= count) {
* break;
* }
* out *= 2;
* index += 1;
* }
* out += 1000;
* }
*/
CustomMF_Constant<int> const_1_fn{1};
CustomMF_Constant<int> const_0_fn{0};
CustomMF_SI_SI_SO<int, int, bool> greater_or_equal_fn{"greater or equal",
[](int a, int b) { return a >= b; }};
CustomMF_SM<int> double_fn{"double", [](int &a) { a *= 2; }};
CustomMF_SM<int> add_1000_fn{"add 1000", [](int &a) { a += 1000; }};
CustomMF_SM<int> add_1_fn{"add 1", [](int &a) { a += 1; }};
MFProcedure procedure;
MFProcedureBuilder builder{procedure};
MFVariable *var_count = &builder.add_single_input_parameter<int>("count");
auto [var_out] = builder.add_call<1>(const_1_fn);
var_out->set_name("out");
auto [var_index] = builder.add_call<1>(const_0_fn);
var_index->set_name("index");
MFProcedureBuilder::Loop loop = builder.add_loop();
auto [var_condition] = builder.add_call<1>(greater_or_equal_fn, {var_index, var_count});
var_condition->set_name("condition");
MFProcedureBuilder::Branch branch = builder.add_branch(*var_condition);
branch.branch_true.add_destruct(*var_condition);
branch.branch_true.add_loop_break(loop);
branch.branch_false.add_destruct(*var_condition);
builder.set_cursor_after_branch(branch);
builder.add_call(double_fn, {var_out});
builder.add_call(add_1_fn, {var_index});
builder.add_loop_continue(loop);
builder.set_cursor_after_loop(loop);
builder.add_call(add_1000_fn, {var_out});
builder.add_destruct({var_count, var_index});
builder.add_return();
builder.add_output_parameter(*var_out);
EXPECT_TRUE(procedure.validate());
MFProcedureExecutor procedure_fn{"Simple Loop", procedure};
MFParamsBuilder params{procedure_fn, 5};
Array<int> counts = {4, 3, 7, 6, 4};
Array<int> results(5, -1);
params.add_readonly_single_input(counts.as_span());
params.add_uninitialized_single_output(results.as_mutable_span());
MFContextBuilder context;
procedure_fn.call({0, 1, 3, 4}, params, context);
EXPECT_EQ(results[0], 1016);
EXPECT_EQ(results[1], 1008);
EXPECT_EQ(results[2], -1);
EXPECT_EQ(results[3], 1064);
EXPECT_EQ(results[4], 1016);
}
TEST(multi_function_procedure, Vectors)
{
/**
* procedure(vector<int> v1, vector<int> &v2, vector<int> *v3) {
* v1.extend(v2);
* int constant = 5;
* v2.append(constant);
* v2.extend(v1);
* int len = sum(v2);
* v3 = range(len);
* }
*/
CreateRangeFunction create_range_fn;
ConcatVectorsFunction extend_fn;
GenericAppendFunction append_fn{CPPType::get<int>()};
SumVectorFunction sum_elements_fn;
CustomMF_Constant<int> constant_5_fn{5};
MFProcedure procedure;
MFProcedureBuilder builder{procedure};
MFVariable *var_v1 = &builder.add_input_parameter(MFDataType::ForVector<int>());
MFVariable *var_v2 = &builder.add_parameter(MFParamType::ForMutableVector(CPPType::get<int>()));
builder.add_call(extend_fn, {var_v1, var_v2});
auto [var_constant] = builder.add_call<1>(constant_5_fn);
builder.add_call(append_fn, {var_v2, var_constant});
builder.add_destruct(*var_constant);
builder.add_call(extend_fn, {var_v2, var_v1});
auto [var_len] = builder.add_call<1>(sum_elements_fn, {var_v2});
auto [var_v3] = builder.add_call<1>(create_range_fn, {var_len});
builder.add_destruct({var_v1, var_len});
builder.add_return();
builder.add_output_parameter(*var_v3);
EXPECT_TRUE(procedure.validate());
MFProcedureExecutor procedure_fn{"Vectors", procedure};
MFParamsBuilder params{procedure_fn, 5};
Array<int> v1 = {5, 2, 3};
GVectorArray v2{CPPType::get<int>(), 5};
GVectorArray v3{CPPType::get<int>(), 5};
int value_10 = 10;
v2.append(0, &value_10);
v2.append(4, &value_10);
params.add_readonly_vector_input(v1.as_span());
params.add_vector_mutable(v2);
params.add_vector_output(v3);
MFContextBuilder context;
procedure_fn.call({0, 1, 3, 4}, params, context);
EXPECT_EQ(v2[0].size(), 6);
EXPECT_EQ(v2[1].size(), 4);
EXPECT_EQ(v2[2].size(), 0);
EXPECT_EQ(v2[3].size(), 4);
EXPECT_EQ(v2[4].size(), 6);
EXPECT_EQ(v3[0].size(), 35);
EXPECT_EQ(v3[1].size(), 15);
EXPECT_EQ(v3[2].size(), 0);
EXPECT_EQ(v3[3].size(), 15);
EXPECT_EQ(v3[4].size(), 35);
}
TEST(multi_function_procedure, BufferReuse)
{
/**
* procedure(int a, int *out) {
* int b = a + 10;
* int c = c + 10;
* int d = d + 10;
* int e = d + 10;
* out = e + 10;
* }
*/
CustomMF_SI_SO<int, int> add_10_fn{"add 10", [](int a) { return a + 10; }};
MFProcedure procedure;
MFProcedureBuilder builder{procedure};
MFVariable *var_a = &builder.add_single_input_parameter<int>();
auto [var_b] = builder.add_call<1>(add_10_fn, {var_a});
builder.add_destruct(*var_a);
auto [var_c] = builder.add_call<1>(add_10_fn, {var_b});
builder.add_destruct(*var_b);
auto [var_d] = builder.add_call<1>(add_10_fn, {var_c});
builder.add_destruct(*var_c);
auto [var_e] = builder.add_call<1>(add_10_fn, {var_d});
builder.add_destruct(*var_d);
auto [var_out] = builder.add_call<1>(add_10_fn, {var_e});
builder.add_destruct(*var_e);
builder.add_return();
builder.add_output_parameter(*var_out);
EXPECT_TRUE(procedure.validate());
MFProcedureExecutor procedure_fn{"Buffer Reuse", procedure};
Array<int> inputs = {4, 1, 6, 2, 3};
Array<int> results(5, -1);
MFParamsBuilder params{procedure_fn, 5};
params.add_readonly_single_input(inputs.as_span());
params.add_uninitialized_single_output(results.as_mutable_span());
MFContextBuilder context;
procedure_fn.call({0, 2, 3, 4}, params, context);
EXPECT_EQ(results[0], 54);
EXPECT_EQ(results[1], -1);
EXPECT_EQ(results[2], 56);
EXPECT_EQ(results[3], 52);
EXPECT_EQ(results[4], 53);
}
} // namespace blender::fn::tests

View File

@@ -31,6 +31,8 @@
extern "C" {
#endif
struct AnonymousAttributeID;
/** Descriptor and storage for a custom data layer. */
typedef struct CustomDataLayer {
/** Type of data in layer. */
@@ -53,6 +55,9 @@ typedef struct CustomDataLayer {
char name[64];
/** Layer data. */
void *data;
/** Run-time identifier for this layer. If no
* one has a strong reference to this id anymore, the layer can be removed. */
const struct AnonymousAttributeID *anonymous_id;
} CustomDataLayer;
#define MAX_CUSTOMDATA_LAYER_NAME 64

View File

@@ -32,10 +32,13 @@ struct ModifierData;
namespace blender::nodes {
using bke::AttributeIDRef;
using bke::geometry_set_realize_instances;
using bke::OutputAttribute;
using bke::OutputAttribute_Typed;
using bke::ReadAttributeLookup;
using bke::StrongAnonymousAttributeID;
using bke::WeakAnonymousAttributeID;
using bke::WriteAttributeLookup;
using fn::CPPType;
using fn::GMutablePointer;

View File

@@ -76,7 +76,7 @@ struct CurveToPointsResults {
MutableSpan<float> radii;
MutableSpan<float> tilts;
Map<std::string, GMutableSpan> point_attributes;
Map<AttributeIDRef, GMutableSpan> point_attributes;
MutableSpan<float3> tangents;
MutableSpan<float3> normals;

View File

@@ -60,25 +60,26 @@ static void copy_spline_domain_attributes(const CurveComponent &curve_component,
Span<int> offsets,
PointCloudComponent &points)
{
curve_component.attribute_foreach([&](StringRefNull name, const AttributeMetaData &meta_data) {
if (meta_data.domain != ATTR_DOMAIN_CURVE) {
return true;
}
GVArrayPtr spline_attribute = curve_component.attribute_get_for_read(
name, ATTR_DOMAIN_CURVE, meta_data.data_type);
curve_component.attribute_foreach(
[&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) {
if (meta_data.domain != ATTR_DOMAIN_CURVE) {
return true;
}
GVArrayPtr spline_attribute = curve_component.attribute_get_for_read(
attribute_id, ATTR_DOMAIN_CURVE, meta_data.data_type);
OutputAttribute result_attribute = points.attribute_try_get_for_output_only(
name, ATTR_DOMAIN_POINT, meta_data.data_type);
GMutableSpan result = result_attribute.as_span();
OutputAttribute result_attribute = points.attribute_try_get_for_output_only(
attribute_id, ATTR_DOMAIN_POINT, meta_data.data_type);
GMutableSpan result = result_attribute.as_span();
/* Only copy the attributes of splines in the offsets. */
for (const int i : offsets.index_range()) {
spline_attribute->get(offsets[i], result[i]);
}
/* Only copy the attributes of splines in the offsets. */
for (const int i : offsets.index_range()) {
spline_attribute->get(offsets[i], result[i]);
}
result_attribute.save();
return true;
});
result_attribute.save();
return true;
});
}
/**
@@ -128,20 +129,20 @@ static void copy_endpoint_attributes(Span<SplinePtr> splines,
/* Copy the point attribute data over. */
for (const auto &item : start_data.point_attributes.items()) {
const StringRef name = item.key;
const AttributeIDRef attribute_id = item.key;
GMutableSpan point_span = item.value;
BLI_assert(spline.attributes.get_for_read(name));
GSpan spline_span = *spline.attributes.get_for_read(name);
BLI_assert(spline.attributes.get_for_read(attribute_id));
GSpan spline_span = *spline.attributes.get_for_read(attribute_id);
blender::fn::GVArray_For_GSpan(spline_span).get(0, point_span[i]);
}
for (const auto &item : end_data.point_attributes.items()) {
const StringRef name = item.key;
const AttributeIDRef attribute_id = item.key;
GMutableSpan point_span = item.value;
BLI_assert(spline.attributes.get_for_read(name));
GSpan spline_span = *spline.attributes.get_for_read(name);
BLI_assert(spline.attributes.get_for_read(attribute_id));
GSpan spline_span = *spline.attributes.get_for_read(attribute_id);
blender::fn::GVArray_For_GSpan(spline_span).get(spline.size() - 1, point_span[i]);
}
}

View File

@@ -88,14 +88,14 @@ static SplinePtr resample_spline(const Spline &input_spline, const int count)
input_spline.radii().first());
output_spline->attributes.reallocate(1);
input_spline.attributes.foreach_attribute(
[&](StringRefNull name, const AttributeMetaData &meta_data) {
std::optional<GSpan> src = input_spline.attributes.get_for_read(name);
[&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) {
std::optional<GSpan> src = input_spline.attributes.get_for_read(attribute_id);
BLI_assert(src);
if (!output_spline->attributes.create(name, meta_data.data_type)) {
if (!output_spline->attributes.create(attribute_id, meta_data.data_type)) {
BLI_assert_unreachable();
return false;
}
std::optional<GMutableSpan> dst = output_spline->attributes.get_for_write(name);
std::optional<GMutableSpan> dst = output_spline->attributes.get_for_write(attribute_id);
if (!dst) {
BLI_assert_unreachable();
return false;
@@ -126,15 +126,15 @@ static SplinePtr resample_spline(const Spline &input_spline, const int count)
output_spline->attributes.reallocate(count);
input_spline.attributes.foreach_attribute(
[&](StringRefNull name, const AttributeMetaData &meta_data) {
std::optional<GSpan> input_attribute = input_spline.attributes.get_for_read(name);
[&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) {
std::optional<GSpan> input_attribute = input_spline.attributes.get_for_read(attribute_id);
BLI_assert(input_attribute);
if (!output_spline->attributes.create(name, meta_data.data_type)) {
if (!output_spline->attributes.create(attribute_id, meta_data.data_type)) {
BLI_assert_unreachable();
return false;
}
std::optional<GMutableSpan> output_attribute = output_spline->attributes.get_for_write(
name);
attribute_id);
if (!output_attribute) {
BLI_assert_unreachable();
return false;

View File

@@ -87,9 +87,9 @@ static void geo_node_curve_reverse_exec(GeoNodeExecParams params)
reverse_data<float>(splines[i]->tilts());
splines[i]->attributes.foreach_attribute(
[&](StringRefNull name, const AttributeMetaData &meta_data) {
[&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) {
std::optional<blender::fn::GMutableSpan> output_attribute =
splines[i]->attributes.get_for_write(name);
splines[i]->attributes.get_for_write(attribute_id);
if (!output_attribute) {
BLI_assert_unreachable();
return false;

View File

@@ -78,14 +78,14 @@ template<typename CopyFn>
static void copy_attributes(const Spline &input_spline, Spline &output_spline, CopyFn copy_fn)
{
input_spline.attributes.foreach_attribute(
[&](StringRefNull name, const AttributeMetaData &meta_data) {
std::optional<GSpan> src = input_spline.attributes.get_for_read(name);
[&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) {
std::optional<GSpan> src = input_spline.attributes.get_for_read(attribute_id);
BLI_assert(src);
if (!output_spline.attributes.create(name, meta_data.data_type)) {
if (!output_spline.attributes.create(attribute_id, meta_data.data_type)) {
BLI_assert_unreachable();
return false;
}
std::optional<GMutableSpan> dst = output_spline.attributes.get_for_write(name);
std::optional<GMutableSpan> dst = output_spline.attributes.get_for_write(attribute_id);
if (!dst) {
BLI_assert_unreachable();
return false;

View File

@@ -287,16 +287,16 @@ static void subdivide_dynamic_attributes(const Spline &src_spline,
{
const bool is_cyclic = src_spline.is_cyclic();
src_spline.attributes.foreach_attribute(
[&](StringRefNull name, const AttributeMetaData &meta_data) {
std::optional<GSpan> src = src_spline.attributes.get_for_read(name);
[&](const bke::AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) {
std::optional<GSpan> src = src_spline.attributes.get_for_read(attribute_id);
BLI_assert(src);
if (!dst_spline.attributes.create(name, meta_data.data_type)) {
if (!dst_spline.attributes.create(attribute_id, meta_data.data_type)) {
/* Since the source spline of the same type had the attribute, adding it should work. */
BLI_assert_unreachable();
}
std::optional<GMutableSpan> dst = dst_spline.attributes.get_for_write(name);
std::optional<GMutableSpan> dst = dst_spline.attributes.get_for_write(attribute_id);
BLI_assert(dst);
attribute_math::convert_to_static_type(dst->type(), [&](auto dummy) {

View File

@@ -119,21 +119,21 @@ static Array<int> calculate_spline_point_offsets(GeoNodeExecParams &params,
}
static GMutableSpan create_attribute_and_retrieve_span(PointCloudComponent &points,
const StringRef name,
const AttributeIDRef &attribute_id,
const CustomDataType data_type)
{
points.attribute_try_create(name, ATTR_DOMAIN_POINT, data_type, AttributeInitDefault());
WriteAttributeLookup attribute = points.attribute_try_get_for_write(name);
points.attribute_try_create(attribute_id, ATTR_DOMAIN_POINT, data_type, AttributeInitDefault());
WriteAttributeLookup attribute = points.attribute_try_get_for_write(attribute_id);
BLI_assert(attribute);
return attribute.varray->get_internal_span();
}
template<typename T>
static MutableSpan<T> create_attribute_and_retrieve_span(PointCloudComponent &points,
const StringRef name)
const AttributeIDRef &attribute_id)
{
GMutableSpan attribute = create_attribute_and_retrieve_span(
points, name, bke::cpp_type_to_custom_data_type(CPPType::get<T>()));
points, attribute_id, bke::cpp_type_to_custom_data_type(CPPType::get<T>()));
return attribute.typed<T>();
}
@@ -151,9 +151,10 @@ CurveToPointsResults curve_to_points_create_result_attributes(PointCloudComponen
/* Because of the invariants of the curve component, we use the attributes of the
* first spline as a representative for the attribute meta data all splines. */
curve.splines().first()->attributes.foreach_attribute(
[&](StringRefNull name, const AttributeMetaData &meta_data) {
[&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) {
attributes.point_attributes.add_new(
name, create_attribute_and_retrieve_span(points, name, meta_data.data_type));
attribute_id,
create_attribute_and_retrieve_span(points, attribute_id, meta_data.data_type));
return true;
},
ATTR_DOMAIN_POINT);
@@ -183,12 +184,12 @@ static void copy_evaluated_point_attributes(Span<SplinePtr> splines,
spline.interpolate_to_evaluated(spline.radii())->materialize(data.radii.slice(offset, size));
spline.interpolate_to_evaluated(spline.tilts())->materialize(data.tilts.slice(offset, size));
for (const Map<std::string, GMutableSpan>::Item &item : data.point_attributes.items()) {
const StringRef name = item.key;
for (const Map<AttributeIDRef, GMutableSpan>::Item &item : data.point_attributes.items()) {
const AttributeIDRef attribute_id = item.key;
GMutableSpan point_span = item.value;
BLI_assert(spline.attributes.get_for_read(name));
GSpan spline_span = *spline.attributes.get_for_read(name);
BLI_assert(spline.attributes.get_for_read(attribute_id));
GSpan spline_span = *spline.attributes.get_for_read(attribute_id);
spline.interpolate_to_evaluated(spline_span)
->materialize(point_span.slice(offset, size).data());
@@ -226,12 +227,12 @@ static void copy_uniform_sample_point_attributes(Span<SplinePtr> splines,
uniform_samples,
data.tilts.slice(offset, size));
for (const Map<std::string, GMutableSpan>::Item &item : data.point_attributes.items()) {
const StringRef name = item.key;
for (const Map<AttributeIDRef, GMutableSpan>::Item &item : data.point_attributes.items()) {
const AttributeIDRef attribute_id = item.key;
GMutableSpan point_span = item.value;
BLI_assert(spline.attributes.get_for_read(name));
GSpan spline_span = *spline.attributes.get_for_read(name);
BLI_assert(spline.attributes.get_for_read(attribute_id));
GSpan spline_span = *spline.attributes.get_for_read(attribute_id);
spline.sample_with_index_factors(*spline.interpolate_to_evaluated(spline_span),
uniform_samples,
@@ -261,31 +262,32 @@ static void copy_spline_domain_attributes(const CurveComponent &curve_component,
Span<int> offsets,
PointCloudComponent &points)
{
curve_component.attribute_foreach([&](StringRefNull name, const AttributeMetaData &meta_data) {
if (meta_data.domain != ATTR_DOMAIN_CURVE) {
return true;
}
GVArrayPtr spline_attribute = curve_component.attribute_get_for_read(
name, ATTR_DOMAIN_CURVE, meta_data.data_type);
const CPPType &type = spline_attribute->type();
curve_component.attribute_foreach(
[&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) {
if (meta_data.domain != ATTR_DOMAIN_CURVE) {
return true;
}
GVArrayPtr spline_attribute = curve_component.attribute_get_for_read(
attribute_id, ATTR_DOMAIN_CURVE, meta_data.data_type);
const CPPType &type = spline_attribute->type();
OutputAttribute result_attribute = points.attribute_try_get_for_output_only(
name, ATTR_DOMAIN_POINT, meta_data.data_type);
GMutableSpan result = result_attribute.as_span();
OutputAttribute result_attribute = points.attribute_try_get_for_output_only(
attribute_id, ATTR_DOMAIN_POINT, meta_data.data_type);
GMutableSpan result = result_attribute.as_span();
for (const int i : IndexRange(spline_attribute->size())) {
const int offset = offsets[i];
const int size = offsets[i + 1] - offsets[i];
if (size != 0) {
BUFFER_FOR_CPP_TYPE_VALUE(type, buffer);
spline_attribute->get(i, buffer);
type.fill_assign_n(buffer, result[offset], size);
}
}
for (const int i : IndexRange(spline_attribute->size())) {
const int offset = offsets[i];
const int size = offsets[i + 1] - offsets[i];
if (size != 0) {
BUFFER_FOR_CPP_TYPE_VALUE(type, buffer);
spline_attribute->get(i, buffer);
type.fill_assign_n(buffer, result[offset], size);
}
}
result_attribute.save();
return true;
});
result_attribute.save();
return true;
});
}
void curve_create_default_rotation_attribute(Span<float3> tangents,

View File

@@ -162,8 +162,8 @@ static void trim_poly_spline(Spline &spline,
linear_trim_data<float>(start, end, spline.tilts());
spline.attributes.foreach_attribute(
[&](StringRefNull name, const AttributeMetaData &UNUSED(meta_data)) {
std::optional<GMutableSpan> src = spline.attributes.get_for_write(name);
[&](const AttributeIDRef &attribute_id, const AttributeMetaData &UNUSED(meta_data)) {
std::optional<GMutableSpan> src = spline.attributes.get_for_write(attribute_id);
BLI_assert(src);
attribute_math::convert_to_static_type(src->type(), [&](auto dummy) {
using T = decltype(dummy);
@@ -197,14 +197,14 @@ static PolySpline trim_nurbs_spline(const Spline &spline,
/* Copy generic attribute data. */
spline.attributes.foreach_attribute(
[&](StringRefNull name, const AttributeMetaData &meta_data) {
std::optional<GSpan> src = spline.attributes.get_for_read(name);
[&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) {
std::optional<GSpan> src = spline.attributes.get_for_read(attribute_id);
BLI_assert(src);
if (!new_spline.attributes.create(name, meta_data.data_type)) {
if (!new_spline.attributes.create(attribute_id, meta_data.data_type)) {
BLI_assert_unreachable();
return false;
}
std::optional<GMutableSpan> dst = new_spline.attributes.get_for_write(name);
std::optional<GMutableSpan> dst = new_spline.attributes.get_for_write(attribute_id);
BLI_assert(dst);
attribute_math::convert_to_static_type(src->type(), [&](auto dummy) {
@@ -253,8 +253,8 @@ static void trim_bezier_spline(Spline &spline,
linear_trim_data<float>(start, end, bezier_spline.radii());
linear_trim_data<float>(start, end, bezier_spline.tilts());
spline.attributes.foreach_attribute(
[&](StringRefNull name, const AttributeMetaData &UNUSED(meta_data)) {
std::optional<GMutableSpan> src = spline.attributes.get_for_write(name);
[&](const AttributeIDRef &attribute_id, const AttributeMetaData &UNUSED(meta_data)) {
std::optional<GMutableSpan> src = spline.attributes.get_for_write(attribute_id);
BLI_assert(src);
attribute_math::convert_to_static_type(src->type(), [&](auto dummy) {
using T = decltype(dummy);

View File

@@ -97,16 +97,16 @@ static void copy_dynamic_attributes(const CustomDataAttributes &src,
const IndexMask mask)
{
src.foreach_attribute(
[&](StringRefNull name, const AttributeMetaData &meta_data) {
std::optional<GSpan> src_attribute = src.get_for_read(name);
[&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) {
std::optional<GSpan> src_attribute = src.get_for_read(attribute_id);
BLI_assert(src_attribute);
if (!dst.create(name, meta_data.data_type)) {
if (!dst.create(attribute_id, meta_data.data_type)) {
/* Since the source spline of the same type had the attribute, adding it should work. */
BLI_assert_unreachable();
}
std::optional<GMutableSpan> new_attribute = dst.get_for_write(name);
std::optional<GMutableSpan> new_attribute = dst.get_for_write(attribute_id);
BLI_assert(new_attribute);
attribute_math::convert_to_static_type(new_attribute->type(), [&](auto dummy) {

View File

@@ -161,34 +161,35 @@ static Array<const GeometryComponent *> to_base_components(Span<const Component
return components;
}
static Map<std::string, AttributeMetaData> get_final_attribute_info(
static Map<AttributeIDRef, AttributeMetaData> get_final_attribute_info(
Span<const GeometryComponent *> components, Span<StringRef> ignored_attributes)
{
Map<std::string, AttributeMetaData> info;
Map<AttributeIDRef, AttributeMetaData> info;
for (const GeometryComponent *component : components) {
component->attribute_foreach([&](StringRefNull name, const AttributeMetaData &meta_data) {
if (ignored_attributes.contains(name)) {
return true;
}
info.add_or_modify(
name,
[&](AttributeMetaData *meta_data_final) { *meta_data_final = meta_data; },
[&](AttributeMetaData *meta_data_final) {
meta_data_final->data_type = blender::bke::attribute_data_type_highest_complexity(
{meta_data_final->data_type, meta_data.data_type});
meta_data_final->domain = blender::bke::attribute_domain_highest_priority(
{meta_data_final->domain, meta_data.domain});
});
return true;
});
component->attribute_foreach(
[&](const bke::AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) {
if (attribute_id.is_named() && ignored_attributes.contains(attribute_id.name())) {
return true;
}
info.add_or_modify(
attribute_id,
[&](AttributeMetaData *meta_data_final) { *meta_data_final = meta_data; },
[&](AttributeMetaData *meta_data_final) {
meta_data_final->data_type = blender::bke::attribute_data_type_highest_complexity(
{meta_data_final->data_type, meta_data.data_type});
meta_data_final->domain = blender::bke::attribute_domain_highest_priority(
{meta_data_final->domain, meta_data.domain});
});
return true;
});
}
return info;
}
static void fill_new_attribute(Span<const GeometryComponent *> src_components,
StringRef attribute_name,
const AttributeIDRef &attribute_id,
const CustomDataType data_type,
const AttributeDomain domain,
GMutableSpan dst_span)
@@ -203,7 +204,7 @@ static void fill_new_attribute(Span<const GeometryComponent *> src_components,
continue;
}
GVArrayPtr read_attribute = component->attribute_get_for_read(
attribute_name, domain, data_type, nullptr);
attribute_id, domain, data_type, nullptr);
GVArray_GSpan src_span{*read_attribute};
const void *src_buffer = src_span.data();
@@ -218,20 +219,21 @@ static void join_attributes(Span<const GeometryComponent *> src_components,
GeometryComponent &result,
Span<StringRef> ignored_attributes = {})
{
const Map<std::string, AttributeMetaData> info = get_final_attribute_info(src_components,
ignored_attributes);
const Map<AttributeIDRef, AttributeMetaData> info = get_final_attribute_info(src_components,
ignored_attributes);
for (const Map<std::string, AttributeMetaData>::Item &item : info.items()) {
const StringRef name = item.key;
for (const Map<AttributeIDRef, AttributeMetaData>::Item &item : info.items()) {
const AttributeIDRef attribute_id = item.key;
const AttributeMetaData &meta_data = item.value;
OutputAttribute write_attribute = result.attribute_try_get_for_output_only(
name, meta_data.domain, meta_data.data_type);
attribute_id, meta_data.domain, meta_data.data_type);
if (!write_attribute) {
continue;
}
GMutableSpan dst_span = write_attribute.as_span();
fill_new_attribute(src_components, name, meta_data.data_type, meta_data.domain, dst_span);
fill_new_attribute(
src_components, attribute_id, meta_data.data_type, meta_data.domain, dst_span);
write_attribute.save();
}
}
@@ -306,7 +308,7 @@ static void join_components(Span<const VolumeComponent *> src_components, Geomet
* \note This takes advantage of the fact that creating attributes on joined curves never
* changes a point attribute into a spline attribute; it is always the other way around.
*/
static void ensure_control_point_attribute(const StringRef name,
static void ensure_control_point_attribute(const AttributeIDRef &attribute_id,
const CustomDataType data_type,
Span<CurveComponent *> src_components,
CurveEval &result)
@@ -321,7 +323,7 @@ static void ensure_control_point_attribute(const StringRef name,
const CurveEval *current_curve = src_components[src_component_index]->get_for_read();
for (SplinePtr &spline : splines) {
std::optional<GSpan> attribute = spline->attributes.get_for_read(name);
std::optional<GSpan> attribute = spline->attributes.get_for_read(attribute_id);
if (attribute) {
if (attribute->type() != type) {
@@ -334,22 +336,22 @@ static void ensure_control_point_attribute(const StringRef name,
conversions.try_convert(std::make_unique<GVArray_For_GSpan>(*attribute), type)
->materialize(converted_buffer);
spline->attributes.remove(name);
spline->attributes.create_by_move(name, data_type, converted_buffer);
spline->attributes.remove(attribute_id);
spline->attributes.create_by_move(attribute_id, data_type, converted_buffer);
}
}
else {
spline->attributes.create(name, data_type);
spline->attributes.create(attribute_id, data_type);
if (current_curve->attributes.get_for_read(name)) {
if (current_curve->attributes.get_for_read(attribute_id)) {
/* In this case the attribute did not exist, but there is a spline domain attribute
* we can retrieve a value from, as a spline to point domain conversion. So fill the
* new attribute with the value for this spline. */
GVArrayPtr current_curve_attribute = current_curve->attributes.get_for_read(
name, data_type, nullptr);
attribute_id, data_type, nullptr);
BLI_assert(spline->attributes.get_for_read(name));
std::optional<GMutableSpan> new_attribute = spline->attributes.get_for_write(name);
BLI_assert(spline->attributes.get_for_read(attribute_id));
std::optional<GMutableSpan> new_attribute = spline->attributes.get_for_write(attribute_id);
BUFFER_FOR_CPP_TYPE_VALUE(type, buffer);
current_curve_attribute->get(spline_index_in_component, buffer);
@@ -371,15 +373,15 @@ static void ensure_control_point_attribute(const StringRef name,
/**
* Fill data for an attribute on the new curve based on all source curves.
*/
static void ensure_spline_attribute(const StringRef name,
static void ensure_spline_attribute(const AttributeIDRef &attribute_id,
const CustomDataType data_type,
Span<CurveComponent *> src_components,
CurveEval &result)
{
const CPPType &type = *bke::custom_data_type_to_cpp_type(data_type);
result.attributes.create(name, data_type);
GMutableSpan result_attribute = *result.attributes.get_for_write(name);
result.attributes.create(attribute_id, data_type);
GMutableSpan result_attribute = *result.attributes.get_for_write(attribute_id);
int offset = 0;
for (const CurveComponent *component : src_components) {
@@ -388,7 +390,7 @@ static void ensure_spline_attribute(const StringRef name,
if (size == 0) {
continue;
}
GVArrayPtr read_attribute = curve.attributes.get_for_read(name, data_type, nullptr);
GVArrayPtr read_attribute = curve.attributes.get_for_read(attribute_id, data_type, nullptr);
GVArray_GSpan src_span{*read_attribute};
const void *src_buffer = src_span.data();
@@ -406,19 +408,19 @@ static void ensure_spline_attribute(const StringRef name,
* \warning Splines have been moved out of the source components at this point, so it
* is important to only read curve-level data (spline domain attributes) from them.
*/
static void join_curve_attributes(const Map<std::string, AttributeMetaData> &info,
static void join_curve_attributes(const Map<AttributeIDRef, AttributeMetaData> &info,
Span<CurveComponent *> src_components,
CurveEval &result)
{
for (const Map<std::string, AttributeMetaData>::Item &item : info.items()) {
const StringRef name = item.key;
for (const Map<AttributeIDRef, AttributeMetaData>::Item &item : info.items()) {
const AttributeIDRef attribute_id = item.key;
const AttributeMetaData meta_data = item.value;
if (meta_data.domain == ATTR_DOMAIN_CURVE) {
ensure_spline_attribute(name, meta_data.data_type, src_components, result);
ensure_spline_attribute(attribute_id, meta_data.data_type, src_components, result);
}
else {
ensure_control_point_attribute(name, meta_data.data_type, src_components, result);
ensure_control_point_attribute(attribute_id, meta_data.data_type, src_components, result);
}
}
}
@@ -446,7 +448,7 @@ static void join_curve_components(MutableSpan<GeometrySet> src_geometry_sets, Ge
}
/* Retrieve attribute info before moving the splines out of the input components. */
const Map<std::string, AttributeMetaData> info = get_final_attribute_info(
const Map<AttributeIDRef, AttributeMetaData> info = get_final_attribute_info(
{(const GeometryComponent **)src_components.data(), src_components.size()},
{"position", "radius", "tilt", "cyclic", "resolution"});

View File

@@ -56,10 +56,10 @@ static void copy_attributes_to_points(CurveEval &curve,
Span<Vector<int>> point_to_vert_maps)
{
MutableSpan<SplinePtr> splines = curve.splines();
Set<std::string> source_attribute_names = mesh_component.attribute_names();
Set<AttributeIDRef> source_attribute_ids = mesh_component.attribute_ids();
/* Copy builtin control point attributes. */
if (source_attribute_names.contains_as("tilt")) {
if (source_attribute_ids.contains("tilt")) {
const GVArray_Typed<float> tilt_attribute = mesh_component.attribute_get_for_read<float>(
"tilt", ATTR_DOMAIN_POINT, 0.0f);
threading::parallel_for(splines.index_range(), 256, [&](IndexRange range) {
@@ -68,9 +68,9 @@ static void copy_attributes_to_points(CurveEval &curve,
*tilt_attribute, point_to_vert_maps[i], splines[i]->tilts());
}
});
source_attribute_names.remove_contained_as("tilt");
source_attribute_ids.remove_contained("tilt");
}
if (source_attribute_names.contains_as("radius")) {
if (source_attribute_ids.contains("radius")) {
const GVArray_Typed<float> radius_attribute = mesh_component.attribute_get_for_read<float>(
"radius", ATTR_DOMAIN_POINT, 1.0f);
threading::parallel_for(splines.index_range(), 256, [&](IndexRange range) {
@@ -79,15 +79,15 @@ static void copy_attributes_to_points(CurveEval &curve,
*radius_attribute, point_to_vert_maps[i], splines[i]->radii());
}
});
source_attribute_names.remove_contained_as("radius");
source_attribute_ids.remove_contained("radius");
}
/* Don't copy other builtin control point attributes. */
source_attribute_names.remove_as("position");
source_attribute_ids.remove("position");
/* Copy dynamic control point attributes. */
for (const StringRef name : source_attribute_names) {
const GVArrayPtr mesh_attribute = mesh_component.attribute_try_get_for_read(name,
for (const AttributeIDRef &attribute_id : source_attribute_ids) {
const GVArrayPtr mesh_attribute = mesh_component.attribute_try_get_for_read(attribute_id,
ATTR_DOMAIN_POINT);
/* Some attributes might not exist if they were builtin attribute on domains that don't
* have any elements, i.e. a face attribute on the output of the line primitive node. */
@@ -100,8 +100,9 @@ static void copy_attributes_to_points(CurveEval &curve,
threading::parallel_for(splines.index_range(), 128, [&](IndexRange range) {
for (const int i : range) {
/* Create attribute on the spline points. */
splines[i]->attributes.create(name, data_type);
std::optional<GMutableSpan> spline_attribute = splines[i]->attributes.get_for_write(name);
splines[i]->attributes.create(attribute_id, data_type);
std::optional<GMutableSpan> spline_attribute = splines[i]->attributes.get_for_write(
attribute_id);
BLI_assert(spline_attribute);
/* Copy attribute based on the map for this spline. */

View File

@@ -277,17 +277,17 @@ BLI_NOINLINE static void interpolate_attribute(const Mesh &mesh,
BLI_NOINLINE static void interpolate_existing_attributes(
Span<GeometryInstanceGroup> set_groups,
Span<int> instance_start_offsets,
const Map<std::string, AttributeKind> &attributes,
const Map<AttributeIDRef, AttributeKind> &attributes,
GeometryComponent &component,
Span<Vector<float3>> bary_coords_array,
Span<Vector<int>> looptri_indices_array)
{
for (Map<std::string, AttributeKind>::Item entry : attributes.items()) {
StringRef attribute_name = entry.key;
for (Map<AttributeIDRef, AttributeKind>::Item entry : attributes.items()) {
const AttributeIDRef attribute_id = entry.key;
const CustomDataType output_data_type = entry.value.data_type;
/* The output domain is always #ATTR_DOMAIN_POINT, since we are creating a point cloud. */
OutputAttribute attribute_out = component.attribute_try_get_for_output_only(
attribute_name, ATTR_DOMAIN_POINT, output_data_type);
attribute_id, ATTR_DOMAIN_POINT, output_data_type);
if (!attribute_out) {
continue;
}
@@ -301,7 +301,7 @@ BLI_NOINLINE static void interpolate_existing_attributes(
const Mesh &mesh = *source_component.get_for_read();
std::optional<AttributeMetaData> attribute_info = component.attribute_get_meta_data(
attribute_name);
attribute_id);
if (!attribute_info) {
i_instance += set_group.transforms.size();
continue;
@@ -309,7 +309,7 @@ BLI_NOINLINE static void interpolate_existing_attributes(
const AttributeDomain source_domain = attribute_info->domain;
GVArrayPtr source_attribute = source_component.attribute_get_for_read(
attribute_name, source_domain, output_data_type, nullptr);
attribute_id, source_domain, output_data_type, nullptr);
if (!source_attribute) {
i_instance += set_group.transforms.size();
continue;
@@ -406,7 +406,7 @@ BLI_NOINLINE static void compute_special_attributes(Span<GeometryInstanceGroup>
BLI_NOINLINE static void add_remaining_point_attributes(
Span<GeometryInstanceGroup> set_groups,
Span<int> instance_start_offsets,
const Map<std::string, AttributeKind> &attributes,
const Map<AttributeIDRef, AttributeKind> &attributes,
GeometryComponent &component,
Span<Vector<float3>> bary_coords_array,
Span<Vector<int>> looptri_indices_array)
@@ -629,7 +629,7 @@ static void geo_node_point_distribute_exec(GeoNodeExecParams params)
PointCloudComponent &point_component =
geometry_set_out.get_component_for_write<PointCloudComponent>();
Map<std::string, AttributeKind> attributes;
Map<AttributeIDRef, AttributeKind> attributes;
bke::geometry_set_gather_instances_attribute_info(
set_groups, {GEO_COMPONENT_TYPE_MESH}, {"position", "normal", "id"}, attributes);
add_remaining_point_attributes(set_groups,

View File

@@ -57,8 +57,8 @@ void copy_point_attributes_based_on_mask(const GeometryComponent &in_component,
Span<bool> masks,
const bool invert)
{
for (const std::string &name : in_component.attribute_names()) {
ReadAttributeLookup attribute = in_component.attribute_try_get_for_read(name);
for (const AttributeIDRef &attribute_id : in_component.attribute_ids()) {
ReadAttributeLookup attribute = in_component.attribute_try_get_for_read(attribute_id);
const CustomDataType data_type = bke::cpp_type_to_custom_data_type(attribute.varray->type());
/* Only copy point attributes. Theoretically this could interpolate attributes on other
@@ -69,7 +69,7 @@ void copy_point_attributes_based_on_mask(const GeometryComponent &in_component,
}
OutputAttribute result_attribute = result_component.attribute_try_get_for_output_only(
name, ATTR_DOMAIN_POINT, data_type);
attribute_id, ATTR_DOMAIN_POINT, data_type);
attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
using T = decltype(dummy);

View File

@@ -161,8 +161,10 @@ GeometryValueLog::GeometryValueLog(const GeometrySet &geometry_set, bool log_ful
{
bke::geometry_set_instances_attribute_foreach(
geometry_set,
[&](StringRefNull attribute_name, const AttributeMetaData &meta_data) {
this->attributes_.append({attribute_name, meta_data.domain, meta_data.data_type});
[&](const bke::AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) {
if (attribute_id.is_named()) {
this->attributes_.append({attribute_id.name(), meta_data.domain, meta_data.data_type});
}
return true;
},
8);