Compare commits
4 Commits
temp-fix-n
...
temp-copy-
Author | SHA1 | Date | |
---|---|---|---|
3f3adef4a0 | |||
c5b55a84f5 | |||
1713a780c5 | |||
e0ac932156 |
@@ -23,13 +23,13 @@
|
|||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
|
#include "BLI_copy_on_write.h"
|
||||||
#include "BLI_float3.hh"
|
#include "BLI_float3.hh"
|
||||||
#include "BLI_float4x4.hh"
|
#include "BLI_float4x4.hh"
|
||||||
#include "BLI_function_ref.hh"
|
#include "BLI_function_ref.hh"
|
||||||
#include "BLI_hash.hh"
|
#include "BLI_hash.hh"
|
||||||
#include "BLI_map.hh"
|
#include "BLI_map.hh"
|
||||||
#include "BLI_set.hh"
|
#include "BLI_set.hh"
|
||||||
#include "BLI_user_counter.hh"
|
|
||||||
#include "BLI_vector_set.hh"
|
#include "BLI_vector_set.hh"
|
||||||
|
|
||||||
#include "BKE_anonymous_attribute.hh"
|
#include "BKE_anonymous_attribute.hh"
|
||||||
@@ -63,14 +63,13 @@ class GeometryComponent;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* This is the base class for specialized geometry component types. A geometry component handles
|
* 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
|
* a user count to allow avoiding duplication when it is wrapped with #bCopyOnWrite. It also
|
||||||
* the attribute API, which generalizes storing and modifying generic information on a geometry.
|
* handles the attribute API, which generalizes storing and modifying generic information on a
|
||||||
|
* geometry.
|
||||||
*/
|
*/
|
||||||
class GeometryComponent {
|
class GeometryComponent {
|
||||||
private:
|
private:
|
||||||
/* The reference count has two purposes. When it becomes zero, the component is freed. When it is
|
blender::bCopyOnWrite cow_;
|
||||||
* larger than one, the component becomes immutable. */
|
|
||||||
mutable std::atomic<int> users_ = 1;
|
|
||||||
GeometryComponentType type_;
|
GeometryComponentType type_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@@ -81,14 +80,22 @@ class GeometryComponent {
|
|||||||
/* The returned component should be of the same type as the type this is called on. */
|
/* The returned component should be of the same type as the type this is called on. */
|
||||||
virtual GeometryComponent *copy() const = 0;
|
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.
|
/* 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
|
* 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. */
|
* evaluation ends. Instances can only be valid as long as the data they instance is valid. */
|
||||||
virtual bool owns_direct_data() const = 0;
|
virtual bool owns_direct_data() const = 0;
|
||||||
virtual void ensure_owns_direct_data() = 0;
|
virtual void ensure_owns_direct_data() = 0;
|
||||||
|
|
||||||
void user_add() const;
|
|
||||||
void user_remove() const;
|
|
||||||
bool is_mutable() const;
|
bool is_mutable() const;
|
||||||
|
|
||||||
GeometryComponentType type() const;
|
GeometryComponentType type() const;
|
||||||
@@ -310,7 +317,7 @@ inline constexpr bool is_geometry_component_v = std::is_base_of_v<GeometryCompon
|
|||||||
*/
|
*/
|
||||||
struct GeometrySet {
|
struct GeometrySet {
|
||||||
private:
|
private:
|
||||||
using GeometryComponentPtr = blender::UserCounter<class GeometryComponent>;
|
using GeometryComponentPtr = blender::COWUser<class GeometryComponent>;
|
||||||
/* Indexed by #GeometryComponentType. */
|
/* Indexed by #GeometryComponentType. */
|
||||||
std::array<GeometryComponentPtr, GEO_COMPONENT_TYPE_ENUM_SIZE> components_;
|
std::array<GeometryComponentPtr, GEO_COMPONENT_TYPE_ENUM_SIZE> components_;
|
||||||
|
|
||||||
|
@@ -69,24 +69,9 @@ GeometryComponent *GeometryComponent::create(GeometryComponentType component_typ
|
|||||||
return nullptr;
|
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
|
bool GeometryComponent::is_mutable() const
|
||||||
{
|
{
|
||||||
/* If the item is shared, it is read-only. */
|
return cow_.is_mutable();
|
||||||
/* The user count can be 0, when this is called from the destructor. */
|
|
||||||
return users_ <= 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
GeometryComponentType GeometryComponent::type() const
|
GeometryComponentType GeometryComponent::type() const
|
||||||
@@ -168,7 +153,7 @@ void GeometrySet::keep_only(const blender::Span<GeometryComponentType> component
|
|||||||
void GeometrySet::add(const GeometryComponent &component)
|
void GeometrySet::add(const GeometryComponent &component)
|
||||||
{
|
{
|
||||||
BLI_assert(!components_[component.type()]);
|
BLI_assert(!components_[component.type()]);
|
||||||
component.user_add();
|
component.cow().user_add();
|
||||||
components_[component.type()] = const_cast<GeometryComponent *>(&component);
|
components_[component.type()] = const_cast<GeometryComponent *>(&component);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -20,41 +20,95 @@
|
|||||||
* \ingroup bli
|
* \ingroup bli
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <atomic>
|
#include "BLI_assert.h"
|
||||||
|
#include "BLI_compiler_attrs.h"
|
||||||
|
#include "BLI_utility_mixins.hh"
|
||||||
|
|
||||||
|
#include "DNA_copy_on_write.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
bCopyOnWrite *BLI_cow_new(int user_count);
|
||||||
|
void BLI_cow_free(const bCopyOnWrite *cow);
|
||||||
|
|
||||||
|
void BLI_cow_init(const bCopyOnWrite *cow);
|
||||||
|
|
||||||
|
bool BLI_cow_is_shared(const bCopyOnWrite *cow);
|
||||||
|
bool BLI_cow_is_mutable(const bCopyOnWrite *cow);
|
||||||
|
|
||||||
|
void BLI_cow_user_add(const bCopyOnWrite *cow);
|
||||||
|
bool BLI_cow_user_remove(const bCopyOnWrite *cow) ATTR_WARN_UNUSED_RESULT;
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
|
||||||
namespace blender {
|
namespace blender {
|
||||||
|
|
||||||
/**
|
class bCopyOnWrite : public ::bCopyOnWrite, private NonCopyable, NonMovable {
|
||||||
* A simple automatic reference counter. It is similar to std::shared_ptr, but expects that the
|
public:
|
||||||
* reference count is inside the object.
|
bCopyOnWrite()
|
||||||
*/
|
{
|
||||||
template<typename T> class UserCounter {
|
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<typename T> class COWUser {
|
||||||
private:
|
private:
|
||||||
T *data_ = nullptr;
|
T *data_ = nullptr;
|
||||||
|
|
||||||
public:
|
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->user_add(data_);
|
||||||
}
|
}
|
||||||
|
|
||||||
UserCounter(UserCounter &&other) : data_(other.data_)
|
COWUser(COWUser &&other) : data_(other.data_)
|
||||||
{
|
{
|
||||||
other.data_ = nullptr;
|
other.data_ = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
~UserCounter()
|
~COWUser()
|
||||||
{
|
{
|
||||||
this->user_remove(data_);
|
this->user_remove(data_);
|
||||||
}
|
}
|
||||||
|
|
||||||
UserCounter &operator=(const UserCounter &other)
|
COWUser &operator=(const COWUser &other)
|
||||||
{
|
{
|
||||||
if (this == &other) {
|
if (this == &other) {
|
||||||
return *this;
|
return *this;
|
||||||
@@ -66,7 +120,7 @@ template<typename T> class UserCounter {
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
UserCounter &operator=(UserCounter &&other)
|
COWUser &operator=(COWUser &&other)
|
||||||
{
|
{
|
||||||
if (this == &other) {
|
if (this == &other) {
|
||||||
return *this;
|
return *this;
|
||||||
@@ -140,31 +194,29 @@ template<typename T> class UserCounter {
|
|||||||
return get_default_hash(data_);
|
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_;
|
return a.data_ == b.data_;
|
||||||
}
|
}
|
||||||
|
|
||||||
friend std::ostream &operator<<(std::ostream &stream, const UserCounter &value)
|
|
||||||
{
|
|
||||||
stream << value.data_;
|
|
||||||
return stream;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static void user_add(T *data)
|
static void user_add(T *data)
|
||||||
{
|
{
|
||||||
if (data != nullptr) {
|
if (data != nullptr) {
|
||||||
data->user_add();
|
data->cow().user_add();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void user_remove(T *data)
|
static void user_remove(T *data)
|
||||||
{
|
{
|
||||||
if (data != nullptr) {
|
if (data != nullptr) {
|
||||||
data->user_remove();
|
if (data->cow().user_remove()) {
|
||||||
|
data->cow_delete_self();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace blender
|
} // namespace blender
|
||||||
|
|
||||||
|
#endif
|
@@ -68,6 +68,7 @@ set(SRC
|
|||||||
intern/boxpack_2d.c
|
intern/boxpack_2d.c
|
||||||
intern/buffer.c
|
intern/buffer.c
|
||||||
intern/convexhull_2d.c
|
intern/convexhull_2d.c
|
||||||
|
intern/copy_on_write.cc
|
||||||
intern/delaunay_2d.cc
|
intern/delaunay_2d.cc
|
||||||
intern/dot_export.cc
|
intern/dot_export.cc
|
||||||
intern/dynlib.c
|
intern/dynlib.c
|
||||||
@@ -187,6 +188,7 @@ set(SRC
|
|||||||
BLI_compiler_typecheck.h
|
BLI_compiler_typecheck.h
|
||||||
BLI_console.h
|
BLI_console.h
|
||||||
BLI_convexhull_2d.h
|
BLI_convexhull_2d.h
|
||||||
|
BLI_copy_on_write.h
|
||||||
BLI_delaunay_2d.h
|
BLI_delaunay_2d.h
|
||||||
BLI_dial_2d.h
|
BLI_dial_2d.h
|
||||||
BLI_disjoint_set.hh
|
BLI_disjoint_set.hh
|
||||||
@@ -310,7 +312,6 @@ set(SRC
|
|||||||
BLI_timecode.h
|
BLI_timecode.h
|
||||||
BLI_timeit.hh
|
BLI_timeit.hh
|
||||||
BLI_timer.h
|
BLI_timer.h
|
||||||
BLI_user_counter.hh
|
|
||||||
BLI_utildefines.h
|
BLI_utildefines.h
|
||||||
BLI_utildefines_iter.h
|
BLI_utildefines_iter.h
|
||||||
BLI_utildefines_stack.h
|
BLI_utildefines_stack.h
|
||||||
|
73
source/blender/blenlib/intern/copy_on_write.cc
Normal file
73
source/blender/blenlib/intern/copy_on_write.cc
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
/*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** \file
|
||||||
|
* \ingroup bli
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "atomic_ops.h"
|
||||||
|
|
||||||
|
#include "BLI_assert.h"
|
||||||
|
#include "BLI_copy_on_write.h"
|
||||||
|
|
||||||
|
#include "MEM_guardedalloc.h"
|
||||||
|
|
||||||
|
static int &get_counter(const bCopyOnWrite *cow)
|
||||||
|
{
|
||||||
|
BLI_assert(cow != nullptr);
|
||||||
|
return *const_cast<int *>(&cow->user_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
bCopyOnWrite *BLI_cow_new(int user_count)
|
||||||
|
{
|
||||||
|
bCopyOnWrite *cow = MEM_cnew<bCopyOnWrite>(__func__);
|
||||||
|
cow->user_count = user_count;
|
||||||
|
return cow;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BLI_cow_free(const bCopyOnWrite *cow)
|
||||||
|
{
|
||||||
|
BLI_assert(cow->user_count == 0);
|
||||||
|
MEM_freeN(const_cast<bCopyOnWrite *>(cow));
|
||||||
|
}
|
||||||
|
|
||||||
|
void BLI_cow_init(const bCopyOnWrite *cow)
|
||||||
|
{
|
||||||
|
get_counter(cow) = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BLI_cow_is_mutable(const bCopyOnWrite *cow)
|
||||||
|
{
|
||||||
|
return !BLI_cow_is_shared(cow);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BLI_cow_is_shared(const bCopyOnWrite *cow)
|
||||||
|
{
|
||||||
|
return cow->user_count >= 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BLI_cow_user_add(const bCopyOnWrite *cow)
|
||||||
|
{
|
||||||
|
atomic_fetch_and_add_int32(&get_counter(cow), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BLI_cow_user_remove(const bCopyOnWrite *cow)
|
||||||
|
{
|
||||||
|
const int new_user_count = atomic_sub_and_fetch_int32(&get_counter(cow), 1);
|
||||||
|
BLI_assert(new_user_count >= 0);
|
||||||
|
const bool has_no_user_anymore = new_user_count == 0;
|
||||||
|
return has_no_user_anymore;
|
||||||
|
}
|
@@ -191,7 +191,7 @@ struct GatherTasks {
|
|||||||
|
|
||||||
/* Volumes only have very simple support currently. Only the first found volume is put into the
|
/* Volumes only have very simple support currently. Only the first found volume is put into the
|
||||||
* output. */
|
* output. */
|
||||||
UserCounter<VolumeComponent> first_volume;
|
COWUser<VolumeComponent> first_volume;
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Current offsets while during the gather operation. */
|
/** 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: {
|
case GEO_COMPONENT_TYPE_VOLUME: {
|
||||||
const VolumeComponent *volume_component = static_cast<const VolumeComponent *>(component);
|
const VolumeComponent *volume_component = static_cast<const VolumeComponent *>(component);
|
||||||
if (!gather_info.r_tasks.first_volume) {
|
if (!gather_info.r_tasks.first_volume) {
|
||||||
volume_component->user_add();
|
volume_component->cow().user_add();
|
||||||
gather_info.r_tasks.first_volume = const_cast<VolumeComponent *>(volume_component);
|
gather_info.r_tasks.first_volume = const_cast<VolumeComponent *>(volume_component);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
25
source/blender/makesdna/DNA_copy_on_write.h
Normal file
25
source/blender/makesdna/DNA_copy_on_write.h
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
/*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** \file
|
||||||
|
* \ingroup DNA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
typedef struct bCopyOnWrite {
|
||||||
|
int user_count;
|
||||||
|
} bCopyOnWrite;
|
@@ -141,6 +141,7 @@ static const char *includefiles[] = {
|
|||||||
"DNA_pointcache_types.h",
|
"DNA_pointcache_types.h",
|
||||||
"DNA_uuid_types.h",
|
"DNA_uuid_types.h",
|
||||||
"DNA_asset_types.h",
|
"DNA_asset_types.h",
|
||||||
|
"DNA_copy_on_write.h",
|
||||||
|
|
||||||
/* see comment above before editing! */
|
/* see comment above before editing! */
|
||||||
|
|
||||||
@@ -1624,6 +1625,7 @@ int main(int argc, char **argv)
|
|||||||
#include "DNA_collection_types.h"
|
#include "DNA_collection_types.h"
|
||||||
#include "DNA_color_types.h"
|
#include "DNA_color_types.h"
|
||||||
#include "DNA_constraint_types.h"
|
#include "DNA_constraint_types.h"
|
||||||
|
#include "DNA_copy_on_write.h"
|
||||||
#include "DNA_curve_types.h"
|
#include "DNA_curve_types.h"
|
||||||
#include "DNA_curveprofile_types.h"
|
#include "DNA_curveprofile_types.h"
|
||||||
#include "DNA_customdata_types.h"
|
#include "DNA_customdata_types.h"
|
||||||
|
Reference in New Issue
Block a user