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
This commit is contained in:
Hans Goudey
2025-06-26 17:03:56 +02:00
committed by Hans Goudey
parent 9bc67b78ce
commit f74e304b00
12 changed files with 532 additions and 319 deletions

View File

@@ -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.

View File

@@ -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<void(const GeometrySet &geometry_set)> 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_;
}

View File

@@ -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

View File

@@ -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(

View File

@@ -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<bke::Attribute::ArrayData>(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,

View File

@@ -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::ArrayData>(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::SingleData>(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<StringRef, AttrUpdateOnChange> &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::ArrayData>(attribute.data_for_write());
BLI_assert(data.size == domain_size);
std::function<void()> 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<const AttributeInitVArray &>(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<const AttributeInitMoveArray &>(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<const AttributeInitShared &>(initializer);
Attribute::ArrayData data;
data.data = const_cast<void *>(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<bke::Attribute::ArrayData>(attr->data());
const GSpan span(cpp_type, data.data, data.size);
return GVArray::ForSpan(span);
}
case bke::AttrStorageType::Single: {
const auto &data = std::get<bke::Attribute::SingleData>(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<bke::Attribute::ArrayData>(&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<bke::Attribute::SingleData>(&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<bke::Attribute::ArrayData>(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<bke::Attribute::ArrayData>(attr.data_for_write());
BLI_assert(array_data.size == domain_size);
return GMutableSpan(cpp_type, array_data.data, domain_size);
}
} // namespace blender::bke

View File

@@ -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<StringRef, AttrUpdateOnChange> &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<typename T>
inline VArray<T> 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<T>(), name, domain_size, &default_value);
return varray.typed<T>();
}
GSpan get_span_attribute(const AttributeStorage &storage,
AttrDomain domain,
const CPPType &cpp_type,
StringRef name,
const int64_t domain_size);
template<typename T>
inline Span<T> 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<T>(), name, domain_size);
return span.typed<T>();
}
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<typename T>
inline MutableSpan<T> 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<T>(), name, domain_size, &default_value);
return span.typed<T>();
}
} // namespace blender::bke

View File

@@ -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<int> Instances::reference_handles() const
{
return {static_cast<const int *>(
CustomData_get_layer_named(&attributes_, CD_PROP_INT32, ".reference_index")),
instances_num_};
return get_span_attribute<int>(
attributes_, AttrDomain::Instance, ".reference_index", instances_num_);
}
MutableSpan<int> Instances::reference_handles_for_write()
{
int *data = static_cast<int *>(CustomData_get_layer_named_for_write(
&attributes_, CD_PROP_INT32, ".reference_index", instances_num_));
if (!data) {
data = static_cast<int *>(CustomData_add_layer_named(
&attributes_, CD_PROP_INT32, CD_SET_DEFAULT, instances_num_, ".reference_index"));
}
return {data, instances_num_};
return get_mutable_attribute<int>(
attributes_, AttrDomain::Instance, ".reference_index", instances_num_);
}
Span<float4x4> Instances::transforms() const
{
return {static_cast<const float4x4 *>(
CustomData_get_layer_named(&attributes_, CD_PROP_FLOAT4X4, "instance_transform")),
instances_num_};
return get_span_attribute<float4x4>(
attributes_, AttrDomain::Instance, "instance_transform", instances_num_);
}
MutableSpan<float4x4> Instances::transforms_for_write()
{
float4x4 *data = static_cast<float4x4 *>(CustomData_get_layer_named_for_write(
&attributes_, CD_PROP_FLOAT4X4, "instance_transform", instances_num_));
if (!data) {
data = static_cast<float4x4 *>(CustomData_add_layer_named(
&attributes_, CD_PROP_FLOAT4X4, CD_SET_DEFAULT, instances_num_, "instance_transform"));
}
return {data, instances_num_};
return get_mutable_attribute<float4x4>(
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);
}

View File

@@ -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<Instances *>(owner);
return &instances->custom_data_attributes();
},
[](const void *owner) -> const CustomData * {
const Instances *instances = static_cast<const Instances *>(owner);
return &instances->custom_data_attributes();
},
[](const void *owner) -> int {
const Instances *instances = static_cast<const Instances *>(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<StringRef, AttrUpdateOnChange> 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<providers>();
fn.domain_size = [](const void *owner, const AttrDomain domain) {
if (owner == nullptr) {
return 0;
}
const Instances *instances = static_cast<const Instances *>(owner);
switch (domain) {
case AttrDomain::Instance:
return instances->instances_num();
default:
return 0;
}
};
static auto attributes = []() {
Map<StringRef, AttrBuiltinInfo> 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<const Instances *>(owner)->instances_num() :
0;
};
fn.builtin_domain_and_type = [](const void * /*owner*/,
const StringRef name) -> std::optional<AttributeDomainAndType> {
const AttrBuiltinInfo *info = builtin_attributes().lookup_ptr(name);
if (!info) {
return std::nullopt;
}
const std::optional<eCustomDataType> 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<const Instances *>(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<void(const AttributeIter &)> fn,
const AttributeAccessor &accessor) {
const Instances &instances = *static_cast<const Instances *>(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<eCustomDataType> 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<Instances *>(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<Instances *>(owner);
AttributeStorage &storage = instances.attribute_storage();
if (const AttrBuiltinInfo *info = builtin_attributes().lookup_ptr(name)) {
if (!info->deletable) {
return false;
}
}
const std::optional<AttrUpdateOnChange> 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<Instances *>(owner);
const int domain_size = instances.instances_num();
AttributeStorage &storage = instances.attribute_storage();
const std::optional<AttrType> 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;
}

View File

@@ -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<typename T>
static VArray<T> 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<T>::ForSingle(default_value, pointcloud.totpoint);
}
switch (attr->storage_type()) {
case bke::AttrStorageType::Array: {
const auto &data = std::get<bke::Attribute::ArrayData>(attr->data());
const Span span(static_cast<const T *>(data.data), data.size);
BLI_assert(data.size == pointcloud.totpoint);
return VArray<T>::ForSpan(span);
}
case bke::AttrStorageType::Single: {
const auto &data = std::get<bke::Attribute::SingleData>(attr->data());
return VArray<T>::ForSingle(*static_cast<const T *>(data.value), pointcloud.totpoint);
}
}
return VArray<T>::ForSingle(default_value, pointcloud.totpoint);
}
template<typename T>
static Span<T> 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<bke::Attribute::ArrayData>(&attr->data())) {
BLI_assert(array_data->size == pointcloud.totpoint);
return Span(static_cast<const T *>(array_data->data), array_data->size);
}
return {};
}
template<typename T>
static MutableSpan<T> 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<T>());
if (bke::Attribute *attr = pointcloud.attribute_storage.wrap().lookup(name)) {
if (attr->data_type() == type) {
if (const auto *single_data = std::get_if<bke::Attribute::SingleData>(&attr->data())) {
/* Convert single value storage to array storage. */
const GPointer g_value(CPPType::get<T>(), single_data->value);
attr->assign_data(bke::Attribute::ArrayData::ForValue(g_value, pointcloud.totpoint));
}
auto &array_data = std::get<bke::Attribute::ArrayData>(attr->data_for_write());
BLI_assert(array_data.size == pointcloud.totpoint);
return MutableSpan(static_cast<T *>(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<T>(), &default_value},
pointcloud.totpoint));
auto &array_data = std::get<bke::Attribute::ArrayData>(attr.data_for_write());
BLI_assert(array_data.size == pointcloud.totpoint);
return MutableSpan(static_cast<T *>(array_data.data), pointcloud.totpoint);
}
Span<float3> PointCloud::positions() const
{
return get_span_attribute<float3>(*this, "position");
return blender::bke::get_span_attribute<float3>(
this->attribute_storage.wrap(), blender::bke::AttrDomain::Point, "position", this->totpoint);
}
MutableSpan<float3> PointCloud::positions_for_write()
{
return get_mutable_attribute<float3>(*this, "position");
return blender::bke::get_mutable_attribute<float3>(
this->attribute_storage.wrap(), blender::bke::AttrDomain::Point, "position", this->totpoint);
}
VArray<float> PointCloud::radius() const
{
return get_varray_attribute<float>(*this, "radius", 0.01f);
return blender::bke::get_varray_attribute<float>(this->attribute_storage.wrap(),
blender::bke::AttrDomain::Point,
"radius",
this->totpoint,
0.01f);
}
MutableSpan<float> PointCloud::radius_for_write()
{
return get_mutable_attribute<float>(*this, "radius", 0.01f);
return blender::bke::get_mutable_attribute<float>(this->attribute_storage.wrap(),
blender::bke::AttrDomain::Point,
"radius",
this->totpoint,
0.01f);
}
PointCloud *BKE_pointcloud_add(Main *bmain, const char *name)

View File

@@ -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<StringRef, UpdateOnChange> attributes{{"position", tag_position_changed},
{"radius", tag_radius_changed}};
static Map<StringRef, AttrUpdateOnChange> 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<StringRef, BuiltinInfo> map;
Map<StringRef, AttrBuiltinInfo> 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::ArrayData>(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::SingleData>(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::ArrayData>(attribute.data_for_write());
BLI_assert(data.size == domain_size);
std::function<void()> 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<const AttributeInitVArray &>(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<const AttributeInitMoveArray &>(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<const AttributeInitShared &>(initializer);
Attribute::ArrayData data;
data.data = const_cast<void *>(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<AttributeDomainAndType> {
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<PointCloud *>(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<UpdateOnChange> fn = changed_tags().lookup_try(name);
const std::optional<AttrUpdateOnChange> 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<AttrType> 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;
}

View File

@@ -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<int> 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()