Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/blenkernel/BKE_attribute_access.hh')
-rw-r--r--source/blender/blenkernel/BKE_attribute_access.hh382
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