This implements bundles and closures which are described in more detail in this blog post: https://code.blender.org/2024/11/geometry-nodes-workshop-october-2024/ tl;dr: * Bundles are containers that allow storing multiple socket values in a single value. Each value in the bundle is identified by a name. Bundles can be nested. * Closures are functions that are created with the Closure Zone and can be evaluated with the Evaluate Closure node. To use the patch, the `Bundle and Closure Nodes` experimental feature has to be enabled. This is necessary, because these features are not fully done yet and still need iterations to improve the workflow before they can be officially released. These iterations are easier to do in `main` than in a separate branch though. That's because this patch is quite large and somewhat prone to merge conflicts. Also other work we want to do, depends on this. This adds the following new nodes: * Combine Bundle: can pack multiple values into one. * Separate Bundle: extracts values from a bundle. * Closure Zone: outputs a closure zone for use in the `Evaluate Closure` node. * Evaluate Closure: evaluates the passed in closure. Things that will be added soon after this lands: * Fields in bundles and closures. The way this is done changes with #134811, so I rather implement this once both are in `main`. * UI features for keeping sockets in sync (right now there are warnings only). One bigger issue is the limited support for lazyness. For example, all inputs of a Combine Bundle node will be evaluated, even if they are not all needed. The same is true for all captured values of a closure. This is a deeper limitation that needs to be resolved at some point. This will likely be done after an initial version of this patch is done. Pull Request: https://projects.blender.org/blender/blender/pulls/128340
147 lines
4.9 KiB
C++
147 lines
4.9 KiB
C++
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
|
|
#pragma once
|
|
|
|
/** \file
|
|
* \ingroup fn
|
|
*
|
|
* This file provides means to create a #LazyFunction from #Graph (which could then e.g. be used in
|
|
* another #Graph again).
|
|
*/
|
|
|
|
#include "BLI_generic_pointer.hh"
|
|
#include "BLI_vector.hh"
|
|
|
|
#include "FN_lazy_function_graph.hh"
|
|
|
|
namespace blender::fn::lazy_function {
|
|
|
|
/**
|
|
* Can be implemented to log values produced during graph evaluation.
|
|
*/
|
|
class GraphExecutorLogger {
|
|
public:
|
|
virtual ~GraphExecutorLogger() = default;
|
|
|
|
virtual void log_socket_value(const Socket &socket,
|
|
GPointer value,
|
|
const Context &context) const;
|
|
|
|
virtual void log_before_node_execute(const FunctionNode &node,
|
|
const Params ¶ms,
|
|
const Context &context) const;
|
|
|
|
virtual void log_after_node_execute(const FunctionNode &node,
|
|
const Params ¶ms,
|
|
const Context &context) const;
|
|
|
|
virtual void dump_when_outputs_are_missing(const FunctionNode &node,
|
|
Span<const OutputSocket *> missing_sockets,
|
|
const Context &context) const;
|
|
virtual void dump_when_input_is_set_twice(const InputSocket &target_socket,
|
|
const OutputSocket &from_socket,
|
|
const Context &context) const;
|
|
};
|
|
|
|
/**
|
|
* Has to be implemented when some of the nodes in the graph may have side effects. The
|
|
* #GraphExecutor has to know about that to make sure that these nodes will be executed even though
|
|
* their outputs are not needed.
|
|
*/
|
|
class GraphExecutorSideEffectProvider {
|
|
public:
|
|
virtual ~GraphExecutorSideEffectProvider() = default;
|
|
virtual Vector<const FunctionNode *> get_nodes_with_side_effects(const Context &context) const;
|
|
};
|
|
|
|
/**
|
|
* Can be used to pass extra context into the execution of a function. The main alternative to this
|
|
* is to create a wrapper `LazyFunction` for the `FunctionNode`s. Using this light weight wrapper
|
|
* is preferable if possible.
|
|
*/
|
|
class GraphExecutorNodeExecuteWrapper {
|
|
public:
|
|
virtual ~GraphExecutorNodeExecuteWrapper() = default;
|
|
|
|
/**
|
|
* Is expected to run `node.function().execute(params, context)` but might do some extra work,
|
|
* like adjusting the context.
|
|
*/
|
|
virtual void execute_node(const FunctionNode &node,
|
|
Params ¶ms,
|
|
const Context &context) const = 0;
|
|
};
|
|
|
|
class GraphExecutor : public LazyFunction {
|
|
public:
|
|
using Logger = GraphExecutorLogger;
|
|
using SideEffectProvider = GraphExecutorSideEffectProvider;
|
|
using NodeExecuteWrapper = GraphExecutorNodeExecuteWrapper;
|
|
|
|
private:
|
|
/**
|
|
* The graph that is evaluated.
|
|
*/
|
|
const Graph &graph_;
|
|
/**
|
|
* Input and output sockets of the entire graph.
|
|
*/
|
|
Vector<const GraphInputSocket *> graph_inputs_;
|
|
Vector<const GraphOutputSocket *> graph_outputs_;
|
|
Array<int> graph_input_index_by_socket_index_;
|
|
Array<int> graph_output_index_by_socket_index_;
|
|
/**
|
|
* Optional logger for events that happen during execution.
|
|
*/
|
|
const Logger *logger_;
|
|
/**
|
|
* Optional side effect provider. It knows which nodes have side effects based on the context
|
|
* during evaluation.
|
|
*/
|
|
const SideEffectProvider *side_effect_provider_;
|
|
/**
|
|
* Optional wrapper for node execution functions.
|
|
*/
|
|
const NodeExecuteWrapper *node_execute_wrapper_;
|
|
|
|
/**
|
|
* When a graph is executed, various things have to be allocated (e.g. the state of all nodes).
|
|
* Instead of doing many small allocations, a single bigger allocation is done. This struct
|
|
* contains the preprocessed offsets into that bigger buffer.
|
|
*/
|
|
struct {
|
|
int node_states_array_offset;
|
|
int loaded_inputs_array_offset;
|
|
Array<int> node_states_offsets;
|
|
int total_size;
|
|
} init_buffer_info_;
|
|
|
|
friend class Executor;
|
|
|
|
public:
|
|
GraphExecutor(const Graph &graph,
|
|
const Logger *logger,
|
|
const SideEffectProvider *side_effect_provider,
|
|
const NodeExecuteWrapper *node_execute_wrapper);
|
|
|
|
GraphExecutor(const Graph &graph,
|
|
Vector<const GraphInputSocket *> graph_inputs,
|
|
Vector<const GraphOutputSocket *> graph_outputs,
|
|
const Logger *logger,
|
|
const SideEffectProvider *side_effect_provider,
|
|
const NodeExecuteWrapper *node_execute_wrapper);
|
|
|
|
void *init_storage(LinearAllocator<> &allocator) const override;
|
|
void destruct_storage(void *storage) const override;
|
|
|
|
std::string input_name(int index) const override;
|
|
std::string output_name(int index) const override;
|
|
|
|
private:
|
|
void execute_impl(Params ¶ms, const Context &context) const override;
|
|
};
|
|
|
|
} // namespace blender::fn::lazy_function
|