Blender V5.0
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
12
13#include "COM_compile_state.hh"
14#include "COM_context.hh"
15#include "COM_node_operation.hh"
16#include "COM_operation.hh"
17
18namespace blender::compositor {
19
20using namespace nodes::derived_node_tree_types;
21
22/* ------------------------------------------------------------------------------------------------
23 * Evaluator
24 *
25 * The evaluator is the main class of the compositor and the entry point of its execution. It is
26 * constructed from a compositor node tree and a compositor context. It compiles the node tree into
27 * an operations stream, evaluating the operations in the process. It should be noted that
28 * operations are eagerly evaluated as soon as they are compiled, as opposed to compiling the whole
29 * operations stream and then evaluating it in a separate step. This is done because the evaluator
30 * uses the evaluated results of previously compiled operations to compile the operations that
31 * follow them in an optimized manner.
32 *
33 * Evaluation starts by computing an optimized node execution schedule by calling the
34 * compute_schedule function, see the discussion in COM_scheduler.hh for more details. For the node
35 * tree shown below, the execution schedule is denoted by the node numbers. The compiler then goes
36 * over the execution schedule in order and compiles each node into either a Node Operation or a
37 * Pixel Operation, depending on the node type, see the is_pixel_node function. A pixel operation
38 * is constructed from a group of nodes forming a contiguous subset of the node execution schedule.
39 * For instance, in the node tree shown below, nodes 3 and 4 are compiled together into a pixel
40 * operation and node 5 is compiled into its own pixel operation, both of which are contiguous
41 * subsets of the node execution schedule. This process is described in details in the following
42 * section.
43 *
44 * Pixel Operation 1 Pixel Operation 2
45 * +-----------------------------------+ +------------------+
46 * .------------. | .------------. .------------. | | .------------. | .------------.
47 * | Node 1 | | | Node 3 | | Node 4 | | | | Node 5 | | | Node 6 |
48 * | |----|--| |--| |---|-----|--| |--|--| |
49 * | | .-|--| | | | | .--|--| | | | |
50 * '------------' | | '------------' '------------' | | | '------------' | '------------'
51 * | +-----------------------------------+ | +------------------+
52 * .------------. | |
53 * | Node 2 | | |
54 * | |--'----------------------------------------'
55 * | |
56 * '------------'
57 *
58 * For non pixel nodes, the compilation process is straight forward, the compiler instantiates a
59 * node operation from the node, map its inputs to the results of the outputs they are linked to,
60 * and evaluates the operations. However, for pixel nodes, since a group of nodes can be compiled
61 * together into a pixel operation, the compilation process is a bit involved. The compiler uses
62 * an instance of the Compile State class to keep track of the compilation process. The compiler
63 * state stores the so called "pixel compile unit", which is the current group of nodes that will
64 * eventually be compiled together into a pixel operation. While going over the schedule, the
65 * compiler adds the pixel nodes to the compile unit until it decides that the compile unit is
66 * complete and should be compiled. This is typically decided when the current node is not
67 * compatible with the compile unit and can't be added to it, only then it compiles the compile
68 * unit into a pixel operation and resets it to ready it to track the next potential group of
69 * nodes that will form a pixel operation. This decision is made based on various criteria in the
70 * should_compile_pixel_compile_unit function. See the discussion in COM_compile_state.hh for more
71 * details of those criteria, but perhaps the most evident of which is whether the node is actually
72 * a pixel node, if it isn't, then it evidently can't be added to the compile unit and the compile
73 * unit is should be compiled.
74 *
75 * For the node tree above, the compilation process is as follows. The compiler goes over the node
76 * execution schedule in order considering each node. Nodes 1 and 2 are not pixel node so they are
77 * compiled into node operations and added to the operations stream. The current compile unit is
78 * empty, so it is not compiled. Node 3 is a pixel node, and since the compile unit is currently
79 * empty, it is unconditionally added to it. Node 4 is a pixel node, it was decided---for the sake
80 * of the demonstration---that it is compatible with the compile unit and can be added to it. Node
81 * 5 is a pixel node, but it was decided---for the sake of the demonstration---that it is not
82 * compatible with the compile unit, so the compile unit is considered complete and is compiled
83 * first, adding the first pixel operation to the operations stream and resetting the compile
84 * unit. Node 5 is then added to the now empty compile unit similar to node 3. Node 6 is not a
85 * pixel node, so the compile unit is considered complete and is compiled first, adding the first
86 * pixel operation to the operations stream and resetting the compile unit. Finally, node 6 is
87 * compiled into a node operation similar to nodes 1 and 2 and added to the operations stream. */
88class Evaluator {
89 private:
90 /* A reference to the compositor context. */
91 Context &context_;
92 /* A derived node tree representing the compositor node tree. */
93 std::unique_ptr<DerivedNodeTree> derived_node_tree_;
94 /* The compiled operations stream, which contains all compiled operations so far. */
95 Vector<std::unique_ptr<Operation>> operations_stream_;
96
97 public:
98 /* Construct an evaluator from a context. */
99 Evaluator(Context &context);
100
101 /* Evaluates the compositor node tree by compiling it into an operations stream and evaluating
102 * it. */
103 void evaluate();
104
105 private:
106 /* Check if the compositor node tree is valid by checking if it has things like cyclic links and
107 * undefined nodes or sockets. If the node tree is valid, true is returned. Otherwise, false is
108 * returned, and an appropriate error message is set by calling the context's set_info_message
109 * method. */
110 bool validate_node_tree();
111
112 /* Compile the given node into a node operation, map each input to the result of the output
113 * linked to it, update the compile state, add the newly created operation to the operations
114 * stream, and evaluate the operation. */
115 void evaluate_node(DNode node, CompileState &compile_state);
116
117 /* Map each input of the node operation to the result of the output linked to it. Unlinked inputs
118 * are mapped to the result of a newly created Input Single Value Operation, which is added to
119 * the operations stream and evaluated. Since this method might add operations to the operations
120 * stream, the actual node operation should only be added to the stream once this method is
121 * called. */
122 void map_node_operation_inputs_to_their_results(DNode node,
123 NodeOperation *operation,
124 CompileState &compile_state);
125
126 /* Compile the pixel compile unit into a pixel operation, map each input of the operation to
127 * the result of the output linked to it, update the compile state, add the newly created
128 * operation to the operations stream, evaluate the operation, and finally reset the pixel
129 * compile unit. */
130 void evaluate_pixel_compile_unit(CompileState &compile_state);
131
132 /* Map each input of the pixel operation to the result of the output linked to it. This might
133 * also correct the reference counts of the results, see the implementation for more details. */
134 void map_pixel_operation_inputs_to_their_results(PixelOperation *operation,
135 CompileState &compile_state);
136
137 /* Cancels the evaluation by informing the static cache manager of the cancellation and freeing
138 * the results of the operations that were already evaluated, that's because later operations
139 * that use the already allocated results will not be evaluated, so they consequently will not
140 * release the results that they use and we need to free them manually. */
141 void cancel_evaluation();
142};
143
144} // namespace blender::compositor
Evaluator(Context &context)
Definition evaluator.cc:29