Files
test2/source/blender/nodes/intern/geometry_nodes_bundle.cc
Jacques Lucke 21d603895e Refactor: Geometry Nodes: support detection of syncable closure/bundle nodes
This is extracted from #140967.

Previously, it was possible to sync a bundle/closure node based on what's linked.
However, it was not possible to detect if the syncing was possible or necessary
in the first place. Knowing this helps building UI features that inform the user
about outdated nodes.
2025-07-02 07:38:30 +02:00

224 lines
5.5 KiB
C++

/* SPDX-FileCopyrightText: 2025 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "BLI_cpp_type.hh"
#include "BKE_node_runtime.hh"
#include "NOD_geometry_nodes_bundle.hh"
#include "NOD_geometry_nodes_bundle_signature.hh"
namespace blender::nodes {
SocketInterfaceKey::SocketInterfaceKey(std::string identifier)
{
identifiers_.append(std::move(identifier));
}
SocketInterfaceKey::SocketInterfaceKey(Vector<std::string> identifiers)
: identifiers_(std::move(identifiers))
{
BLI_assert(!identifiers_.is_empty());
}
Span<std::string> SocketInterfaceKey::identifiers() const
{
return identifiers_;
}
bool SocketInterfaceKey::matches(const SocketInterfaceKey &other) const
{
for (const std::string &identifier : other.identifiers_) {
if (identifiers_.contains(identifier)) {
return true;
}
}
return false;
}
bool SocketInterfaceKey::matches_exactly(const SocketInterfaceKey &other) const
{
for (const std::string &identifier : identifiers_) {
if (std::none_of(
other.identifiers_.begin(),
other.identifiers_.end(),
[&](const std::string &other_identifier) { return other_identifier == identifier; }))
{
return false;
}
}
return true;
}
bool BundleSignature::matches_exactly(const BundleSignature &other) const
{
if (items.size() != other.items.size()) {
return false;
}
for (const Item &item : items) {
if (std::none_of(other.items.begin(), other.items.end(), [&](const Item &other_item) {
return item.key.matches_exactly(other_item.key);
}))
{
return false;
}
}
return true;
}
bool BundleSignature::all_matching_exactly(const Span<BundleSignature> signatures)
{
if (signatures.is_empty()) {
return true;
}
for (const BundleSignature &signature : signatures.drop_front(1)) {
if (!signatures[0].matches_exactly(signature)) {
return false;
}
}
return true;
}
Bundle::Bundle() = default;
Bundle::~Bundle()
{
for (StoredItem &item : items_) {
item.type->geometry_nodes_cpp_type->destruct(item.value);
}
for (void *buffer : buffers_) {
MEM_freeN(buffer);
}
}
Bundle::Bundle(const Bundle &other)
{
for (const StoredItem &item : other.items_) {
this->add_new(item.key, *item.type, item.value);
}
}
Bundle::Bundle(Bundle &&other) noexcept
: items_(std::move(other.items_)), buffers_(std::move(other.buffers_))
{
}
Bundle &Bundle::operator=(const Bundle &other)
{
if (this == &other) {
return *this;
}
this->~Bundle();
new (this) Bundle(other);
return *this;
}
Bundle &Bundle::operator=(Bundle &&other) noexcept
{
if (this == &other) {
return *this;
}
this->~Bundle();
new (this) Bundle(std::move(other));
return *this;
}
void Bundle::add_new(SocketInterfaceKey key, const bke::bNodeSocketType &type, const void *value)
{
BLI_assert(!this->contains(key));
BLI_assert(type.geometry_nodes_cpp_type);
const CPPType &cpp_type = *type.geometry_nodes_cpp_type;
void *buffer = MEM_mallocN_aligned(cpp_type.size, cpp_type.alignment, __func__);
cpp_type.copy_construct(value, buffer);
items_.append(StoredItem{std::move(key), &type, buffer});
buffers_.append(buffer);
}
bool Bundle::add(const SocketInterfaceKey &key,
const bke::bNodeSocketType &type,
const void *value)
{
if (this->contains(key)) {
return false;
}
this->add_new(key, type, value);
return true;
}
bool Bundle::add(SocketInterfaceKey &&key, const bke::bNodeSocketType &type, const void *value)
{
if (this->contains(key)) {
return false;
}
this->add_new(std::move(key), type, value);
return true;
}
std::optional<Bundle::Item> Bundle::lookup(const SocketInterfaceKey &key) const
{
for (const StoredItem &item : items_) {
if (item.key.matches(key)) {
return Item{item.type, item.value};
}
}
return std::nullopt;
}
bool Bundle::remove(const SocketInterfaceKey &key)
{
const int removed_num = items_.remove_if([&key](StoredItem &item) {
if (item.key.matches(key)) {
item.type->geometry_nodes_cpp_type->destruct(item.value);
return true;
}
return false;
});
return removed_num >= 1;
}
bool Bundle::contains(const SocketInterfaceKey &key) const
{
for (const StoredItem &item : items_) {
if (item.key.matches(key)) {
return true;
}
}
return false;
}
void Bundle::delete_self()
{
MEM_delete(this);
}
BundleSignature BundleSignature::FromCombineBundleNode(const bNode &node)
{
BLI_assert(node.is_type("GeometryNodeCombineBundle"));
const auto &storage = *static_cast<const NodeGeometryCombineBundle *>(node.storage);
BundleSignature signature;
for (const int i : IndexRange(storage.items_num)) {
const NodeGeometryCombineBundleItem &item = storage.items[i];
if (const bke::bNodeSocketType *stype = bke::node_socket_type_find_static(item.socket_type)) {
signature.items.append({nodes::SocketInterfaceKey(item.name), stype});
}
}
return signature;
}
BundleSignature BundleSignature::FromSeparateBundleNode(const bNode &node)
{
BLI_assert(node.is_type("GeometryNodeSeparateBundle"));
const auto &storage = *static_cast<const NodeGeometrySeparateBundle *>(node.storage);
BundleSignature signature;
for (const int i : IndexRange(storage.items_num)) {
const NodeGeometrySeparateBundleItem &item = storage.items[i];
if (const bke::bNodeSocketType *stype = bke::node_socket_type_find_static(item.socket_type)) {
signature.items.append({nodes::SocketInterfaceKey(item.name), stype});
}
}
return signature;
}
} // namespace blender::nodes