Avoid copying the positions array into the evaluated edit hints array that's used to support editing with deformed positions when there is a topology-changing procedural operation. In a simple test in sculpt mode with 706k curve points, memory usage went from 78 to 70 MB. This adds more duplication would be ideal, mainly because retrieving the data with write access and making implicit sharing info for arbitrary arrays aren't abstracted by implicit sharing utilities. It may be possible to improve both of those aspects, either now or in the future. Pull Request: https://projects.blender.org/blender/blender/pulls/120146
199 lines
4.2 KiB
C++
199 lines
4.2 KiB
C++
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
|
|
#pragma once
|
|
|
|
/** \file
|
|
* \ingroup bli
|
|
*/
|
|
|
|
#include <memory>
|
|
#include <utility>
|
|
|
|
#include "BLI_implicit_sharing.hh"
|
|
#include "BLI_struct_equality_utils.hh"
|
|
|
|
namespace blender {
|
|
|
|
/**
|
|
* #ImplicitSharingPtr is a smart pointer that manages implicit sharing. It's designed to work with
|
|
* types that derive from #ImplicitSharingMixin. It is fairly similar to #std::shared_ptr but
|
|
* requires the reference count to be embedded in the data.
|
|
*/
|
|
template<typename T> class ImplicitSharingPtr {
|
|
private:
|
|
const T *data_ = nullptr;
|
|
|
|
public:
|
|
ImplicitSharingPtr() = default;
|
|
|
|
explicit ImplicitSharingPtr(const T *data) : data_(data) {}
|
|
|
|
/* Implicit conversion from nullptr. */
|
|
ImplicitSharingPtr(std::nullptr_t) : data_(nullptr) {}
|
|
|
|
ImplicitSharingPtr(const ImplicitSharingPtr &other) : data_(other.data_)
|
|
{
|
|
this->add_user(data_);
|
|
}
|
|
|
|
ImplicitSharingPtr(ImplicitSharingPtr &&other) : data_(other.data_)
|
|
{
|
|
other.data_ = nullptr;
|
|
}
|
|
|
|
~ImplicitSharingPtr()
|
|
{
|
|
this->remove_user_and_delete_if_last(data_);
|
|
}
|
|
|
|
ImplicitSharingPtr &operator=(const ImplicitSharingPtr &other)
|
|
{
|
|
if (this == &other) {
|
|
return *this;
|
|
}
|
|
|
|
this->remove_user_and_delete_if_last(data_);
|
|
data_ = other.data_;
|
|
this->add_user(data_);
|
|
return *this;
|
|
}
|
|
|
|
ImplicitSharingPtr &operator=(ImplicitSharingPtr &&other)
|
|
{
|
|
if (this == &other) {
|
|
return *this;
|
|
}
|
|
|
|
this->remove_user_and_delete_if_last(data_);
|
|
data_ = other.data_;
|
|
other.data_ = nullptr;
|
|
return *this;
|
|
}
|
|
|
|
const T *operator->() const
|
|
{
|
|
BLI_assert(data_ != nullptr);
|
|
return data_;
|
|
}
|
|
|
|
const T &operator*() const
|
|
{
|
|
BLI_assert(data_ != nullptr);
|
|
return *data_;
|
|
}
|
|
|
|
operator bool() const
|
|
{
|
|
return data_ != nullptr;
|
|
}
|
|
|
|
const T *get() const
|
|
{
|
|
return data_;
|
|
}
|
|
|
|
const T *release()
|
|
{
|
|
const T *data = data_;
|
|
data_ = nullptr;
|
|
return data;
|
|
}
|
|
|
|
void reset()
|
|
{
|
|
this->remove_user_and_delete_if_last(data_);
|
|
data_ = nullptr;
|
|
}
|
|
|
|
bool has_value() const
|
|
{
|
|
return data_ != nullptr;
|
|
}
|
|
|
|
uint64_t hash() const
|
|
{
|
|
return get_default_hash(data_);
|
|
}
|
|
|
|
BLI_STRUCT_EQUALITY_OPERATORS_1(ImplicitSharingPtr, data_)
|
|
|
|
private:
|
|
static void add_user(const T *data)
|
|
{
|
|
if (data != nullptr) {
|
|
data->add_user();
|
|
}
|
|
}
|
|
|
|
static void remove_user_and_delete_if_last(const T *data)
|
|
{
|
|
if (data != nullptr) {
|
|
data->remove_user_and_delete_if_last();
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Utility struct to allow used #ImplicitSharingPtr when it's necessary to type-erase the backing
|
|
* storage for user-exposed data. For example, #blender::Vector, or #std::vector might be used to
|
|
* store an implicitly shared array that is only accessed with #Span or #MutableSpan.
|
|
*
|
|
* This class handles RAII for the sharing info and the exposed data pointer.
|
|
* Retrieving the data with write access and type safety must be handled elsewhere.
|
|
*/
|
|
class ImplicitSharingPtrAndData {
|
|
public:
|
|
ImplicitSharingPtr<ImplicitSharingInfo> sharing_info;
|
|
const void *data = nullptr;
|
|
|
|
ImplicitSharingPtrAndData() = default;
|
|
ImplicitSharingPtrAndData(ImplicitSharingPtr<ImplicitSharingInfo> sharing_info, const void *data)
|
|
: sharing_info(std::move(sharing_info)), data(data)
|
|
{
|
|
}
|
|
|
|
ImplicitSharingPtrAndData(const ImplicitSharingPtrAndData &other)
|
|
: sharing_info(other.sharing_info), data(other.data)
|
|
{
|
|
}
|
|
|
|
ImplicitSharingPtrAndData(ImplicitSharingPtrAndData &&other)
|
|
: sharing_info(std::move(other.sharing_info)), data(std::exchange(other.data, nullptr))
|
|
{
|
|
}
|
|
|
|
ImplicitSharingPtrAndData &operator=(const ImplicitSharingPtrAndData &other)
|
|
{
|
|
if (this == &other) {
|
|
return *this;
|
|
}
|
|
std::destroy_at(this);
|
|
new (this) ImplicitSharingPtrAndData(other);
|
|
return *this;
|
|
}
|
|
|
|
ImplicitSharingPtrAndData &operator=(ImplicitSharingPtrAndData &&other)
|
|
{
|
|
if (this == &other) {
|
|
return *this;
|
|
}
|
|
std::destroy_at(this);
|
|
new (this) ImplicitSharingPtrAndData(std::move(other));
|
|
return *this;
|
|
}
|
|
|
|
~ImplicitSharingPtrAndData()
|
|
{
|
|
this->data = nullptr;
|
|
}
|
|
|
|
bool has_value() const
|
|
{
|
|
return this->sharing_info.has_value();
|
|
}
|
|
};
|
|
|
|
} // namespace blender
|