From 1713a780c56b1c2fc1ba3e1b02fc6f860cd2604b Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Mon, 3 Jan 2022 16:55:45 +0100 Subject: progress --- source/blender/blenkernel/BKE_geometry_set.hh | 24 ++- source/blender/blenkernel/intern/geometry_set.cc | 19 +-- source/blender/blenlib/BLI_copy_on_write.h | 181 ++++++++++++++++++++- source/blender/blenlib/intern/copy_on_write.cc | 9 +- .../blender/geometry/intern/realize_instances.cc | 4 +- 5 files changed, 201 insertions(+), 36 deletions(-) (limited to 'source/blender') diff --git a/source/blender/blenkernel/BKE_geometry_set.hh b/source/blender/blenkernel/BKE_geometry_set.hh index 0f9c2c1062b..b3e757a83ba 100644 --- a/source/blender/blenkernel/BKE_geometry_set.hh +++ b/source/blender/blenkernel/BKE_geometry_set.hh @@ -23,6 +23,7 @@ #include #include +#include "BLI_copy_on_write.h" #include "BLI_float3.hh" #include "BLI_float4x4.hh" #include "BLI_function_ref.hh" @@ -63,14 +64,13 @@ 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. + * a user count to allow avoiding duplication when it is wrapped with #bCopyOnWrite. It also + * handles the attribute API, which generalizes storing and modifying generic information on a + * geometry. */ class GeometryComponent { 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 users_ = 1; + blender::bCopyOnWrite cow_; GeometryComponentType type_; public: @@ -81,14 +81,22 @@ class GeometryComponent { /* The returned component should be of the same type as the type this is called on. */ virtual GeometryComponent *copy() const = 0; + const blender::bCopyOnWrite &cow() const + { + return cow_; + } + + void cow_delete_self() const + { + delete this; + } + /* 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; - void user_add() const; - void user_remove() const; bool is_mutable() const; GeometryComponentType type() const; @@ -310,7 +318,7 @@ inline constexpr bool is_geometry_component_v = std::is_base_of_v; + using GeometryComponentPtr = blender::COWUser; /* Indexed by #GeometryComponentType. */ std::array components_; diff --git a/source/blender/blenkernel/intern/geometry_set.cc b/source/blender/blenkernel/intern/geometry_set.cc index 06e0e78745b..5defda3aa63 100644 --- a/source/blender/blenkernel/intern/geometry_set.cc +++ b/source/blender/blenkernel/intern/geometry_set.cc @@ -69,24 +69,9 @@ GeometryComponent *GeometryComponent::create(GeometryComponentType component_typ return nullptr; } -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; + return cow_.is_mutable(); } GeometryComponentType GeometryComponent::type() const @@ -168,7 +153,7 @@ void GeometrySet::keep_only(const blender::Span component void GeometrySet::add(const GeometryComponent &component) { BLI_assert(!components_[component.type()]); - component.user_add(); + component.cow().user_add(); components_[component.type()] = const_cast(&component); } diff --git a/source/blender/blenlib/BLI_copy_on_write.h b/source/blender/blenlib/BLI_copy_on_write.h index 838d4c38b0e..e1dba871207 100644 --- a/source/blender/blenlib/BLI_copy_on_write.h +++ b/source/blender/blenlib/BLI_copy_on_write.h @@ -20,7 +20,9 @@ * \ingroup bli */ +#include "BLI_assert.h" #include "BLI_compiler_attrs.h" +#include "BLI_utility_mixins.hh" #include "DNA_copy_on_write.h" @@ -31,11 +33,10 @@ extern "C" { bCopyOnWrite *BLI_cow_new(int user_count); void BLI_cow_free(const bCopyOnWrite *cow); -void BLI_cow_init(const bCopyOnWrite *cow, int user_count); +void BLI_cow_init(const bCopyOnWrite *cow); bool BLI_cow_is_shared(const bCopyOnWrite *cow); bool BLI_cow_is_mutable(const bCopyOnWrite *cow); -bool BLI_cow_is_zero(const bCopyOnWrite *cow); void BLI_cow_user_add(const bCopyOnWrite *cow); bool BLI_cow_user_remove(const bCopyOnWrite *cow) ATTR_WARN_UNUSED_RESULT; @@ -43,3 +44,179 @@ bool BLI_cow_user_remove(const bCopyOnWrite *cow) ATTR_WARN_UNUSED_RESULT; #ifdef __cplusplus } #endif + +#ifdef __cplusplus + +namespace blender { + +class bCopyOnWrite : public ::bCopyOnWrite, private NonCopyable, NonMovable { + public: + bCopyOnWrite() + { + BLI_cow_init(this); + } + + ~bCopyOnWrite() + { + BLI_assert(this->is_mutable()); + } + + bool is_shared() const + { + return BLI_cow_is_shared(this); + } + + bool is_mutable() const + { + return BLI_cow_is_mutable(this); + } + + void user_add() const + { + BLI_cow_user_add(this); + } + + bool user_remove() const ATTR_WARN_UNUSED_RESULT + { + return BLI_cow_user_remove(this); + } +}; + +template class COWUser { + private: + T *data_ = nullptr; + + public: + COWUser() = default; + + COWUser(T *data) : data_(data) + { + } + + COWUser(const COWUser &other) : data_(other.data_) + { + this->user_add(data_); + } + + COWUser(COWUser &&other) : data_(other.data_) + { + other.data_ = nullptr; + } + + ~COWUser() + { + this->user_remove(data_); + } + + COWUser &operator=(const COWUser &other) + { + if (this == &other) { + return *this; + } + + this->user_remove(data_); + data_ = other.data_; + this->user_add(data_); + return *this; + } + + COWUser &operator=(COWUser &&other) + { + if (this == &other) { + return *this; + } + + this->user_remove(data_); + data_ = other.data_; + other.data_ = nullptr; + return *this; + } + + T *operator->() + { + BLI_assert(data_ != nullptr); + return data_; + } + + const T *operator->() const + { + BLI_assert(data_ != nullptr); + return data_; + } + + T &operator*() + { + BLI_assert(data_ != nullptr); + return *data_; + } + + const T &operator*() const + { + BLI_assert(data_ != nullptr); + return *data_; + } + + operator bool() const + { + return data_ != nullptr; + } + + T *get() + { + return data_; + } + + const T *get() const + { + return data_; + } + + T *release() + { + T *data = data_; + data_ = nullptr; + return data; + } + + void reset() + { + this->user_remove(data_); + data_ = nullptr; + } + + bool has_value() const + { + return data_ != nullptr; + } + + uint64_t hash() const + { + return get_default_hash(data_); + } + + friend bool operator==(const COWUser &a, const COWUser &b) + { + return a.data_ == b.data_; + } + + private: + static void user_add(T *data) + { + if (data != nullptr) { + data->cow().user_add(); + } + } + + static void user_remove(T *data) + { + if (data != nullptr) { + if (data->cow().user_remove()) { + data->cow_delete_self(); + } + } + } +}; + +} // namespace blender + +#endif diff --git a/source/blender/blenlib/intern/copy_on_write.cc b/source/blender/blenlib/intern/copy_on_write.cc index c1fc7a7569b..4479db326b2 100644 --- a/source/blender/blenlib/intern/copy_on_write.cc +++ b/source/blender/blenlib/intern/copy_on_write.cc @@ -44,9 +44,9 @@ void BLI_cow_free(const bCopyOnWrite *cow) MEM_freeN(const_cast(cow)); } -void BLI_cow_init(const bCopyOnWrite *cow, int user_count) +void BLI_cow_init(const bCopyOnWrite *cow) { - get_counter(cow) = user_count; + get_counter(cow) = 1; } bool BLI_cow_is_mutable(const bCopyOnWrite *cow) @@ -59,11 +59,6 @@ bool BLI_cow_is_shared(const bCopyOnWrite *cow) return cow->user_count >= 2; } -bool BLI_cow_is_zero(const bCopyOnWrite *cow) -{ - return cow->user_count == 0; -} - void BLI_cow_user_add(const bCopyOnWrite *cow) { atomic_fetch_and_add_int32(&get_counter(cow), 1); diff --git a/source/blender/geometry/intern/realize_instances.cc b/source/blender/geometry/intern/realize_instances.cc index 4022794d53f..16f0db3f430 100644 --- a/source/blender/geometry/intern/realize_instances.cc +++ b/source/blender/geometry/intern/realize_instances.cc @@ -191,7 +191,7 @@ struct GatherTasks { /* Volumes only have very simple support currently. Only the first found volume is put into the * output. */ - UserCounter first_volume; + COWUser first_volume; }; /** Current offsets while during the gather operation. */ @@ -480,7 +480,7 @@ static void gather_realize_tasks_recursive(GatherTasksInfo &gather_info, case GEO_COMPONENT_TYPE_VOLUME: { const VolumeComponent *volume_component = static_cast(component); if (!gather_info.r_tasks.first_volume) { - volume_component->user_add(); + volume_component->cow().user_add(); gather_info.r_tasks.first_volume = const_cast(volume_component); } break; -- cgit v1.2.3