Files
test2/source/blender/nodes/NOD_geometry_nodes_closure.hh
Jacques Lucke 183dfa68c9 Geometry Nodes: log closure evaluations
The goal is to log information about which closures are evaluated where. This
information is not exposed in the UI yet, but will be needed to be able to debug
the evaluation and inspect socket values within closures.

Pull Request: https://projects.blender.org/blender/blender/pulls/137351
2025-04-11 17:58:40 +02:00

146 lines
4.0 KiB
C++

/* SPDX-FileCopyrightText: 2025 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include "BKE_node.hh"
#include "NOD_geometry_nodes_closure_fwd.hh"
#include "NOD_geometry_nodes_closure_location.hh"
#include "NOD_socket_interface_key.hh"
#include "BLI_resource_scope.hh"
#include "FN_lazy_function.hh"
namespace blender::nodes {
/** Describes the names and types of the inputs and outputs of a closure. */
class ClosureSignature {
public:
struct Item {
SocketInterfaceKey key;
const bke::bNodeSocketType *type = nullptr;
};
Vector<Item> inputs;
Vector<Item> outputs;
std::optional<int> find_input_index(const SocketInterfaceKey &key) const;
std::optional<int> find_output_index(const SocketInterfaceKey &key) const;
};
/**
* Describes the meaning of the various inputs and outputs of the lazy-function that's contained
* in the closure.
*/
struct ClosureFunctionIndices {
struct {
IndexRange main;
/** A boolean input for each output indicating whether that output is used. */
IndexRange output_usages;
/**
* A #GeometryNodesReferenceSet input for a subset of the outputs. This is used to tell the
* closure which attributes it has to propagate to the outputs.
*
* Main output index -> input `lf` socket index.
*/
Map<int, int> output_data_reference_sets;
} inputs;
struct {
IndexRange main;
/** A boolean output for each input indicating whether that input is used. */
IndexRange input_usages;
} outputs;
};
/**
* A closure is like a node group that is passed around as a value. It's typically evaluated using
* the Evaluate Closure node.
*
* Internally, a closure is a lazy-function. So the inputs that are passed to the closure are
* requested lazily. It's *not* yet supported to request the potentially captured values from the
* Closure Zone lazily.
*/
class Closure : public ImplicitSharingMixin {
private:
std::shared_ptr<ClosureSignature> signature_;
std::optional<ClosureSourceLocation> source_location_;
std::shared_ptr<ClosureEvalLog> eval_log_;
/**
* When building complex lazy-functions, e.g. from Geometry Nodes, one often has to allocate
* various additional resources (e.g. the lazy-functions for the individual nodes). Using
* #ResourceScope provides a simple way to pass ownership of all these additional resources to
* the Closure.
*/
std::unique_ptr<ResourceScope> scope_;
const fn::lazy_function::LazyFunction &function_;
ClosureFunctionIndices indices_;
Vector<const void *> default_input_values_;
public:
Closure(std::shared_ptr<ClosureSignature> signature,
std::unique_ptr<ResourceScope> scope,
const fn::lazy_function::LazyFunction &function,
ClosureFunctionIndices indices,
Vector<const void *> default_input_values,
std::optional<ClosureSourceLocation> source_location,
std::shared_ptr<ClosureEvalLog> eval_log)
: signature_(signature),
source_location_(source_location),
eval_log_(eval_log),
scope_(std::move(scope)),
function_(function),
indices_(indices),
default_input_values_(std::move(default_input_values))
{
}
const ClosureSignature &signature() const
{
return *signature_;
}
const ClosureFunctionIndices &indices() const
{
return indices_;
}
const fn::lazy_function::LazyFunction &function() const
{
return function_;
}
const std::optional<ClosureSourceLocation> &source_location() const
{
return source_location_;
}
const std::shared_ptr<ClosureEvalLog> &eval_log_ptr() const
{
return eval_log_;
}
const void *default_input_value(const int index) const
{
return default_input_values_[index];
}
void delete_self() override
{
MEM_delete(this);
}
void log_evaluation(const ClosureEvalLocation &location) const
{
if (!eval_log_) {
return;
}
std::lock_guard lock{eval_log_->mutex};
eval_log_->evaluations.append(location);
}
};
} // namespace blender::nodes