Blender V4.3
COM_evaluator.hh
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
5#pragma once
6
7#include <memory>
8
9#include "BLI_vector.hh"
10
11#include "DNA_node_types.h"
12
14
15#include "COM_compile_state.hh"
16#include "COM_context.hh"
17#include "COM_node_operation.hh"
18#include "COM_operation.hh"
20
22
23using namespace nodes::derived_node_tree_types;
24
25/* ------------------------------------------------------------------------------------------------
26 * Evaluator
27 *
28 * The evaluator is the main class of the compositor and the entry point of its execution. The
29 * evaluator compiles the compositor node tree and evaluates it to compute its output. It is
30 * constructed from a compositor node tree and a compositor context. Upon calling the evaluate
31 * method, the evaluator will check if the node tree is already compiled into an operations stream,
32 * and if it is, it will go over it and evaluate the operations in order. It is then the
33 * responsibility of the caller to call the reset method when the node tree changes to invalidate
34 * the operations stream. A reset is also required if the resources used by the node tree change,
35 * for instances, when the dimensions of an image used by the node tree changes. This is necessary
36 * because the evaluator compiles the node tree into an operations stream that is specifically
37 * optimized for the structure of the resources used by the node tree.
38 *
39 * Otherwise, if the node tree is not yet compiled, the evaluator will compile it into an
40 * operations stream, evaluating the operations in the process. It should be noted that operations
41 * are evaluated as soon as they are compiled, as opposed to compiling the whole operations stream
42 * and then evaluating it in a separate step. This is important because, as mentioned before, the
43 * operations stream is optimized specifically for the structure of the resources used by the node
44 * tree, which is only known after the operations are evaluated. In other words, the evaluator uses
45 * the evaluated results of previously compiled operations to compile the operations that follow
46 * them in an optimized manner.
47 *
48 * Compilation starts by computing an optimized node execution schedule by calling the
49 * compute_schedule function, see the discussion in COM_scheduler.hh for more details. For the node
50 * tree shown below, the execution schedule is denoted by the node numbers. The compiler then goes
51 * over the execution schedule in order and compiles each node into either a Node Operation or a
52 * Pixel Operation, depending on the node type, see the is_pixel_node function. A pixel operation
53 * is constructed from a group of nodes forming a contiguous subset of the node execution schedule.
54 * For instance, in the node tree shown below, nodes 3 and 4 are compiled together into a pixel
55 * operation and node 5 is compiled into its own pixel operation, both of which are contiguous
56 * subsets of the node execution schedule. This process is described in details in the following
57 * section.
58 *
59 * Pixel Operation 1 Pixel Operation 2
60 * +-----------------------------------+ +------------------+
61 * .------------. | .------------. .------------. | | .------------. | .------------.
62 * | Node 1 | | | Node 3 | | Node 4 | | | | Node 5 | | | Node 6 |
63 * | |----|--| |--| |---|-----|--| |--|--| |
64 * | | .-|--| | | | | .--|--| | | | |
65 * '------------' | | '------------' '------------' | | | '------------' | '------------'
66 * | +-----------------------------------+ | +------------------+
67 * .------------. | |
68 * | Node 2 | | |
69 * | |--'----------------------------------------'
70 * | |
71 * '------------'
72 *
73 * For non pixel nodes, the compilation process is straight forward, the compiler instantiates a
74 * node operation from the node, map its inputs to the results of the outputs they are linked to,
75 * and evaluates the operations. However, for pixel nodes, since a group of nodes can be compiled
76 * together into a pixel operation, the compilation process is a bit involved. The compiler uses
77 * an instance of the Compile State class to keep track of the compilation process. The compiler
78 * state stores the so called "pixel compile unit", which is the current group of nodes that will
79 * eventually be compiled together into a pixel operation. While going over the schedule, the
80 * compiler adds the pixel nodes to the compile unit until it decides that the compile unit is
81 * complete and should be compiled. This is typically decided when the current node is not
82 * compatible with the compile unit and can't be added to it, only then it compiles the compile
83 * unit into a pixel operation and resets it to ready it to track the next potential group of
84 * nodes that will form a pixel operation. This decision is made based on various criteria in the
85 * should_compile_pixel_compile_unit function. See the discussion in COM_compile_state.hh for more
86 * details of those criteria, but perhaps the most evident of which is whether the node is actually
87 * a pixel node, if it isn't, then it evidently can't be added to the compile unit and the compile
88 * unit is should be compiled.
89 *
90 * For the node tree above, the compilation process is as follows. The compiler goes over the node
91 * execution schedule in order considering each node. Nodes 1 and 2 are not pixel node so they are
92 * compiled into node operations and added to the operations stream. The current compile unit is
93 * empty, so it is not compiled. Node 3 is a pixel node, and since the compile unit is currently
94 * empty, it is unconditionally added to it. Node 4 is a pixel node, it was decided---for the sake
95 * of the demonstration---that it is compatible with the compile unit and can be added to it. Node
96 * 5 is a pixel node, but it was decided---for the sake of the demonstration---that it is not
97 * compatible with the compile unit, so the compile unit is considered complete and is compiled
98 * first, adding the first pixel operation to the operations stream and resetting the compile
99 * unit. Node 5 is then added to the now empty compile unit similar to node 3. Node 6 is not a
100 * pixel node, so the compile unit is considered complete and is compiled first, adding the first
101 * pixel operation to the operations stream and resetting the compile unit. Finally, node 6 is
102 * compiled into a node operation similar to nodes 1 and 2 and added to the operations stream. */
104 private:
105 /* A reference to the compositor context. */
106 Context &context_;
107 /* A derived node tree representing the compositor node tree. This is constructed when the node
108 * tree is compiled and reset when the evaluator is reset, so it gets reconstructed every time
109 * the node tree changes. */
110 std::unique_ptr<DerivedNodeTree> derived_node_tree_;
111 /* The compiled operations stream. This contains ordered pointers to the operations that were
112 * compiled. This is initialized when the node tree is compiled and freed when the evaluator
113 * resets. The is_compiled_ member indicates whether the operation stream can be used or needs to
114 * be compiled first. Note that the operations stream can be empty even when compiled, this can
115 * happen when the node tree is empty or invalid for instance. */
116 Vector<std::unique_ptr<Operation>> operations_stream_;
117 /* True if the node tree is already compiled into an operations stream that can be evaluated
118 * directly. False if the node tree is not compiled yet and needs to be compiled. */
119 bool is_compiled_ = false;
120
121 public:
122 /* Construct an evaluator from a context. */
123 Evaluator(Context &context);
124
125 /* Evaluate the compositor node tree. If the node tree is already compiled into an operations
126 * stream, that stream will be evaluated directly. Otherwise, the node tree will be compiled and
127 * evaluated. */
128 void evaluate();
129
130 /* Invalidate the operations stream that was compiled for the node tree. This should be called
131 * when the node tree changes or the structure of any of the resources used by it changes. By
132 * structure, we mean things like the dimensions of the used images, while changes to their
133 * contents do not necessitate a reset. */
134 void reset();
135
136 private:
137 /* Check if the compositor node tree is valid by checking if it has:
138 * - Cyclic links.
139 * - Undefined nodes or sockets.
140 * - Unsupported nodes.
141 * If the node tree is valid, true is returned. Otherwise, false is returned, and an appropriate
142 * error message is set by calling the context's set_info_message method. */
143 bool validate_node_tree();
144
145 /* Compile the node tree into an operations stream and evaluate it. */
146 void compile_and_evaluate();
147
148 /* Compile the given node into a node operation, map each input to the result of the output
149 * linked to it, update the compile state, add the newly created operation to the operations
150 * stream, and evaluate the operation. */
151 void compile_and_evaluate_node(DNode node, CompileState &compile_state);
152
153 /* Map each input of the node operation to the result of the output linked to it. Unlinked inputs
154 * are mapped to the result of a newly created Input Single Value Operation, which is added to
155 * the operations stream and evaluated. Since this method might add operations to the operations
156 * stream, the actual node operation should only be added to the stream once this method is
157 * called. */
158 void map_node_operation_inputs_to_their_results(DNode node,
159 NodeOperation *operation,
160 CompileState &compile_state);
161
162 /* Compile the pixel compile unit into a pixel operation, map each input of the operation to
163 * the result of the output linked to it, update the compile state, add the newly created
164 * operation to the operations stream, evaluate the operation, and finally reset the pixel
165 * compile unit. */
166 void compile_and_evaluate_pixel_compile_unit(CompileState &compile_state);
167
168 /* Map each input of the pixel operation to the result of the output linked to it. */
169 void map_pixel_operation_inputs_to_their_results(PixelOperation *operation,
170 CompileState &compile_state);
171};
172
173} // namespace blender::realtime_compositor