WIP: use generic copy-on-write system to avoid unnecessary data copies #104470

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

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
9 changed files with 35 additions and 76 deletions
Showing only changes of commit ff6f1a0a70 - Show all commits

View File

@ -10,24 +10,6 @@
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
@ -50,10 +32,7 @@ class AnonymousAttributeIDCOW : public bCopyOnWrite {
* 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:
AnonymousAttributeIDCOW cow_;
class AnonymousAttributeID : public bCopyOnWriteMixin<AnonymousAttributeID> {
protected:
std::string name_;
@ -68,12 +47,7 @@ class AnonymousAttributeID {
virtual std::string user_name() const;
const bCopyOnWrite &cow() const
{
return cow_;
}
void cow_delete_self() const
void delete_self() const
{
MEM_delete(this);
}

View File

@ -38,30 +38,14 @@ class CurvesEditHints;
class Instances;
} // namespace blender::bke
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
* the attribute API, which generalizes storing and modifying generic information on a geometry.
*/
class GeometryComponent {
class GeometryComponent : public bCopyOnWriteMixin<GeometryComponent> {
private:
GeometryComponentType type_;
GeometryComponentCOW cow_;
public:
GeometryComponent(GeometryComponentType type);
@ -80,22 +64,20 @@ class GeometryComponent {
/* The returned component should be of the same type as the type this is called on. */
virtual GeometryComponent *copy() const = 0;
const bCopyOnWrite &cow() const
{
return cow_;
}
/* Direct data is everything except for instances of objects/collections.
* If this returns true, the geometry set can be cached and is still valid after e.g. modifier
* evaluation ends. Instances can only be valid as long as the data they instance is valid. */
virtual bool owns_direct_data() const = 0;
virtual void ensure_owns_direct_data() = 0;
bool is_mutable() const;
GeometryComponentType type() const;
virtual bool is_empty() const;
void delete_self()
{
delete this;
}
};
template<typename T>

View File

@ -4,9 +4,7 @@
namespace blender::bke {
AnonymousAttributeID::AnonymousAttributeID() : cow_(this)
{
}
AnonymousAttributeID::AnonymousAttributeID() = default;
std::string AnonymousAttributeID::user_name() const
{

View File

@ -2297,7 +2297,7 @@ static bool customdata_merge_internal(const CustomData *source,
if (src_layer.anonymous_id != nullptr) {
new_layer->anonymous_id = src_layer.anonymous_id;
new_layer->anonymous_id->cow().add_user();
new_layer->anonymous_id->add_user();
}
}
@ -2473,7 +2473,7 @@ void CustomData_copy_without_data(const struct CustomData *source,
static void customData_free_layer__internal(CustomDataLayer *layer, const int totelem)
{
if (layer->anonymous_id != nullptr) {
layer->anonymous_id->cow().remove_user_and_delete_if_last();
layer->anonymous_id->remove_user_and_delete_if_last();
layer->anonymous_id = nullptr;
}
const eCustomDataType type = eCustomDataType(layer->type);
@ -2998,7 +2998,7 @@ void *CustomData_add_layer_anonymous(CustomData *data,
return nullptr;
}
anonymous_id->cow().add_user();
anonymous_id->add_user();
layer->anonymous_id = anonymous_id;
return layer->data;
}
@ -3019,7 +3019,7 @@ const void *CustomData_add_layer_anonymous_with_existing_data(
if (layer == nullptr) {
return nullptr;
}
anonymous_id->cow().add_user();
anonymous_id->add_user();
layer->anonymous_id = anonymous_id;
return layer->data;
}

View File

@ -35,16 +35,11 @@ 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_(this)
GeometryComponent::GeometryComponent(GeometryComponentType type) : type_(type)
{
}
@ -89,11 +84,6 @@ std::optional<blender::bke::MutableAttributeAccessor> GeometryComponent::attribu
return std::nullopt;
}
bool GeometryComponent::is_mutable() const
{
return cow_.is_mutable();
}
GeometryComponentType GeometryComponent::type() const
{
return type_;
@ -188,7 +178,7 @@ void GeometrySet::remove_geometry_during_modify()
void GeometrySet::add(const GeometryComponent &component)
{
BLI_assert(!components_[component.type()]);
component.cow().add_user();
component.add_user();
components_[component.type()] = const_cast<GeometryComponent *>(&component);
}

View File

@ -55,3 +55,18 @@ struct bCopyOnWrite : blender::NonCopyable, blender::NonMovable {
private:
virtual void delete_self_with_data() = 0;
};
template<typename T> struct bCopyOnWriteMixin : public bCopyOnWrite {
public:
bCopyOnWriteMixin() : bCopyOnWrite(1)
{
}
private:
void delete_self_with_data() override
{
static_assert(std::is_base_of_v<bCopyOnWriteMixin<T>, T>);
T *data = static_cast<T *>(this);
data->delete_self();
}
};

View File

@ -131,14 +131,14 @@ template<typename T> class COWUser {
static void add_user(T *data)
{
if (data != nullptr) {
data->cow().add_user();
data->add_user();
}
}
static void remove_user_and_delete_if_last(T *data)
{
if (data != nullptr) {
data->cow().remove_user_and_delete_if_last();
data->remove_user_and_delete_if_last();
}
}
};

View File

@ -82,7 +82,7 @@ static void add_new_edges(Mesh &mesh,
}
else {
anonymous_ids.append(&id.anonymous_id());
id.anonymous_id().cow().add_user();
id.anonymous_id().add_user();
}
}
Vector<bke::AttributeIDRef> local_edge_ids;

View File

@ -598,7 +598,7 @@ 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->cow().add_user();
volume_component->add_user();
gather_info.r_tasks.first_volume = const_cast<VolumeComponent *>(volume_component);
}
break;
@ -607,7 +607,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->cow().add_user();
edit_component->add_user();
gather_info.r_tasks.first_edit_data = edit_component;
}
break;