WIP: use generic copy-on-write system to avoid unnecessary data copies #104470
|
@ -10,6 +10,24 @@
|
|||
|
||||
namespace blender::bke {
|
||||
|
||||
class AnonymousAttributeID;
|
||||
|
||||
class AnonymousAttributeIDCOW : public bCopyOnWrite {
|
||||
private:
|
||||
AnonymousAttributeID *id_;
|
||||
|
||||
public:
|
||||
AnonymousAttributeIDCOW(AnonymousAttributeID *id) : bCopyOnWrite(1), id_(id)
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
void delete_self_with_data() override
|
||||
{
|
||||
MEM_delete(id_);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* An #AnonymousAttributeID contains information about a specific anonymous attribute.
|
||||
* Like normal attributes, anonymous attributes are also identified by their name, so one should
|
||||
|
@ -34,7 +52,7 @@ namespace blender::bke {
|
|||
*/
|
||||
class AnonymousAttributeID {
|
||||
private:
|
||||
bCopyOnWrite cow_;
|
||||
AnonymousAttributeIDCOW cow_;
|
||||
|
||||
protected:
|
||||
std::string name_;
|
||||
|
|
|
@ -41,6 +41,19 @@ class Instances;
|
|||
|
||||
class GeometryComponent;
|
||||
|
||||
class GeometryComponentCOW : public bCopyOnWrite {
|
||||
private:
|
||||
GeometryComponent *component_;
|
||||
|
||||
public:
|
||||
GeometryComponentCOW(GeometryComponent *component) : bCopyOnWrite(1), component_(component)
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
void delete_self_with_data() override;
|
||||
};
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
@ -49,7 +62,7 @@ class GeometryComponent;
|
|||
class GeometryComponent {
|
||||
private:
|
||||
GeometryComponentType type_;
|
||||
bCopyOnWrite cow_;
|
||||
GeometryComponentCOW cow_;
|
||||
|
||||
public:
|
||||
GeometryComponent(GeometryComponentType type);
|
||||
|
|
|
@ -4,8 +4,7 @@
|
|||
|
||||
namespace blender::bke {
|
||||
|
||||
AnonymousAttributeID::AnonymousAttributeID()
|
||||
: cow_(1, this, [this](const bCopyOnWrite * /*cow*/) { MEM_delete(this); })
|
||||
AnonymousAttributeID::AnonymousAttributeID() : cow_(this)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -2361,14 +2361,31 @@ CustomData CustomData_shallow_copy_remove_non_bmesh_attributes(const CustomData
|
|||
return dst;
|
||||
}
|
||||
|
||||
class CustomDataLayerCOW : public bCopyOnWrite {
|
||||
private:
|
||||
const void *data_;
|
||||
int totelem_;
|
||||
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);
|
||||
}
|
||||
};
|
||||
|
||||
static bCopyOnWrite *make_cow_for_array(const eCustomDataType type,
|
||||
const void *data,
|
||||
const int totelem)
|
||||
{
|
||||
return MEM_new<bCopyOnWrite>(__func__, 1, data, [type, totelem](const bCopyOnWrite *cow) {
|
||||
free_layer_data(type, cow->data(), totelem);
|
||||
MEM_delete(cow);
|
||||
});
|
||||
return MEM_new<CustomDataLayerCOW>(__func__, data, totelem, type);
|
||||
}
|
||||
|
||||
static void ensure_layer_data_is_mutable(CustomDataLayer &layer, const int totelem)
|
||||
|
@ -5119,7 +5136,7 @@ void CustomData_blend_write(BlendWriter *writer,
|
|||
|
||||
for (const CustomDataLayer &layer : layers_to_write) {
|
||||
if (BLO_write_is_undo(writer) && layer.cow != nullptr) {
|
||||
BLO_write_cow(writer, layer.cow);
|
||||
BLO_write_cow(writer, layer.data, layer.cow);
|
||||
continue;
|
||||
}
|
||||
switch (layer.type) {
|
||||
|
|
|
@ -35,12 +35,16 @@ using blender::Vector;
|
|||
using blender::bke::InstanceReference;
|
||||
using blender::bke::Instances;
|
||||
|
||||
void GeometryComponentCOW::delete_self_with_data()
|
||||
{
|
||||
delete component_;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Geometry Component
|
||||
* \{ */
|
||||
|
||||
GeometryComponent::GeometryComponent(GeometryComponentType type)
|
||||
: type_(type), cow_(1, this, [this](const bCopyOnWrite * /*cow*/) { delete this; })
|
||||
GeometryComponent::GeometryComponent(GeometryComponentType type) : type_(type), cow_(this)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -30,12 +30,9 @@ struct bCopyOnWrite : blender::NonCopyable, blender::NonMovable {
|
|||
using DeleteFn = std::function<void(const bCopyOnWrite *cow)>;
|
||||
|
||||
mutable std::atomic<int> users_;
|
||||
const void *data_;
|
||||
DeleteFn delete_fn_;
|
||||
|
||||
public:
|
||||
bCopyOnWrite(const int initial_users, const void *data, DeleteFn delete_fn)
|
||||
: users_(initial_users), data_(data), delete_fn_(std::move(delete_fn))
|
||||
bCopyOnWrite(const int initial_users) : users_(initial_users)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -44,11 +41,6 @@ struct bCopyOnWrite : blender::NonCopyable, blender::NonMovable {
|
|||
BLI_assert(this->is_mutable());
|
||||
}
|
||||
|
||||
const void *data() const
|
||||
{
|
||||
return data_;
|
||||
}
|
||||
|
||||
bool is_shared() const
|
||||
{
|
||||
return users_.load(std::memory_order_relaxed) >= 2;
|
||||
|
@ -64,16 +56,18 @@ struct bCopyOnWrite : blender::NonCopyable, blender::NonMovable {
|
|||
users_.fetch_add(1, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
bool user_remove_and_delete_if_last() const
|
||||
void user_remove_and_delete_if_last() const
|
||||
{
|
||||
const int old_user_count = users_.fetch_sub(1, std::memory_order_relaxed);
|
||||
BLI_assert(old_user_count >= 1);
|
||||
const bool was_last_user = old_user_count == 1;
|
||||
if (was_last_user) {
|
||||
delete_fn_(this);
|
||||
const_cast<bCopyOnWrite *>(this)->delete_self_with_data();
|
||||
}
|
||||
return was_last_user;
|
||||
}
|
||||
|
||||
private:
|
||||
virtual void delete_self_with_data() = 0;
|
||||
};
|
||||
|
||||
namespace blender {
|
||||
|
|
|
@ -186,7 +186,7 @@ void BLO_write_string(BlendWriter *writer, const char *data_ptr);
|
|||
* copied. A free-function has to be provided, which is called when the undo step is freed and no
|
||||
* one else references the data anymore.
|
||||
*/
|
||||
void BLO_write_cow(BlendWriter *writer, const bCopyOnWrite *cow);
|
||||
void BLO_write_cow(BlendWriter *writer, const void *data_ptr, const bCopyOnWrite *cow);
|
||||
#endif
|
||||
|
||||
/**
|
||||
|
|
|
@ -1687,14 +1687,17 @@ bool BLO_write_is_undo(BlendWriter *writer)
|
|||
return writer->wd->use_memfile;
|
||||
}
|
||||
|
||||
void BLO_write_cow(BlendWriter *writer, const bCopyOnWrite *cow)
|
||||
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) {
|
||||
memfile.cow_storage = MEM_new<MemFileCowStorage>(__func__);
|
||||
}
|
||||
if (memfile.cow_storage->map.add(cow->data(), cow)) {
|
||||
if (memfile.cow_storage->map.add(data_ptr, cow)) {
|
||||
cow->user_add();
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue