WIP: Core: use generic copy-on-write system to avoid redundant copies #104478

Closed
Jacques Lucke wants to merge 66 commits from JacquesLucke/blender:temp-copy-on-write into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
44 changed files with 827 additions and 752 deletions

View File

@ -4,9 +4,9 @@
#include <atomic>
#include "BLI_copy_on_write_user.hh"
#include "BLI_set.hh"
#include "BLI_string_ref.hh"
#include "BLI_user_counter.hh"
namespace blender::bke {
@ -32,10 +32,7 @@ namespace blender::bke {
* because that is not available in C code. If possible, the #AutoAnonymousAttributeID wrapper
* should be used to avoid manual reference counting in C++ code.
*/
class AnonymousAttributeID {
private:
mutable std::atomic<int> users_ = 1;
class AnonymousAttributeID : public bCopyOnWriteMixin {
protected:
std::string name_;
@ -49,22 +46,15 @@ class AnonymousAttributeID {
virtual std::string user_name() const;
void user_add() const
private:
void delete_self() override
{
users_.fetch_add(1);
}
void user_remove() const
{
const int new_users = users_.fetch_sub(1) - 1;
if (new_users == 0) {
MEM_delete(this);
}
MEM_delete(this);
}
};
/** Wrapper for #AnonymousAttributeID that avoids manual reference counting. */
using AutoAnonymousAttributeID = UserCounter<const AnonymousAttributeID>;
using AutoAnonymousAttributeID = COWUser<const AnonymousAttributeID>;
/**
* A set of anonymous attribute names that is passed around in geometry nodes.

View File

@ -812,7 +812,6 @@ class CustomDataAttributes {
std::optional<blender::GMutableSpan> get_for_write(const AttributeIDRef &attribute_id);
bool create(const AttributeIDRef &attribute_id, eCustomDataType data_type);
bool create_by_move(const AttributeIDRef &attribute_id, eCustomDataType data_type, void *buffer);
bool remove(const AttributeIDRef &attribute_id);
bool foreach_attribute(const AttributeForeachCallback callback, eAttrDomain domain) const;

View File

@ -74,14 +74,10 @@ extern const CustomData_MeshMasks CD_MASK_EVERYTHING;
/** Add/copy/merge allocation types. */
typedef enum eCDAllocType {
/** Use the data pointer. */
/** Use the data pointer. This is only used internally. */
CD_ASSIGN = 0,
/** Allocate and set to default, which is usually just zeroed memory. */
CD_SET_DEFAULT = 2,
/** Use data pointers, set layer flag NOFREE. */
CD_REFERENCE = 3,
/** Do a full copy of all layers, only allowed if source has same number of elements. */
CD_DUPLICATE = 4,
/**
* Default construct new layer values. Does nothing for trivial types. This should be used
* if all layer values will be set by the caller after creating the layer.
@ -126,11 +122,6 @@ bool CustomData_has_interp(const struct CustomData *data);
*/
bool CustomData_bmesh_has_free(const struct CustomData *data);
/**
* Checks if any of the custom-data layers is referenced.
*/
bool CustomData_has_referenced(const struct CustomData *data);
/**
* Copies the "value" (e.g. mloopuv uv or mloopcol colors) from one block to
* another, while not overwriting anything else (e.g. flags). probably only
@ -158,15 +149,23 @@ void CustomData_data_multiply(int type, void *data, float fac);
void CustomData_data_add(int type, void *data1, const void *data2);
/**
* Initializes a CustomData object with the same layer setup as source.
* mask is a bit-field where `(mask & (1 << (layer type)))` indicates
* if a layer should be copied or not. alloctype must be one of the above.
* Initializes a CustomData object with the same layer setup as source. `mask` is a bit-field where
* `(mask & (1 << (layer type)))` indicates if a layer should be copied or not. The data layers
* will be shared or copied depending on whether the layer uses COW.
*/
void CustomData_copy(const struct CustomData *source,
struct CustomData *dest,
eCustomDataMask mask,
eCDAllocType alloctype,
int totelem);
/**
* Initializes a CustomData object with the same layers as source. The data is not copied from the
* source. Instead, the new layers are initialized using the given `alloctype`.
*/
void CustomData_copy_new(const struct CustomData *source,
Review

What about CustomData_copy_construct to make the naming consistent with typical RAII patterns? It would tell the reader that the dest customdata struct doesn't have to be initialized a bit better.

What about `CustomData_copy_construct` to make the naming consistent with typical RAII patterns? It would tell the reader that the `dest` customdata struct doesn't have to be initialized a bit better.
struct CustomData *dest,
eCustomDataMask mask,
eCDAllocType alloctype,
int totelem);
/* BMESH_TODO, not really a public function but readfile.c needs it */
void CustomData_update_typemap(struct CustomData *data);
@ -178,8 +177,12 @@ void CustomData_update_typemap(struct CustomData *data);
bool CustomData_merge(const struct CustomData *source,
struct CustomData *dest,
eCustomDataMask mask,
eCDAllocType alloctype,
int totelem);
bool CustomData_merge_new(const struct CustomData *source,
struct CustomData *dest,
eCustomDataMask mask,
eCDAllocType alloctype,
int totelem);
/**
* Reallocate custom data to a new element count. If the new size is larger, the new values use
@ -193,12 +196,12 @@ void CustomData_realloc(struct CustomData *data, int old_size, int new_size);
* then goes through the mesh and makes sure all the custom-data blocks are
* consistent with the new layout.
*/
bool CustomData_bmesh_merge(const struct CustomData *source,
struct CustomData *dest,
eCustomDataMask mask,
eCDAllocType alloctype,
struct BMesh *bm,
char htype);
bool CustomData_bmesh_merge_new(const struct CustomData *source,
struct CustomData *dest,
eCustomDataMask mask,
eCDAllocType alloctype,
struct BMesh *bm,
char htype);
/**
* Remove layers that aren't stored in BMesh or are stored as flags on BMesh.
@ -229,18 +232,23 @@ void CustomData_free_typemask(struct CustomData *data, int totelem, eCustomDataM
void CustomData_free_temporary(struct CustomData *data, int totelem);
/**
* Adds a data layer of the given type to the #CustomData object, optionally
* backed by an external data array. the different allocation types are
* defined above. returns the data of the layer.
* Adds a layer of the given type to the #CustomData object. The new layer is initialized based on
* the given alloctype. \return The layer data.
*/
void *CustomData_add_layer(struct CustomData *data,
eCustomDataType type,
eCDAllocType alloctype,
int totelem);
/**
* Adds a layer of the given type to the #CustomData object. The new layer takes ownership of the
* passed in `layer_data`. If a #bCopyOnWrite is passed in, it's user count is increased.
*/
const void *CustomData_add_layer_with_data(struct CustomData *data,
eCustomDataType type,
void *layer_data,
int totelem);
int totelem,
const struct bCopyOnWrite *cow);
/**
* Same as above but accepts a name.
@ -250,17 +258,26 @@ void *CustomData_add_layer_named(struct CustomData *data,
eCDAllocType alloctype,
int totelem,
const char *name);
const void *CustomData_add_layer_named_with_data(struct CustomData *data,
eCustomDataType type,
void *layer_data,
int totelem,
const char *name);
const char *name,
const struct bCopyOnWrite *cow);
void *CustomData_add_layer_anonymous(struct CustomData *data,
int type,
eCustomDataType type,
eCDAllocType alloctype,
void *layer,
int totelem,
const AnonymousAttributeIDHandle *anonymous_id);
const void *CustomData_add_layer_anonymous_with_data(
struct CustomData *data,
eCustomDataType type,
const AnonymousAttributeIDHandle *anonymous_id,
int totelem,
void *layer_data,
const struct bCopyOnWrite *cow);
/**
* Frees the active or first data layer with the give type.
@ -296,11 +313,6 @@ int CustomData_number_of_layers(const struct CustomData *data, int type);
int CustomData_number_of_anonymous_layers(const struct CustomData *data, int type);
int CustomData_number_of_layers_typemask(const struct CustomData *data, eCustomDataMask mask);
/**
* Duplicate all the layers with flag NOFREE, and remove the flag from duplicated layers.
*/
void CustomData_duplicate_referenced_layers(CustomData *data, int totelem);
/**
* Set the #CD_FLAG_NOCOPY flag in custom data layers where the mask is
* zero for the layer type, so only layer types specified by the mask will be copied

View File

@ -12,8 +12,6 @@
#include "BLI_function_ref.hh"
#include "BLI_map.hh"
#include "BLI_math_vector_types.hh"
#include "BLI_user_counter.hh"
#include "BLI_vector_set.hh"
#include "BKE_attribute.hh"
@ -40,18 +38,13 @@ class CurvesEditHints;
class Instances;
} // namespace blender::bke
class GeometryComponent;
/**
* This is the base class for specialized geometry component types. A geometry component handles
* a user count to allow avoiding duplication when it is wrapped with #UserCounter. It also handles
* the attribute API, which generalizes storing and modifying generic information on a geometry.
* This is the base class for specialized geometry component types. A geometry component uses
* copy-on-write behavior to avoid read-only copies. It also integrates with attribute API, which
* generalizes storing and modifying generic information on a geometry.
*/
class GeometryComponent {
class GeometryComponent : public bCopyOnWriteMixin {
private:
/* The reference count has two purposes. When it becomes zero, the component is freed. When it is
* larger than one, the component becomes immutable. */
mutable std::atomic<int> users_ = 1;
GeometryComponentType type_;
public:
@ -77,13 +70,12 @@ class GeometryComponent {
virtual bool owns_direct_data() const = 0;
virtual void ensure_owns_direct_data() = 0;
void user_add() const;
void user_remove() const;
bool is_mutable() const;
GeometryComponentType type() const;
virtual bool is_empty() const;
private:
void delete_self() override;
};
template<typename T>
@ -109,7 +101,7 @@ inline constexpr bool is_geometry_component_v = std::is_base_of_v<GeometryCompon
*/
struct GeometrySet {
private:
using GeometryComponentPtr = blender::UserCounter<class GeometryComponent>;
using GeometryComponentPtr = blender::COWUser<class GeometryComponent>;
/* Indexed by #GeometryComponentType. */
std::array<GeometryComponentPtr, GEO_COMPONENT_TYPE_ENUM_SIZE> components_;

View File

@ -239,11 +239,11 @@ void DM_from_template(DerivedMesh *dm,
int numPolys)
{
const CustomData_MeshMasks *mask = &CD_MASK_DERIVEDMESH;
CustomData_copy(&source->vertData, &dm->vertData, mask->vmask, CD_SET_DEFAULT, numVerts);
CustomData_copy(&source->edgeData, &dm->edgeData, mask->emask, CD_SET_DEFAULT, numEdges);
CustomData_copy(&source->faceData, &dm->faceData, mask->fmask, CD_SET_DEFAULT, numTessFaces);
CustomData_copy(&source->loopData, &dm->loopData, mask->lmask, CD_SET_DEFAULT, numLoops);
CustomData_copy(&source->polyData, &dm->polyData, mask->pmask, CD_SET_DEFAULT, numPolys);
CustomData_copy_new(&source->vertData, &dm->vertData, mask->vmask, CD_SET_DEFAULT, numVerts);
CustomData_copy_new(&source->edgeData, &dm->edgeData, mask->emask, CD_SET_DEFAULT, numEdges);
CustomData_copy_new(&source->faceData, &dm->faceData, mask->fmask, CD_SET_DEFAULT, numTessFaces);
CustomData_copy_new(&source->loopData, &dm->loopData, mask->lmask, CD_SET_DEFAULT, numLoops);
CustomData_copy_new(&source->polyData, &dm->polyData, mask->pmask, CD_SET_DEFAULT, numPolys);
dm->type = type;
dm->numVertData = numVerts;

View File

@ -198,13 +198,16 @@ static bool add_builtin_type_custom_data_layer_from_init(CustomData &custom_data
return true;
}
case AttributeInit::Type::MoveArray: {
void *source_data = static_cast<const AttributeInitMoveArray &>(initializer).data;
const void *data = CustomData_add_layer_with_data(
&custom_data, data_type, source_data, domain_num);
if (data == nullptr) {
MEM_freeN(source_data);
void *src_data = static_cast<const AttributeInitMoveArray &>(initializer).data;
const void *stored_data = CustomData_add_layer_with_data(
&custom_data, data_type, src_data, domain_num, nullptr);
if (stored_data == nullptr) {
return false;
}
if (stored_data != src_data) {
MEM_freeN(src_data);
return true;
}
return true;
}
}
@ -227,25 +230,26 @@ static void *add_generic_custom_data_layer(CustomData &custom_data,
}
const AnonymousAttributeID &anonymous_id = attribute_id.anonymous_id();
return CustomData_add_layer_anonymous(
&custom_data, data_type, alloctype, nullptr, domain_size, &anonymous_id);
&custom_data, data_type, alloctype, domain_size, &anonymous_id);
}
static const void *add_generic_custom_data_layer_with_existing_data(
CustomData &custom_data,
const eCustomDataType data_type,
void *layer_data,
const AttributeIDRef &attribute_id,
const int domain_size,
const AttributeIDRef &attribute_id)
void *layer_data,
const bCopyOnWrite *cow)
{
if (!attribute_id.is_anonymous()) {
char attribute_name_c[MAX_CUSTOMDATA_LAYER_NAME];
attribute_id.name().copy(attribute_name_c);
return CustomData_add_layer_named_with_data(
&custom_data, data_type, layer_data, domain_size, attribute_name_c);
if (attribute_id.is_anonymous()) {
const AnonymousAttributeID &anonymous_id = attribute_id.anonymous_id();
return CustomData_add_layer_anonymous_with_data(
&custom_data, data_type, &anonymous_id, domain_size, layer_data, cow);
}
const AnonymousAttributeID &anonymous_id = attribute_id.anonymous_id();
return CustomData_add_layer_anonymous(
&custom_data, data_type, CD_ASSIGN, layer_data, domain_size, &anonymous_id);
char attribute_name_c[MAX_CUSTOMDATA_LAYER_NAME];
attribute_id.name().copy(attribute_name_c);
return CustomData_add_layer_named_with_data(
&custom_data, data_type, layer_data, domain_size, attribute_name_c, cow);
}
static bool add_custom_data_layer_from_attribute_init(const AttributeIDRef &attribute_id,
@ -276,9 +280,9 @@ static bool add_custom_data_layer_from_attribute_init(const AttributeIDRef &attr
break;
}
case AttributeInit::Type::MoveArray: {
void *source_data = static_cast<const AttributeInitMoveArray &>(initializer).data;
void *data = static_cast<const AttributeInitMoveArray &>(initializer).data;
add_generic_custom_data_layer_with_existing_data(
custom_data, data_type, source_data, domain_num, attribute_id);
custom_data, data_type, attribute_id, domain_num, data, nullptr);
break;
}
}
@ -567,7 +571,7 @@ CustomDataAttributes::~CustomDataAttributes()
CustomDataAttributes::CustomDataAttributes(const CustomDataAttributes &other)
{
size_ = other.size_;
CustomData_copy(&other.data, &data, CD_MASK_ALL, CD_DUPLICATE, size_);
CustomData_copy(&other.data, &data, CD_MASK_ALL, size_);
}
CustomDataAttributes::CustomDataAttributes(CustomDataAttributes &&other)
@ -651,15 +655,6 @@ bool CustomDataAttributes::create(const AttributeIDRef &attribute_id,
return result != nullptr;
}
bool CustomDataAttributes::create_by_move(const AttributeIDRef &attribute_id,
const eCustomDataType data_type,
void *buffer)
{
const void *result = add_generic_custom_data_layer_with_existing_data(
data, data_type, buffer, size_, attribute_id);
return result != nullptr;
}
bool CustomDataAttributes::remove(const AttributeIDRef &attribute_id)
{
for (const int i : IndexRange(data.totlayer)) {

View File

@ -182,9 +182,7 @@ static CDDerivedMesh *cdDM_create(const char *desc)
return cddm;
}
static DerivedMesh *cdDM_from_mesh_ex(Mesh *mesh,
eCDAllocType alloctype,
const CustomData_MeshMasks *mask)
static DerivedMesh *cdDM_from_mesh_ex(Mesh *mesh, const CustomData_MeshMasks *mask)
{
CDDerivedMesh *cddm = cdDM_create(__func__);
DerivedMesh *dm = &cddm->dm;
@ -207,15 +205,14 @@ static DerivedMesh *cdDM_from_mesh_ex(Mesh *mesh,
* (which isn't generally the case). */
dm->deformedOnly = 1;
CustomData_merge(&mesh->vdata, &dm->vertData, cddata_masks.vmask, alloctype, mesh->totvert);
CustomData_merge(&mesh->edata, &dm->edgeData, cddata_masks.emask, alloctype, mesh->totedge);
CustomData_merge(&mesh->vdata, &dm->vertData, cddata_masks.vmask, mesh->totvert);
CustomData_merge(&mesh->edata, &dm->edgeData, cddata_masks.emask, mesh->totedge);
CustomData_merge(&mesh->fdata,
&dm->faceData,
cddata_masks.fmask | CD_MASK_ORIGINDEX,
alloctype,
0 /* `mesh->totface` */);
CustomData_merge(&mesh->ldata, &dm->loopData, cddata_masks.lmask, alloctype, mesh->totloop);
CustomData_merge(&mesh->pdata, &dm->polyData, cddata_masks.pmask, alloctype, mesh->totpoly);
CustomData_merge(&mesh->ldata, &dm->loopData, cddata_masks.lmask, mesh->totloop);
CustomData_merge(&mesh->pdata, &dm->polyData, cddata_masks.pmask, mesh->totpoly);
cddm->vert_positions = static_cast<float(*)[3]>(CustomData_get_layer_named_for_write(
&dm->vertData, CD_PROP_FLOAT3, "position", mesh->totvert));
@ -245,5 +242,5 @@ static DerivedMesh *cdDM_from_mesh_ex(Mesh *mesh,
DerivedMesh *CDDM_from_mesh(Mesh *mesh)
{
return cdDM_from_mesh_ex(mesh, CD_REFERENCE, &CD_MASK_MESH);
return cdDM_from_mesh_ex(mesh, &CD_MASK_MESH);
}

View File

@ -62,7 +62,7 @@ static void curves_init_data(ID *id)
new (&curves->geometry) blender::bke::CurvesGeometry();
}
static void curves_copy_data(Main * /*bmain*/, ID *id_dst, const ID *id_src, const int flag)
static void curves_copy_data(Main * /*bmain*/, ID *id_dst, const ID *id_src, const int /*flag*/)
{
using namespace blender;
@ -80,9 +80,8 @@ static void curves_copy_data(Main * /*bmain*/, ID *id_dst, const ID *id_src, con
dst.point_num = src.point_num;
dst.curve_num = src.curve_num;
const eCDAllocType alloc_type = (flag & LIB_ID_COPY_CD_REFERENCE) ? CD_REFERENCE : CD_DUPLICATE;
CustomData_copy(&src.point_data, &dst.point_data, CD_MASK_ALL, alloc_type, dst.point_num);
CustomData_copy(&src.curve_data, &dst.curve_data, CD_MASK_ALL, alloc_type, dst.curve_num);
CustomData_copy(&src.point_data, &dst.point_data, CD_MASK_ALL, dst.point_num);
CustomData_copy(&src.curve_data, &dst.curve_data, CD_MASK_ALL, dst.curve_num);
dst.curve_offsets = static_cast<int *>(MEM_dupallocN(src.curve_offsets));

View File

@ -82,8 +82,8 @@ static void copy_curves_geometry(CurvesGeometry &dst, const CurvesGeometry &src)
CustomData_free(&dst.curve_data, dst.curve_num);
dst.point_num = src.point_num;
dst.curve_num = src.curve_num;
CustomData_copy(&src.point_data, &dst.point_data, CD_MASK_ALL, CD_DUPLICATE, dst.point_num);
CustomData_copy(&src.curve_data, &dst.curve_data, CD_MASK_ALL, CD_DUPLICATE, dst.curve_num);
CustomData_copy(&src.point_data, &dst.point_data, CD_MASK_ALL, dst.point_num);
CustomData_copy(&src.curve_data, &dst.curve_data, CD_MASK_ALL, dst.curve_num);
MEM_SAFE_FREE(dst.curve_offsets);
dst.curve_offsets = (int *)MEM_malloc_arrayN(dst.point_num + 1, sizeof(int), __func__);

View File

@ -101,11 +101,8 @@ void fill_points(const OffsetIndices<int> points_by_curve,
bke::CurvesGeometry copy_only_curve_domain(const bke::CurvesGeometry &src_curves)
{
bke::CurvesGeometry dst_curves(0, src_curves.curves_num());
CustomData_copy(&src_curves.curve_data,
&dst_curves.curve_data,
CD_MASK_ALL,
CD_DUPLICATE,
src_curves.curves_num());
CustomData_copy(
&src_curves.curve_data, &dst_curves.curve_data, CD_MASK_ALL, src_curves.curves_num());
dst_curves.runtime->type_counts = src_curves.runtime->type_counts;
return dst_curves;
}

View File

@ -2160,9 +2160,10 @@ void customData_mask_layers__print(const CustomData_MeshMasks *mask)
static void customData_update_offsets(CustomData *data);
static CustomDataLayer *customData_add_layer__internal(CustomData *data,
int type,
eCustomDataType type,
eCDAllocType alloctype,
void *layerdata,
void *layer_data_to_assign,
const bCopyOnWrite *cow_to_assign,
int totelem,
const char *name);
@ -2193,93 +2194,108 @@ static bool customdata_typemap_is_valid(const CustomData *data)
}
#endif
bool CustomData_merge(const CustomData *source,
CustomData *dest,
eCustomDataMask mask,
eCDAllocType alloctype,
int totelem)
static void *copy_layer_data(const eCustomDataType type, const void *data, const int totelem)
{
const LayerTypeInfo &type_info = *layerType_getInfo(type);
if (type_info.copy) {
void *new_data = MEM_malloc_arrayN(size_t(totelem), type_info.size, __func__);
type_info.copy(data, new_data, totelem);
return new_data;
}
return MEM_dupallocN(data);
}
static void free_layer_data(const eCustomDataType type, const void *data, const int totelem)
{
const LayerTypeInfo &type_info = *layerType_getInfo(type);
if (type_info.free) {
type_info.free(const_cast<void *>(data), totelem, type_info.size);
}
MEM_freeN(const_cast<void *>(data));
}
static bool customdata_merge_internal(const CustomData *source,
CustomData *dest,
const eCustomDataMask mask,
const eCDAllocType alloctype,
const int totelem)
{
// const LayerTypeInfo *typeInfo;
CustomDataLayer *layer, *newlayer;
int lasttype = -1, lastactive = 0, lastrender = 0, lastclone = 0, lastmask = 0;
int number = 0, maxnumber = -1;
bool changed = false;
int last_type = -1;
int last_active = 0;
int last_render = 0;
int last_clone = 0;
int last_mask = 0;
int current_type_layer_count = 0;
int max_current_type_layer_count = -1;
for (int i = 0; i < source->totlayer; i++) {
layer = &source->layers[i];
// typeInfo = layerType_getInfo(layer->type); /* UNUSED */
const CustomDataLayer &src_layer = source->layers[i];
const eCustomDataType type = eCustomDataType(src_layer.type);
const int src_layer_flag = src_layer.flag;
int type = layer->type;
int flag = layer->flag;
if (type != lasttype) {
number = 0;
maxnumber = CustomData_layertype_layers_max(type);
lastactive = layer->active;
lastrender = layer->active_rnd;
lastclone = layer->active_clone;
lastmask = layer->active_mask;
lasttype = type;
if (type != last_type) {
current_type_layer_count = 0;
max_current_type_layer_count = CustomData_layertype_layers_max(type);
last_active = src_layer.active;
last_render = src_layer.active_rnd;
last_clone = src_layer.active_clone;
last_mask = src_layer.active_mask;
last_type = type;
}
else {
number++;
current_type_layer_count++;
}
if (flag & CD_FLAG_NOCOPY) {
if (src_layer_flag & CD_FLAG_NOCOPY) {
/* Don't merge this layer because it's not supposed to leave the source data. */
continue;
}
if (!(mask & CD_TYPE_AS_MASK(type))) {
/* Don't merge this layer because it does not match the type mask. */
continue;
}
if ((maxnumber != -1) && (number >= maxnumber)) {
if ((max_current_type_layer_count != -1) &&
(current_type_layer_count >= max_current_type_layer_count)) {
/* Don't merge this layer because the maximum amount of layers of this type is reached. */
Review

Wow, great comments here :D

Wow, great comments here :D
continue;
}
if (CustomData_get_named_layer_index(dest, type, layer->name) != -1) {
if (CustomData_get_named_layer_index(dest, type, src_layer.name) != -1) {
/* Don't merge this layer because it exists in the destination already. */
continue;
}
void *data;
switch (alloctype) {
case CD_ASSIGN:
case CD_REFERENCE:
case CD_DUPLICATE:
data = layer->data;
break;
default:
data = nullptr;
break;
}
if ((alloctype == CD_ASSIGN) && (flag & CD_FLAG_NOFREE)) {
newlayer = customData_add_layer__internal(
dest, type, CD_REFERENCE, data, totelem, layer->name);
}
else {
newlayer = customData_add_layer__internal(dest, type, alloctype, data, totelem, layer->name);
}
if (newlayer) {
newlayer->uid = layer->uid;
newlayer->active = lastactive;
newlayer->active_rnd = lastrender;
newlayer->active_clone = lastclone;
newlayer->active_mask = lastmask;
newlayer->flag |= flag & (CD_FLAG_EXTERNAL | CD_FLAG_IN_MEMORY);
changed = true;
if (layer->anonymous_id != nullptr) {
newlayer->anonymous_id = layer->anonymous_id;
if (alloctype == CD_ASSIGN) {
layer->anonymous_id = nullptr;
void *layer_data_to_assign = nullptr;
const bCopyOnWrite *cow_to_assign = nullptr;
if (alloctype == CD_ASSIGN) {
if (src_layer.data != nullptr) {
if (src_layer.cow == nullptr) {
/* Can't share the layer, duplicate it instead. */
layer_data_to_assign = copy_layer_data(type, src_layer.data, totelem);
}
else {
layer->anonymous_id->user_add();
/* Share the layer. */
layer_data_to_assign = src_layer.data;
cow_to_assign = src_layer.cow;
}
}
if (alloctype == CD_ASSIGN) {
layer->data = nullptr;
}
}
CustomDataLayer *new_layer = customData_add_layer__internal(
dest, type, alloctype, layer_data_to_assign, cow_to_assign, totelem, src_layer.name);
new_layer->uid = src_layer.uid;
new_layer->flag |= src_layer_flag & (CD_FLAG_EXTERNAL | CD_FLAG_IN_MEMORY);
new_layer->active = last_active;
new_layer->active_rnd = last_render;
new_layer->active_clone = last_clone;
new_layer->active_mask = last_mask;
changed = true;
if (src_layer.anonymous_id != nullptr) {
new_layer->anonymous_id = src_layer.anonymous_id;
new_layer->anonymous_id->add_user();
}
}
@ -2287,6 +2303,23 @@ bool CustomData_merge(const CustomData *source,
return changed;
}
bool CustomData_merge(const CustomData *source,
CustomData *dest,
eCustomDataMask mask,
int totelem)
{
return customdata_merge_internal(source, dest, mask, CD_ASSIGN, totelem);
}
bool CustomData_merge_new(const CustomData *source,
CustomData *dest,
const eCustomDataMask mask,
const eCDAllocType alloctype,
const int totelem)
{
return customdata_merge_internal(source, dest, mask, alloctype, totelem);
}
CustomData CustomData_shallow_copy_remove_non_bmesh_attributes(const CustomData *src,
const eCustomDataMask mask)
{
@ -2312,28 +2345,85 @@ CustomData CustomData_shallow_copy_remove_non_bmesh_attributes(const CustomData
return dst;
}
/**
* A #bCopyOnWrite that knows how to free the entire referenced custom data layer (including
* potentially separately allocated chunks like for vertex groups).
*/
class CustomDataLayerCOW : public bCopyOnWrite {
Review

I think it wasn't obvious why this was necessary when you explained it to me at first. A sentence explaining that like "because layer data might be passed outside of the custom data system" would be helpful.

I think it wasn't obvious why this was necessary when you explained it to me at first. A sentence explaining that like "because layer data might be passed outside of the custom data system" would be helpful.
private:
const void *data_;
const int totelem_;
const eCustomDataType type_;
public:
CustomDataLayerCOW(const void *data, const int totelem, const eCustomDataType type)
: bCopyOnWrite(1), data_(data), totelem_(totelem), type_(type)
{
}
private:
void delete_self_with_data() override
{
free_layer_data(type_, data_, totelem_);
MEM_delete(this);
}
};
/** Create a #bCopyOnWrite that takes ownership of the data. */
static bCopyOnWrite *make_cow_for_array(const eCustomDataType type,
const void *data,
const int totelem)
{
return MEM_new<CustomDataLayerCOW>(__func__, data, totelem, type);
}
/**
* 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)
{
if (layer.data == nullptr) {
return;
}
if (layer.cow == nullptr) {
/* Can not be shared without cow data. */
return;
}
if (layer.cow->is_shared()) {
const eCustomDataType type = eCustomDataType(layer.type);
const void *old_data = layer.data;
layer.data = copy_layer_data(type, old_data, totelem);
layer.cow->remove_user_and_delete_if_last();
layer.cow = make_cow_for_array(type, layer.data, totelem);
}
}
void CustomData_realloc(CustomData *data, const int old_size, const int new_size)
{
BLI_assert(new_size >= 0);
for (int i = 0; i < data->totlayer; i++) {
CustomDataLayer *layer = &data->layers[i];
const LayerTypeInfo *typeInfo = layerType_getInfo(layer->type);
const int64_t old_size_in_bytes = int64_t(old_size) * typeInfo->size;
const int64_t new_size_in_bytes = int64_t(new_size) * typeInfo->size;
if (layer->flag & CD_FLAG_NOFREE) {
const void *old_data = layer->data;
layer->data = MEM_malloc_arrayN(new_size, typeInfo->size, __func__);
if (typeInfo->copy) {
typeInfo->copy(old_data, layer->data, std::min(old_size, new_size));
}
else {
std::memcpy(layer->data, old_data, std::min(old_size_in_bytes, new_size_in_bytes));
}
layer->flag &= ~CD_FLAG_NOFREE;
void *new_layer_data = MEM_mallocN(new_size_in_bytes, __func__);
/* Copy or relocate data to new array. */
if (typeInfo->copy) {
typeInfo->copy(layer->data, new_layer_data, std::min(old_size, new_size));
}
else {
layer->data = MEM_reallocN(layer->data, new_size_in_bytes);
memcpy(new_layer_data, layer->data, std::min(old_size_in_bytes, new_size_in_bytes));
}
/* Remove ownership of old array */
if (layer->cow) {
layer->cow->remove_user_and_delete_if_last();
layer->cow = nullptr;
}
/* Take ownership of new array. */
layer->data = new_layer_data;
if (layer->data) {
layer->cow = make_cow_for_array(eCustomDataType(layer->type), layer->data, new_size);
}
if (new_size > old_size) {
@ -2346,11 +2436,7 @@ void CustomData_realloc(CustomData *data, const int old_size, const int new_size
}
}
void CustomData_copy(const CustomData *source,
CustomData *dest,
eCustomDataMask mask,
eCDAllocType alloctype,
int totelem)
void CustomData_copy(const CustomData *source, CustomData *dest, eCustomDataMask mask, int totelem)
{
CustomData_reset(dest);
@ -2358,28 +2444,39 @@ void CustomData_copy(const CustomData *source,
dest->external = static_cast<CustomDataExternal *>(MEM_dupallocN(source->external));
}
CustomData_merge(source, dest, mask, alloctype, totelem);
CustomData_merge(source, dest, mask, totelem);
}
void CustomData_copy_new(const struct CustomData *source,
struct CustomData *dest,
eCustomDataMask mask,
eCDAllocType alloctype,
int totelem)
{
CustomData_reset(dest);
if (source->external) {
dest->external = static_cast<CustomDataExternal *>(MEM_dupallocN(source->external));
}
CustomData_merge_new(source, dest, mask, alloctype, totelem);
}
static void customData_free_layer__internal(CustomDataLayer *layer, const int totelem)
{
const LayerTypeInfo *typeInfo;
if (layer->anonymous_id != nullptr) {
layer->anonymous_id->user_remove();
layer->anonymous_id->remove_user_and_delete_if_last();
layer->anonymous_id = nullptr;
}
if (!(layer->flag & CD_FLAG_NOFREE) && layer->data) {
typeInfo = layerType_getInfo(layer->type);
if (typeInfo->free) {
typeInfo->free(layer->data, totelem, typeInfo->size);
}
const eCustomDataType type = eCustomDataType(layer->type);
if (layer->cow == nullptr) {
if (layer->data) {
MEM_freeN(layer->data);
free_layer_data(type, layer->data, totelem);
}
}
else {
layer->cow->remove_user_and_delete_if_last();
}
}
static void CustomData_external_free(CustomData *data)
@ -2686,107 +2783,40 @@ void CustomData_clear_layer_flag(CustomData *data, const int type, const int fla
}
}
static bool customData_resize(CustomData *data, const int amount)
static void customData_resize(CustomData *data, const int grow_amount)
{
CustomDataLayer *tmp = static_cast<CustomDataLayer *>(
MEM_calloc_arrayN((data->maxlayer + amount), sizeof(*tmp), __func__));
if (!tmp) {
return false;
}
data->maxlayer += amount;
if (data->layers) {
memcpy(tmp, data->layers, sizeof(*tmp) * data->totlayer);
MEM_freeN(data->layers);
}
data->layers = tmp;
return true;
data->layers = static_cast<CustomDataLayer *>(
MEM_reallocN(data->layers, (data->maxlayer + grow_amount) * sizeof(CustomDataLayer)));
data->maxlayer += grow_amount;
}
static CustomDataLayer *customData_add_layer__internal(CustomData *data,
const int type,
const eCustomDataType type,
const eCDAllocType alloctype,
void *layerdata,
void *layer_data_to_assign,
const bCopyOnWrite *cow_to_assign,
const int totelem,
const char *name)
{
const LayerTypeInfo *typeInfo = layerType_getInfo(type);
const LayerTypeInfo &type_info = *layerType_getInfo(type);
int flag = 0;
/* Some layer types only support a single layer. */
if (!typeInfo->defaultname && CustomData_has_layer(data, type)) {
if (!type_info.defaultname && CustomData_has_layer(data, type)) {
/* This function doesn't support dealing with existing layer data for these layer types when
* the layer already exists. */
BLI_assert(layerdata == nullptr);
BLI_assert(layer_data_to_assign == nullptr);
return &data->layers[CustomData_get_layer_index(data, type)];
}
void *newlayerdata = nullptr;
switch (alloctype) {
case CD_SET_DEFAULT:
if (totelem > 0) {
if (typeInfo->set_default_value) {
newlayerdata = MEM_malloc_arrayN(totelem, typeInfo->size, layerType_getName(type));
typeInfo->set_default_value(newlayerdata, totelem);
}
else {
newlayerdata = MEM_calloc_arrayN(totelem, typeInfo->size, layerType_getName(type));
}
}
break;
case CD_CONSTRUCT:
if (totelem > 0) {
newlayerdata = MEM_malloc_arrayN(totelem, typeInfo->size, layerType_getName(type));
if (typeInfo->construct) {
typeInfo->construct(newlayerdata, totelem);
}
}
break;
case CD_ASSIGN:
if (totelem > 0) {
BLI_assert(layerdata != nullptr);
newlayerdata = layerdata;
}
else {
MEM_SAFE_FREE(layerdata);
}
break;
case CD_REFERENCE:
if (totelem > 0) {
BLI_assert(layerdata != nullptr);
newlayerdata = layerdata;
flag |= CD_FLAG_NOFREE;
}
break;
case CD_DUPLICATE:
if (totelem > 0) {
newlayerdata = MEM_malloc_arrayN(totelem, typeInfo->size, layerType_getName(type));
if (typeInfo->copy) {
typeInfo->copy(layerdata, newlayerdata, totelem);
}
else {
BLI_assert(layerdata != nullptr);
BLI_assert(newlayerdata != nullptr);
memcpy(newlayerdata, layerdata, totelem * typeInfo->size);
}
}
break;
}
int index = data->totlayer;
if (index >= data->maxlayer) {
if (!customData_resize(data, CUSTOMDATA_GROW)) {
if (newlayerdata != layerdata) {
MEM_freeN(newlayerdata);
}
return nullptr;
}
customData_resize(data, CUSTOMDATA_GROW);
}
data->totlayer++;
/* keep layers ordered by type */
/* Keep layers ordered by type. */
for (; index > 0 && data->layers[index - 1].type > type; index--) {
data->layers[index] = data->layers[index - 1];
}
@ -2798,15 +2828,56 @@ static CustomDataLayer *customData_add_layer__internal(CustomData *data,
* leaks into the new layer. */
memset(&new_layer, 0, sizeof(CustomDataLayer));
switch (alloctype) {
case CD_SET_DEFAULT: {
if (totelem > 0) {
if (type_info.set_default_value) {
new_layer.data = MEM_malloc_arrayN(totelem, type_info.size, layerType_getName(type));
type_info.set_default_value(new_layer.data, totelem);
}
else {
new_layer.data = MEM_calloc_arrayN(totelem, type_info.size, layerType_getName(type));
}
}
break;
}
case CD_CONSTRUCT: {
if (totelem > 0) {
new_layer.data = MEM_malloc_arrayN(totelem, type_info.size, layerType_getName(type));
if (type_info.construct) {
type_info.construct(new_layer.data, totelem);
}
}
break;
}
case CD_ASSIGN: {
if (totelem == 0 && cow_to_assign == nullptr) {
MEM_SAFE_FREE(layer_data_to_assign);
}
else {
new_layer.data = layer_data_to_assign;
new_layer.cow = cow_to_assign;
if (new_layer.cow) {
new_layer.cow->add_user();
}
}
break;
}
}
if (new_layer.data != nullptr && new_layer.cow == nullptr) {
/* Make layer data shareable. */
new_layer.cow = make_cow_for_array(type, new_layer.data, totelem);
}
new_layer.type = type;
new_layer.flag = flag;
new_layer.data = newlayerdata;
/* Set default name if none exists. Note we only call DATA_() once
* we know there is a default name, to avoid overhead of locale lookups
* in the depsgraph. */
if (!name && typeInfo->defaultname) {
name = DATA_(typeInfo->defaultname);
if (!name && type_info.defaultname) {
name = DATA_(type_info.defaultname);
}
if (name) {
@ -2835,13 +2906,15 @@ static CustomDataLayer *customData_add_layer__internal(CustomData *data,
return &data->layers[index];
}
static void *customdata_add_layer(
CustomData *data, const int type, eCDAllocType alloctype, void *layerdata, const int totelem)
void *CustomData_add_layer(CustomData *data,
const eCustomDataType type,
eCDAllocType alloctype,
const int totelem)
{
const LayerTypeInfo *typeInfo = layerType_getInfo(type);
CustomDataLayer *layer = customData_add_layer__internal(
data, type, alloctype, layerdata, totelem, typeInfo->defaultname);
data, type, alloctype, nullptr, nullptr, totelem, typeInfo->defaultname);
CustomData_update_typemap(data);
if (layer) {
@ -2851,31 +2924,16 @@ static void *customdata_add_layer(
return nullptr;
}
void *CustomData_add_layer(CustomData *data,
const eCustomDataType type,
const eCDAllocType alloctype,
const int totelem)
{
return customdata_add_layer(data, type, alloctype, nullptr, totelem);
}
const void *CustomData_add_layer_with_data(CustomData *data,
const eCustomDataType type,
void *layer_data,
const int totelem)
const int totelem,
const bCopyOnWrite *cow)
{
return customdata_add_layer(data, type, CD_ASSIGN, layer_data, totelem);
}
const LayerTypeInfo *typeInfo = layerType_getInfo(type);
static void *customdata_add_layer_named(CustomData *data,
const eCustomDataType type,
const eCDAllocType alloctype,
void *layerdata,
const int totelem,
const char *name)
{
CustomDataLayer *layer = customData_add_layer__internal(
data, type, alloctype, layerdata, totelem, name);
data, type, CD_ASSIGN, layer_data, cow, totelem, typeInfo->defaultname);
CustomData_update_typemap(data);
if (layer) {
@ -2891,32 +2949,70 @@ void *CustomData_add_layer_named(CustomData *data,
const int totelem,
const char *name)
{
return customdata_add_layer_named(data, type, alloctype, nullptr, totelem, name);
CustomDataLayer *layer = customData_add_layer__internal(
data, type, alloctype, nullptr, nullptr, totelem, name);
CustomData_update_typemap(data);
if (layer) {
return layer->data;
}
return nullptr;
}
const void *CustomData_add_layer_named_with_data(
CustomData *data, const eCustomDataType type, void *layer_data, int totelem, const char *name)
const void *CustomData_add_layer_named_with_data(CustomData *data,
eCustomDataType type,
void *layer_data,
int totelem,
const char *name,
const bCopyOnWrite *cow)
{
return customdata_add_layer_named(data, type, CD_ASSIGN, layer_data, totelem, name);
CustomDataLayer *layer = customData_add_layer__internal(
data, type, CD_ASSIGN, layer_data, cow, totelem, name);
CustomData_update_typemap(data);
if (layer) {
return layer->data;
}
return nullptr;
}
void *CustomData_add_layer_anonymous(CustomData *data,
const int type,
const eCustomDataType type,
const eCDAllocType alloctype,
void *layerdata,
const int totelem,
const AnonymousAttributeIDHandle *anonymous_id)
{
const char *name = anonymous_id->name().c_str();
CustomDataLayer *layer = customData_add_layer__internal(
data, type, alloctype, layerdata, totelem, name);
data, type, alloctype, nullptr, nullptr, totelem, name);
CustomData_update_typemap(data);
if (layer == nullptr) {
return nullptr;
}
anonymous_id->user_add();
anonymous_id->add_user();
layer->anonymous_id = anonymous_id;
return layer->data;
}
const void *CustomData_add_layer_anonymous_with_data(
CustomData *data,
const eCustomDataType type,
const AnonymousAttributeIDHandle *anonymous_id,
const int totelem,
void *layer_data,
const bCopyOnWrite *cow)
{
const char *name = anonymous_id->name().c_str();
CustomDataLayer *layer = customData_add_layer__internal(
data, type, CD_ASSIGN, layer_data, cow, totelem, name);
CustomData_update_typemap(data);
if (layer == nullptr) {
return nullptr;
}
anonymous_id->add_user();
layer->anonymous_id = anonymous_id;
return layer->data;
}
@ -3046,47 +3142,6 @@ int CustomData_number_of_layers_typemask(const CustomData *data, const eCustomDa
return number;
}
static void *customData_duplicate_referenced_layer_index(CustomData *data,
const int layer_index,
const int totelem)
{
if (layer_index == -1) {
return nullptr;
}
CustomDataLayer *layer = &data->layers[layer_index];
if (layer->flag & CD_FLAG_NOFREE) {
/* MEM_dupallocN won't work in case of complex layers, like e.g.
* CD_MDEFORMVERT, which has pointers to allocated data...
* So in case a custom copy function is defined, use it!
*/
const LayerTypeInfo *typeInfo = layerType_getInfo(layer->type);
if (typeInfo->copy) {
void *dst_data = MEM_malloc_arrayN(
size_t(totelem), typeInfo->size, "CD duplicate ref layer");
typeInfo->copy(layer->data, dst_data, totelem);
layer->data = dst_data;
}
else {
layer->data = MEM_dupallocN(layer->data);
}
layer->flag &= ~CD_FLAG_NOFREE;
}
return layer->data;
}
void CustomData_duplicate_referenced_layers(CustomData *data, const int totelem)
{
for (int i = 0; i < data->totlayer; i++) {
CustomDataLayer *layer = &data->layers[i];
layer->data = customData_duplicate_referenced_layer_index(data, i, totelem);
}
}
void CustomData_free_temporary(CustomData *data, const int totelem)
{
int i, j;
@ -3264,14 +3319,12 @@ void CustomData_copy_layer_type_data(const CustomData *source,
void CustomData_free_elem(CustomData *data, const int index, const int count)
{
for (int i = 0; i < data->totlayer; i++) {
if (!(data->layers[i].flag & CD_FLAG_NOFREE)) {
const LayerTypeInfo *typeInfo = layerType_getInfo(data->layers[i].type);
const LayerTypeInfo *typeInfo = layerType_getInfo(data->layers[i].type);
if (typeInfo->free) {
size_t offset = size_t(index) * typeInfo->size;
if (typeInfo->free) {
size_t offset = size_t(index) * typeInfo->size;
typeInfo->free(POINTER_OFFSET(data->layers[i].data, offset), count, typeInfo->size);
}
typeInfo->free(POINTER_OFFSET(data->layers[i].data, offset), count, typeInfo->size);
}
}
}
@ -3436,7 +3489,12 @@ const void *CustomData_get_layer(const CustomData *data, const int type)
void *CustomData_get_layer_for_write(CustomData *data, const int type, const int totelem)
{
const int layer_index = CustomData_get_active_layer_index(data, type);
return customData_duplicate_referenced_layer_index(data, layer_index, totelem);
if (layer_index == -1) {
return nullptr;
}
CustomDataLayer &layer = data->layers[layer_index];
ensure_layer_data_is_mutable(layer, totelem);
return layer.data;
}
const void *CustomData_get_layer_n(const CustomData *data, const int type, const int n)
@ -3445,7 +3503,6 @@ const void *CustomData_get_layer_n(const CustomData *data, const int type, const
if (layer_index == -1) {
return nullptr;
}
return data->layers[layer_index].data;
}
@ -3455,7 +3512,12 @@ void *CustomData_get_layer_n_for_write(CustomData *data,
const int totelem)
{
const int layer_index = CustomData_get_layer_index_n(data, type, n);
return customData_duplicate_referenced_layer_index(data, layer_index, totelem);
if (layer_index == -1) {
return nullptr;
}
CustomDataLayer &layer = data->layers[layer_index];
ensure_layer_data_is_mutable(layer, totelem);
return layer.data;
}
const void *CustomData_get_layer_named(const CustomData *data, const int type, const char *name)
@ -3464,7 +3526,6 @@ const void *CustomData_get_layer_named(const CustomData *data, const int type, c
if (layer_index == -1) {
return nullptr;
}
return data->layers[layer_index].data;
}
@ -3474,7 +3535,12 @@ void *CustomData_get_layer_named_for_write(CustomData *data,
const int totelem)
{
const int layer_index = CustomData_get_named_layer_index(data, type, name);
return customData_duplicate_referenced_layer_index(data, layer_index, totelem);
if (layer_index == -1) {
return nullptr;
}
CustomDataLayer &layer = data->layers[layer_index];
ensure_layer_data_is_mutable(layer, totelem);
return layer.data;
}
int CustomData_get_offset(const CustomData *data, const int type)
@ -3483,7 +3549,6 @@ int CustomData_get_offset(const CustomData *data, const int type)
if (layer_index == -1) {
return -1;
}
return data->layers[layer_index].offset;
}
@ -3561,12 +3626,12 @@ void CustomData_bmesh_init_pool(CustomData *data, const int totelem, const char
}
}
bool CustomData_bmesh_merge(const CustomData *source,
CustomData *dest,
eCustomDataMask mask,
eCDAllocType alloctype,
BMesh *bm,
const char htype)
bool CustomData_bmesh_merge_new(const CustomData *source,
CustomData *dest,
eCustomDataMask mask,
eCDAllocType alloctype,
BMesh *bm,
const char htype)
{
if (CustomData_number_of_layers_typemask(source, mask) == 0) {
@ -3580,7 +3645,7 @@ bool CustomData_bmesh_merge(const CustomData *source,
destold.layers = static_cast<CustomDataLayer *>(MEM_dupallocN(destold.layers));
}
if (CustomData_merge(source, dest, mask, alloctype, 0) == false) {
if (CustomData_merge_new(source, dest, mask, alloctype, 0) == false) {
if (destold.layers) {
MEM_freeN(destold.layers);
}
@ -3660,13 +3725,11 @@ void CustomData_bmesh_free_block(CustomData *data, void **block)
}
for (int i = 0; i < data->totlayer; i++) {
if (!(data->layers[i].flag & CD_FLAG_NOFREE)) {
const LayerTypeInfo *typeInfo = layerType_getInfo(data->layers[i].type);
const LayerTypeInfo *typeInfo = layerType_getInfo(data->layers[i].type);
if (typeInfo->free) {
int offset = data->layers[i].offset;
typeInfo->free(POINTER_OFFSET(*block, offset), 1, typeInfo->size);
}
if (typeInfo->free) {
int offset = data->layers[i].offset;
typeInfo->free(POINTER_OFFSET(*block, offset), 1, typeInfo->size);
}
}
@ -3683,12 +3746,10 @@ void CustomData_bmesh_free_block_data(CustomData *data, void *block)
return;
}
for (int i = 0; i < data->totlayer; i++) {
if (!(data->layers[i].flag & CD_FLAG_NOFREE)) {
const LayerTypeInfo *typeInfo = layerType_getInfo(data->layers[i].type);
if (typeInfo->free) {
const size_t offset = data->layers[i].offset;
typeInfo->free(POINTER_OFFSET(block, offset), 1, typeInfo->size);
}
const LayerTypeInfo *typeInfo = layerType_getInfo(data->layers[i].type);
if (typeInfo->free) {
const size_t offset = data->layers[i].offset;
typeInfo->free(POINTER_OFFSET(block, offset), 1, typeInfo->size);
}
}
if (data->totsize) {
@ -3721,10 +3782,8 @@ void CustomData_bmesh_free_block_data_exclude_by_type(CustomData *data,
if ((CD_TYPE_AS_MASK(data->layers[i].type) & mask_exclude) == 0) {
const LayerTypeInfo *typeInfo = layerType_getInfo(data->layers[i].type);
const size_t offset = data->layers[i].offset;
if (!(data->layers[i].flag & CD_FLAG_NOFREE)) {
if (typeInfo->free) {
typeInfo->free(POINTER_OFFSET(block, offset), 1, typeInfo->size);
}
if (typeInfo->free) {
typeInfo->free(POINTER_OFFSET(block, offset), 1, typeInfo->size);
}
memset(POINTER_OFFSET(block, offset), 0, typeInfo->size);
}
@ -3897,11 +3956,9 @@ bool CustomData_has_math(const CustomData *data)
bool CustomData_bmesh_has_free(const CustomData *data)
{
for (int i = 0; i < data->totlayer; i++) {
if (!(data->layers[i].flag & CD_FLAG_NOFREE)) {
const LayerTypeInfo *typeInfo = layerType_getInfo(data->layers[i].type);
if (typeInfo->free) {
return true;
}
const LayerTypeInfo *typeInfo = layerType_getInfo(data->layers[i].type);
if (typeInfo->free) {
return true;
}
}
return false;
@ -3919,16 +3976,6 @@ bool CustomData_has_interp(const CustomData *data)
return false;
}
bool CustomData_has_referenced(const CustomData *data)
{
for (int i = 0; i < data->totlayer; i++) {
if (data->layers[i].flag & CD_FLAG_NOFREE) {
return true;
}
}
return false;
}
void CustomData_data_copy_value(int type, const void *source, void *dest)
{
const LayerTypeInfo *typeInfo = layerType_getInfo(type);
@ -4989,6 +5036,10 @@ void CustomData_blend_write(BlendWriter *writer,
writer, CustomDataLayer, data->totlayer, data->layers, layers_to_write.data());
for (const CustomDataLayer &layer : layers_to_write) {
if (BLO_write_is_undo(writer) && layer.cow != nullptr) {
BLO_write_cow(writer, layer.data, layer.cow);
continue;
}
switch (layer.type) {
case CD_MDEFORMVERT:
BKE_defvert_blend_write(writer, count, static_cast<const MDeformVert *>(layer.data));
@ -5104,10 +5155,18 @@ void CustomData_blend_read(BlendDataReader *reader, CustomData *data, const int
layer->flag &= ~CD_FLAG_IN_MEMORY;
}
layer->flag &= ~CD_FLAG_NOFREE;
if (CustomData_verify_versions(data, i)) {
if (BLO_read_is_cow_data(reader, layer->data)) {
BLI_assert(layer->cow != nullptr);
layer->cow->add_user();
i++;
continue;
}
BLO_read_data_address(reader, &layer->data);
if (layer->data != nullptr) {
/* Make layer data shareable. */
layer->cow = make_cow_for_array(eCustomDataType(layer->type), layer->data, count);
}
if (CustomData_layer_ensure_data_exists(layer, count)) {
/* Under normal operations, this shouldn't happen, but...
* For a CD_PROP_BOOL example, see #84935.

View File

@ -84,26 +84,6 @@ std::optional<blender::bke::MutableAttributeAccessor> GeometryComponent::attribu
return std::nullopt;
}
void GeometryComponent::user_add() const
{
users_.fetch_add(1);
}
void GeometryComponent::user_remove() const
{
const int new_users = users_.fetch_sub(1) - 1;
if (new_users == 0) {
delete this;
}
}
bool GeometryComponent::is_mutable() const
{
/* If the item is shared, it is read-only. */
/* The user count can be 0, when this is called from the destructor. */
return users_ <= 1;
}
GeometryComponentType GeometryComponent::type() const
{
return type_;
@ -114,6 +94,11 @@ bool GeometryComponent::is_empty() const
return false;
}
void GeometryComponent::delete_self()
{
delete this;
}
/** \} */
/* -------------------------------------------------------------------- */
@ -198,7 +183,7 @@ void GeometrySet::remove_geometry_during_modify()
void GeometrySet::add(const GeometryComponent &component)
{
BLI_assert(!components_[component.type()]);
component.user_add();
component.add_user();
components_[component.type()] = const_cast<GeometryComponent *>(&component);
}

View File

@ -1464,7 +1464,7 @@ Mesh *BKE_mball_polygonize(Depsgraph *depsgraph, Scene *scene, Object *ob)
mesh->totvert = int(process.curvertex);
CustomData_add_layer_named_with_data(
&mesh->vdata, CD_PROP_FLOAT3, process.co, mesh->totvert, "position");
&mesh->vdata, CD_PROP_FLOAT3, process.co, mesh->totvert, "position", nullptr);
process.co = nullptr;
mesh->totpoly = int(process.curindex);

View File

@ -151,13 +151,12 @@ static void mesh_copy_data(Main *bmain, ID *id_dst, const ID *id_src, const int
mesh_dst->default_color_attribute = static_cast<char *>(
MEM_dupallocN(mesh_src->default_color_attribute));
const eCDAllocType alloc_type = (flag & LIB_ID_COPY_CD_REFERENCE) ? CD_REFERENCE : CD_DUPLICATE;
CustomData_copy(&mesh_src->vdata, &mesh_dst->vdata, mask.vmask, alloc_type, mesh_dst->totvert);
CustomData_copy(&mesh_src->edata, &mesh_dst->edata, mask.emask, alloc_type, mesh_dst->totedge);
CustomData_copy(&mesh_src->ldata, &mesh_dst->ldata, mask.lmask, alloc_type, mesh_dst->totloop);
CustomData_copy(&mesh_src->pdata, &mesh_dst->pdata, mask.pmask, alloc_type, mesh_dst->totpoly);
CustomData_copy(&mesh_src->vdata, &mesh_dst->vdata, mask.vmask, mesh_dst->totvert);
CustomData_copy(&mesh_src->edata, &mesh_dst->edata, mask.emask, mesh_dst->totedge);
CustomData_copy(&mesh_src->ldata, &mesh_dst->ldata, mask.lmask, mesh_dst->totloop);
CustomData_copy(&mesh_src->pdata, &mesh_dst->pdata, mask.pmask, mesh_dst->totpoly);
if (do_tessface) {
CustomData_copy(&mesh_src->fdata, &mesh_dst->fdata, mask.fmask, alloc_type, mesh_dst->totface);
CustomData_copy(&mesh_src->fdata, &mesh_dst->fdata, mask.fmask, mesh_dst->totface);
}
else {
mesh_tessface_clear_intern(mesh_dst, false);
@ -1079,12 +1078,12 @@ Mesh *BKE_mesh_new_nomain_from_template_ex(const Mesh *me_src,
BKE_mesh_copy_parameters_for_eval(me_dst, me_src);
CustomData_copy(&me_src->vdata, &me_dst->vdata, mask.vmask, CD_SET_DEFAULT, verts_len);
CustomData_copy(&me_src->edata, &me_dst->edata, mask.emask, CD_SET_DEFAULT, edges_len);
CustomData_copy(&me_src->ldata, &me_dst->ldata, mask.lmask, CD_SET_DEFAULT, loops_len);
CustomData_copy(&me_src->pdata, &me_dst->pdata, mask.pmask, CD_SET_DEFAULT, polys_len);
CustomData_copy_new(&me_src->vdata, &me_dst->vdata, mask.vmask, CD_SET_DEFAULT, verts_len);
CustomData_copy_new(&me_src->edata, &me_dst->edata, mask.emask, CD_SET_DEFAULT, edges_len);
CustomData_copy_new(&me_src->ldata, &me_dst->ldata, mask.lmask, CD_SET_DEFAULT, loops_len);
CustomData_copy_new(&me_src->pdata, &me_dst->pdata, mask.pmask, CD_SET_DEFAULT, polys_len);
if (do_tessface) {
CustomData_copy(&me_src->fdata, &me_dst->fdata, mask.fmask, CD_SET_DEFAULT, tessface_len);
CustomData_copy_new(&me_src->fdata, &me_dst->fdata, mask.fmask, CD_SET_DEFAULT, tessface_len);
}
else {
mesh_tessface_clear_intern(me_dst, false);
@ -1360,7 +1359,7 @@ void BKE_mesh_orco_ensure(Object *ob, Mesh *mesh)
/* Orcos are stored in normalized 0..1 range by convention. */
float(*orcodata)[3] = BKE_mesh_orco_verts_get(ob);
BKE_mesh_orco_verts_transform(mesh, orcodata, mesh->totvert, false);
CustomData_add_layer_with_data(&mesh->vdata, CD_ORCO, orcodata, mesh->totvert);
CustomData_add_layer_with_data(&mesh->vdata, CD_ORCO, orcodata, mesh->totvert, nullptr);
}
Mesh *BKE_mesh_from_object(Object *ob)

View File

@ -658,15 +658,15 @@ static void merge_vertex_loop_poly_customdata_layers(Mesh *target, MeshesToIMesh
for (int mesh_index = 1; mesh_index < mim.meshes.size(); ++mesh_index) {
const Mesh *me = mim.meshes[mesh_index];
if (me->totvert) {
CustomData_merge(
CustomData_merge_new(
&me->vdata, &target->vdata, CD_MASK_MESH.vmask, CD_SET_DEFAULT, target->totvert);
}
if (me->totloop) {
CustomData_merge(
CustomData_merge_new(
&me->ldata, &target->ldata, CD_MASK_MESH.lmask, CD_SET_DEFAULT, target->totloop);
}
if (me->totpoly) {
CustomData_merge(
CustomData_merge_new(
&me->pdata, &target->pdata, CD_MASK_MESH.pmask, CD_SET_DEFAULT, target->totpoly);
}
}
@ -677,7 +677,7 @@ static void merge_edge_customdata_layers(Mesh *target, MeshesToIMeshInfo &mim)
for (int mesh_index = 0; mesh_index < mim.meshes.size(); ++mesh_index) {
const Mesh *me = mim.meshes[mesh_index];
if (me->totedge) {
CustomData_merge(
CustomData_merge_new(
&me->edata, &target->edata, CD_MASK_MESH.emask, CD_SET_DEFAULT, target->totedge);
}
}

View File

@ -240,7 +240,7 @@ void BKE_mesh_calc_edges(Mesh *mesh, bool keep_existing_edges, const bool select
/* Free old CustomData and assign new one. */
CustomData_free(&mesh->edata, mesh->totedge);
CustomData_reset(&mesh->edata);
CustomData_add_layer_with_data(&mesh->edata, CD_MEDGE, new_edges.data(), new_totedge);
CustomData_add_layer_with_data(&mesh->edata, CD_MEDGE, new_edges.data(), new_totedge, nullptr);
mesh->totedge = new_totedge;
if (select_new_edges) {

View File

@ -637,7 +637,8 @@ void BKE_pointcloud_from_mesh(const Mesh *me, PointCloud *pointcloud)
{
CustomData_free(&pointcloud->pdata, pointcloud->totpoint);
pointcloud->totpoint = me->totvert;
CustomData_merge(&me->vdata, &pointcloud->pdata, CD_MASK_PROP_ALL, CD_DUPLICATE, me->totvert);
Review

No need to add a blank line here IMO

No need to add a blank line here IMO
CustomData_merge(&me->vdata, &pointcloud->pdata, CD_MASK_PROP_ALL, me->totvert);
}
void BKE_mesh_to_pointcloud(Main *bmain, Depsgraph *depsgraph, Scene * /*scene*/, Object *ob)
@ -664,8 +665,7 @@ void BKE_mesh_to_pointcloud(Main *bmain, Depsgraph *depsgraph, Scene * /*scene*/
void BKE_mesh_from_pointcloud(const PointCloud *pointcloud, Mesh *me)
{
me->totvert = pointcloud->totpoint;
CustomData_merge(
&pointcloud->pdata, &me->vdata, CD_MASK_PROP_ALL, CD_DUPLICATE, pointcloud->totpoint);
CustomData_merge(&pointcloud->pdata, &me->vdata, CD_MASK_PROP_ALL, pointcloud->totpoint);
}
void BKE_pointcloud_to_mesh(Main *bmain, Depsgraph *depsgraph, Scene * /*scene*/, Object *ob)
@ -1128,13 +1128,6 @@ void BKE_mesh_nomain_to_mesh(Mesh *mesh_src, Mesh *mesh_dst, Object *ob)
BKE_mesh_clear_geometry(mesh_dst);
/* Make sure referenced layers have a single user so assigning them to the mesh in main doesn't
* share them. "Referenced" layers are not expected to be shared between original meshes. */
CustomData_duplicate_referenced_layers(&mesh_src->vdata, mesh_src->totvert);
CustomData_duplicate_referenced_layers(&mesh_src->edata, mesh_src->totedge);
CustomData_duplicate_referenced_layers(&mesh_src->pdata, mesh_src->totpoly);
CustomData_duplicate_referenced_layers(&mesh_src->ldata, mesh_src->totloop);
const bool verts_num_changed = mesh_dst->totvert != mesh_src->totvert;
mesh_dst->totvert = mesh_src->totvert;
mesh_dst->totedge = mesh_src->totedge;
@ -1143,10 +1136,10 @@ void BKE_mesh_nomain_to_mesh(Mesh *mesh_src, Mesh *mesh_dst, Object *ob)
/* Using #CD_MASK_MESH ensures that only data that should exist in Main meshes is moved. */
const CustomData_MeshMasks mask = CD_MASK_MESH;
CustomData_copy(&mesh_src->vdata, &mesh_dst->vdata, mask.vmask, CD_ASSIGN, mesh_src->totvert);
CustomData_copy(&mesh_src->edata, &mesh_dst->edata, mask.emask, CD_ASSIGN, mesh_src->totedge);
CustomData_copy(&mesh_src->pdata, &mesh_dst->pdata, mask.pmask, CD_ASSIGN, mesh_src->totpoly);
CustomData_copy(&mesh_src->ldata, &mesh_dst->ldata, mask.lmask, CD_ASSIGN, mesh_src->totloop);
CustomData_copy(&mesh_src->vdata, &mesh_dst->vdata, mask.vmask, mesh_src->totvert);
CustomData_copy(&mesh_src->edata, &mesh_dst->edata, mask.emask, mesh_src->totedge);
CustomData_copy(&mesh_src->pdata, &mesh_dst->pdata, mask.pmask, mesh_src->totpoly);
CustomData_copy(&mesh_src->ldata, &mesh_dst->ldata, mask.lmask, mesh_src->totloop);
/* Make sure active/default color attribute (names) are brought over. */
if (mesh_src->active_color_attribute) {

View File

@ -228,7 +228,7 @@ void BKE_mesh_calc_edges_legacy(Mesh *me)
return;
}
edges = (MEdge *)CustomData_add_layer_with_data(&me->edata, CD_MEDGE, edges, totedge);
edges = (MEdge *)CustomData_add_layer_with_data(&me->edata, CD_MEDGE, edges, totedge, nullptr);
me->totedge = totedge;
BKE_mesh_tag_topology_changed(me);
@ -1152,11 +1152,11 @@ static int mesh_tessface_calc(Mesh &mesh,
sizeof(*mface_to_poly_map) * size_t(totface));
}
CustomData_add_layer_with_data(fdata, CD_MFACE, mface, totface);
CustomData_add_layer_with_data(fdata, CD_MFACE, mface, totface, nullptr);
/* #CD_ORIGINDEX will contain an array of indices from tessellation-faces to the polygons
* they are directly tessellated from. */
CustomData_add_layer_with_data(fdata, CD_ORIGINDEX, mface_to_poly_map, totface);
CustomData_add_layer_with_data(fdata, CD_ORIGINDEX, mface_to_poly_map, totface, nullptr);
add_mface_layers(mesh, fdata, ldata, totface);
/* NOTE: quad detection issue - fourth vertex-index vs fourth loop-index:
@ -1300,17 +1300,22 @@ void BKE_mesh_legacy_face_set_to_generic(Mesh *mesh)
return;
}
void *faceset_data = nullptr;
const bCopyOnWrite *faceset_cow = nullptr;
for (const int i : IndexRange(mesh->pdata.totlayer)) {
if (mesh->pdata.layers[i].type == CD_SCULPT_FACE_SETS) {
faceset_data = mesh->pdata.layers[i].data;
mesh->pdata.layers[i].data = nullptr;
CustomDataLayer &layer = mesh->pdata.layers[i];
if (layer.type == CD_SCULPT_FACE_SETS) {
faceset_data = layer.data;
faceset_cow = layer.cow;
layer.data = nullptr;
layer.cow = nullptr;
CustomData_free_layer(&mesh->pdata, CD_SCULPT_FACE_SETS, mesh->totpoly, i);
break;
}
}
if (faceset_data != nullptr) {
CustomData_add_layer_named_with_data(
&mesh->pdata, CD_PROP_INT32, faceset_data, mesh->totpoly, ".sculpt_face_set");
&mesh->pdata, CD_PROP_INT32, faceset_data, mesh->totpoly, ".sculpt_face_set", faceset_cow);
faceset_cow->remove_user_and_delete_if_last();
}
}
@ -1798,28 +1803,31 @@ void BKE_mesh_legacy_convert_uvs_to_generic(Mesh *mesh)
CustomData_free_layer_named(&mesh->ldata, name.c_str(), mesh->totloop);
CustomData_add_layer_named_with_data(
&mesh->ldata, CD_PROP_FLOAT2, coords, mesh->totloop, name.c_str());
&mesh->ldata, CD_PROP_FLOAT2, coords, mesh->totloop, name.c_str(), nullptr);
char buffer[MAX_CUSTOMDATA_LAYER_NAME];
if (vert_selection) {
CustomData_add_layer_named_with_data(&mesh->ldata,
CD_PROP_BOOL,
vert_selection,
mesh->totloop,
BKE_uv_map_vert_select_name_get(name.c_str(), buffer));
BKE_uv_map_vert_select_name_get(name.c_str(), buffer),
nullptr);
}
if (edge_selection) {
CustomData_add_layer_named_with_data(&mesh->ldata,
CD_PROP_BOOL,
edge_selection,
mesh->totloop,
BKE_uv_map_edge_select_name_get(name.c_str(), buffer));
BKE_uv_map_edge_select_name_get(name.c_str(), buffer),
nullptr);
}
if (pin) {
CustomData_add_layer_named_with_data(&mesh->ldata,
CD_PROP_BOOL,
pin,
mesh->totloop,
BKE_uv_map_pin_name_get(name.c_str(), buffer));
BKE_uv_map_pin_name_get(name.c_str(), buffer),
nullptr);
}
}

View File

@ -71,18 +71,17 @@ static void pointcloud_init_data(ID *id)
pointcloud->runtime = new blender::bke::PointCloudRuntime();
}
static void pointcloud_copy_data(Main * /*bmain*/, ID *id_dst, const ID *id_src, const int flag)
static void pointcloud_copy_data(Main * /*bmain*/,
ID *id_dst,
const ID *id_src,
const int /*flag*/)
{
PointCloud *pointcloud_dst = (PointCloud *)id_dst;
const PointCloud *pointcloud_src = (const PointCloud *)id_src;
pointcloud_dst->mat = static_cast<Material **>(MEM_dupallocN(pointcloud_src->mat));
const eCDAllocType alloc_type = (flag & LIB_ID_COPY_CD_REFERENCE) ? CD_REFERENCE : CD_DUPLICATE;
CustomData_copy(&pointcloud_src->pdata,
&pointcloud_dst->pdata,
CD_MASK_ALL,
alloc_type,
pointcloud_dst->totpoint);
CustomData_copy(
&pointcloud_src->pdata, &pointcloud_dst->pdata, CD_MASK_ALL, pointcloud_dst->totpoint);
pointcloud_dst->runtime = new blender::bke::PointCloudRuntime();
pointcloud_dst->runtime->bounds_cache = pointcloud_src->runtime->bounds_cache;
@ -259,27 +258,12 @@ void BKE_pointcloud_nomain_to_pointcloud(PointCloud *pointcloud_src,
{
BLI_assert(pointcloud_src->id.tag & LIB_TAG_NO_MAIN);
eCDAllocType alloctype = CD_DUPLICATE;
if (take_ownership) {
bool has_any_referenced_layers = CustomData_has_referenced(&pointcloud_src->pdata);
if (!has_any_referenced_layers) {
alloctype = CD_ASSIGN;
}
}
CustomData_free(&pointcloud_dst->pdata, pointcloud_dst->totpoint);
const int totpoint = pointcloud_dst->totpoint = pointcloud_src->totpoint;
CustomData_copy(
&pointcloud_src->pdata, &pointcloud_dst->pdata, CD_MASK_ALL, alloctype, totpoint);
CustomData_copy(&pointcloud_src->pdata, &pointcloud_dst->pdata, CD_MASK_ALL, totpoint);
if (take_ownership) {
if (alloctype == CD_ASSIGN) {
/* Free the CustomData but keep the layers. */
CustomData_free_typemask(&pointcloud_src->pdata, pointcloud_src->totpoint, 0);
}
BKE_id_free(nullptr, pointcloud_src);
}
}

View File

@ -218,11 +218,11 @@ static void vertex_interpolation_init(const SubdivMeshContext *ctx,
else {
vertex_interpolation->vertex_data = &vertex_interpolation->vertex_data_storage;
/* Allocate storage for loops corresponding to ptex corners. */
CustomData_copy(&ctx->coarse_mesh->vdata,
&vertex_interpolation->vertex_data_storage,
CD_MASK_EVERYTHING.vmask,
CD_SET_DEFAULT,
4);
CustomData_copy_new(&ctx->coarse_mesh->vdata,
&vertex_interpolation->vertex_data_storage,
CD_MASK_EVERYTHING.vmask,
CD_SET_DEFAULT,
4);
/* Initialize indices. */
vertex_interpolation->vertex_indices[0] = 0;
vertex_interpolation->vertex_indices[1] = 1;
@ -351,11 +351,11 @@ static void loop_interpolation_init(const SubdivMeshContext *ctx,
else {
loop_interpolation->loop_data = &loop_interpolation->loop_data_storage;
/* Allocate storage for loops corresponding to ptex corners. */
CustomData_copy(&ctx->coarse_mesh->ldata,
&loop_interpolation->loop_data_storage,
CD_MASK_EVERYTHING.lmask,
CD_SET_DEFAULT,
4);
CustomData_copy_new(&ctx->coarse_mesh->ldata,
&loop_interpolation->loop_data_storage,
CD_MASK_EVERYTHING.lmask,
CD_SET_DEFAULT,
4);
/* Initialize indices. */
loop_interpolation->loop_indices[0] = 0;
loop_interpolation->loop_indices[1] = 1;

View File

@ -0,0 +1,96 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup bli
*/
#include <atomic>
#include "BLI_compiler_attrs.h"
#include "BLI_utildefines.h"
#include "BLI_utility_mixins.hh"
/**
* #bCopyOnWrite allows implementing copy-on-write behavior, i.e. it allows sharing read-only data
* between multiple independend systems (e.g. meshes). The data is only copied when it is shared
Review

Typo: independend -> independent

Typo: `independend` -> `independent`
* and is about to be modified. This is in contrast to making copies before it is actually known
* that it is necessary.
*
* Internally, this is mostly just a glorified reference count. If the reference count is 1, the
* data only has a single owner and is mutable. If it is larger than 1, it is shared and must be
* logically const.
*
* On top of containing a reference count, #bCopyOnWrite also knows how to destruct the referenced
Review

On top of containing a -> In addition to containing the

Just a bit clearer (there's only one reference count per item) and reads a bit more naturally

`On top of containing a` -> `In addition to containing the` Just a bit clearer (there's only one reference count per item) and reads a bit more naturally
* data. This is important because the code freeing the data in the end might not know how it was
* allocated (for example, it doesn't know whether an array was allocated using the system or
* guarded allocator).
*
* #bCopyOnWrite is used in two ways:
* - It can be allocated separately from the referenced data as is typically the case with raw
* arrays (e.g. for mesh attributes).
* - It can be embedded into another struct. For that it's best to use #bCopyOnWriteMixin.
*/
struct bCopyOnWrite : blender::NonCopyable, blender::NonMovable {
Review

This is a C++ header now, and only used in C++ code, so I think it would make sense to:

  • Use the blender:: namespace like other blenlib classes
  • Remove the b prefix which becomes redundant with the namespace.
This is a C++ header now, and only used in C++ code, so I think it would make sense to: - Use the `blender::` namespace like other blenlib classes - Remove the `b` prefix which becomes redundant with the namespace.
private:
mutable std::atomic<int> users_;
public:
bCopyOnWrite(const int initial_users) : users_(initial_users)
{
}
virtual ~bCopyOnWrite()
{
BLI_assert(this->is_mutable());
}
bool is_shared() const
{
return users_.load(std::memory_order_relaxed) >= 2;
}
bool is_mutable() const
{
return !this->is_shared();
}
void add_user() const
{
users_.fetch_add(1, std::memory_order_relaxed);
}
void remove_user_and_delete_if_last() const
{
const int old_user_count = users_.fetch_sub(1, std::memory_order_relaxed);
brecht marked this conversation as resolved

My understanding is that user count decrement requires std::memory_order_acq_rel, while increment can use std::memory_order_relaxed.

For example libc++ does this for shared_ptr. More details:
https://stackoverflow.com/questions/48124031/stdmemory-order-relaxed-atomicity-with-respect-to-the-same-atomic-variable

My understanding is that user count decrement requires `std::memory_order_acq_rel`, while increment can use `std::memory_order_relaxed`. For example libc++ does this for `shared_ptr`. More details: https://stackoverflow.com/questions/48124031/stdmemory-order-relaxed-atomicity-with-respect-to-the-same-atomic-variable

Oops, this was meant to go to #105994.

Oops, this was meant to go to #105994.
BLI_assert(old_user_count >= 1);
const bool was_last_user = old_user_count == 1;
if (was_last_user) {
const_cast<bCopyOnWrite *>(this)->delete_self_with_data();
}
}
private:
/** Has to free the #bCopyOnWrite and the referenced data. */
virtual void delete_self_with_data() = 0;
};
/**
* Makes it easy to embed copy-on-write behavior into a struct.
*/
struct bCopyOnWriteMixin : public bCopyOnWrite {
public:
bCopyOnWriteMixin() : bCopyOnWrite(1)
{
}
private:
void delete_self_with_data() override
{
/* Can't use `delete this` here, because we don't know what allocator was used. */
this->delete_self();
}
virtual void delete_self() = 0;
};

View File

@ -6,59 +6,55 @@
* \ingroup bli
*/
#include <atomic>
#include "BLI_copy_on_write.hh"
namespace blender {
/**
* A simple automatic reference counter. It is similar to std::shared_ptr, but expects that the
* reference count is inside the object.
*/
template<typename T> class UserCounter {
template<typename T> class COWUser {
Review

I guess COWUser should keep the old comment above UserCounter and maybe say "this adds RAII semantics on top of the copy-on-write logic from #bCopyOnWrite."

I guess `COWUser` should keep the old comment above `UserCounter` and maybe say "this adds RAII semantics on top of the copy-on-write logic from #bCopyOnWrite."

I would not abbreviate to COW but use CopyOnWrite, or whichever name we end up choosing.

I would not abbreviate to COW but use CopyOnWrite, or whichever name we end up choosing.
private:
T *data_ = nullptr;
public:
UserCounter() = default;
COWUser() = default;
UserCounter(T *data) : data_(data)
COWUser(T *data) : data_(data)
{
}
UserCounter(const UserCounter &other) : data_(other.data_)
COWUser(const COWUser &other) : data_(other.data_)
{
this->user_add(data_);
this->add_user(data_);
}
UserCounter(UserCounter &&other) : data_(other.data_)
COWUser(COWUser &&other) : data_(other.data_)
{
other.data_ = nullptr;
}
~UserCounter()
~COWUser()
{
this->user_remove(data_);
this->remove_user_and_delete_if_last(data_);
}
UserCounter &operator=(const UserCounter &other)
COWUser &operator=(const COWUser &other)
{
if (this == &other) {
return *this;
}
this->user_remove(data_);
this->remove_user_and_delete_if_last(data_);
data_ = other.data_;
this->user_add(data_);
this->add_user(data_);
return *this;
}
UserCounter &operator=(UserCounter &&other)
COWUser &operator=(COWUser &&other)
{
if (this == &other) {
return *this;
}
this->user_remove(data_);
this->remove_user_and_delete_if_last(data_);
data_ = other.data_;
other.data_ = nullptr;
return *this;
@ -112,7 +108,7 @@ template<typename T> class UserCounter {
void reset()
{
this->user_remove(data_);
this->remove_user_and_delete_if_last(data_);
data_ = nullptr;
}
@ -126,29 +122,23 @@ template<typename T> class UserCounter {
return get_default_hash(data_);
}
friend bool operator==(const UserCounter &a, const UserCounter &b)
friend bool operator==(const COWUser &a, const COWUser &b)
{
return a.data_ == b.data_;
}
friend std::ostream &operator<<(std::ostream &stream, const UserCounter &value)
{
stream << value.data_;
return stream;
}
private:
static void user_add(T *data)
static void add_user(T *data)
{
if (data != nullptr) {
data->user_add();
data->add_user();
}
}
static void user_remove(T *data)
static void remove_user_and_delete_if_last(T *data)
{
if (data != nullptr) {
data->user_remove();
data->remove_user_and_delete_if_last();
}
}
};

View File

@ -197,6 +197,8 @@ set(SRC
BLI_compute_context.hh
BLI_console.h
BLI_convexhull_2d.h
BLI_copy_on_write.hh
BLI_copy_on_write_user.hh
BLI_cpp_type.hh
BLI_cpp_type_make.hh
BLI_cpp_types.hh
@ -350,7 +352,6 @@ set(SRC
BLI_timecode.h
BLI_timeit.hh
BLI_timer.h
BLI_user_counter.hh
BLI_utildefines.h
BLI_utildefines_iter.h
BLI_utildefines_stack.h

View File

@ -42,6 +42,7 @@ typedef struct BlendWriter BlendWriter;
struct BlendFileReadReport;
struct Main;
struct bCopyOnWrite;
/* -------------------------------------------------------------------- */
/** \name Blend Write API
@ -175,6 +176,14 @@ void BLO_write_string(BlendWriter *writer, const char *data_ptr);
/* Misc. */
#ifdef __cplusplus
/**
* Give (shared) ownership of the data to the undo system so that the data does not have to be
* copied.
*/
void BLO_write_cow(BlendWriter *writer, const void *data_ptr, const bCopyOnWrite *cow);
#endif
/**
* Sometimes different data is written depending on whether the file is saved to disk or used for
* undo. This function returns true when the current file-writing is done for undo.
@ -236,6 +245,12 @@ void BLO_read_float3_array(BlendDataReader *reader, int array_size, float **ptr_
void BLO_read_double_array(BlendDataReader *reader, int array_size, double **ptr_p);
void BLO_read_pointer_array(BlendDataReader *reader, void **ptr_p);
/**
* True when the pointer refers to valid data and can still be used. In order to take (shared)
* ownership of the data, the user count of the corresponding #bCopyOnWrite has to be increased.
*/
bool BLO_read_is_cow_data(BlendDataReader *reader, const void *cow_data);
/* Misc. */
int BLO_read_fileversion_get(BlendDataReader *reader);

View File

@ -13,6 +13,25 @@
struct GHash;
struct Scene;
#ifdef __cplusplus
# include "BLI_copy_on_write.hh"
# include "BLI_map.hh"
/**
* Takes (shared) ownership of copy-on-write data so that it does not have to be copied into the
* undo step.
*/
struct MemFileCowStorage {
blender::Map<const void *, const bCopyOnWrite *> map;
~MemFileCowStorage();
};
#else
typedef struct MemFileCowStorage MemFileCowStorage;
#endif
typedef struct {
void *next, *prev;
const char *buf;
@ -32,6 +51,7 @@ typedef struct {
typedef struct MemFile {
ListBase chunks;
size_t size;
MemFileCowStorage *cow_storage;
} MemFile;
typedef struct MemFileWriteData {
@ -102,7 +122,6 @@ extern struct Main *BLO_memfile_main_get(struct MemFile *memfile,
*
* \return success.
*/
extern bool BLO_memfile_write_file(struct MemFile *memfile, const char *filepath);
FileReader *BLO_memfile_new_filereader(MemFile *memfile, int undo_direction);

View File

@ -5275,4 +5275,20 @@ void BLO_expand_id(BlendExpander *expander, ID *id)
expand_doit(expander->fd, expander->main, id);
}
bool BLO_read_is_cow_data(BlendDataReader *reader, const void *cow_data)
{
if (!BLO_read_data_is_undo(reader)) {
return false;
}
if (!(reader->fd->flags & FD_FLAGS_IS_MEMFILE)) {
return false;
}
UndoReader *undo_reader = reinterpret_cast<UndoReader *>(reader->fd->file);
MemFile &memfile = *undo_reader->memfile;
if (memfile.cow_storage == nullptr) {
return false;
}
return memfile.cow_storage->map.contains(cow_data);
}
/** \} */

View File

@ -24,6 +24,7 @@
#include "DNA_listBase.h"
#include "BLI_blenlib.h"
#include "BLI_copy_on_write.hh"
#include "BLI_ghash.h"
#include "BLO_readfile.h"
@ -48,9 +49,20 @@ void BLO_memfile_free(MemFile *memfile)
}
MEM_freeN(chunk);
}
if (memfile->cow_storage) {
MEM_delete(memfile->cow_storage);
memfile->cow_storage = nullptr;
}
memfile->size = 0;
}
MemFileCowStorage::~MemFileCowStorage()
{
for (auto item : this->map.items()) {
item.value->remove_user_and_delete_if_last();
}
}
void BLO_memfile_merge(MemFile *first, MemFile *second)
{
/* We use this mapping to store the memory buffers from second memfile chunks which are not owned
@ -199,61 +211,6 @@ struct Main *BLO_memfile_main_get(struct MemFile *memfile,
return bmain_undo;
}
bool BLO_memfile_write_file(struct MemFile *memfile, const char *filepath)
{
MemFileChunk *chunk;
int file, oflags;
/* NOTE: This is currently used for autosave and 'quit.blend',
* where _not_ following symlinks is OK,
* however if this is ever executed explicitly by the user,
* we may want to allow writing to symlinks.
*/
oflags = O_BINARY | O_WRONLY | O_CREAT | O_TRUNC;
#ifdef O_NOFOLLOW
/* use O_NOFOLLOW to avoid writing to a symlink - use 'O_EXCL' (CVE-2008-1103) */
oflags |= O_NOFOLLOW;
#else
/* TODO(sergey): How to deal with symlinks on windows? */
# ifndef _MSC_VER
# warning "Symbolic links will be followed on undo save, possibly causing CVE-2008-1103"
# endif
#endif
file = BLI_open(filepath, oflags, 0666);
if (file == -1) {
fprintf(stderr,
"Unable to save '%s': %s\n",
filepath,
errno ? strerror(errno) : "Unknown error opening file");
return false;
}
for (chunk = static_cast<MemFileChunk *>(memfile->chunks.first); chunk;
chunk = static_cast<MemFileChunk *>(chunk->next)) {
#ifdef _WIN32
if (size_t(write(file, chunk->buf, uint(chunk->size))) != chunk->size)
#else
if (size_t(write(file, chunk->buf, chunk->size)) != chunk->size)
#endif
{
break;
}
}
close(file);
if (chunk) {
fprintf(stderr,
"Unable to save '%s': %s\n",
filepath,
errno ? strerror(errno) : "Unknown error writing file");
return false;
}
return true;
}
static ssize_t undo_read(FileReader *reader, void *buffer, size_t size)
{
UndoReader *undo = (UndoReader *)reader;

View File

@ -273,19 +273,19 @@ static void customdata_version_242(Mesh *me)
int a, mtfacen, mcoln;
if (!me->vdata.totlayer) {
CustomData_add_layer_with_data(&me->vdata, CD_MVERT, me->mvert, me->totvert);
CustomData_add_layer_with_data(&me->vdata, CD_MVERT, me->mvert, me->totvert, NULL);
if (me->dvert) {
CustomData_add_layer_with_data(&me->vdata, CD_MDEFORMVERT, me->dvert, me->totvert);
CustomData_add_layer_with_data(&me->vdata, CD_MDEFORMVERT, me->dvert, me->totvert, NULL);
}
}
if (!me->edata.totlayer) {
CustomData_add_layer_with_data(&me->edata, CD_MEDGE, me->medge, me->totedge);
CustomData_add_layer_with_data(&me->edata, CD_MEDGE, me->medge, me->totedge, NULL);
}
if (!me->fdata.totlayer) {
CustomData_add_layer_with_data(&me->fdata, CD_MFACE, me->mface, me->totface);
CustomData_add_layer_with_data(&me->fdata, CD_MFACE, me->mface, me->totface, NULL);
if (me->tface) {
if (me->mcol) {
@ -308,7 +308,7 @@ static void customdata_version_242(Mesh *me)
me->tface = NULL;
}
else if (me->mcol) {
CustomData_add_layer_with_data(&me->fdata, CD_MCOL, me->mcol, me->totface);
CustomData_add_layer_with_data(&me->fdata, CD_MCOL, me->mcol, me->totface, NULL);
}
}

View File

@ -85,6 +85,7 @@
#include "BLI_bitmap.h"
#include "BLI_blenlib.h"
#include "BLI_copy_on_write.hh"
#include "BLI_endian_defines.h"
#include "BLI_endian_switch.h"
#include "BLI_link_utils.h"
@ -1686,4 +1687,19 @@ bool BLO_write_is_undo(BlendWriter *writer)
return writer->wd->use_memfile;
}
void BLO_write_cow(BlendWriter *writer, const void *data_ptr, const bCopyOnWrite *cow)
{
BLI_assert(writer->wd->use_memfile);
if (data_ptr == nullptr) {
return;
}
MemFile &memfile = *writer->wd->mem.written_memfile;
if (memfile.cow_storage == nullptr) {
Review

Maybe it doesn't matter, but maybe it would be reasonable to just create this once at the start of the writing process? It's likely to be used, and it would save the check for every write call. OTOH I'm sure this isn't a bottleneck!

Maybe it doesn't matter, but maybe it would be reasonable to just create this once at the start of the writing process? It's likely to be used, and it would save the check for every write call. OTOH I'm sure this isn't a bottleneck!
memfile.cow_storage = MEM_new<MemFileCowStorage>(__func__);
}
if (memfile.cow_storage->map.add(data_ptr, cow)) {
cow->add_user();
}
}
/** \} */

View File

@ -517,16 +517,16 @@ void BM_mesh_copy_init_customdata_from_mesh_array(BMesh *bm_dst,
&me_src->ldata, CD_MASK_BMESH.pmask);
if (i == 0) {
CustomData_copy(&mesh_vdata, &bm_dst->vdata, CD_MASK_BMESH.vmask, CD_SET_DEFAULT, 0);
CustomData_copy(&mesh_edata, &bm_dst->edata, CD_MASK_BMESH.emask, CD_SET_DEFAULT, 0);
CustomData_copy(&mesh_pdata, &bm_dst->pdata, CD_MASK_BMESH.pmask, CD_SET_DEFAULT, 0);
CustomData_copy(&mesh_ldata, &bm_dst->ldata, CD_MASK_BMESH.lmask, CD_SET_DEFAULT, 0);
CustomData_copy_new(&mesh_vdata, &bm_dst->vdata, CD_MASK_BMESH.vmask, CD_SET_DEFAULT, 0);
CustomData_copy_new(&mesh_edata, &bm_dst->edata, CD_MASK_BMESH.emask, CD_SET_DEFAULT, 0);
CustomData_copy_new(&mesh_pdata, &bm_dst->pdata, CD_MASK_BMESH.pmask, CD_SET_DEFAULT, 0);
CustomData_copy_new(&mesh_ldata, &bm_dst->ldata, CD_MASK_BMESH.lmask, CD_SET_DEFAULT, 0);
}
else {
CustomData_merge(&mesh_vdata, &bm_dst->vdata, CD_MASK_BMESH.vmask, CD_SET_DEFAULT, 0);
CustomData_merge(&mesh_edata, &bm_dst->edata, CD_MASK_BMESH.emask, CD_SET_DEFAULT, 0);
CustomData_merge(&mesh_pdata, &bm_dst->pdata, CD_MASK_BMESH.pmask, CD_SET_DEFAULT, 0);
CustomData_merge(&mesh_ldata, &bm_dst->ldata, CD_MASK_BMESH.lmask, CD_SET_DEFAULT, 0);
CustomData_merge_new(&mesh_vdata, &bm_dst->vdata, CD_MASK_BMESH.vmask, CD_SET_DEFAULT, 0);
CustomData_merge_new(&mesh_edata, &bm_dst->edata, CD_MASK_BMESH.emask, CD_SET_DEFAULT, 0);
CustomData_merge_new(&mesh_pdata, &bm_dst->pdata, CD_MASK_BMESH.pmask, CD_SET_DEFAULT, 0);
CustomData_merge_new(&mesh_ldata, &bm_dst->ldata, CD_MASK_BMESH.lmask, CD_SET_DEFAULT, 0);
}
MEM_SAFE_FREE(mesh_vdata.layers);
@ -554,10 +554,10 @@ void BM_mesh_copy_init_customdata(BMesh *bm_dst, BMesh *bm_src, const BMAllocTem
allocsize = &bm_mesh_allocsize_default;
}
CustomData_copy(&bm_src->vdata, &bm_dst->vdata, CD_MASK_BMESH.vmask, CD_SET_DEFAULT, 0);
CustomData_copy(&bm_src->edata, &bm_dst->edata, CD_MASK_BMESH.emask, CD_SET_DEFAULT, 0);
CustomData_copy(&bm_src->ldata, &bm_dst->ldata, CD_MASK_BMESH.lmask, CD_SET_DEFAULT, 0);
CustomData_copy(&bm_src->pdata, &bm_dst->pdata, CD_MASK_BMESH.pmask, CD_SET_DEFAULT, 0);
CustomData_copy_new(&bm_src->vdata, &bm_dst->vdata, CD_MASK_BMESH.vmask, CD_SET_DEFAULT, 0);
CustomData_copy_new(&bm_src->edata, &bm_dst->edata, CD_MASK_BMESH.emask, CD_SET_DEFAULT, 0);
CustomData_copy_new(&bm_src->ldata, &bm_dst->ldata, CD_MASK_BMESH.lmask, CD_SET_DEFAULT, 0);
CustomData_copy_new(&bm_src->pdata, &bm_dst->pdata, CD_MASK_BMESH.pmask, CD_SET_DEFAULT, 0);
CustomData_bmesh_init_pool(&bm_dst->vdata, allocsize->totvert, BM_VERT);
CustomData_bmesh_init_pool(&bm_dst->edata, allocsize->totedge, BM_EDGE);

View File

@ -264,10 +264,10 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const struct BMeshFromMeshPar
if (me->totvert == 0) {
if (is_new) {
/* No verts? still copy custom-data layout. */
CustomData_copy(&mesh_vdata, &bm->vdata, mask.vmask, CD_CONSTRUCT, 0);
CustomData_copy(&mesh_edata, &bm->edata, mask.emask, CD_CONSTRUCT, 0);
CustomData_copy(&mesh_pdata, &bm->pdata, mask.pmask, CD_CONSTRUCT, 0);
CustomData_copy(&mesh_ldata, &bm->ldata, mask.lmask, CD_CONSTRUCT, 0);
CustomData_copy_new(&mesh_vdata, &bm->vdata, mask.vmask, CD_CONSTRUCT, 0);
CustomData_copy_new(&mesh_edata, &bm->edata, mask.emask, CD_CONSTRUCT, 0);
CustomData_copy_new(&mesh_pdata, &bm->pdata, mask.pmask, CD_CONSTRUCT, 0);
CustomData_copy_new(&mesh_ldata, &bm->ldata, mask.lmask, CD_CONSTRUCT, 0);
CustomData_bmesh_init_pool(&bm->vdata, me->totvert, BM_VERT);
CustomData_bmesh_init_pool(&bm->edata, me->totedge, BM_EDGE);
@ -283,16 +283,16 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const struct BMeshFromMeshPar
}
if (is_new) {
CustomData_copy(&mesh_vdata, &bm->vdata, mask.vmask, CD_SET_DEFAULT, 0);
CustomData_copy(&mesh_edata, &bm->edata, mask.emask, CD_SET_DEFAULT, 0);
CustomData_copy(&mesh_pdata, &bm->pdata, mask.pmask, CD_SET_DEFAULT, 0);
CustomData_copy(&mesh_ldata, &bm->ldata, mask.lmask, CD_SET_DEFAULT, 0);
CustomData_copy_new(&mesh_vdata, &bm->vdata, mask.vmask, CD_SET_DEFAULT, 0);
CustomData_copy_new(&mesh_edata, &bm->edata, mask.emask, CD_SET_DEFAULT, 0);
CustomData_copy_new(&mesh_pdata, &bm->pdata, mask.pmask, CD_SET_DEFAULT, 0);
CustomData_copy_new(&mesh_ldata, &bm->ldata, mask.lmask, CD_SET_DEFAULT, 0);
}
else {
CustomData_bmesh_merge(&mesh_vdata, &bm->vdata, mask.vmask, CD_SET_DEFAULT, bm, BM_VERT);
CustomData_bmesh_merge(&mesh_edata, &bm->edata, mask.emask, CD_SET_DEFAULT, bm, BM_EDGE);
CustomData_bmesh_merge(&mesh_pdata, &bm->pdata, mask.pmask, CD_SET_DEFAULT, bm, BM_FACE);
CustomData_bmesh_merge(&mesh_ldata, &bm->ldata, mask.lmask, CD_SET_DEFAULT, bm, BM_LOOP);
CustomData_bmesh_merge_new(&mesh_vdata, &bm->vdata, mask.vmask, CD_SET_DEFAULT, bm, BM_VERT);
CustomData_bmesh_merge_new(&mesh_edata, &bm->edata, mask.emask, CD_SET_DEFAULT, bm, BM_EDGE);
CustomData_bmesh_merge_new(&mesh_pdata, &bm->pdata, mask.pmask, CD_SET_DEFAULT, bm, BM_FACE);
CustomData_bmesh_merge_new(&mesh_ldata, &bm->ldata, mask.lmask, CD_SET_DEFAULT, bm, BM_LOOP);
}
/* -------------------------------------------------------------------- */
@ -1392,10 +1392,10 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh
{
CustomData_MeshMasks mask = CD_MASK_MESH;
CustomData_MeshMasks_update(&mask, &params->cd_mask_extra);
CustomData_copy(&bm->vdata, &me->vdata, mask.vmask, CD_SET_DEFAULT, me->totvert);
CustomData_copy(&bm->edata, &me->edata, mask.emask, CD_SET_DEFAULT, me->totedge);
CustomData_copy(&bm->ldata, &me->ldata, mask.lmask, CD_SET_DEFAULT, me->totloop);
CustomData_copy(&bm->pdata, &me->pdata, mask.pmask, CD_SET_DEFAULT, me->totpoly);
CustomData_copy_new(&bm->vdata, &me->vdata, mask.vmask, CD_SET_DEFAULT, me->totvert);
CustomData_copy_new(&bm->edata, &me->edata, mask.emask, CD_SET_DEFAULT, me->totedge);
CustomData_copy_new(&bm->ldata, &me->ldata, mask.lmask, CD_SET_DEFAULT, me->totloop);
CustomData_copy_new(&bm->pdata, &me->pdata, mask.pmask, CD_SET_DEFAULT, me->totpoly);
}
bool need_select_vert = false;
@ -1606,10 +1606,10 @@ void BM_mesh_bm_to_me_for_eval(BMesh *bm, Mesh *me, const CustomData_MeshMasks *
CustomData_MeshMasks_update(&mask, cd_mask_extra);
}
mask.vmask &= ~CD_MASK_SHAPEKEY;
CustomData_merge(&bm->vdata, &me->vdata, mask.vmask, CD_CONSTRUCT, me->totvert);
CustomData_merge(&bm->edata, &me->edata, mask.emask, CD_CONSTRUCT, me->totedge);
CustomData_merge(&bm->ldata, &me->ldata, mask.lmask, CD_CONSTRUCT, me->totloop);
CustomData_merge(&bm->pdata, &me->pdata, mask.pmask, CD_CONSTRUCT, me->totpoly);
CustomData_merge_new(&bm->vdata, &me->vdata, mask.vmask, CD_CONSTRUCT, me->totvert);
CustomData_merge_new(&bm->edata, &me->edata, mask.emask, CD_CONSTRUCT, me->totedge);
CustomData_merge_new(&bm->ldata, &me->ldata, mask.lmask, CD_CONSTRUCT, me->totloop);
CustomData_merge_new(&bm->pdata, &me->pdata, mask.pmask, CD_CONSTRUCT, me->totpoly);
me->runtime->deformed_only = true;

View File

@ -16,6 +16,7 @@
#include "DNA_scene_types.h"
#include "BLI_array_utils.h"
#include "BLI_copy_on_write.hh"
#include "BLI_listbase.h"
#include "BLI_task.hh"
@ -255,8 +256,16 @@ static void um_arraystore_cd_compact(CustomData *cdata,
}
if (layer->data) {
if (layer->cow) {
/* This assumes that the layer is not shared, which it is not here because it has just
* been created in #BM_mesh_bm_to_me. The situation is a bit tricky here, because the
* layer data may be freed partially below for e.g. vertex groups. */
BLI_assert(layer->cow->is_mutable());
MEM_delete(layer->cow);
}
MEM_freeN(layer->data);
layer->data = nullptr;
layer->cow = nullptr;
}
}

View File

@ -278,7 +278,8 @@ int ED_mesh_uv_add(
CD_PROP_FLOAT2,
MEM_dupallocN(CustomData_get_layer(&me->ldata, CD_PROP_FLOAT2)),
me->totloop,
unique_name);
unique_name,
nullptr);
is_init = true;
}
@ -1139,7 +1140,7 @@ static void mesh_add_verts(Mesh *mesh, int len)
int totvert = mesh->totvert + len;
CustomData vdata;
CustomData_copy(&mesh->vdata, &vdata, CD_MASK_MESH.vmask, CD_SET_DEFAULT, totvert);
CustomData_copy_new(&mesh->vdata, &vdata, CD_MASK_MESH.vmask, CD_SET_DEFAULT, totvert);
CustomData_copy_data(&mesh->vdata, &vdata, 0, 0, mesh->totvert);
if (!CustomData_get_layer_named(&vdata, CD_PROP_FLOAT3, "position")) {
@ -1173,7 +1174,7 @@ static void mesh_add_edges(Mesh *mesh, int len)
totedge = mesh->totedge + len;
/* Update custom-data. */
CustomData_copy(&mesh->edata, &edata, CD_MASK_MESH.emask, CD_SET_DEFAULT, totedge);
CustomData_copy_new(&mesh->edata, &edata, CD_MASK_MESH.emask, CD_SET_DEFAULT, totedge);
CustomData_copy_data(&mesh->edata, &edata, 0, 0, mesh->totedge);
if (!CustomData_has_layer(&edata, CD_MEDGE)) {
@ -1206,7 +1207,7 @@ static void mesh_add_loops(Mesh *mesh, int len)
totloop = mesh->totloop + len; /* new face count */
/* update customdata */
CustomData_copy(&mesh->ldata, &ldata, CD_MASK_MESH.lmask, CD_SET_DEFAULT, totloop);
CustomData_copy_new(&mesh->ldata, &ldata, CD_MASK_MESH.lmask, CD_SET_DEFAULT, totloop);
CustomData_copy_data(&mesh->ldata, &ldata, 0, 0, mesh->totloop);
if (!CustomData_has_layer(&ldata, CD_MLOOP)) {
@ -1234,7 +1235,7 @@ static void mesh_add_polys(Mesh *mesh, int len)
totpoly = mesh->totpoly + len; /* new face count */
/* update customdata */
CustomData_copy(&mesh->pdata, &pdata, CD_MASK_MESH.pmask, CD_SET_DEFAULT, totpoly);
CustomData_copy_new(&mesh->pdata, &pdata, CD_MASK_MESH.pmask, CD_SET_DEFAULT, totpoly);
CustomData_copy_data(&mesh->pdata, &pdata, 0, 0, mesh->totpoly);
if (!CustomData_has_layer(&pdata, CD_MPOLY)) {

View File

@ -103,7 +103,7 @@ static void join_mesh_single(Depsgraph *depsgraph,
if (me->totvert) {
/* standard data */
CustomData_merge(&me->vdata, vdata, CD_MASK_MESH.vmask, CD_SET_DEFAULT, totvert);
CustomData_merge_new(&me->vdata, vdata, CD_MASK_MESH.vmask, CD_SET_DEFAULT, totvert);
CustomData_copy_data_named(&me->vdata, vdata, 0, *vertofs, me->totvert);
/* vertex groups */
@ -203,7 +203,7 @@ static void join_mesh_single(Depsgraph *depsgraph,
}
if (me->totedge) {
CustomData_merge(&me->edata, edata, CD_MASK_MESH.emask, CD_SET_DEFAULT, totedge);
CustomData_merge_new(&me->edata, edata, CD_MASK_MESH.emask, CD_SET_DEFAULT, totedge);
CustomData_copy_data_named(&me->edata, edata, 0, *edgeofs, me->totedge);
for (a = 0; a < me->totedge; a++, edge++) {
@ -224,7 +224,7 @@ static void join_mesh_single(Depsgraph *depsgraph,
}
}
CustomData_merge(&me->ldata, ldata, CD_MASK_MESH.lmask, CD_SET_DEFAULT, totloop);
CustomData_merge_new(&me->ldata, ldata, CD_MASK_MESH.lmask, CD_SET_DEFAULT, totloop);
CustomData_copy_data_named(&me->ldata, ldata, 0, *loopofs, me->totloop);
for (a = 0; a < me->totloop; a++, mloop++) {
@ -248,7 +248,7 @@ static void join_mesh_single(Depsgraph *depsgraph,
}
}
CustomData_merge(&me->pdata, pdata, CD_MASK_MESH.pmask, CD_SET_DEFAULT, totpoly);
CustomData_merge_new(&me->pdata, pdata, CD_MASK_MESH.pmask, CD_SET_DEFAULT, totpoly);
CustomData_copy_data_named(&me->pdata, pdata, 0, *polyofs, me->totpoly);
/* Apply matmap. In case we don't have material indices yet, create them if more than one

View File

@ -731,7 +731,7 @@ static void add_shapekey_layers(Mesh &mesh_dest, const Mesh &mesh_src)
}
CustomData_add_layer_named_with_data(
&mesh_dest.vdata, CD_SHAPEKEY, array, mesh_dest.totvert, kb->name);
&mesh_dest.vdata, CD_SHAPEKEY, array, mesh_dest.totvert, kb->name, nullptr);
const int ci = CustomData_get_layer_index_n(&mesh_dest.vdata, CD_SHAPEKEY, i);
mesh_dest.vdata.layers[ci].uid = kb->uid;

View File

@ -161,14 +161,10 @@ static void SCULPT_dynamic_topology_disable_ex(
me->totpoly = geometry->totpoly;
me->totedge = geometry->totedge;
me->totface = 0;
CustomData_copy(
&geometry->vdata, &me->vdata, CD_MASK_MESH.vmask, CD_DUPLICATE, geometry->totvert);
CustomData_copy(
&geometry->edata, &me->edata, CD_MASK_MESH.emask, CD_DUPLICATE, geometry->totedge);
CustomData_copy(
&geometry->ldata, &me->ldata, CD_MASK_MESH.lmask, CD_DUPLICATE, geometry->totloop);
CustomData_copy(
&geometry->pdata, &me->pdata, CD_MASK_MESH.pmask, CD_DUPLICATE, geometry->totpoly);
CustomData_copy(&geometry->vdata, &me->vdata, CD_MASK_MESH.vmask, geometry->totvert);
CustomData_copy(&geometry->edata, &me->edata, CD_MASK_MESH.emask, geometry->totedge);
CustomData_copy(&geometry->ldata, &me->ldata, CD_MASK_MESH.lmask, geometry->totloop);
CustomData_copy(&geometry->pdata, &me->pdata, CD_MASK_MESH.pmask, geometry->totpoly);
}
else {
BKE_sculptsession_bm_to_me(ob, true);

View File

@ -745,10 +745,10 @@ static void sculpt_undo_geometry_store_data(SculptUndoNodeGeometry *geometry, Ob
BLI_assert(!geometry->is_initialized);
geometry->is_initialized = true;
CustomData_copy(&mesh->vdata, &geometry->vdata, CD_MASK_MESH.vmask, CD_DUPLICATE, mesh->totvert);
CustomData_copy(&mesh->edata, &geometry->edata, CD_MASK_MESH.emask, CD_DUPLICATE, mesh->totedge);
CustomData_copy(&mesh->ldata, &geometry->ldata, CD_MASK_MESH.lmask, CD_DUPLICATE, mesh->totloop);
CustomData_copy(&mesh->pdata, &geometry->pdata, CD_MASK_MESH.pmask, CD_DUPLICATE, mesh->totpoly);
CustomData_copy(&mesh->vdata, &geometry->vdata, CD_MASK_MESH.vmask, mesh->totvert);
CustomData_copy(&mesh->edata, &geometry->edata, CD_MASK_MESH.emask, mesh->totedge);
CustomData_copy(&mesh->ldata, &geometry->ldata, CD_MASK_MESH.lmask, mesh->totloop);
CustomData_copy(&mesh->pdata, &geometry->pdata, CD_MASK_MESH.pmask, mesh->totpoly);
geometry->totvert = mesh->totvert;
geometry->totedge = mesh->totedge;
@ -774,14 +774,10 @@ static void sculpt_undo_geometry_restore_data(SculptUndoNodeGeometry *geometry,
mesh->totpoly = geometry->totpoly;
mesh->totface = 0;
CustomData_copy(
&geometry->vdata, &mesh->vdata, CD_MASK_MESH.vmask, CD_DUPLICATE, geometry->totvert);
CustomData_copy(
&geometry->edata, &mesh->edata, CD_MASK_MESH.emask, CD_DUPLICATE, geometry->totedge);
CustomData_copy(
&geometry->ldata, &mesh->ldata, CD_MASK_MESH.lmask, CD_DUPLICATE, geometry->totloop);
CustomData_copy(
&geometry->pdata, &mesh->pdata, CD_MASK_MESH.pmask, CD_DUPLICATE, geometry->totpoly);
CustomData_copy(&geometry->vdata, &mesh->vdata, CD_MASK_MESH.vmask, geometry->totvert);
CustomData_copy(&geometry->edata, &mesh->edata, CD_MASK_MESH.emask, geometry->totedge);
CustomData_copy(&geometry->ldata, &mesh->ldata, CD_MASK_MESH.lmask, geometry->totloop);
CustomData_copy(&geometry->pdata, &mesh->pdata, CD_MASK_MESH.pmask, geometry->totpoly);
BKE_mesh_runtime_clear_cache(mesh);
}

View File

@ -2,7 +2,6 @@
#include "BLI_array_utils.hh"
#include "BLI_index_mask.hh"
#include "BLI_user_counter.hh"
#include "BKE_attribute.hh"
#include "BKE_attribute_math.hh"
@ -69,7 +68,7 @@ static void add_new_edges(Mesh &mesh,
/* Store a copy of the IDs locally since we will remove the existing attributes which
* can also free the names, since the API does not provide pointer stability. */
Vector<std::string> named_ids;
Vector<UserCounter<const bke::AnonymousAttributeID>> anonymous_ids;
Vector<bke::AutoAnonymousAttributeID> anonymous_ids;
for (const bke::AttributeIDRef &id : attributes.all_ids()) {
if (attributes.lookup_meta_data(id)->domain != ATTR_DOMAIN_EDGE) {
continue;
@ -82,14 +81,14 @@ static void add_new_edges(Mesh &mesh,
}
else {
anonymous_ids.append(&id.anonymous_id());
id.anonymous_id().user_add();
id.anonymous_id().add_user();
}
}
Vector<bke::AttributeIDRef> local_edge_ids;
for (const StringRef name : named_ids) {
local_edge_ids.append(name);
}
for (const UserCounter<const bke::AnonymousAttributeID> &id : anonymous_ids) {
for (const bke::AutoAnonymousAttributeID &id : anonymous_ids) {
local_edge_ids.append(*id);
}
@ -139,7 +138,8 @@ static void add_new_edges(Mesh &mesh,
mesh.edges_for_write().copy_from(new_edges);
if (new_orig_indices != nullptr) {
CustomData_add_layer_with_data(&mesh.edata, CD_ORIGINDEX, new_orig_indices, mesh.totedge);
CustomData_add_layer_with_data(
&mesh.edata, CD_ORIGINDEX, new_orig_indices, mesh.totedge, nullptr);
}
for (NewAttributeData &new_data : dst_attributes) {

View File

@ -225,8 +225,8 @@ struct GatherTasks {
/* Volumes only have very simple support currently. Only the first found volume is put into the
* output. */
UserCounter<const VolumeComponent> first_volume;
UserCounter<const GeometryComponentEditData> first_edit_data;
COWUser<const VolumeComponent> first_volume;
COWUser<const GeometryComponentEditData> first_edit_data;
};
/** Current offsets while during the gather operation. */
@ -607,8 +607,8 @@ static void gather_realize_tasks_recursive(GatherTasksInfo &gather_info,
case GEO_COMPONENT_TYPE_VOLUME: {
const VolumeComponent *volume_component = static_cast<const VolumeComponent *>(component);
if (!gather_info.r_tasks.first_volume) {
volume_component->user_add();
gather_info.r_tasks.first_volume = volume_component;
volume_component->add_user();
gather_info.r_tasks.first_volume = const_cast<VolumeComponent *>(volume_component);
}
break;
}
@ -616,7 +616,7 @@ static void gather_realize_tasks_recursive(GatherTasksInfo &gather_info,
const GeometryComponentEditData *edit_component =
static_cast<const GeometryComponentEditData *>(component);
if (!gather_info.r_tasks.first_edit_data) {
edit_component->user_add();
edit_component->add_user();
gather_info.r_tasks.first_edit_data = edit_component;
}
break;

View File

@ -553,7 +553,7 @@ void MeshImporter::mesh_add_edges(Mesh *mesh, int len)
totedge = mesh->totedge + len;
/* Update custom-data. */
CustomData_copy(&mesh->edata, &edata, CD_MASK_MESH.emask, CD_SET_DEFAULT, totedge);
CustomData_copy_new(&mesh->edata, &edata, CD_MASK_MESH.emask, CD_SET_DEFAULT, totedge);
CustomData_copy_data(&mesh->edata, &edata, 0, 0, mesh->totedge);
if (!CustomData_has_layer(&edata, CD_MEDGE)) {

View File

@ -25,6 +25,8 @@ using AnonymousAttributeIDHandle = blender::bke::AnonymousAttributeID;
typedef struct AnonymousAttributeIDHandle AnonymousAttributeIDHandle;
#endif
struct bCopyOnWrite;
/** Descriptor and storage for a custom data layer. */
typedef struct CustomDataLayer {
/** Type of data in layer. */
@ -53,6 +55,11 @@ typedef struct CustomDataLayer {
* attribute was created.
*/
const AnonymousAttributeIDHandle *anonymous_id;
/**
* Run-time data that allows sharing `data` with other entities (mostly custom data layers on
* other geometries).
*/
const struct bCopyOnWrite *cow;
} CustomDataLayer;
#define MAX_CUSTOMDATA_LAYER_NAME 68
@ -243,8 +250,7 @@ typedef struct CustomData_MeshMasks {
enum {
/* Indicates layer should not be copied by CustomData_from_template or CustomData_copy_data */
CD_FLAG_NOCOPY = (1 << 0),
/* Indicates layer should not be freed (for layers backed by external data) */
CD_FLAG_NOFREE = (1 << 1),
CD_FLAG_UNUSED = (1 << 1),
/* Indicates the layer is only temporary, also implies no copy */
CD_FLAG_TEMPORARY = ((1 << 2) | CD_FLAG_NOCOPY),
/* Indicates the layer is stored in an external file */

View File

@ -2010,33 +2010,20 @@ static void wm_autosave_location(char filepath[FILE_MAX])
BLI_path_join(filepath, FILE_MAX, tempdir_base, filename);
}
static void wm_autosave_write(Main *bmain, wmWindowManager *wm)
static void wm_autosave_write(Main *bmain)
{
char filepath[FILE_MAX];
wm_autosave_location(filepath);
/* Fast save of last undo-buffer, now with UI. */
const bool use_memfile = (U.uiflag & USER_GLOBALUNDO) != 0;
MemFile *memfile = use_memfile ? ED_undosys_stack_memfile_get_active(wm->undo_stack) : nullptr;
if (memfile != nullptr) {
BLO_memfile_write_file(memfile, filepath);
}
else {
if (use_memfile) {
/* This is very unlikely, alert developers of this unexpected case. */
CLOG_WARN(&LOG, "undo-data not found for writing, fallback to regular file write!");
}
/* Save as regular blend file with recovery information. */
const int fileflags = (G.fileflags & ~G_FILE_COMPRESS) | G_FILE_RECOVER_WRITE;
/* Save as regular blend file with recovery information. */
const int fileflags = (G.fileflags & ~G_FILE_COMPRESS) | G_FILE_RECOVER_WRITE;
ED_editors_flush_edits(bmain);
ED_editors_flush_edits(bmain);
/* Error reporting into console. */
BlendFileWriteParams params{};
BLO_write_file(bmain, filepath, fileflags, &params, nullptr);
}
/* Error reporting into console. */
BlendFileWriteParams params{};
BLO_write_file(bmain, filepath, fileflags, &params, nullptr);
}
static void wm_autosave_timer_begin_ex(wmWindowManager *wm, double timestep)
@ -2084,7 +2071,7 @@ void wm_autosave_timer(Main *bmain, wmWindowManager *wm, wmTimer * /*wt*/)
}
}
wm_autosave_write(bmain, wm);
wm_autosave_write(bmain);
/* Restart the timer after file write, just in case file write takes a long time. */
wm_autosave_timer_begin(wm);

View File

@ -439,27 +439,16 @@ void WM_exit_ex(bContext *C, const bool do_python)
/* NOTE: same code copied in `wm_files.cc`. */
if (C && wm) {
if (!G.background) {
struct MemFile *undo_memfile = wm->undo_stack ?
ED_undosys_stack_memfile_get_active(wm->undo_stack) :
nullptr;
if (undo_memfile != nullptr) {
/* save the undo state as quit.blend */
Main *bmain = CTX_data_main(C);
char filepath[FILE_MAX];
bool has_edited;
const int fileflags = G.fileflags & ~G_FILE_COMPRESS;
/* Save latest state before closing as quit.blend. */
char filepath[FILE_MAX];
const int fileflags = G.fileflags & ~G_FILE_COMPRESS;
BLI_path_join(filepath, sizeof(filepath), BKE_tempdir_base(), BLENDER_QUIT_FILE);
BLI_path_join(filepath, sizeof(filepath), BKE_tempdir_base(), BLENDER_QUIT_FILE);
has_edited = ED_editors_flush_edits(bmain);
BlendFileWriteParams blend_file_write_params{};
if ((has_edited &&
BLO_write_file(bmain, filepath, fileflags, &blend_file_write_params, nullptr)) ||
BLO_memfile_write_file(undo_memfile, filepath)) {
printf("Saved session recovery to \"%s\"\n", filepath);
}
}
Main *bmain = CTX_data_main(C);
ED_editors_flush_edits(bmain);
BlendFileWriteParams blend_file_write_params{};
BLO_write_file(bmain, filepath, fileflags, &blend_file_write_params, nullptr);
printf("Saved session recovery to \"%s\"\n", filepath);
}
WM_jobs_kill_all(wm);

View File

@ -51,12 +51,6 @@
# include "creator_intern.h" /* own include */
// #define USE_WRITE_CRASH_BLEND
# ifdef USE_WRITE_CRASH_BLEND
# include "BKE_undo_system.h"
# include "BLO_undofile.h"
# endif
/* set breakpoints here when running in debug mode, useful to catch floating point errors */
# if defined(__linux__) || defined(_WIN32) || defined(OSX_SSE_FPE)
static void sig_handle_fpe(int UNUSED(sig))
@ -94,28 +88,6 @@ static void sig_handle_crash(int signum)
wmWindowManager *wm = G_MAIN ? G_MAIN->wm.first : NULL;
# ifdef USE_WRITE_CRASH_BLEND
if (wm && wm->undo_stack) {
struct MemFile *memfile = BKE_undosys_stack_memfile_get_active(wm->undo_stack);
if (memfile) {
char fname[FILE_MAX];
if (!(G_MAIN && G_MAIN->filepath[0])) {
BLI_path_join(fname, sizeof(fname), BKE_tempdir_base(), "crash.blend");
}
else {
STRNCPY(fname, G_MAIN->filepath);
BLI_path_extension_replace(fname, sizeof(fname), ".crash.blend");
}
printf("Writing: %s\n", fname);
fflush(stdout);
BLO_memfile_write_file(memfile, fname);
}
}
# endif
FILE *fp;
char header[512];