Files
test/source/blender/nodes/NOD_partial_eval.hh
Jacques Lucke aab2b6004b Geometry Nodes: add compute context cache
For various purposes we traverse the computation done by a node tree (e.g. for
gizmos and socket usage infeferencing). For that we generally have to keep track
of the compute context we're in at any given time. During the traversal, it's
common to enter and exist the same compute contexts multiple times. Currently,
we'd always build a new compute context when that happens. That happens even
though the old one is generally still around, because other data may reference
it. This patch implements a `ComputeContextHash` type that avoids rebuilding the
same compute contexts over and over again.

I'm considering to also replace the usage of `ComputeContextBuilder` with this
cache somehow, but will see how that works out.

The reason I'm working on this now is that I have to traverse the node tree a
bit again to find where closures might be evaluated. I wanted to be able to
cache the compute contexts for a while already.

Pull Request: https://projects.blender.org/blender/blender/pulls/137360
2025-04-11 21:36:41 +02:00

96 lines
4.5 KiB
C++

/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include "NOD_node_in_compute_context.hh"
#include "BLI_function_ref.hh"
#include "BLI_set.hh"
#include "BKE_compute_context_cache.hh"
#include "DNA_node_types.h"
/**
* This header provides functionality that makes it relatively straight forward to evaluate parts
* of a node tree. The evaluator is designed to be flexible and simple to use in different
* contexts. It's not designed to be highly efficient and parallel. However, it has a lower
* start-up cost compared to e.g. the lazy-function evaluation for geometry nodes, which needs to
* convert the entire node graph into a lazy-function graph first. So it can be more efficient when
* only very few nodes of a larger graph have to be evaluated and those nodes are cheap.
*
* The evaluator does not use recursion, so it can be used on node graphs of every size and depth.
*/
namespace blender::nodes::partial_eval {
/**
* Evaluating part of a node tree from left-to-right. The part that's evaluated starts at
* the given sockets and is propagated downstream step-by-step. The caller is responsible for
* storing the socket values (a value per #SocketInContext).
*
* \note This handles node groups transparently, but does not handle e.g. repeat zones yet.
*
* \param initial_sockets: Sockets where the evaluation should start.
* \param scope: Is used to construct compute contexts which the caller may want to outlive the
* entire evaluation.
* \param evaluate_node_fn: Is called when all (relevant) upstream nodes are already evaluated and
* evaluates the given node. This should updated the values the caller stores for the output
* sockets.
* \param propagate_value_fn: Should copy the value stored for one socket to the other socket. This
* may have to do type conversions. The return value indicates success. False indicates that the
* value was not propagated and as such the target node also shouldn't be evaluated (unless there
* are other reasons to evaluate it).
*/
void eval_downstream(
const Span<SocketInContext> initial_sockets,
bke::ComputeContextCache &compute_context_cache,
FunctionRef<void(const NodeInContext &ctx_node,
Vector<const bNodeSocket *> &r_outputs_to_propagate)> evaluate_node_fn,
FunctionRef<bool(const SocketInContext &ctx_from, const SocketInContext &ctx_to)>
propagate_value_fn);
struct UpstreamEvalTargets {
Set<SocketInContext> sockets;
Set<NodeInContext> value_nodes;
Set<SocketInContext> group_inputs;
};
/**
* Evaluates part of a node tree from right-to-left (inverse direction). The caller is responsible
* for storing the socket values (a value per #SocketInContext). Evaluation in the upstream
* direction is not always well defined, because output sockets may be linked to multiple inputs
* and nodes may not always have an inverse evaluation function. The caller is responsible for
* handling these cases gracefully in the given callbacks.
*
* \note This handles node groups transparently, but does not handle e.g. repeat zones yet.
*
* \param initial_sockets: Sockets where the evaluation should start.
* \param scope: Is used to construct compute contexts which the caller may want to outlive the
* entire evaluation.
* \param evaluate_node_fn: Called to evaluate the node in reverse, i.e. it's outputs are computed
* first, and the node evaluation computes the inputs.
* \param propagate_value_fn: Should copy the value from one socket to another, while optionally
* doing type conversions. This has to handle the case when multiple values are propagated to the
* same socket. Returning false indicates that no value was propagated.
* \param get_inputs_to_propagate_fn: Gathers a list of input sockets that should be propagated
* further.
*
* \return Places in the node tree that have gotten new values that can't be propagated further in
* the node tree.
*/
UpstreamEvalTargets eval_upstream(
const Span<SocketInContext> initial_sockets,
bke::ComputeContextCache &compute_context_cache,
FunctionRef<void(const NodeInContext &ctx_node,
Vector<const bNodeSocket *> &r_modified_inputs)> evaluate_node_fn,
FunctionRef<bool(const SocketInContext &ctx_from, const SocketInContext &ctx_to)>
propagate_value_fn,
FunctionRef<void(const NodeInContext &ctx_node, Vector<const bNodeSocket *> &r_sockets)>
get_inputs_to_propagate_fn);
bool is_supported_value_node(const bNode &node);
} // namespace blender::nodes::partial_eval