Files
test2/source/blender/blenkernel/BKE_attribute_storage.hh
2025-10-08 15:19:01 +11:00

259 lines
8.1 KiB
C++

/* SPDX-FileCopyrightText: 2025 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include <variant>
#include "BLI_function_ref.hh"
#include "BLI_implicit_sharing_ptr.hh"
#include "BLI_memory_counter_fwd.hh"
#include "BLI_string_ref.hh"
#include "BLI_vector_set.hh"
#include "DNA_attribute_types.h"
struct BlendDataReader;
struct BlendWriter;
struct IDTypeForeachColorFunctionCallback;
namespace blender {
class GPointer;
class CPPType;
class ResourceScope;
} // namespace blender
namespace blender::bke {
enum class AttrDomain : int8_t;
enum class AttrType : int16_t;
enum class AttrStorageType : int8_t;
/** Data and metadata for a single geometry attribute. */
class Attribute {
public:
/**
* Data for an attribute stored as a full contiguous array with a data type exactly matching the
* attribute's type. The array referenced must match the size of the domain and the data type.
*/
struct ArrayData {
/* NOTE: Since the shared data pointed to by `sharing_info` knows how to free itself, it often
* stores the size and type itself. It may be possible to make use of that fact to avoid
* storing it here, or even vice versa. */
void *data;
/** The number of elements in the array. */
int64_t size;
ImplicitSharingPtr<> sharing_info;
static ArrayData from_value(const GPointer &value, int64_t domain_size);
static ArrayData from_default_value(const CPPType &type, int64_t domain_size);
static ArrayData from_uninitialized(const CPPType &type, int64_t domain_size);
static ArrayData from_constructed(const CPPType &type, int64_t domain_size);
};
/** Data for an attribute stored as a single value for the entire domain. */
struct SingleData {
/* NOTE: For simplicity and to avoid a bit of redundancy, the domain size isn't stored here.
* It's not necessary to manage a single value. */
void *value;
ImplicitSharingPtr<> sharing_info;
static SingleData from_value(const GPointer &value);
static SingleData from_default_value(const CPPType &type);
};
using DataVariant = std::variant<ArrayData, SingleData>;
friend AttributeStorage;
private:
/**
* Because it's used as the custom ID for the attributes vector set, the name cannot be changed
* without removing and adding the attribute.
*/
std::string name_;
AttrDomain domain_;
AttrType type_;
DataVariant data_;
public:
/**
* Unique name across all domains.
* \note Compared to #CustomData, which doesn't enforce uniqueness, across domains on its own,
* this is enforced by asserts when adding attributes. See #unique_name_calc() (which is also
* called during the conversion process).
*/
StringRefNull name() const;
/** Which part of a geometry the attribute corresponds to. */
AttrDomain domain() const;
/**
* The data type exposed to the user. Depending on the storage type, the actual internal values
* may not be the same type.
*/
AttrType data_type() const;
/**
* The method used to store the data. This gives flexibility to optimize the internal storage
* even though conceptually the attribute is an array of values.
*/
AttrStorageType storage_type() const;
/**
* Low level access to the data stored for the attribute. The variant's type will correspond to
* the storage type.
*/
const DataVariant &data() const;
/**
* The same as #data(), but if the attribute data is shared initially, it will be unshared and
* made mutable.
*/
DataVariant &data_for_write();
/** Replace the attribute's data without first making the existing data mutable. */
void assign_data(DataVariant &&data);
};
class AttributeStorageRuntime {
friend AttributeStorage;
struct AttributeNameGetter {
StringRef operator()(const std::unique_ptr<Attribute> &value) const
{
return value->name();
}
};
/**
* For quick access, the attributes are stored in a vector set, keyed by their name. Attributes
* can still be reordered by rebuilding the vector set from scratch. Each attribute is allocated
* to give pointer stability across additions and removals.
*/
CustomIDVectorSet<std::unique_ptr<Attribute>, AttributeNameGetter> attributes;
};
class AttributeStorage : public ::AttributeStorage {
public:
AttributeStorage();
AttributeStorage(const AttributeStorage &other);
AttributeStorage(AttributeStorage &&other);
AttributeStorage &operator=(const AttributeStorage &other);
AttributeStorage &operator=(AttributeStorage &&other);
~AttributeStorage();
/**
* Iterate over all attributes, with the order defined by the order of insertion. It is not safe
* to add or remove attributes while iterating.
*/
void foreach(FunctionRef<void(Attribute &)> fn);
void foreach(FunctionRef<void(const Attribute &)> fn) const;
void foreach_with_stop(FunctionRef<bool(Attribute &)> fn);
void foreach_with_stop(FunctionRef<bool(const Attribute &)> fn) const;
/** Return the number of attributes. */
int count() const;
/** Return the attribute at the given index. */
Attribute &at_index(int index);
const Attribute &at_index(int index) const;
/** Return the index of the attribute with the given name, or -1 if not found. */
int index_of(StringRef name) const;
/**
* Try to find the attribute with a given name. The non-const overload does not make the
* attribute data itself mutable.
*/
Attribute *lookup(StringRef name);
const Attribute *lookup(StringRef name) const;
/**
* Attempt to remove the attribute with the given name, returning `true` if successful. Should
* not be called while iterating over attributes.
*/
bool remove(StringRef name);
/**
* Add an attribute with the given name, which must not already be used by an existing attribute
* or this will invoke undefined behavior.
*/
Attribute &add(std::string name,
bke::AttrDomain domain,
bke::AttrType data_type,
Attribute::DataVariant data);
/** Return a possibly changed version of the input name that is unique within existing names. */
std::string unique_name_calc(StringRef name) const;
/** 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.
*/
void blend_read(BlendDataReader &reader);
/**
* Temporary data used to write a #AttributeStorage struct embedded in another struct. See
* #attribute_storage_blend_write_prepare for more information.
*/
struct BlendWriteData {
ResourceScope &scope;
Vector<::Attribute, 16> &attributes;
explicit BlendWriteData(ResourceScope &scope);
};
/**
* Write the prepared data and the data stored in the DNA fields in
* the #AttributeStorage struct.
*/
void blend_write(BlendWriter &writer, const BlendWriteData &write_data);
/**
* Iterate over every color to change it to another colorspace.
*/
void foreach_working_space_color(const IDTypeForeachColorFunctionCallback &fn);
void count_memory(MemoryCounter &memory) const;
};
/** The C++ wrapper needs to be the same size as the DNA struct. */
static_assert(sizeof(AttributeStorage) == sizeof(::AttributeStorage));
inline StringRefNull Attribute::name() const
{
return name_;
}
inline AttrDomain Attribute::domain() const
{
return domain_;
}
inline AttrType Attribute::data_type() const
{
return type_;
}
inline const Attribute::DataVariant &Attribute::data() const
{
return data_;
}
inline void Attribute::assign_data(DataVariant &&data)
{
data_ = std::move(data);
}
} // namespace blender::bke
inline blender::bke::AttributeStorage &AttributeStorage::wrap()
{
return *reinterpret_cast<blender::bke::AttributeStorage *>(this);
}
inline const blender::bke::AttributeStorage &AttributeStorage::wrap() const
{
return *reinterpret_cast<const blender::bke::AttributeStorage *>(this);
}