From f74e304b00a99489ff0b63ac2928b13821656107 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Thu, 26 Jun 2025 17:03:56 +0200 Subject: [PATCH] Attributes: Use AttributeStorage instead of CustomData for Instances Part of #122398. This commit moves the runtime instances type to use `AttributeStorage`. Besides sharing some utility functions with point clouds, a resize method is implemented. Pull Request: https://projects.blender.org/blender/blender/pulls/140991 --- .../blenkernel/BKE_attribute_storage.hh | 6 + source/blender/blenkernel/BKE_instances.hh | 13 +- source/blender/blenkernel/CMakeLists.txt | 1 + .../intern/attribute_access_intern.hh | 6 +- .../blenkernel/intern/attribute_storage.cc | 28 +++ .../intern/attribute_storage_access.cc | 206 ++++++++++++++++++ .../intern/attribute_storage_access.hh | 88 ++++++++ source/blender/blenkernel/intern/instances.cc | 58 ++--- .../blenkernel/intern/instances_attributes.cc | 205 +++++++++++------ .../blender/blenkernel/intern/pointcloud.cc | 96 ++------ .../intern/pointcloud_attributes.cc | 141 ++---------- source/blender/geometry/intern/randomize.cc | 3 +- 12 files changed, 532 insertions(+), 319 deletions(-) create mode 100644 source/blender/blenkernel/intern/attribute_storage_access.cc create mode 100644 source/blender/blenkernel/intern/attribute_storage_access.hh diff --git a/source/blender/blenkernel/BKE_attribute_storage.hh b/source/blender/blenkernel/BKE_attribute_storage.hh index ee53dbef360..c2b7af2781b 100644 --- a/source/blender/blenkernel/BKE_attribute_storage.hh +++ b/source/blender/blenkernel/BKE_attribute_storage.hh @@ -185,6 +185,12 @@ class AttributeStorage : public ::AttributeStorage { /** Change the name of a single existing attribute. */ void rename(StringRef old_name, std::string new_name); + /** + * Resize the data for a given domain. New values will be default initialized (meaning no zero + * initialization for trivial types). + */ + void resize(AttrDomain domain, int64_t new_size); + /** * Read data owned by the #AttributeStorage struct. This works by converting the DNA-specific * types stored in the files to the runtime data structures. diff --git a/source/blender/blenkernel/BKE_instances.hh b/source/blender/blenkernel/BKE_instances.hh index d7b30f1b525..9805f076b37 100644 --- a/source/blender/blenkernel/BKE_instances.hh +++ b/source/blender/blenkernel/BKE_instances.hh @@ -31,9 +31,8 @@ #include "BLI_vector.hh" #include "BLI_virtual_array_fwd.hh" -#include "DNA_customdata_types.h" - #include "BKE_attribute_filter.hh" +#include "BKE_attribute_storage.hh" struct Object; struct Collection; @@ -120,7 +119,7 @@ class Instances { int instances_num_ = 0; - CustomData attributes_; + bke::AttributeStorage attributes_; /** * Caches how often each reference is used. @@ -210,8 +209,8 @@ class Instances { bke::AttributeAccessor attributes() const; bke::MutableAttributeAccessor attributes_for_write(); - CustomData &custom_data_attributes(); - const CustomData &custom_data_attributes() const; + bke::AttributeStorage &attribute_storage(); + const bke::AttributeStorage &attribute_storage() const; void foreach_referenced_geometry( FunctionRef callback) const; @@ -306,12 +305,12 @@ inline const GeometrySet &InstanceReference::geometry_set() const return *geometry_set_; } -inline CustomData &Instances::custom_data_attributes() +inline AttributeStorage &Instances::attribute_storage() { return attributes_; } -inline const CustomData &Instances::custom_data_attributes() const +inline const AttributeStorage &Instances::attribute_storage() const { return attributes_; } diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index 6dabfa894e5..9481e70a8d9 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -59,6 +59,7 @@ set(SRC intern/attribute_legacy_convert.cc intern/attribute_math.cc intern/attribute_storage.cc + intern/attribute_storage_access.cc intern/autoexec.cc intern/bake_data_block_map.cc intern/bake_geometry_nodes_modifier.cc diff --git a/source/blender/blenkernel/intern/attribute_access_intern.hh b/source/blender/blenkernel/intern/attribute_access_intern.hh index c6749a2694f..2da73a1ee11 100644 --- a/source/blender/blenkernel/intern/attribute_access_intern.hh +++ b/source/blender/blenkernel/intern/attribute_access_intern.hh @@ -179,9 +179,9 @@ class CustomDataAttributeProvider final : public DynamicAttributesProvider { * if the stored type is the same as the attribute type. */ class BuiltinCustomDataLayerProvider final : public BuiltinAttributeProvider { - using UpdateOnChange = void (*)(void *owner); + using AttrUpdateOnChange = void (*)(void *owner); const CustomDataAccessInfo custom_data_access_; - const UpdateOnChange update_on_change_; + const AttrUpdateOnChange update_on_change_; public: BuiltinCustomDataLayerProvider(std::string attribute_name, @@ -189,7 +189,7 @@ class BuiltinCustomDataLayerProvider final : public BuiltinAttributeProvider { const eCustomDataType data_type, const DeletableEnum deletable, const CustomDataAccessInfo custom_data_access, - const UpdateOnChange update_on_change, + const AttrUpdateOnChange update_on_change, const AttributeValidator validator = {}, const GPointer default_value = {}) : BuiltinAttributeProvider( diff --git a/source/blender/blenkernel/intern/attribute_storage.cc b/source/blender/blenkernel/intern/attribute_storage.cc index 72554195409..08aa979bbfa 100644 --- a/source/blender/blenkernel/intern/attribute_storage.cc +++ b/source/blender/blenkernel/intern/attribute_storage.cc @@ -314,6 +314,34 @@ void AttributeStorage::rename(const StringRef old_name, std::string new_name) } } +void AttributeStorage::resize(const AttrDomain domain, const int64_t new_size) +{ + this->foreach([&](Attribute &attr) { + if (attr.domain() != domain) { + return; + } + const CPPType &type = attribute_type_to_cpp_type(attr.data_type()); + switch (attr.storage_type()) { + case bke::AttrStorageType::Array: { + const auto &data = std::get(attr.data()); + const int64_t old_size = data.size; + + auto new_data = bke::Attribute::ArrayData::ForUninitialized(type, new_size); + type.copy_construct_n(data.data, new_data.data, std::min(old_size, new_size)); + if (old_size < new_size) { + type.default_construct_n(POINTER_OFFSET(new_data.data, type.size * old_size), + new_size - old_size); + } + + attr.assign_data(std::move(new_data)); + } + case bke::AttrStorageType::Single: { + return; + } + } + }); +} + static void read_array_data(BlendDataReader &reader, const int8_t dna_attr_type, const int64_t size, diff --git a/source/blender/blenkernel/intern/attribute_storage_access.cc b/source/blender/blenkernel/intern/attribute_storage_access.cc new file mode 100644 index 00000000000..9b0c45e2f9f --- /dev/null +++ b/source/blender/blenkernel/intern/attribute_storage_access.cc @@ -0,0 +1,206 @@ +/* SPDX-FileCopyrightText: 2025 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "BKE_attribute.hh" +#include "BKE_attribute_storage.hh" + +#include "attribute_storage_access.hh" + +namespace blender::bke { + +GAttributeReader attribute_to_reader(const Attribute &attribute, + const AttrDomain domain, + const int64_t domain_size) +{ + const CPPType &cpp_type = attribute_type_to_cpp_type(attribute.data_type()); + switch (attribute.storage_type()) { + case AttrStorageType::Array: { + const auto &data = std::get(attribute.data()); + return GAttributeReader{GVArray::ForSpan(GSpan(cpp_type, data.data, data.size)), + domain, + data.sharing_info.get()}; + } + case AttrStorageType::Single: { + const auto &data = std::get(attribute.data()); + return GAttributeReader{GVArray::ForSingleRef(cpp_type, domain_size, data.value), + domain, + data.sharing_info.get()}; + } + } + BLI_assert_unreachable(); + return {}; +} + +GAttributeWriter attribute_to_writer(void *owner, + const Map &changed_tags, + const int64_t domain_size, + Attribute &attribute) +{ + const CPPType &cpp_type = attribute_type_to_cpp_type(attribute.data_type()); + switch (attribute.storage_type()) { + case AttrStorageType::Array: { + auto &data = std::get(attribute.data_for_write()); + BLI_assert(data.size == domain_size); + + std::function tag_modified_fn; + if (const AttrUpdateOnChange update_fn = changed_tags.lookup_default(attribute.name(), + nullptr)) + { + tag_modified_fn = [owner, update_fn]() { update_fn(owner); }; + }; + + return GAttributeWriter{ + GVMutableArray::ForSpan(GMutableSpan(cpp_type, data.data, domain_size)), + attribute.domain(), + std::move(tag_modified_fn)}; + } + case AttrStorageType::Single: { + /* Not yet implemented. */ + BLI_assert_unreachable(); + } + } + BLI_assert_unreachable(); + return {}; +} + +Attribute::DataVariant attribute_init_to_data(const bke::AttrType data_type, + const int64_t domain_size, + const AttributeInit &initializer) +{ + switch (initializer.type) { + case AttributeInit::Type::Construct: { + const CPPType &type = bke::attribute_type_to_cpp_type(data_type); + return Attribute::ArrayData::ForConstructed(type, domain_size); + } + case AttributeInit::Type::DefaultValue: { + const CPPType &type = bke::attribute_type_to_cpp_type(data_type); + return Attribute::ArrayData::ForDefaultValue(type, domain_size); + } + case AttributeInit::Type::VArray: { + const auto &init = static_cast(initializer); + const GVArray &varray = init.varray; + BLI_assert(varray.size() == domain_size); + const CPPType &type = varray.type(); + Attribute::ArrayData data; + data.data = MEM_malloc_arrayN_aligned(domain_size, type.size, type.alignment, __func__); + varray.materialize_to_uninitialized(varray.index_range(), data.data); + data.size = domain_size; + data.sharing_info = ImplicitSharingPtr<>(implicit_sharing::info_for_mem_free(data.data)); + return data; + } + case AttributeInit::Type::MoveArray: { + const auto &init = static_cast(initializer); + Attribute::ArrayData data; + data.data = init.data; + data.size = domain_size; + data.sharing_info = ImplicitSharingPtr<>(implicit_sharing::info_for_mem_free(data.data)); + return data; + } + case AttributeInit::Type::Shared: { + const auto &init = static_cast(initializer); + Attribute::ArrayData data; + data.data = const_cast(init.data); + data.size = domain_size; + data.sharing_info = ImplicitSharingPtr<>(init.sharing_info); + data.sharing_info->add_user(); + return data; + } + } + BLI_assert_unreachable(); + return {}; +} + +GVArray get_varray_attribute(const AttributeStorage &storage, + AttrDomain domain, + const CPPType &cpp_type, + StringRef name, + int64_t domain_size, + const void *default_value) +{ + const bke::Attribute *attr = storage.wrap().lookup(name); + + const auto return_default = [&]() { + return GVArray::ForSingle(cpp_type, domain_size, default_value); + }; + + if (!attr) { + return return_default(); + } + if (attr->domain() != domain) { + return return_default(); + } + if (attr->data_type() != cpp_type_to_attribute_type(cpp_type)) { + return return_default(); + } + switch (attr->storage_type()) { + case bke::AttrStorageType::Array: { + const auto &data = std::get(attr->data()); + const GSpan span(cpp_type, data.data, data.size); + return GVArray::ForSpan(span); + } + case bke::AttrStorageType::Single: { + const auto &data = std::get(attr->data()); + return GVArray::ForSingle(cpp_type, domain_size, data.value); + } + } + return return_default(); +} + +GSpan get_span_attribute(const AttributeStorage &storage, + const AttrDomain domain, + const CPPType &cpp_type, + const StringRef name, + const int64_t domain_size) +{ + const bke::Attribute *attr = storage.wrap().lookup(name); + if (!attr) { + return {}; + } + if (attr->domain() != domain) { + return {}; + } + if (const auto *array_data = std::get_if(&attr->data())) { + BLI_assert(array_data->size == domain_size); + UNUSED_VARS_NDEBUG(domain_size); + return GSpan(cpp_type, array_data->data, array_data->size); + } + return {}; +} + +GMutableSpan get_mutable_attribute(AttributeStorage &storage, + const AttrDomain domain, + const CPPType &cpp_type, + const StringRef name, + const int64_t domain_size, + const void *default_value) +{ + if (domain_size <= 0) { + return {}; + } + const bke::AttrType type = bke::cpp_type_to_attribute_type(cpp_type); + if (bke::Attribute *attr = storage.wrap().lookup(name)) { + if (attr->data_type() == type) { + if (const auto *single_data = std::get_if(&attr->data())) { + /* Convert single value storage to array storage. */ + const GPointer g_value(cpp_type, single_data->value); + attr->assign_data(bke::Attribute::ArrayData::ForValue(g_value, domain_size)); + } + auto &array_data = std::get(attr->data_for_write()); + return GMutableSpan(cpp_type, array_data.data, domain_size); + } + /* The attribute has the wrong type. This shouldn't happen for builtin attributes, but just + * in case, remove it. */ + storage.wrap().remove(name); + } + bke::Attribute &attr = storage.wrap().add( + name, + domain, + type, + bke::Attribute::ArrayData::ForValue({cpp_type, &default_value}, domain_size)); + auto &array_data = std::get(attr.data_for_write()); + BLI_assert(array_data.size == domain_size); + return GMutableSpan(cpp_type, array_data.data, domain_size); +} + +} // namespace blender::bke diff --git a/source/blender/blenkernel/intern/attribute_storage_access.hh b/source/blender/blenkernel/intern/attribute_storage_access.hh new file mode 100644 index 00000000000..fa6f497c32f --- /dev/null +++ b/source/blender/blenkernel/intern/attribute_storage_access.hh @@ -0,0 +1,88 @@ +/* SPDX-FileCopyrightText: 2025 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "BKE_attribute.hh" +#include "BKE_attribute_storage.hh" + +namespace blender::bke { + +using AttrUpdateOnChange = void (*)(void *owner); + +struct AttrBuiltinInfo { + AttrDomain domain; + AttrType type; + GPointer default_value = {}; + AttributeValidator validator = {}; + bool deletable = true; + AttrBuiltinInfo(AttrDomain domain, AttrType type) : domain(domain), type(type) {} +}; + +GAttributeReader attribute_to_reader(const Attribute &attribute, + const AttrDomain domain, + const int64_t domain_size); + +GAttributeWriter attribute_to_writer(void *owner, + const Map &changed_tags, + const int64_t domain_size, + Attribute &attribute); + +Attribute::DataVariant attribute_init_to_data(const bke::AttrType data_type, + const int64_t domain_size, + const AttributeInit &initializer); + +GVArray get_varray_attribute(const AttributeStorage &storage, + AttrDomain domain, + const CPPType &cpp_type, + StringRef name, + int64_t domain_size, + const void *default_value); + +template +inline VArray get_varray_attribute(const AttributeStorage &storage, + const AttrDomain domain, + const StringRef name, + const int64_t domain_size, + const T &default_value) +{ + GVArray varray = get_varray_attribute( + storage, domain, CPPType::get(), name, domain_size, &default_value); + return varray.typed(); +} + +GSpan get_span_attribute(const AttributeStorage &storage, + AttrDomain domain, + const CPPType &cpp_type, + StringRef name, + const int64_t domain_size); + +template +inline Span get_span_attribute(const AttributeStorage &storage, + const AttrDomain domain, + const StringRef name, + const int64_t domain_size) +{ + const GSpan span = get_span_attribute(storage, domain, CPPType::get(), name, domain_size); + return span.typed(); +} + +GMutableSpan get_mutable_attribute(AttributeStorage &storage, + const AttrDomain domain, + const CPPType &cpp_type, + const StringRef name, + const int64_t domain_size, + const void *default_value); + +template +inline MutableSpan get_mutable_attribute(AttributeStorage &storage, + const AttrDomain domain, + const StringRef name, + const int64_t domain_size, + const T &default_value = T()) +{ + const GMutableSpan span = get_mutable_attribute( + storage, domain, CPPType::get(), name, domain_size, &default_value); + return span.typed(); +} + +} // namespace blender::bke diff --git a/source/blender/blenkernel/intern/instances.cc b/source/blender/blenkernel/intern/instances.cc index 3432d824b18..7e6a706b82e 100644 --- a/source/blender/blenkernel/intern/instances.cc +++ b/source/blender/blenkernel/intern/instances.cc @@ -10,11 +10,12 @@ #include "DNA_collection_types.h" #include "DNA_object_types.h" -#include "BKE_customdata.hh" #include "BKE_geometry_set.hh" #include "BKE_geometry_set_instances.hh" #include "BKE_instances.hh" +#include "attribute_storage_access.hh" + namespace blender::bke { InstanceReference::InstanceReference(GeometrySet geometry_set) @@ -145,34 +146,27 @@ uint64_t InstanceReference::hash() const return get_default_hash(geometry_hash, type_, data_); } -Instances::Instances() -{ - CustomData_reset(&attributes_); -} +Instances::Instances() = default; Instances::Instances(Instances &&other) : references_(std::move(other.references_)), instances_num_(other.instances_num_), - attributes_(other.attributes_), + attributes_(std::move(other.attributes_)), reference_user_counts_(std::move(other.reference_user_counts_)), almost_unique_ids_cache_(std::move(other.almost_unique_ids_cache_)) { - CustomData_reset(&other.attributes_); } Instances::Instances(const Instances &other) : references_(other.references_), instances_num_(other.instances_num_), + attributes_(other.attributes_), reference_user_counts_(other.reference_user_counts_), almost_unique_ids_cache_(other.almost_unique_ids_cache_) { - CustomData_init_from(&other.attributes_, &attributes_, CD_MASK_ALL, other.instances_num_); } -Instances::~Instances() -{ - CustomData_free(&attributes_); -} +Instances::~Instances() = default; Instances &Instances::operator=(const Instances &other) { @@ -196,17 +190,21 @@ Instances &Instances::operator=(Instances &&other) void Instances::resize(int capacity) { - CustomData_realloc(&attributes_, instances_num_, capacity, CD_SET_DEFAULT); + const int old_size = this->instances_num(); + attributes_.resize(AttrDomain::Instance, capacity); instances_num_ = capacity; + fill_attribute_range_default(this->attributes_for_write(), + AttrDomain::Instance, + {}, + IndexRange::from_begin_end(old_size, capacity)); } void Instances::add_instance(const int instance_handle, const float4x4 &transform) { BLI_assert(instance_handle >= 0); BLI_assert(instance_handle < references_.size()); - const int old_size = instances_num_; instances_num_++; - CustomData_realloc(&attributes_, old_size, instances_num_); + attributes_.resize(AttrDomain::Instance, instances_num_); this->reference_handles_for_write().last() = instance_handle; this->transforms_for_write().last() = transform; this->tag_reference_handles_changed(); @@ -214,38 +212,26 @@ void Instances::add_instance(const int instance_handle, const float4x4 &transfor Span Instances::reference_handles() const { - return {static_cast( - CustomData_get_layer_named(&attributes_, CD_PROP_INT32, ".reference_index")), - instances_num_}; + return get_span_attribute( + attributes_, AttrDomain::Instance, ".reference_index", instances_num_); } MutableSpan Instances::reference_handles_for_write() { - int *data = static_cast(CustomData_get_layer_named_for_write( - &attributes_, CD_PROP_INT32, ".reference_index", instances_num_)); - if (!data) { - data = static_cast(CustomData_add_layer_named( - &attributes_, CD_PROP_INT32, CD_SET_DEFAULT, instances_num_, ".reference_index")); - } - return {data, instances_num_}; + return get_mutable_attribute( + attributes_, AttrDomain::Instance, ".reference_index", instances_num_); } Span Instances::transforms() const { - return {static_cast( - CustomData_get_layer_named(&attributes_, CD_PROP_FLOAT4X4, "instance_transform")), - instances_num_}; + return get_span_attribute( + attributes_, AttrDomain::Instance, "instance_transform", instances_num_); } MutableSpan Instances::transforms_for_write() { - float4x4 *data = static_cast(CustomData_get_layer_named_for_write( - &attributes_, CD_PROP_FLOAT4X4, "instance_transform", instances_num_)); - if (!data) { - data = static_cast(CustomData_add_layer_named( - &attributes_, CD_PROP_FLOAT4X4, CD_SET_DEFAULT, instances_num_, "instance_transform")); - } - return {data, instances_num_}; + return get_mutable_attribute( + attributes_, AttrDomain::Instance, "instance_transform", instances_num_); } GeometrySet &Instances::geometry_set_from_reference(const int reference_index) @@ -427,7 +413,7 @@ void Instances::ensure_owns_direct_data() void Instances::count_memory(MemoryCounter &memory) const { - CustomData_count_memory(attributes_, instances_num_, memory); + attributes_.count_memory(memory); for (const InstanceReference &reference : references_) { reference.count_memory(memory); } diff --git a/source/blender/blenkernel/intern/instances_attributes.cc b/source/blender/blenkernel/intern/instances_attributes.cc index d3acfc788cf..0f4f6b08309 100644 --- a/source/blender/blenkernel/intern/instances_attributes.cc +++ b/source/blender/blenkernel/intern/instances_attributes.cc @@ -2,9 +2,11 @@ * * SPDX-License-Identifier: GPL-2.0-or-later */ +#include "BKE_attribute_legacy_convert.hh" #include "BKE_instances.hh" #include "attribute_access_intern.hh" +#include "attribute_storage_access.hh" namespace blender::bke { @@ -14,77 +16,76 @@ static void tag_component_reference_index_changed(void *owner) instances.tag_reference_handles_changed(); } -static GeometryAttributeProviders create_attribute_providers_for_instances() +static const auto &changed_tags() { - static CustomDataAccessInfo instance_custom_data_access = { - [](void *owner) -> CustomData * { - Instances *instances = static_cast(owner); - return &instances->custom_data_attributes(); - }, - [](const void *owner) -> const CustomData * { - const Instances *instances = static_cast(owner); - return &instances->custom_data_attributes(); - }, - [](const void *owner) -> int { - const Instances *instances = static_cast(owner); - return instances->instances_num(); - }}; - - /** - * IDs of the instances. They are used for consistency over multiple frames for things like - * motion blur. Proper stable ID data that actually helps when rendering can only be generated - * in some situations, so this vector is allowed to be empty, in which case the index of each - * instance will be used for the final ID. - */ - static BuiltinCustomDataLayerProvider id("id", - AttrDomain::Instance, - CD_PROP_INT32, - BuiltinAttributeProvider::Deletable, - instance_custom_data_access, - nullptr); - - static BuiltinCustomDataLayerProvider instance_transform("instance_transform", - AttrDomain::Instance, - CD_PROP_FLOAT4X4, - BuiltinAttributeProvider::NonDeletable, - instance_custom_data_access, - nullptr); - - /** Indices into `Instances::references_`. Determines what data is instanced. */ - static BuiltinCustomDataLayerProvider reference_index(".reference_index", - AttrDomain::Instance, - CD_PROP_INT32, - BuiltinAttributeProvider::NonDeletable, - instance_custom_data_access, - tag_component_reference_index_changed); - - static CustomDataAttributeProvider instance_custom_data(AttrDomain::Instance, - instance_custom_data_access); - - return GeometryAttributeProviders({&instance_transform, &id, &reference_index}, - {&instance_custom_data}); + static Map attributes{ + {".reference_index", tag_component_reference_index_changed}, + }; + return attributes; } -static AttributeAccessorFunctions get_instances_accessor_functions() +static const auto &builtin_attributes() { - static const GeometryAttributeProviders providers = create_attribute_providers_for_instances(); - AttributeAccessorFunctions fn = - attribute_accessor_functions::accessor_functions_for_providers(); - fn.domain_size = [](const void *owner, const AttrDomain domain) { - if (owner == nullptr) { - return 0; - } - const Instances *instances = static_cast(owner); - switch (domain) { - case AttrDomain::Instance: - return instances->instances_num(); - default: - return 0; - } - }; + static auto attributes = []() { + Map map; + + /** + * IDs of the instances. They are used for consistency over multiple frames for things like + * motion blur. Proper stable ID data that actually helps when rendering can only be generated + * in some situations, so this vector is allowed to be empty, in which case the index of each + * instance will be used for the final ID. + */ + AttrBuiltinInfo id(bke::AttrDomain::Instance, bke::AttrType::Int32); + map.add_new("id", std::move(id)); + + AttrBuiltinInfo instance_transform(bke::AttrDomain::Instance, bke::AttrType::Float4x4); + instance_transform.deletable = false; + map.add_new("instance_transform", std::move(instance_transform)); + + /** Indices into `Instances::references_`. Determines what data is instanced. */ + AttrBuiltinInfo reference_index(bke::AttrDomain::Instance, bke::AttrType::Int32); + reference_index.deletable = false; + map.add_new(".reference_index", std::move(reference_index)); + + return map; + }(); + return attributes; +} + +static constexpr AttributeAccessorFunctions get_instances_accessor_functions() +{ + AttributeAccessorFunctions fn{}; fn.domain_supported = [](const void * /*owner*/, const AttrDomain domain) { return domain == AttrDomain::Instance; }; + fn.domain_size = [](const void *owner, const AttrDomain domain) { + return domain == AttrDomain::Instance ? + static_cast(owner)->instances_num() : + 0; + }; + fn.builtin_domain_and_type = [](const void * /*owner*/, + const StringRef name) -> std::optional { + const AttrBuiltinInfo *info = builtin_attributes().lookup_ptr(name); + if (!info) { + return std::nullopt; + } + const std::optional cd_type = attr_type_to_custom_data_type(info->type); + BLI_assert(cd_type.has_value()); + return AttributeDomainAndType{info->domain, *cd_type}; + }; + fn.get_builtin_default = [](const void * /*owner*/, StringRef name) -> GPointer { + const AttrBuiltinInfo &info = builtin_attributes().lookup(name); + return info.default_value; + }; + fn.lookup = [](const void *owner, const StringRef name) -> GAttributeReader { + const Instances &instances = *static_cast(owner); + const AttributeStorage &storage = instances.attribute_storage(); + const Attribute *attribute = storage.lookup(name); + if (!attribute) { + return {}; + } + return attribute_to_reader(*attribute, AttrDomain::Instance, instances.instances_num()); + }; fn.adapt_domain = [](const void * /*owner*/, const GVArray &varray, const AttrDomain from_domain, @@ -94,6 +95,82 @@ static AttributeAccessorFunctions get_instances_accessor_functions() } return GVArray{}; }; + fn.foreach_attribute = [](const void *owner, + const FunctionRef fn, + const AttributeAccessor &accessor) { + const Instances &instances = *static_cast(owner); + const AttributeStorage &storage = instances.attribute_storage(); + storage.foreach_with_stop([&](const Attribute &attribute) { + const auto get_fn = [&]() { + return attribute_to_reader(attribute, AttrDomain::Instance, instances.instances_num()); + }; + const std::optional cd_type = attr_type_to_custom_data_type( + attribute.data_type()); + BLI_assert(cd_type.has_value()); + AttributeIter iter(attribute.name(), attribute.domain(), *cd_type, get_fn); + iter.is_builtin = builtin_attributes().contains(attribute.name()); + iter.accessor = &accessor; + fn(iter); + return !iter.is_stopped(); + }); + }; + fn.lookup_validator = [](const void * /*owner*/, const StringRef name) -> AttributeValidator { + const AttrBuiltinInfo *info = builtin_attributes().lookup_ptr(name); + if (!info) { + return {}; + } + return info->validator; + }; + fn.lookup_for_write = [](void *owner, const StringRef name) -> GAttributeWriter { + Instances &instances = *static_cast(owner); + AttributeStorage &storage = instances.attribute_storage(); + Attribute *attribute = storage.lookup(name); + if (!attribute) { + return {}; + } + return attribute_to_writer(&instances, changed_tags(), instances.instances_num(), *attribute); + }; + fn.remove = [](void *owner, const StringRef name) -> bool { + Instances &instances = *static_cast(owner); + AttributeStorage &storage = instances.attribute_storage(); + if (const AttrBuiltinInfo *info = builtin_attributes().lookup_ptr(name)) { + if (!info->deletable) { + return false; + } + } + const std::optional fn = changed_tags().lookup_try(name); + const bool removed = storage.remove(name); + if (!removed) { + return false; + } + if (fn) { + (*fn)(owner); + } + return true; + }; + fn.add = [](void *owner, + const StringRef name, + const AttrDomain domain, + const eCustomDataType data_type, + const AttributeInit &initializer) { + Instances &instances = *static_cast(owner); + const int domain_size = instances.instances_num(); + AttributeStorage &storage = instances.attribute_storage(); + const std::optional type = custom_data_type_to_attr_type(data_type); + BLI_assert(type.has_value()); + if (const AttrBuiltinInfo *info = builtin_attributes().lookup_ptr(name)) { + if (info->domain != domain || info->type != type) { + return false; + } + } + if (storage.lookup(name)) { + return false; + } + Attribute::DataVariant data = attribute_init_to_data(*type, domain_size, initializer); + storage.add(name, domain, *type, std::move(data)); + return true; + }; + return fn; } diff --git a/source/blender/blenkernel/intern/pointcloud.cc b/source/blender/blenkernel/intern/pointcloud.cc index 9adec5bad5c..b3b5cbeb5a9 100644 --- a/source/blender/blenkernel/intern/pointcloud.cc +++ b/source/blender/blenkernel/intern/pointcloud.cc @@ -43,6 +43,8 @@ #include "BLO_read_write.hh" +#include "attribute_storage_access.hh" + using blender::CPPType; using blender::float3; using blender::IndexRange; @@ -177,98 +179,32 @@ IDTypeInfo IDType_ID_PT = { /*lib_override_apply_post*/ nullptr, }; -template -static VArray get_varray_attribute(const PointCloud &pointcloud, - const StringRef name, - const T default_value) -{ - using namespace blender; - const bke::Attribute *attr = pointcloud.attribute_storage.wrap().lookup(name); - if (!attr) { - return VArray::ForSingle(default_value, pointcloud.totpoint); - } - switch (attr->storage_type()) { - case bke::AttrStorageType::Array: { - const auto &data = std::get(attr->data()); - const Span span(static_cast(data.data), data.size); - BLI_assert(data.size == pointcloud.totpoint); - return VArray::ForSpan(span); - } - case bke::AttrStorageType::Single: { - const auto &data = std::get(attr->data()); - return VArray::ForSingle(*static_cast(data.value), pointcloud.totpoint); - } - } - return VArray::ForSingle(default_value, pointcloud.totpoint); -} - -template -static Span get_span_attribute(const PointCloud &pointcloud, const StringRef name) -{ - using namespace blender; - const bke::Attribute *attr = pointcloud.attribute_storage.wrap().lookup(name); - if (!attr) { - return {}; - } - if (const auto *array_data = std::get_if(&attr->data())) { - BLI_assert(array_data->size == pointcloud.totpoint); - return Span(static_cast(array_data->data), array_data->size); - } - return {}; -} - -template -static MutableSpan get_mutable_attribute(PointCloud &pointcloud, - const StringRef name, - const T default_value = T()) -{ - using namespace blender; - if (pointcloud.totpoint <= 0) { - return {}; - } - const bke::AttrType type = bke::cpp_type_to_attribute_type(CPPType::get()); - if (bke::Attribute *attr = pointcloud.attribute_storage.wrap().lookup(name)) { - if (attr->data_type() == type) { - if (const auto *single_data = std::get_if(&attr->data())) { - /* Convert single value storage to array storage. */ - const GPointer g_value(CPPType::get(), single_data->value); - attr->assign_data(bke::Attribute::ArrayData::ForValue(g_value, pointcloud.totpoint)); - } - auto &array_data = std::get(attr->data_for_write()); - BLI_assert(array_data.size == pointcloud.totpoint); - return MutableSpan(static_cast(array_data.data), pointcloud.totpoint); - } - /* The attribute has the wrong type. This shouldn't happen for builtin attributes, but just in - * case, remove it. */ - pointcloud.attribute_storage.wrap().remove(name); - } - bke::Attribute &attr = pointcloud.attribute_storage.wrap().add( - name, - bke::AttrDomain::Point, - type, - bke::Attribute::ArrayData::ForValue({CPPType::get(), &default_value}, - pointcloud.totpoint)); - auto &array_data = std::get(attr.data_for_write()); - BLI_assert(array_data.size == pointcloud.totpoint); - return MutableSpan(static_cast(array_data.data), pointcloud.totpoint); -} - Span PointCloud::positions() const { - return get_span_attribute(*this, "position"); + return blender::bke::get_span_attribute( + this->attribute_storage.wrap(), blender::bke::AttrDomain::Point, "position", this->totpoint); } MutableSpan PointCloud::positions_for_write() { - return get_mutable_attribute(*this, "position"); + return blender::bke::get_mutable_attribute( + this->attribute_storage.wrap(), blender::bke::AttrDomain::Point, "position", this->totpoint); } VArray PointCloud::radius() const { - return get_varray_attribute(*this, "radius", 0.01f); + return blender::bke::get_varray_attribute(this->attribute_storage.wrap(), + blender::bke::AttrDomain::Point, + "radius", + this->totpoint, + 0.01f); } MutableSpan PointCloud::radius_for_write() { - return get_mutable_attribute(*this, "radius", 0.01f); + return blender::bke::get_mutable_attribute(this->attribute_storage.wrap(), + blender::bke::AttrDomain::Point, + "radius", + this->totpoint, + 0.01f); } PointCloud *BKE_pointcloud_add(Main *bmain, const char *name) diff --git a/source/blender/blenkernel/intern/pointcloud_attributes.cc b/source/blender/blenkernel/intern/pointcloud_attributes.cc index a466937d54b..c4f82b5280b 100644 --- a/source/blender/blenkernel/intern/pointcloud_attributes.cc +++ b/source/blender/blenkernel/intern/pointcloud_attributes.cc @@ -9,6 +9,7 @@ #include "BKE_pointcloud.hh" #include "attribute_access_intern.hh" +#include "attribute_storage_access.hh" namespace blender::bke { @@ -24,38 +25,23 @@ static void tag_radius_changed(void *owner) points.tag_radii_changed(); } -using UpdateOnChange = void (*)(void *owner); - static const auto &changed_tags() { - static Map attributes{{"position", tag_position_changed}, - {"radius", tag_radius_changed}}; + static Map attributes{{"position", tag_position_changed}, + {"radius", tag_radius_changed}}; return attributes; } -namespace { - -struct BuiltinInfo { - bke::AttrDomain domain; - bke::AttrType type; - GPointer default_value = {}; - AttributeValidator validator = {}; - bool deletable = false; - BuiltinInfo(bke::AttrDomain domain, bke::AttrType type) : domain(domain), type(type) {} -}; - -} // namespace - static const auto &builtin_attributes() { static auto attributes = []() { - Map map; + Map map; - BuiltinInfo position(bke::AttrDomain::Point, bke::AttrType::Float3); + AttrBuiltinInfo position(bke::AttrDomain::Point, bke::AttrType::Float3); position.deletable = false; map.add_new("position", std::move(position)); - BuiltinInfo radius(bke::AttrDomain::Point, bke::AttrType::Float); + AttrBuiltinInfo radius(bke::AttrDomain::Point, bke::AttrType::Float); map.add_new("radius", std::move(radius)); return map; @@ -63,107 +49,6 @@ static const auto &builtin_attributes() return attributes; } -static GAttributeReader attribute_to_reader(const Attribute &attribute, - const AttrDomain domain, - const int64_t domain_size) -{ - const CPPType &cpp_type = attribute_type_to_cpp_type(attribute.data_type()); - switch (attribute.storage_type()) { - case AttrStorageType::Array: { - const auto &data = std::get(attribute.data()); - return GAttributeReader{GVArray::ForSpan(GSpan(cpp_type, data.data, data.size)), - domain, - data.sharing_info.get()}; - } - case AttrStorageType::Single: { - const auto &data = std::get(attribute.data()); - return GAttributeReader{GVArray::ForSingleRef(cpp_type, domain_size, data.value), - domain, - data.sharing_info.get()}; - } - } - BLI_assert_unreachable(); - return {}; -} - -static GAttributeWriter attribute_to_writer(PointCloud &pointcloud, - const int64_t domain_size, - Attribute &attribute) -{ - const CPPType &cpp_type = attribute_type_to_cpp_type(attribute.data_type()); - switch (attribute.storage_type()) { - case AttrStorageType::Array: { - auto &data = std::get(attribute.data_for_write()); - BLI_assert(data.size == domain_size); - - std::function tag_modified_fn; - if (const UpdateOnChange update_fn = changed_tags().lookup_default(attribute.name(), - nullptr)) - { - tag_modified_fn = [pointcloud = &pointcloud, update_fn]() { update_fn(pointcloud); }; - }; - - return GAttributeWriter{ - GVMutableArray::ForSpan(GMutableSpan(cpp_type, data.data, domain_size)), - attribute.domain(), - std::move(tag_modified_fn)}; - } - case AttrStorageType::Single: { - /* Not yet implemented. */ - BLI_assert_unreachable(); - } - } - BLI_assert_unreachable(); - return {}; -} - -static Attribute::DataVariant attribute_init_to_data(const bke::AttrType data_type, - const int64_t domain_size, - const AttributeInit &initializer) -{ - switch (initializer.type) { - case AttributeInit::Type::Construct: { - const CPPType &type = bke::attribute_type_to_cpp_type(data_type); - return Attribute::ArrayData::ForConstructed(type, domain_size); - } - case AttributeInit::Type::DefaultValue: { - const CPPType &type = bke::attribute_type_to_cpp_type(data_type); - return Attribute::ArrayData::ForDefaultValue(type, domain_size); - } - case AttributeInit::Type::VArray: { - const auto &init = static_cast(initializer); - const GVArray &varray = init.varray; - BLI_assert(varray.size() == domain_size); - const CPPType &type = varray.type(); - Attribute::ArrayData data; - data.data = MEM_malloc_arrayN_aligned(domain_size, type.size, type.alignment, __func__); - varray.materialize_to_uninitialized(varray.index_range(), data.data); - data.size = domain_size; - data.sharing_info = ImplicitSharingPtr<>(implicit_sharing::info_for_mem_free(data.data)); - return data; - } - case AttributeInit::Type::MoveArray: { - const auto &init = static_cast(initializer); - Attribute::ArrayData data; - data.data = init.data; - data.size = domain_size; - data.sharing_info = ImplicitSharingPtr<>(implicit_sharing::info_for_mem_free(data.data)); - return data; - } - case AttributeInit::Type::Shared: { - const auto &init = static_cast(initializer); - Attribute::ArrayData data; - data.data = const_cast(init.data); - data.size = domain_size; - data.sharing_info = ImplicitSharingPtr<>(init.sharing_info); - data.sharing_info->add_user(); - return data; - } - } - BLI_assert_unreachable(); - return {}; -} - static constexpr AttributeAccessorFunctions get_pointcloud_accessor_functions() { AttributeAccessorFunctions fn{}; @@ -175,7 +60,7 @@ static constexpr AttributeAccessorFunctions get_pointcloud_accessor_functions() }; fn.builtin_domain_and_type = [](const void * /*owner*/, const StringRef name) -> std::optional { - const BuiltinInfo *info = builtin_attributes().lookup_ptr(name); + const AttrBuiltinInfo *info = builtin_attributes().lookup_ptr(name); if (!info) { return std::nullopt; } @@ -184,7 +69,7 @@ static constexpr AttributeAccessorFunctions get_pointcloud_accessor_functions() return AttributeDomainAndType{info->domain, *cd_type}; }; fn.get_builtin_default = [](const void * /*owner*/, StringRef name) -> GPointer { - const BuiltinInfo &info = builtin_attributes().lookup(name); + const AttrBuiltinInfo &info = builtin_attributes().lookup(name); return info.default_value; }; fn.lookup = [](const void *owner, const StringRef name) -> GAttributeReader { @@ -225,7 +110,7 @@ static constexpr AttributeAccessorFunctions get_pointcloud_accessor_functions() }); }; fn.lookup_validator = [](const void * /*owner*/, const StringRef name) -> AttributeValidator { - const BuiltinInfo *info = builtin_attributes().lookup_ptr(name); + const AttrBuiltinInfo *info = builtin_attributes().lookup_ptr(name); if (!info) { return {}; } @@ -238,17 +123,17 @@ static constexpr AttributeAccessorFunctions get_pointcloud_accessor_functions() if (!attribute) { return {}; } - return attribute_to_writer(pointcloud, pointcloud.totpoint, *attribute); + return attribute_to_writer(&pointcloud, changed_tags(), pointcloud.totpoint, *attribute); }; fn.remove = [](void *owner, const StringRef name) -> bool { PointCloud &pointcloud = *static_cast(owner); AttributeStorage &storage = pointcloud.attribute_storage.wrap(); - if (const BuiltinInfo *info = builtin_attributes().lookup_ptr(name)) { + if (const AttrBuiltinInfo *info = builtin_attributes().lookup_ptr(name)) { if (!info->deletable) { return false; } } - const std::optional fn = changed_tags().lookup_try(name); + const std::optional fn = changed_tags().lookup_try(name); const bool removed = storage.remove(name); if (!removed) { return false; @@ -268,7 +153,7 @@ static constexpr AttributeAccessorFunctions get_pointcloud_accessor_functions() AttributeStorage &storage = pointcloud.attribute_storage.wrap(); const std::optional type = custom_data_type_to_attr_type(data_type); BLI_assert(type.has_value()); - if (const BuiltinInfo *info = builtin_attributes().lookup_ptr(name)) { + if (const AttrBuiltinInfo *info = builtin_attributes().lookup_ptr(name)) { if (info->domain != domain || info->type != type) { return false; } diff --git a/source/blender/geometry/intern/randomize.cc b/source/blender/geometry/intern/randomize.cc index 0a6989254f3..218443c2e1f 100644 --- a/source/blender/geometry/intern/randomize.cc +++ b/source/blender/geometry/intern/randomize.cc @@ -259,7 +259,8 @@ void debug_randomize_instance_order(bke::Instances *instances) const int instances_num = instances->instances_num(); const int seed = seed_from_instances(*instances); const Array new_by_old_map = get_permutation(instances_num, seed); - reorder_customdata(instances->custom_data_attributes(), new_by_old_map); + reorder_attribute_domain( + instances->attribute_storage(), bke::AttrDomain::Instance, new_by_old_map); } bool use_debug_randomization()