Blender V4.3
COM_compile_state.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 "BLI_map.hh"
8
10
11#include "COM_domain.hh"
12#include "COM_node_operation.hh"
14#include "COM_scheduler.hh"
15
17
18using namespace nodes::derived_node_tree_types;
19
20/* ------------------------------------------------------------------------------------------------
21 * Compile State
22 *
23 * The compile state is a utility class used to track the state of compilation when compiling the
24 * node tree. In particular, it tracks two important pieces of information, each of which is
25 * described in one of the following sections.
26 *
27 * First, it stores a mapping between all nodes and the operations they were compiled into. The
28 * mapping are stored independently depending on the type of the operation in the node_operations_
29 * and pixel_operations_ maps. So those two maps are mutually exclusive. The compiler should call
30 * the map_node_to_node_operation and map_node_to_pixel_operation methods to populate those maps
31 * as soon as it compiles a node or multiple nodes into an operation. Those maps are used to
32 * retrieve the results of outputs linked to the inputs of operations. For more details, see the
33 * get_result_from_output_socket method. For the node tree shown below, nodes 1, 2, and 6 are
34 * mapped to their compiled operations in the node_operation_ map. While nodes 3 and 4 are both
35 * mapped to the first pixel operation, and node 5 is mapped to the second pixel operation in the
36 * pixel_operations_ map.
37 *
38 * Pixel Operation 1 Pixel Operation 2
39 * +-----------------------------------+ +------------------+
40 * .------------. | .------------. .------------. | | .------------. | .------------.
41 * | Node 1 | | | Node 3 | | Node 4 | | | | Node 5 | | | Node 6 |
42 * | |----|--| |--| |---|-----|--| |--|--| |
43 * | | .-|--| | | | | .--|--| | | | |
44 * '------------' | | '------------' '------------' | | | '------------' | '------------'
45 * | +-----------------------------------+ | +------------------+
46 * .------------. | |
47 * | Node 2 | | |
48 * | |--'----------------------------------------'
49 * | |
50 * '------------'
51 *
52 * Second, it stores the pixel compile unit as well as its domain. One should first go over the
53 * discussion in COM_evaluator.hh for a high level description of the mechanism of the compile
54 * unit. The one important detail in this class is the should_compile_pixel_compile_unit method,
55 * which implements the criteria of whether the compile unit should be compiled given the node
56 * currently being processed as an argument. Those criteria are described as follows. If the
57 * compile unit is empty as is the case when processing nodes 1, 2, and 3, then it plainly
58 * shouldn't be compiled. If the given node is not a pixel node, then it can't be added to the
59 * compile unit and the unit is considered complete and should be compiled, as is the case when
60 * processing node 6. If the computed domain of the given node is not compatible with the domain of
61 * the compiled unit, then it can't be added to the unit and the unit is considered complete and
62 * should be compiled, as is the case when processing node 5, more on this in the next section.
63 * Otherwise, the given node is compatible with the compile unit and can be added to it, so the
64 * unit shouldn't be compiled just yet, as is the case when processing node 4.
65 *
66 * Special attention should be given to the aforementioned domain compatibility criterion. One
67 * should first go over the discussion in COM_domain.hh for more information on domains. When a
68 * compile unit gets eventually compiled to a pixel operation, that operation will have a certain
69 * operation domain, and any node that gets added to the compile unit should itself have a computed
70 * node domain that is compatible with that operation domain, otherwise, had the node been compiled
71 * into its own operation separately, the result would have been be different. For instance,
72 * consider the above node tree where node 1 outputs a 100x100 result, node 2 outputs a 50x50
73 * result, the first input in node 3 has the highest domain priority, and the second input in node
74 * 5 has the highest domain priority. In this case, pixel operation 1 will output a 100x100
75 * result, and pixel operation 2 will output a 50x50 result, because that's the computed operation
76 * domain for each of them. So node 6 will get a 50x50 result. Now consider the same node tree, but
77 * where all three nodes 3, 4, and 5 were compiled into a single pixel operation as shown the node
78 * tree below. In that case, pixel operation 1 will output a 100x100 result, because that's its
79 * computed operation domain. So node 6 will get a 100x100 result. As can be seen, the final result
80 * is different even though the node tree is the same. That's why the compiler can decide to
81 * compile the compile unit early even though further nodes can still be technically added to it.
82 *
83 * Pixel Operation 1
84 * +------------------------------------------------------+
85 * .------------. | .------------. .------------. .------------. | .------------.
86 * | Node 1 | | | Node 3 | | Node 4 | | Node 5 | | | Node 6 |
87 * | |----|--| |--| |------| |--|--| |
88 * | | .-|--| | | | .---| | | | |
89 * '------------' | | '------------' '------------' | '------------' | '------------'
90 * | +----------------------------------|-------------------+
91 * .------------. | |
92 * | Node 2 | | |
93 * | |--'------------------------------------'
94 * | |
95 * '------------'
96 *
97 * To check for the domain compatibility between the compile unit and the node being processed, the
98 * domain of the compile unit is assumed to be the domain of the first node whose computed domain
99 * is not an identity domain. Identity domains corresponds to single value results, so those are
100 * always compatible with any domain. The domain of the compile unit is computed and set in the
101 * add_node_to_pixel_compile_unit method. When processing a node, the computed domain of node is
102 * compared to the compile unit domain in the should_compile_pixel_compile_unit method, noting that
103 * identity domains are always compatible. Node domains are computed in the
104 * compute_pixel_node_domain method, which is analogous to Operation::compute_domain for nodes
105 * that are not yet compiled. */
107 private:
108 /* A reference to the node execution schedule that is being compiled. */
109 const Schedule &schedule_;
110 /* Those two maps associate each node with the operation it was compiled into. Each node is
111 * either compiled into a node operation and added to node_operations, or compiled into a pixel
112 * operation and added to pixel_operations. Those maps are used to retrieve the results of
113 * outputs linked to the inputs of operations. See the get_result_from_output_socket method for
114 * more information. */
115 Map<DNode, NodeOperation *> node_operations_;
116 Map<DNode, PixelOperation *> pixel_operations_;
117 /* A contiguous subset of the node execution schedule that contains the group of nodes that will
118 * be compiled together into a pixel operation. See the discussion in COM_evaluator.hh for more
119 * information. */
120 PixelCompileUnit pixel_compile_unit_;
121 /* The domain of the pixel compile unit. */
122 Domain pixel_compile_unit_domain_ = Domain::identity();
123
124 public:
125 /* Construct a compile state from the node execution schedule being compiled. */
126 CompileState(const Schedule &schedule);
127
128 /* Get a reference to the node execution schedule being compiled. */
129 const Schedule &get_schedule();
130
131 /* Add an association between the given node and the give node operation that the node was
132 * compiled into in the node_operations_ map. */
133 void map_node_to_node_operation(DNode node, NodeOperation *operation);
134
135 /* Add an association between the given node and the give pixel operation that the node was
136 * compiled into in the pixel_operations_ map. */
137 void map_node_to_pixel_operation(DNode node, PixelOperation *operation);
138
139 /* Returns a reference to the result of the operation corresponding to the given output that the
140 * given output's node was compiled to. */
141 Result &get_result_from_output_socket(DOutputSocket output);
142
143 /* Add the given node to the compile unit. And if the domain of the compile unit is not yet
144 * determined or was determined to be an identity domain, update it to the computed domain for
145 * the give node. */
146 void add_node_to_pixel_compile_unit(DNode node);
147
148 /* Get a reference to the pixel compile unit. */
150
151 /* Clear the compile unit. This should be called once the compile unit is compiled to ready it to
152 * track the next potential compile unit. */
154
155 /* Determines if the compile unit should be compiled based on a number of criteria give the node
156 * currently being processed. Those criteria are as follows:
157 * - If compile unit is empty, then it can't and shouldn't be compiled.
158 * - If the given node is not a pixel node, then it can't be added to the compile unit
159 * and the unit is considered complete and should be compiled.
160 * - If the computed domain of the given node is not compatible with the domain of the compile
161 * unit, then it can't be added to it and the unit is considered complete and should be
162 * compiled. */
163 bool should_compile_pixel_compile_unit(DNode node);
164
165 /* Computes the number of pixel operation outputs that will be added for this node in the current
166 * pixel compile unit. This is essentially the number of outputs that will be added for the node
167 * in PixelOperation::populate_results_for_node. */
169
170 private:
171 /* Compute the node domain of the given pixel node. This is analogous to the
172 * Operation::compute_domain method, except it is computed from the node itself as opposed to a
173 * compiled operation. See the discussion in COM_domain.hh for more information. */
174 Domain compute_pixel_node_domain(DNode node);
175};
176
177} // namespace blender::realtime_compositor
void map_node_to_node_operation(DNode node, NodeOperation *operation)
void map_node_to_pixel_operation(DNode node, PixelOperation *operation)
Result & get_result_from_output_socket(DOutputSocket output)