WIP: Attributes: avoid unnecessary data copies by generalizing attribute unshare policy #119588

Draft
Jacques Lucke wants to merge 4 commits from JacquesLucke/blender:attribute-unshare into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
13 changed files with 288 additions and 74 deletions

View File

@ -10,6 +10,7 @@
#include "BLI_function_ref.hh"
#include "BLI_generic_span.hh"
#include "BLI_generic_virtual_array.hh"
#include "BLI_implicit_sharing_unshare.hh"
#include "BLI_offset_indices.hh"
#include "BLI_set.hh"
#include "BLI_struct_equality_utils.hh"
@ -426,7 +427,9 @@ struct AttributeAccessorFunctions {
bool (*for_all)(const void *owner,
FunctionRef<bool(const AttributeIDRef &, const AttributeMetaData &)> fn);
AttributeValidator (*lookup_validator)(const void *owner, const AttributeIDRef &attribute_id);
GAttributeWriter (*lookup_for_write)(void *owner, const AttributeIDRef &attribute_id);
GAttributeWriter (*lookup_for_write)(void *owner,
const AttributeIDRef &attribute_id,
const ArrayUnsharePolicy &unshare_policy);
bool (*remove)(void *owner, const AttributeIDRef &attribute_id);
bool (*add)(void *owner,
const AttributeIDRef &attribute_id,
@ -642,20 +645,27 @@ class MutableAttributeAccessor : public AttributeAccessor {
* Get a writable attribute or none if it does not exist.
* Make sure to call #finish after changes are done.
*/
GAttributeWriter lookup_for_write(const AttributeIDRef &attribute_id);
GAttributeWriter lookup_for_write(
const AttributeIDRef &attribute_id,
const ArrayUnsharePolicy &unshare_policy = DefaultArrayUnsharePolicy());
/**
* Same as above, but returns a type that makes it easier to work with the attribute as a span.
*/
GSpanAttributeWriter lookup_for_write_span(const AttributeIDRef &attribute_id);
GSpanAttributeWriter lookup_for_write_span(
const AttributeIDRef &attribute_id,
const ArrayUnsharePolicy &unshare_policy = DefaultArrayUnsharePolicy());
/**
* Get a writable attribute or non if it does not exist.
* Make sure to call #finish after changes are done.
*/
template<typename T> AttributeWriter<T> lookup_for_write(const AttributeIDRef &attribute_id)
template<typename T>
AttributeWriter<T> lookup_for_write(
const AttributeIDRef &attribute_id,
const ArrayUnsharePolicy &unshare_policy = DefaultArrayUnsharePolicy())
{
GAttributeWriter attribute = this->lookup_for_write(attribute_id);
GAttributeWriter attribute = this->lookup_for_write(attribute_id, unshare_policy);
if (!attribute) {
return {};
}
@ -669,9 +679,11 @@ class MutableAttributeAccessor : public AttributeAccessor {
* Same as above, but returns a type that makes it easier to work with the attribute as a span.
*/
template<typename T>
SpanAttributeWriter<T> lookup_for_write_span(const AttributeIDRef &attribute_id)
SpanAttributeWriter<T> lookup_for_write_span(
const AttributeIDRef &attribute_id,
const ArrayUnsharePolicy &unshare_policy = DefaultArrayUnsharePolicy())
{
AttributeWriter<T> attribute = this->lookup_for_write<T>(attribute_id);
AttributeWriter<T> attribute = this->lookup_for_write<T>(attribute_id, unshare_policy);
if (attribute) {
return SpanAttributeWriter<T>{std::move(attribute), true};
}
@ -714,7 +726,8 @@ class MutableAttributeAccessor : public AttributeAccessor {
const AttributeIDRef &attribute_id,
const AttrDomain domain,
const eCustomDataType data_type,
const AttributeInit &initializer = AttributeInitDefaultValue());
const AttributeInit &initializer = AttributeInitDefaultValue(),
const ArrayUnsharePolicy &unshare_policy = DefaultArrayUnsharePolicy());
/**
* Same as above, but returns a type that makes it easier to work with the attribute as a span.
@ -725,7 +738,8 @@ class MutableAttributeAccessor : public AttributeAccessor {
const AttributeIDRef &attribute_id,
const AttrDomain domain,
const eCustomDataType data_type,
const AttributeInit &initializer = AttributeInitDefaultValue());
const AttributeInit &initializer = AttributeInitDefaultValue(),
const ArrayUnsharePolicy &unshare_policy = DefaultArrayUnsharePolicy());
/**
* Same as above, but should be used when the type is known at compile time.
@ -734,11 +748,14 @@ class MutableAttributeAccessor : public AttributeAccessor {
AttributeWriter<T> lookup_or_add_for_write(
const AttributeIDRef &attribute_id,
const AttrDomain domain,
const AttributeInit &initializer = AttributeInitDefaultValue())
const AttributeInit &initializer = AttributeInitDefaultValue(),
const ArrayUnsharePolicy &unshare_policy = DefaultArrayUnsharePolicy())
{
const CPPType &cpp_type = CPPType::get<T>();
const eCustomDataType data_type = cpp_type_to_custom_data_type(cpp_type);
return this->lookup_or_add_for_write(attribute_id, domain, data_type, initializer).typed<T>();
return this
->lookup_or_add_for_write(attribute_id, domain, data_type, initializer, unshare_policy)
.typed<T>();
}
/**
@ -748,10 +765,11 @@ class MutableAttributeAccessor : public AttributeAccessor {
SpanAttributeWriter<T> lookup_or_add_for_write_span(
const AttributeIDRef &attribute_id,
const AttrDomain domain,
const AttributeInit &initializer = AttributeInitDefaultValue())
const AttributeInit &initializer = AttributeInitDefaultValue(),
const ArrayUnsharePolicy &unshare_policy = DefaultArrayUnsharePolicy())
{
AttributeWriter<T> attribute = this->lookup_or_add_for_write<T>(
attribute_id, domain, initializer);
attribute_id, domain, initializer, unshare_policy);
if (attribute) {
return SpanAttributeWriter<T>{std::move(attribute), true};
}
@ -780,7 +798,10 @@ class MutableAttributeAccessor : public AttributeAccessor {
const AttrDomain domain)
{
AttributeWriter<T> attribute = this->lookup_or_add_for_write<T>(
attribute_id, domain, AttributeInitConstruct());
attribute_id,
domain,
AttributeInitConstruct(),
implicit_sharing::unshare::IgnoreOldValues());
if (attribute) {
return SpanAttributeWriter<T>{std::move(attribute), false};

View File

@ -11,6 +11,7 @@
#include "BLI_cpp_type.hh"
#include "BLI_implicit_sharing.h"
#include "BLI_implicit_sharing_unshare.hh"
#include "BLI_set.hh"
#include "BLI_span.hh"
#include "BLI_string_ref.hh"
@ -464,24 +465,75 @@ void CustomData_bmesh_interp(CustomData *data,
*/
void CustomData_swap_corners(CustomData *data, int index, const int *corner_indices);
class CustomDataUnsharePolicy {
public:
void unshare_data(CustomDataLayer &layer, int totelem) const
{
void *old_data = layer.data;
this->unshare_data_impl(layer, totelem);
BLI_assert(totelem == 0 || old_data != layer.data);
UNUSED_VARS_NDEBUG(old_data);
}
private:
virtual void unshare_data_impl(CustomDataLayer &layer, int totelem) const = 0;
};
class CustomDataUnsharePolicy_CopyAll : public CustomDataUnsharePolicy {
void unshare_data_impl(CustomDataLayer &layer, int totelem) const override;
};
class CustomDataUnsharePolicy_ArrayUnsharePolicy : public CustomDataUnsharePolicy {
private:
const blender::ArrayUnsharePolicy &array_unshare_policy_;
public:
CustomDataUnsharePolicy_ArrayUnsharePolicy(
const blender::ArrayUnsharePolicy &array_unshare_policy)
: array_unshare_policy_(array_unshare_policy)
{
}
private:
void unshare_data_impl(CustomDataLayer &layer, int totelem) const override;
};
using DefaultCustomDataUnsharePolicy = CustomDataUnsharePolicy_CopyAll;
/**
* Custom data layers can be shared through implicit sharing (`BLI_implicit_sharing.h`). This
* function makes sure that the layer is unshared if it was shared, which makes it mutable.
*/
void CustomData_ensure_data_is_mutable(CustomDataLayer *layer, int totelem);
void CustomData_ensure_layers_are_mutable(CustomData *data, int totelem);
void CustomData_ensure_data_is_mutable(
CustomDataLayer *layer,
int totelem,
const CustomDataUnsharePolicy &unshare_policy = DefaultCustomDataUnsharePolicy());
void CustomData_ensure_layers_are_mutable(
CustomData *data,
int totelem,
const CustomDataUnsharePolicy &unshare_policy = DefaultCustomDataUnsharePolicy());
/**
* Retrieve a pointer to an element of the active layer of the given \a type, chosen by the
* \a index, if it exists.
*/
void *CustomData_get_for_write(CustomData *data, int index, eCustomDataType type, int totelem);
void *CustomData_get_for_write(
CustomData *data,
int index,
eCustomDataType type,
int totelem,
const CustomDataUnsharePolicy &unshare_policy = DefaultCustomDataUnsharePolicy());
/**
* Retrieve a pointer to an element of the \a nth layer of the given \a type, chosen by the
* \a index, if it exists.
*/
void *CustomData_get_n_for_write(
CustomData *data, eCustomDataType type, int index, int n, int totelem);
CustomData *data,
eCustomDataType type,
int index,
int n,
int totelem,
const CustomDataUnsharePolicy &unshare_policy = DefaultCustomDataUnsharePolicy());
/* BMesh Custom Data Functions.
* Should replace edit-mesh ones with these as well, due to more efficient memory alloc. */
@ -506,14 +558,23 @@ const char *CustomData_get_layer_name(const CustomData *data, eCustomDataType ty
* otherwise.
*/
const void *CustomData_get_layer(const CustomData *data, eCustomDataType type);
void *CustomData_get_layer_for_write(CustomData *data, eCustomDataType type, int totelem);
void *CustomData_get_layer_for_write(
CustomData *data,
eCustomDataType type,
int totelem,
const CustomDataUnsharePolicy &unshare_policy = DefaultCustomDataUnsharePolicy());
/**
* Retrieve the data array of the \a nth layer of the given \a type, if it exists. Return null
* otherwise.
*/
const void *CustomData_get_layer_n(const CustomData *data, eCustomDataType type, int n);
void *CustomData_get_layer_n_for_write(CustomData *data, eCustomDataType type, int n, int totelem);
void *CustomData_get_layer_n_for_write(
CustomData *data,
eCustomDataType type,
int n,
int totelem,
const CustomDataUnsharePolicy &unshare_policy = DefaultCustomDataUnsharePolicy());
/**
* Retrieve the data array of the layer with the given \a name and \a type, if it exists. Return
@ -522,10 +583,12 @@ void *CustomData_get_layer_n_for_write(CustomData *data, eCustomDataType type, i
const void *CustomData_get_layer_named(const CustomData *data,
eCustomDataType type,
blender::StringRef name);
void *CustomData_get_layer_named_for_write(CustomData *data,
eCustomDataType type,
blender::StringRef name,
int totelem);
void *CustomData_get_layer_named_for_write(
CustomData *data,
eCustomDataType type,
blender::StringRef name,
int totelem,
const CustomDataUnsharePolicy &unshare_policy = DefaultCustomDataUnsharePolicy());
int CustomData_get_offset(const CustomData *data, eCustomDataType type);
int CustomData_get_offset_named(const CustomData *data,

View File

@ -430,7 +430,8 @@ GAttributeReader BuiltinCustomDataLayerProvider::try_get_for_read(const void *ow
return {GVArray::ForSpan({type, layer.data, element_num}), domain_, layer.sharing_info};
}
GAttributeWriter BuiltinCustomDataLayerProvider::try_get_for_write(void *owner) const
GAttributeWriter BuiltinCustomDataLayerProvider::try_get_for_write(
void *owner, const ArrayUnsharePolicy &unshare_policy) const
{
CustomData *custom_data = custom_data_access_.get_custom_data(owner);
if (custom_data == nullptr) {
@ -454,10 +455,19 @@ GAttributeWriter BuiltinCustomDataLayerProvider::try_get_for_write(void *owner)
void *data = nullptr;
if (stored_as_named_attribute_) {
data = CustomData_get_layer_named_for_write(custom_data, stored_type_, name_, element_num);
data = CustomData_get_layer_named_for_write(
custom_data,
stored_type_,
name_,
element_num,
CustomDataUnsharePolicy_ArrayUnsharePolicy(unshare_policy));
}
else {
data = CustomData_get_layer_for_write(custom_data, stored_type_, element_num);
data = CustomData_get_layer_for_write(
custom_data,
stored_type_,
element_num,
CustomDataUnsharePolicy_ArrayUnsharePolicy(unshare_policy));
}
if (data == nullptr) {
return {};
@ -563,7 +573,9 @@ GAttributeReader CustomDataAttributeProvider::try_get_for_read(
}
GAttributeWriter CustomDataAttributeProvider::try_get_for_write(
void *owner, const AttributeIDRef &attribute_id) const
void *owner,
const AttributeIDRef &attribute_id,
const ArrayUnsharePolicy &unshare_policy) const
{
CustomData *custom_data = custom_data_access_.get_custom_data(owner);
if (custom_data == nullptr) {
@ -575,7 +587,11 @@ GAttributeWriter CustomDataAttributeProvider::try_get_for_write(
continue;
}
CustomData_get_layer_named_for_write(
custom_data, eCustomDataType(layer.type), layer.name, element_num);
custom_data,
eCustomDataType(layer.type),
layer.name,
element_num,
CustomDataUnsharePolicy_ArrayUnsharePolicy(unshare_policy));
const CPPType *type = custom_data_type_to_cpp_type(eCustomDataType(layer.type));
if (type == nullptr) {
@ -753,9 +769,10 @@ struct FinishCallChecker {
};
#endif
GAttributeWriter MutableAttributeAccessor::lookup_for_write(const AttributeIDRef &attribute_id)
GAttributeWriter MutableAttributeAccessor::lookup_for_write(
const AttributeIDRef &attribute_id, const ArrayUnsharePolicy &unshare_policy)
{
GAttributeWriter attribute = fn_->lookup_for_write(owner_, attribute_id);
GAttributeWriter attribute = fn_->lookup_for_write(owner_, attribute_id, unshare_policy);
/* Check that the #finish method is called in debug builds. */
#ifndef NDEBUG
if (attribute) {
@ -774,9 +791,9 @@ GAttributeWriter MutableAttributeAccessor::lookup_for_write(const AttributeIDRef
}
GSpanAttributeWriter MutableAttributeAccessor::lookup_for_write_span(
const AttributeIDRef &attribute_id)
const AttributeIDRef &attribute_id, const ArrayUnsharePolicy &unshare_policy)
{
GAttributeWriter attribute = this->lookup_for_write(attribute_id);
GAttributeWriter attribute = this->lookup_for_write(attribute_id, unshare_policy);
if (attribute) {
return GSpanAttributeWriter{std::move(attribute), true};
}
@ -787,17 +804,18 @@ GAttributeWriter MutableAttributeAccessor::lookup_or_add_for_write(
const AttributeIDRef &attribute_id,
const AttrDomain domain,
const eCustomDataType data_type,
const AttributeInit &initializer)
const AttributeInit &initializer,
const ArrayUnsharePolicy &unshare_policy)
{
std::optional<AttributeMetaData> meta_data = this->lookup_meta_data(attribute_id);
if (meta_data.has_value()) {
if (meta_data->domain == domain && meta_data->data_type == data_type) {
return this->lookup_for_write(attribute_id);
return this->lookup_for_write(attribute_id, unshare_policy);
}
return {};
}
if (this->add(attribute_id, domain, data_type, initializer)) {
return this->lookup_for_write(attribute_id);
return this->lookup_for_write(attribute_id, unshare_policy);
}
return {};
}
@ -806,10 +824,11 @@ GSpanAttributeWriter MutableAttributeAccessor::lookup_or_add_for_write_span(
const AttributeIDRef &attribute_id,
const AttrDomain domain,
const eCustomDataType data_type,
const AttributeInit &initializer)
const AttributeInit &initializer,
const ArrayUnsharePolicy &unshare_policy)
{
GAttributeWriter attribute = this->lookup_or_add_for_write(
attribute_id, domain, data_type, initializer);
attribute_id, domain, data_type, initializer, unshare_policy);
if (attribute) {
return GSpanAttributeWriter{std::move(attribute), true};
}
@ -820,7 +839,11 @@ GSpanAttributeWriter MutableAttributeAccessor::lookup_or_add_for_write_only_span
const AttributeIDRef &attribute_id, const AttrDomain domain, const eCustomDataType data_type)
{
GAttributeWriter attribute = this->lookup_or_add_for_write(
attribute_id, domain, data_type, AttributeInitConstruct());
attribute_id,
domain,
data_type,
AttributeInitConstruct(),
implicit_sharing::unshare::IgnoreOldValues());
if (attribute) {
return GSpanAttributeWriter{std::move(attribute), false};
}

View File

@ -69,7 +69,8 @@ class BuiltinAttributeProvider {
}
virtual GAttributeReader try_get_for_read(const void *owner) const = 0;
virtual GAttributeWriter try_get_for_write(void *owner) const = 0;
virtual GAttributeWriter try_get_for_write(void *owner,
const ArrayUnsharePolicy &unshare_policy) const = 0;
virtual bool try_delete(void *owner) const = 0;
virtual bool try_create(void *onwer, const AttributeInit &initializer) const = 0;
virtual bool exists(const void *owner) const = 0;
@ -104,7 +105,8 @@ class DynamicAttributesProvider {
virtual GAttributeReader try_get_for_read(const void *owner,
const AttributeIDRef &attribute_id) const = 0;
virtual GAttributeWriter try_get_for_write(void *owner,
const AttributeIDRef &attribute_id) const = 0;
const AttributeIDRef &attribute_id,
const ArrayUnsharePolicy &unshare_policy) const = 0;
virtual bool try_delete(void *owner, const AttributeIDRef &attribute_id) const = 0;
virtual bool try_create(void *owner,
const AttributeIDRef &attribute_id,
@ -141,7 +143,9 @@ class CustomDataAttributeProvider final : public DynamicAttributesProvider {
GAttributeReader try_get_for_read(const void *owner,
const AttributeIDRef &attribute_id) const final;
GAttributeWriter try_get_for_write(void *owner, const AttributeIDRef &attribute_id) const final;
GAttributeWriter try_get_for_write(void *owner,
const AttributeIDRef &attribute_id,
const ArrayUnsharePolicy &unshare_policy) const final;
bool try_delete(void *owner, const AttributeIDRef &attribute_id) const final;
@ -199,7 +203,8 @@ class BuiltinCustomDataLayerProvider final : public BuiltinAttributeProvider {
}
GAttributeReader try_get_for_read(const void *owner) const final;
GAttributeWriter try_get_for_write(void *owner) const final;
GAttributeWriter try_get_for_write(void *owner,
const ArrayUnsharePolicy &unshare_policy) const final;
bool try_delete(void *owner) const final;
bool try_create(void *owner, const AttributeInit &initializer) const final;
bool exists(const void *owner) const final;
@ -373,18 +378,20 @@ inline std::optional<AttributeMetaData> lookup_meta_data(const void *owner,
}
template<const ComponentAttributeProviders &providers>
inline GAttributeWriter lookup_for_write(void *owner, const AttributeIDRef &attribute_id)
inline GAttributeWriter lookup_for_write(void *owner,
const AttributeIDRef &attribute_id,
const ArrayUnsharePolicy &unshare_policy)
{
if (!attribute_id.is_anonymous()) {
const StringRef name = attribute_id.name();
if (const BuiltinAttributeProvider *provider =
providers.builtin_attribute_providers().lookup_default_as(name, nullptr))
{
return provider->try_get_for_write(owner);
return provider->try_get_for_write(owner, unshare_policy);
}
}
for (const DynamicAttributesProvider *provider : providers.dynamic_attribute_providers()) {
GAttributeWriter attribute = provider->try_get_for_write(owner, attribute_id);
GAttributeWriter attribute = provider->try_get_for_write(owner, attribute_id, unshare_policy);
if (attribute) {
return attribute;
}

View File

@ -44,6 +44,7 @@
#include "BLT_translation.hh"
#include "BKE_anonymous_attribute_id.hh"
#include "BKE_attribute.hh"
#include "BKE_customdata.hh"
#include "BKE_customdata_file.h"
#include "BKE_deform.hh"
@ -2377,6 +2378,27 @@ static void free_layer_data(const eCustomDataType type, const void *data, const
MEM_freeN(const_cast<void *>(data));
}
void CustomDataUnsharePolicy_CopyAll::unshare_data_impl(CustomDataLayer &layer,
const int totelem) const
{
layer.data = copy_layer_data(eCustomDataType(layer.type), layer.data, totelem);
}
void CustomDataUnsharePolicy_ArrayUnsharePolicy::unshare_data_impl(CustomDataLayer &layer,
const int totelem) const
{
const eCustomDataType data_type = eCustomDataType(layer.type);
const blender::CPPType *type = blender::bke::custom_data_type_to_cpp_type(data_type);
/* This unshare policy can't be used if there is no #CPPType for the layer type. */
BLI_assert(type != nullptr);
void *new_data = MEM_mallocN_aligned(
type->size() * int64_t(totelem), type->alignment(), __func__);
array_unshare_policy_.unshare_array(blender::GVArray::ForSpan({type, layer.data, totelem}),
blender::GMutableSpan{type, new_data, totelem});
layer.data = new_data;
}
static bool customdata_merge_internal(const CustomData *source,
CustomData *dest,
const eCustomDataMask mask,
@ -2560,7 +2582,9 @@ static const ImplicitSharingInfo *make_implicit_sharing_info_for_layer(const eCu
/**
* If the layer data is currently shared (hence it is immutable), create a copy that can be edited.
*/
static void ensure_layer_data_is_mutable(CustomDataLayer &layer, const int totelem)
static void ensure_layer_data_is_mutable(CustomDataLayer &layer,
const int totelem,
const CustomDataUnsharePolicy &unshare_policy)
{
if (layer.data == nullptr) {
return;
@ -2574,10 +2598,9 @@ static void ensure_layer_data_is_mutable(CustomDataLayer &layer, const int totel
}
else {
const eCustomDataType type = eCustomDataType(layer.type);
const void *old_data = layer.data;
/* Copy the layer before removing the user because otherwise the data might be freed while
* we're still copying from it here. */
layer.data = copy_layer_data(type, old_data, totelem);
/* Unshare the layer data before removing the user because otherwise the data might be freed
* while we're still copying from it here. */
unshare_policy.unshare_data(layer, totelem);
layer.sharing_info->remove_user_and_delete_if_last();
layer.sharing_info = make_implicit_sharing_info_for_layer(type, layer.data, totelem);
}
@ -2591,15 +2614,19 @@ static void ensure_layer_data_is_mutable(CustomDataLayer &layer, const int totel
return layer.sharing_info->is_mutable();
}
void CustomData_ensure_data_is_mutable(CustomDataLayer *layer, const int totelem)
void CustomData_ensure_data_is_mutable(CustomDataLayer *layer,
const int totelem,
const CustomDataUnsharePolicy &unshare_policy)
{
ensure_layer_data_is_mutable(*layer, totelem);
ensure_layer_data_is_mutable(*layer, totelem, unshare_policy);
}
void CustomData_ensure_layers_are_mutable(CustomData *data, int totelem)
void CustomData_ensure_layers_are_mutable(CustomData *data,
int totelem,
const CustomDataUnsharePolicy &unshare_policy)
{
for (const int i : IndexRange(data->totlayer)) {
ensure_layer_data_is_mutable(data->layers[i], totelem);
ensure_layer_data_is_mutable(data->layers[i], totelem, unshare_policy);
}
}
@ -3691,21 +3718,26 @@ void CustomData_swap_corners(CustomData *data, const int index, const int *corne
void *CustomData_get_for_write(CustomData *data,
const int index,
const eCustomDataType type,
int totelem)
int totelem,
const CustomDataUnsharePolicy &unshare_policy)
{
BLI_assert(index >= 0);
void *layer_data = CustomData_get_layer_for_write(data, type, totelem);
void *layer_data = CustomData_get_layer_for_write(data, type, totelem, unshare_policy);
if (!layer_data) {
return nullptr;
}
return POINTER_OFFSET(layer_data, size_t(index) * layerType_getInfo(type)->size);
}
void *CustomData_get_n_for_write(
CustomData *data, const eCustomDataType type, const int index, const int n, int totelem)
void *CustomData_get_n_for_write(CustomData *data,
const eCustomDataType type,
const int index,
const int n,
int totelem,
const CustomDataUnsharePolicy &unshare_policy)
{
BLI_assert(index >= 0);
void *layer_data = CustomData_get_layer_n_for_write(data, type, n, totelem);
void *layer_data = CustomData_get_layer_n_for_write(data, type, n, totelem, unshare_policy);
if (!layer_data) {
return nullptr;
}
@ -3725,14 +3757,15 @@ const void *CustomData_get_layer(const CustomData *data, const eCustomDataType t
void *CustomData_get_layer_for_write(CustomData *data,
const eCustomDataType type,
const int totelem)
const int totelem,
const CustomDataUnsharePolicy &unshare_policy)
{
const int layer_index = CustomData_get_active_layer_index(data, type);
if (layer_index == -1) {
return nullptr;
}
CustomDataLayer &layer = data->layers[layer_index];
ensure_layer_data_is_mutable(layer, totelem);
ensure_layer_data_is_mutable(layer, totelem, unshare_policy);
return layer.data;
}
@ -3748,14 +3781,15 @@ const void *CustomData_get_layer_n(const CustomData *data, const eCustomDataType
void *CustomData_get_layer_n_for_write(CustomData *data,
const eCustomDataType type,
const int n,
const int totelem)
const int totelem,
const CustomDataUnsharePolicy &unshare_policy)
{
const int layer_index = CustomData_get_layer_index_n(data, type, n);
if (layer_index == -1) {
return nullptr;
}
CustomDataLayer &layer = data->layers[layer_index];
ensure_layer_data_is_mutable(layer, totelem);
ensure_layer_data_is_mutable(layer, totelem, unshare_policy);
return layer.data;
}
@ -3773,14 +3807,15 @@ const void *CustomData_get_layer_named(const CustomData *data,
void *CustomData_get_layer_named_for_write(CustomData *data,
const eCustomDataType type,
const StringRef name,
const int totelem)
const int totelem,
const CustomDataUnsharePolicy &unshare_policy)
{
const int layer_index = CustomData_get_named_layer_index(data, type, name);
if (layer_index == -1) {
return nullptr;
}
CustomDataLayer &layer = data->layers[layer_index];
ensure_layer_data_is_mutable(layer, totelem);
ensure_layer_data_is_mutable(layer, totelem, unshare_policy);
return layer.data;
}

View File

@ -372,7 +372,9 @@ class CurvesVertexGroupsAttributeProvider final : public DynamicAttributesProvid
return {varray_for_deform_verts(dverts, vertex_group_index), AttrDomain::Point};
}
GAttributeWriter try_get_for_write(void *owner, const AttributeIDRef &attribute_id) const final
GAttributeWriter try_get_for_write(void *owner,
const AttributeIDRef &attribute_id,
const ArrayUnsharePolicy & /*unshare_policy*/) const final
{
if (attribute_id.is_anonymous()) {
return {};

View File

@ -900,7 +900,9 @@ class MeshVertexGroupsAttributeProvider final : public DynamicAttributesProvider
return {varray_for_deform_verts(dverts, vertex_group_index), AttrDomain::Point};
}
GAttributeWriter try_get_for_write(void *owner, const AttributeIDRef &attribute_id) const final
GAttributeWriter try_get_for_write(void *owner,
const AttributeIDRef &attribute_id,
const ArrayUnsharePolicy & /*unshare_policy*/) const final
{
if (attribute_id.is_anonymous()) {
return {};

View File

@ -609,10 +609,15 @@ Span<float3> Mesh::vert_positions() const
CustomData_get_layer_named(&this->vert_data, CD_PROP_FLOAT3, "position")),
this->verts_num};
}
MutableSpan<float3> Mesh::vert_positions_for_write()
MutableSpan<float3> Mesh::vert_positions_for_write(
const blender::ArrayUnsharePolicy &unshare_policy)
{
return {static_cast<float3 *>(CustomData_get_layer_named_for_write(
&this->vert_data, CD_PROP_FLOAT3, "position", this->verts_num)),
&this->vert_data,
CD_PROP_FLOAT3,
"position",
this->verts_num,
CustomDataUnsharePolicy_ArrayUnsharePolicy(unshare_policy))),
this->verts_num};
}

View File

@ -209,10 +209,15 @@ Span<float3> PointCloud::positions() const
this->totpoint};
}
MutableSpan<float3> PointCloud::positions_for_write()
MutableSpan<float3> PointCloud::positions_for_write(
const blender::ArrayUnsharePolicy &unshare_policy)
{
return {static_cast<float3 *>(CustomData_get_layer_named_for_write(
&this->pdata, CD_PROP_FLOAT3, "position", this->totpoint)),
&this->pdata,
CD_PROP_FLOAT3,
"position",
this->totpoint,
CustomDataUnsharePolicy_ArrayUnsharePolicy(unshare_policy))),
this->totpoint};
}

View File

@ -0,0 +1,46 @@
/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include "BLI_generic_virtual_array.hh"
namespace blender::implicit_sharing::unshare {
class ArrayUnsharePolicy {
public:
void unshare_array(const GVArray &src, GMutableSpan dst) const
{
BLI_assert(src.type() == dst.type());
BLI_assert(src.size() == dst.size());
this->unshare_array_impl(src, dst);
}
private:
virtual void unshare_array_impl(const GVArray &src, GMutableSpan dst) const = 0;
};
class CopyAll : public ArrayUnsharePolicy {
virtual void unshare_array_impl(const GVArray &src, GMutableSpan dst) const override
{
src.materialize_to_uninitialized(dst.data());
}
};
class IgnoreOldValues : public ArrayUnsharePolicy {
void unshare_array_impl(const GVArray & /*src*/, GMutableSpan dst) const override
{
const CPPType &type = dst.type();
type.default_construct_n(dst.data(), dst.size());
}
};
using DefaultArrayUnsharePolicy = CopyAll;
} // namespace blender::implicit_sharing::unshare
namespace blender {
using implicit_sharing::unshare::ArrayUnsharePolicy;
using implicit_sharing::unshare::DefaultArrayUnsharePolicy;
} // namespace blender

View File

@ -251,6 +251,7 @@ set(SRC
BLI_implicit_sharing.h
BLI_implicit_sharing.hh
BLI_implicit_sharing_ptr.hh
BLI_implicit_sharing_unshare.hh
BLI_index_mask.hh
BLI_index_mask_fwd.hh
BLI_index_mask_expression.hh

View File

@ -18,6 +18,7 @@
# include <optional>
# include "BLI_implicit_sharing_unshare.hh"
# include "BLI_math_vector_types.hh"
namespace blender {
@ -236,7 +237,8 @@ typedef struct Mesh {
*/
blender::Span<blender::float3> vert_positions() const;
/** Write access to vertex data. */
blender::MutableSpan<blender::float3> vert_positions_for_write();
blender::MutableSpan<blender::float3> vert_positions_for_write(
const blender::ArrayUnsharePolicy &unshare_policy = blender::DefaultArrayUnsharePolicy());
/**
* Array of edges, containing vertex indices, stored in the ".edge_verts" attribute. For simple
* triangle or quad meshes, edges could be calculated from the face and #corner_edge arrays.

View File

@ -15,6 +15,7 @@
# include <optional>
# include "BLI_bounds_types.hh"
# include "BLI_implicit_sharing_unshare.hh"
# include "BLI_math_vector_types.hh"
# include "BLI_span.hh"
#endif
@ -54,7 +55,8 @@ typedef struct PointCloud {
#ifdef __cplusplus
blender::Span<blender::float3> positions() const;
blender::MutableSpan<blender::float3> positions_for_write();
blender::MutableSpan<blender::float3> positions_for_write(
const blender::ArrayUnsharePolicy &unshare_policy = blender::DefaultArrayUnsharePolicy());
blender::bke::AttributeAccessor attributes() const;
blender::bke::MutableAttributeAccessor attributes_for_write();