Core: new blenlib library for implicit-sharing #105994
|
@ -4,9 +4,9 @@
|
||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
|
|
||||||
|
#include "BLI_copy_on_write_user.hh"
|
||||||
#include "BLI_set.hh"
|
#include "BLI_set.hh"
|
||||||
#include "BLI_string_ref.hh"
|
#include "BLI_string_ref.hh"
|
||||||
#include "BLI_user_counter.hh"
|
|
||||||
|
|
||||||
namespace blender::bke {
|
namespace blender::bke {
|
||||||
|
|
||||||
|
@ -32,10 +32,7 @@ namespace blender::bke {
|
||||||
* because that is not available in C code. If possible, the #AutoAnonymousAttributeID wrapper
|
* because that is not available in C code. If possible, the #AutoAnonymousAttributeID wrapper
|
||||||
* should be used to avoid manual reference counting in C++ code.
|
* should be used to avoid manual reference counting in C++ code.
|
||||||
*/
|
*/
|
||||||
class AnonymousAttributeID {
|
class AnonymousAttributeID : public bCopyOnWriteMixin {
|
||||||
private:
|
|
||||||
mutable std::atomic<int> users_ = 1;
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
std::string name_;
|
std::string name_;
|
||||||
|
|
||||||
|
@ -49,22 +46,15 @@ class AnonymousAttributeID {
|
||||||
|
|
||||||
virtual std::string user_name() const;
|
virtual std::string user_name() const;
|
||||||
|
|
||||||
void user_add() const
|
private:
|
||||||
|
void delete_self() override
|
||||||
{
|
{
|
||||||
users_.fetch_add(1);
|
MEM_delete(this);
|
||||||
}
|
|
||||||
|
|
||||||
void user_remove() const
|
|
||||||
{
|
|
||||||
const int new_users = users_.fetch_sub(1) - 1;
|
|
||||||
if (new_users == 0) {
|
|
||||||
MEM_delete(this);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Wrapper for #AnonymousAttributeID that avoids manual reference counting. */
|
/** 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.
|
* A set of anonymous attribute names that is passed around in geometry nodes.
|
||||||
|
|
|
@ -12,8 +12,6 @@
|
||||||
|
|
||||||
#include "BLI_function_ref.hh"
|
#include "BLI_function_ref.hh"
|
||||||
#include "BLI_map.hh"
|
#include "BLI_map.hh"
|
||||||
#include "BLI_math_vector_types.hh"
|
|
||||||
#include "BLI_user_counter.hh"
|
|
||||||
#include "BLI_vector_set.hh"
|
#include "BLI_vector_set.hh"
|
||||||
|
|
||||||
#include "BKE_attribute.hh"
|
#include "BKE_attribute.hh"
|
||||||
|
@ -40,18 +38,13 @@ class CurvesEditHints;
|
||||||
class Instances;
|
class Instances;
|
||||||
} // namespace blender::bke
|
} // namespace blender::bke
|
||||||
|
|
||||||
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 uses
|
||||||
* a user count to allow avoiding duplication when it is wrapped with #UserCounter. It also handles
|
* copy-on-write behavior to avoid read-only copies. It also integrates with attribute API, which
|
||||||
* the attribute API, which generalizes storing and modifying generic information on a geometry.
|
* generalizes storing and modifying generic information on a geometry.
|
||||||
*/
|
*/
|
||||||
class GeometryComponent {
|
class GeometryComponent : public bCopyOnWriteMixin {
|
||||||
private:
|
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_;
|
GeometryComponentType type_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@ -77,13 +70,12 @@ class GeometryComponent {
|
||||||
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;
|
|
||||||
|
|
||||||
GeometryComponentType type() const;
|
GeometryComponentType type() const;
|
||||||
|
|
||||||
virtual bool is_empty() const;
|
virtual bool is_empty() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void delete_self() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
|
@ -109,7 +101,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_;
|
||||||
|
|
||||||
|
|
|
@ -2273,7 +2273,7 @@ bool CustomData_merge(const CustomData *source,
|
||||||
layer->anonymous_id = nullptr;
|
layer->anonymous_id = nullptr;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
layer->anonymous_id->user_add();
|
layer->anonymous_id->add_user();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (alloctype == CD_ASSIGN) {
|
if (alloctype == CD_ASSIGN) {
|
||||||
|
@ -2365,7 +2365,7 @@ static void customData_free_layer__internal(CustomDataLayer *layer, const int to
|
||||||
const LayerTypeInfo *typeInfo;
|
const LayerTypeInfo *typeInfo;
|
||||||
|
|
||||||
if (layer->anonymous_id != nullptr) {
|
if (layer->anonymous_id != nullptr) {
|
||||||
layer->anonymous_id->user_remove();
|
layer->anonymous_id->remove_user_and_delete_if_last();
|
||||||
layer->anonymous_id = nullptr;
|
layer->anonymous_id = nullptr;
|
||||||
}
|
}
|
||||||
if (!(layer->flag & CD_FLAG_NOFREE) && layer->data) {
|
if (!(layer->flag & CD_FLAG_NOFREE) && layer->data) {
|
||||||
|
@ -2924,7 +2924,7 @@ void *CustomData_add_layer_anonymous(CustomData *data,
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
anonymous_id->user_add();
|
anonymous_id->add_user();
|
||||||
layer->anonymous_id = anonymous_id;
|
layer->anonymous_id = anonymous_id;
|
||||||
return layer->data;
|
return layer->data;
|
||||||
}
|
}
|
||||||
|
|
|
@ -84,26 +84,6 @@ std::optional<blender::bke::MutableAttributeAccessor> GeometryComponent::attribu
|
||||||
return std::nullopt;
|
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
|
GeometryComponentType GeometryComponent::type() const
|
||||||
{
|
{
|
||||||
return type_;
|
return type_;
|
||||||
|
@ -114,6 +94,11 @@ bool GeometryComponent::is_empty() const
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GeometryComponent::delete_self()
|
||||||
|
{
|
||||||
|
delete this;
|
||||||
|
}
|
||||||
|
|
||||||
/** \} */
|
/** \} */
|
||||||
|
|
||||||
/* -------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------- */
|
||||||
|
@ -198,7 +183,7 @@ void GeometrySet::remove_geometry_during_modify()
|
||||||
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.add_user();
|
||||||
components_[component.type()] = const_cast<GeometryComponent *>(&component);
|
components_[component.type()] = const_cast<GeometryComponent *>(&component);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
* 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
|
||||||
|
* 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 {
|
||||||
|
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);
|
||||||
|
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;
|
||||||
|
};
|
|
@ -6,59 +6,55 @@
|
||||||
* \ingroup bli
|
* \ingroup bli
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <atomic>
|
#include "BLI_copy_on_write.hh"
|
||||||
|
|
||||||
namespace blender {
|
namespace blender {
|
||||||
|
|
||||||
/**
|
template<typename T> class COWUser {
|
||||||
* 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 {
|
|
||||||
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->add_user(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->remove_user_and_delete_if_last(data_);
|
||||||
}
|
}
|
||||||
|
|
||||||
UserCounter &operator=(const UserCounter &other)
|
COWUser &operator=(const COWUser &other)
|
||||||
{
|
{
|
||||||
if (this == &other) {
|
if (this == &other) {
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
this->user_remove(data_);
|
this->remove_user_and_delete_if_last(data_);
|
||||||
data_ = other.data_;
|
data_ = other.data_;
|
||||||
this->user_add(data_);
|
this->add_user(data_);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
UserCounter &operator=(UserCounter &&other)
|
COWUser &operator=(COWUser &&other)
|
||||||
{
|
{
|
||||||
if (this == &other) {
|
if (this == &other) {
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
this->user_remove(data_);
|
this->remove_user_and_delete_if_last(data_);
|
||||||
data_ = other.data_;
|
data_ = other.data_;
|
||||||
other.data_ = nullptr;
|
other.data_ = nullptr;
|
||||||
return *this;
|
return *this;
|
||||||
|
@ -112,7 +108,7 @@ template<typename T> class UserCounter {
|
||||||
|
|
||||||
void reset()
|
void reset()
|
||||||
{
|
{
|
||||||
this->user_remove(data_);
|
this->remove_user_and_delete_if_last(data_);
|
||||||
data_ = nullptr;
|
data_ = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -126,29 +122,23 @@ 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 add_user(T *data)
|
||||||
{
|
{
|
||||||
if (data != nullptr) {
|
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) {
|
if (data != nullptr) {
|
||||||
data->user_remove();
|
data->remove_user_and_delete_if_last();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
|
@ -198,6 +198,8 @@ set(SRC
|
||||||
BLI_compute_context.hh
|
BLI_compute_context.hh
|
||||||
BLI_console.h
|
BLI_console.h
|
||||||
BLI_convexhull_2d.h
|
BLI_convexhull_2d.h
|
||||||
|
BLI_copy_on_write.hh
|
||||||
|
BLI_copy_on_write_user.hh
|
||||||
BLI_cpp_type.hh
|
BLI_cpp_type.hh
|
||||||
BLI_cpp_type_make.hh
|
BLI_cpp_type_make.hh
|
||||||
BLI_cpp_types.hh
|
BLI_cpp_types.hh
|
||||||
|
@ -351,7 +353,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
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
|
|
||||||
#include "BLI_array_utils.hh"
|
#include "BLI_array_utils.hh"
|
||||||
#include "BLI_index_mask.hh"
|
#include "BLI_index_mask.hh"
|
||||||
#include "BLI_user_counter.hh"
|
|
||||||
|
|
||||||
#include "BKE_attribute.hh"
|
#include "BKE_attribute.hh"
|
||||||
#include "BKE_attribute_math.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
|
/* 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. */
|
* can also free the names, since the API does not provide pointer stability. */
|
||||||
Vector<std::string> named_ids;
|
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()) {
|
for (const bke::AttributeIDRef &id : attributes.all_ids()) {
|
||||||
if (attributes.lookup_meta_data(id)->domain != ATTR_DOMAIN_EDGE) {
|
if (attributes.lookup_meta_data(id)->domain != ATTR_DOMAIN_EDGE) {
|
||||||
continue;
|
continue;
|
||||||
|
@ -82,14 +81,14 @@ static void add_new_edges(Mesh &mesh,
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
anonymous_ids.append(&id.anonymous_id());
|
anonymous_ids.append(&id.anonymous_id());
|
||||||
id.anonymous_id().user_add();
|
id.anonymous_id().add_user();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Vector<bke::AttributeIDRef> local_edge_ids;
|
Vector<bke::AttributeIDRef> local_edge_ids;
|
||||||
for (const StringRef name : named_ids) {
|
for (const StringRef name : named_ids) {
|
||||||
local_edge_ids.append(name);
|
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);
|
local_edge_ids.append(*id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -226,8 +226,8 @@ 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<const VolumeComponent> first_volume;
|
COWUser<const VolumeComponent> first_volume;
|
||||||
UserCounter<const GeometryComponentEditData> first_edit_data;
|
COWUser<const GeometryComponentEditData> first_edit_data;
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Current offsets while during the gather operation. */
|
/** Current offsets while during the gather operation. */
|
||||||
|
@ -608,8 +608,8 @@ 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->add_user();
|
||||||
gather_info.r_tasks.first_volume = volume_component;
|
gather_info.r_tasks.first_volume = const_cast<VolumeComponent *>(volume_component);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -617,7 +617,7 @@ static void gather_realize_tasks_recursive(GatherTasksInfo &gather_info,
|
||||||
const GeometryComponentEditData *edit_component =
|
const GeometryComponentEditData *edit_component =
|
||||||
static_cast<const GeometryComponentEditData *>(component);
|
static_cast<const GeometryComponentEditData *>(component);
|
||||||
if (!gather_info.r_tasks.first_edit_data) {
|
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;
|
gather_info.r_tasks.first_edit_data = edit_component;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
Loading…
Reference in New Issue