Files
test/source/blender/blenlib/BLI_implicit_sharing_ptr.hh
Jacques Lucke 5f132d3f07 Nodes: support storing internal data in Bundle
Currently, bundles can only store socket values of Geometry Nodes. However, it
can make sense to store other kinds of data too. Specifically, this patch adds
support for storing arbitrary internal data in a bundle. This is useful when
storing e.g. the physics world when implementing a proper physics solver for
Geometry Nodes (like in #143171).

One can still see that the data exists in Geometry Nodes in the tooltip, but one
can't extract it. Built-in nodes can still read that data.

Storing built-in data in bundles can also be done as an alternative to having a
new "internal data socket" as we talked in a workshop in the past:
https://code.blender.org/2024/11/geometry-nodes-workshop-october-2024/#internal-data-sockets

A bundle still has to be copyable. Internal data is expected to use implicit
sharing. That way copying it just requires increasing the user count of the
data.

Pull Request: https://projects.blender.org/blender/blender/pulls/143472
2025-07-28 19:05:18 +02:00

229 lines
4.9 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 = ImplicitSharingInfo, bool IsStrong = true> class ImplicitSharingPtr {
private:
const T *data_ = nullptr;
public:
using element_type = T;
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_);
}
static uint64_t hash_as(const T *data)
{
return get_default_hash(data);
}
BLI_STRUCT_EQUALITY_OPERATORS_1(ImplicitSharingPtr, data_)
friend bool operator==(const T *a, const ImplicitSharingPtr &b)
{
return a == b.data_;
}
friend bool operator==(const ImplicitSharingPtr &a, const T *b)
{
return a.data_ == b;
}
private:
static void add_user(const T *data)
{
if (data != nullptr) {
if constexpr (IsStrong) {
data->add_user();
}
else {
data->add_weak_user();
}
}
}
static void remove_user_and_delete_if_last(const T *data)
{
if (data != nullptr) {
if constexpr (IsStrong) {
data->remove_user_and_delete_if_last();
}
else {
data->remove_weak_user_and_delete_if_last();
}
}
}
};
using WeakImplicitSharingPtr = ImplicitSharingPtr<ImplicitSharingInfo, false>;
/**
* 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<> sharing_info;
const void *data = nullptr;
ImplicitSharingPtrAndData() = default;
ImplicitSharingPtrAndData(ImplicitSharingPtr<> sharing_info, const void *data)
: sharing_info(std::move(sharing_info)), data(data)
{
}
ImplicitSharingPtrAndData(const ImplicitSharingPtrAndData &other) = default;
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();
}
};
template<typename T> static constexpr bool is_ImplicitSharingPtr_strong_v = false;
template<typename T>
static constexpr bool is_ImplicitSharingPtr_strong_v<ImplicitSharingPtr<T, true>> = true;
} // namespace blender