diff options
Diffstat (limited to 'source/blender/blenkernel/BKE_attribute_access.hh')
-rw-r--r-- | source/blender/blenkernel/BKE_attribute_access.hh | 382 |
1 files changed, 186 insertions, 196 deletions
diff --git a/source/blender/blenkernel/BKE_attribute_access.hh b/source/blender/blenkernel/BKE_attribute_access.hh index 120b4e08b9c..358daa40723 100644 --- a/source/blender/blenkernel/BKE_attribute_access.hh +++ b/source/blender/blenkernel/BKE_attribute_access.hh @@ -20,299 +20,289 @@ #include "FN_cpp_type.hh" #include "FN_generic_span.hh" +#include "FN_generic_virtual_array.hh" #include "BKE_attribute.h" #include "BLI_color.hh" #include "BLI_float2.hh" #include "BLI_float3.hh" - -namespace blender::bke { - -using fn::CPPType; - -const CPPType *custom_data_type_to_cpp_type(const CustomDataType type); -CustomDataType cpp_type_to_custom_data_type(const CPPType &type); -CustomDataType attribute_data_type_highest_complexity(Span<CustomDataType> data_types); -AttributeDomain attribute_domain_highest_priority(Span<AttributeDomain> domains); +#include "BLI_function_ref.hh" /** - * This class offers an indirection for reading an attribute. - * This is useful for the following reasons: - * - Blender does not store all attributes the same way. - * The simplest case are custom data layers with primitive types. - * A bit more complex are mesh attributes like the position of vertices, - * which are embedded into the MVert struct. - * Even more complex to access are vertex weights. - * - Sometimes attributes are stored on one domain, but we want to access - * the attribute on a different domain. Therefore, we have to interpolate - * between the domains. + * Contains information about an attribute in a geometry component. + * More information can be added in the future. E.g. whether the attribute is builtin and how it is + * stored (uv map, vertex group, ...). */ -class ReadAttribute { - protected: - const AttributeDomain domain_; - const CPPType &cpp_type_; - const CustomDataType custom_data_type_; - const int64_t size_; - - /* Protects the span below, so that no two threads initialize it at the same time. */ - mutable std::mutex span_mutex_; - /* When it is not null, it points to the attribute array or a temporary array that contains all - * the attribute values. */ - mutable void *array_buffer_ = nullptr; - /* Is true when the buffer above is owned by the attribute accessor. */ - mutable bool array_is_temporary_ = false; +struct AttributeMetaData { + AttributeDomain domain; + CustomDataType data_type; +}; - public: - ReadAttribute(AttributeDomain domain, const CPPType &cpp_type, const int64_t size) - : domain_(domain), - cpp_type_(cpp_type), - custom_data_type_(cpp_type_to_custom_data_type(cpp_type)), - size_(size) +/** + * Base class for the attribute intializer types described below. + */ +struct AttributeInit { + enum class Type { + Default, + VArray, + MoveArray, + }; + Type type; + AttributeInit(const Type type) : type(type) { } +}; - virtual ~ReadAttribute(); - - AttributeDomain domain() const +/** + * Create an attribute using the default value for the data type. + * The default values may depend on the attribute provider implementation. + */ +struct AttributeInitDefault : public AttributeInit { + AttributeInitDefault() : AttributeInit(Type::Default) { - return domain_; } +}; - const CPPType &cpp_type() const - { - return cpp_type_; - } +/** + * Create an attribute by copying data from an existing virtual array. The virtual array + * must have the same type as the newly created attribute. + * + * Note that this can be used to fill the new attribute with the default + */ +struct AttributeInitVArray : public AttributeInit { + const blender::fn::GVArray *varray; - CustomDataType custom_data_type() const + AttributeInitVArray(const blender::fn::GVArray *varray) + : AttributeInit(Type::VArray), varray(varray) { - return custom_data_type_; } +}; - int64_t size() const - { - return size_; - } +/** + * Create an attribute with a by passing ownership of a pre-allocated contiguous array of data. + * Sometimes data is created before a geometry component is available. In that case, it's + * preferable to move data directly to the created attribute to avoid a new allocation and a copy. + * + * Note that this will only have a benefit for attributes that are stored directly as contiguous + * arrays, so not for some built-in attributes. + * + * The array must be allocated with MEM_*, since `attribute_try_create` will free the array if it + * can't be used directly, and that is generally how Blender expects custom data to be allocated. + */ +struct AttributeInitMove : public AttributeInit { + void *data = nullptr; - void get(const int64_t index, void *r_value) const + AttributeInitMove(void *data) : AttributeInit(Type::MoveArray), data(data) { - BLI_assert(index < size_); - this->get_internal(index, r_value); } +}; + +/* Returns false when the iteration should be stopped. */ +using AttributeForeachCallback = blender::FunctionRef<bool(blender::StringRefNull attribute_name, + const AttributeMetaData &meta_data)>; - /* Get a span that contains all attribute values. */ - fn::GSpan get_span() const; +namespace blender::bke { + +using fn::CPPType; +using fn::GVArray; +using fn::GVArrayPtr; +using fn::GVMutableArray; +using fn::GVMutableArrayPtr; + +const CPPType *custom_data_type_to_cpp_type(const CustomDataType type); +CustomDataType cpp_type_to_custom_data_type(const CPPType &type); +CustomDataType attribute_data_type_highest_complexity(Span<CustomDataType> data_types); +AttributeDomain attribute_domain_highest_priority(Span<AttributeDomain> domains); - template<typename T> Span<T> get_span() const +/** + * Used when looking up a "plain attribute" based on a name for reading from it. + */ +struct ReadAttributeLookup { + /* The virtual array that is used to read from this attribute. */ + GVArrayPtr varray; + /* Domain the attribute lives on in the geometry. */ + AttributeDomain domain; + + /* Convenience function to check if the attribute has been found. */ + operator bool() const { - return this->get_span().typed<T>(); + return this->varray.get() != nullptr; } +}; - protected: - /* r_value is expected to be uninitialized. */ - virtual void get_internal(const int64_t index, void *r_value) const = 0; - - virtual void initialize_span() const; +/** + * Used when looking up a "plain attribute" based on a name for reading from it and writing to it. + */ +struct WriteAttributeLookup { + /* The virtual array that is used to read from and write to the attribute. */ + GVMutableArrayPtr varray; + /* Domain the attributes lives on in the geometry. */ + AttributeDomain domain; + + /* Convenience function to check if the attribute has been found. */ + operator bool() const + { + return this->varray.get() != nullptr; + } }; /** - * This exists for similar reasons as the ReadAttribute class, except that - * it does not deal with interpolation between domains. + * An output attribute allows writing to an attribute (and optionally reading as well). It adds + * some convenience features on top of `GVMutableArray` that are very commonly used. + * + * Supported convenience features: + * - Implicit type conversion when writing to builtin attributes. + * - Supports simple access to a span containing the attribute values (that avoids the use of + * VMutableArray_Span in many cases). + * - An output attribute can live side by side with an existing attribute with a different domain + * or data type. The old attribute will only be overwritten when the #save function is called. */ -class WriteAttribute { - protected: - const AttributeDomain domain_; - const CPPType &cpp_type_; - const CustomDataType custom_data_type_; - const int64_t size_; - - /* When not null, this points either to the attribute array or to a temporary array. */ - void *array_buffer_ = nullptr; - /* True, when the buffer points to a temporary array. */ - bool array_is_temporary_ = false; - /* This helps to protect against forgetting to apply changes done to the array. */ - bool array_should_be_applied_ = false; +class OutputAttribute { + public: + using SaveFn = std::function<void(OutputAttribute &)>; + + private: + GVMutableArrayPtr varray_; + AttributeDomain domain_; + SaveFn save_; + std::optional<fn::GVMutableArray_GSpan> optional_span_varray_; + bool ignore_old_values_ = false; + bool save_has_been_called_ = false; public: - WriteAttribute(AttributeDomain domain, const CPPType &cpp_type, const int64_t size) - : domain_(domain), - cpp_type_(cpp_type), - custom_data_type_(cpp_type_to_custom_data_type(cpp_type)), - size_(size) + OutputAttribute() = default; + + OutputAttribute(GVMutableArrayPtr varray, + AttributeDomain domain, + SaveFn save, + const bool ignore_old_values) + : varray_(std::move(varray)), + domain_(domain), + save_(std::move(save)), + ignore_old_values_(ignore_old_values) { } - virtual ~WriteAttribute(); + OutputAttribute(OutputAttribute &&other) = default; - AttributeDomain domain() const + ~OutputAttribute(); + + operator bool() const { - return domain_; + return varray_.get() != nullptr; } - const CPPType &cpp_type() const + GVMutableArray &operator*() { - return cpp_type_; + return *varray_; } - CustomDataType custom_data_type() const + GVMutableArray *operator->() { - return custom_data_type_; + return varray_.get(); } - int64_t size() const + GVMutableArray &varray() { - return size_; + return *varray_; } - void get(const int64_t index, void *r_value) const + AttributeDomain domain() const { - BLI_assert(index < size_); - this->get_internal(index, r_value); + return domain_; } - void set(const int64_t index, const void *value) + const CPPType &cpp_type() const { - BLI_assert(index < size_); - this->set_internal(index, value); + return varray_->type(); } - /* Get a span that new attribute values can be written into. When all values have been changed, - * #apply_span has to be called. */ - fn::GMutableSpan get_span(); - /* The span returned by this method might not contain the current attribute values. */ - fn::GMutableSpan get_span_for_write_only(); - /* Write the changes to the span into the actual attribute, if they aren't already. */ - void apply_span(); - - template<typename T> MutableSpan<T> get_span() + CustomDataType custom_data_type() const { - return this->get_span().typed<T>(); + return cpp_type_to_custom_data_type(this->cpp_type()); } - template<typename T> MutableSpan<T> get_span_for_write_only() + fn::GMutableSpan as_span() { - return this->get_span_for_write_only().typed<T>(); + if (!optional_span_varray_.has_value()) { + const bool materialize_old_values = !ignore_old_values_; + optional_span_varray_.emplace(*varray_, materialize_old_values); + } + fn::GVMutableArray_GSpan &span_varray = *optional_span_varray_; + return span_varray; } - protected: - virtual void get_internal(const int64_t index, void *r_value) const = 0; - virtual void set_internal(const int64_t index, const void *value) = 0; + template<typename T> MutableSpan<T> as_span() + { + return this->as_span().typed<T>(); + } - virtual void initialize_span(const bool write_only); - virtual void apply_span_if_necessary(); + void save(); }; -using ReadAttributePtr = std::unique_ptr<ReadAttribute>; -using WriteAttributePtr = std::unique_ptr<WriteAttribute>; - -/* This provides type safe access to an attribute. - * The underlying ReadAttribute is owned optionally. */ -template<typename T> class TypedReadAttribute { +/** + * Same as OutputAttribute, but should be used when the data type is known at compile time. + */ +template<typename T> class OutputAttribute_Typed { private: - std::unique_ptr<const ReadAttribute> owned_attribute_; - const ReadAttribute *attribute_; + OutputAttribute attribute_; + std::optional<fn::GVMutableArray_Typed<T>> optional_varray_; + VMutableArray<T> *varray_ = nullptr; public: - TypedReadAttribute(ReadAttributePtr attribute) : TypedReadAttribute(*attribute) + OutputAttribute_Typed(OutputAttribute attribute) : attribute_(std::move(attribute)) { - owned_attribute_ = std::move(attribute); - BLI_assert(owned_attribute_); + if (attribute_) { + optional_varray_.emplace(attribute_.varray()); + varray_ = &**optional_varray_; + } } - TypedReadAttribute(const ReadAttribute &attribute) : attribute_(&attribute) + operator bool() const { - BLI_assert(attribute_->cpp_type().is<T>()); + return varray_ != nullptr; } - int64_t size() const + VMutableArray<T> &operator*() { - return attribute_->size(); + return *varray_; } - T operator[](const int64_t index) const + VMutableArray<T> *operator->() { - BLI_assert(index < attribute_->size()); - T value; - value.~T(); - attribute_->get(index, &value); - return value; + return varray_; } - /* Get a span to that contains all attribute values for faster and more convenient access. */ - Span<T> get_span() const + VMutableArray<T> &varray() { - return attribute_->get_span().template typed<T>(); + return *varray_; } -}; -/* This provides type safe access to an attribute. - * The underlying WriteAttribute is owned optionally. */ -template<typename T> class TypedWriteAttribute { - private: - std::unique_ptr<WriteAttribute> owned_attribute_; - WriteAttribute *attribute_; - - public: - TypedWriteAttribute(WriteAttributePtr attribute) : TypedWriteAttribute(*attribute) - { - owned_attribute_ = std::move(attribute); - BLI_assert(owned_attribute_); - } - - TypedWriteAttribute(WriteAttribute &attribute) : attribute_(&attribute) - { - BLI_assert(attribute_->cpp_type().is<T>()); - } - - int64_t size() const + AttributeDomain domain() const { - return attribute_->size(); + return attribute_.domain(); } - T operator[](const int64_t index) const + const CPPType &cpp_type() const { - BLI_assert(index < attribute_->size()); - T value; - value.~T(); - attribute_->get(index, &value); - return value; + return CPPType::get<T>(); } - void set(const int64_t index, const T &value) + CustomDataType custom_data_type() const { - attribute_->set(index, &value); + return cpp_type_to_custom_data_type(this->cpp_type()); } - /* Get a span that new values can be written into. Once all values have been updated #apply_span - * has to be called. */ - MutableSpan<T> get_span() + MutableSpan<T> as_span() { - return attribute_->get_span().typed<T>(); - } - /* The span returned by this method might not contain the current attribute values. */ - MutableSpan<T> get_span_for_write_only() - { - return attribute_->get_span_for_write_only().typed<T>(); + return attribute_.as_span<T>(); } - /* Write back all changes to the actual attribute, if necessary. */ - void apply_span() + void save() { - attribute_->apply_span(); + attribute_.save(); } }; -using BooleanReadAttribute = TypedReadAttribute<bool>; -using FloatReadAttribute = TypedReadAttribute<float>; -using Float2ReadAttribute = TypedReadAttribute<float2>; -using Float3ReadAttribute = TypedReadAttribute<float3>; -using Int32ReadAttribute = TypedReadAttribute<int>; -using Color4fReadAttribute = TypedReadAttribute<Color4f>; -using BooleanWriteAttribute = TypedWriteAttribute<bool>; -using FloatWriteAttribute = TypedWriteAttribute<float>; -using Float2WriteAttribute = TypedWriteAttribute<float2>; -using Float3WriteAttribute = TypedWriteAttribute<float3>; -using Int32WriteAttribute = TypedWriteAttribute<int>; -using Color4fWriteAttribute = TypedWriteAttribute<Color4f>; - } // namespace blender::bke |