Blender V4.3
COM_FullFrameExecutionModel.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2021 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
6
7#include "BLI_string.h"
8
9#include "BLT_translation.hh"
10
11#include "COM_Debug.h"
12#include "COM_ViewerOperation.h"
13#include "COM_WorkScheduler.h"
14
15#include "COM_profiler.hh"
16
17#include "BLI_timeit.hh"
18
19#ifdef WITH_CXX_GUARDEDALLOC
20# include "MEM_guardedalloc.h"
21#endif
22
23namespace blender::compositor {
24
26 SharedOperationBuffers &shared_buffers,
27 Span<NodeOperation *> operations)
28 : ExecutionModel(context, operations),
29 active_buffers_(shared_buffers),
30 num_operations_finished_(0)
31{
35}
36
38{
40 node_tree->runtime->stats_draw(node_tree->runtime->sdh,
41 RPT_("Compositing | Initializing execution"));
42
43 DebugInfo::graphviz(&exec_system, "compositor_prior_rendering");
44
45 determine_areas_to_render_and_reads();
46 render_operations();
47}
48
49void FullFrameExecutionModel::determine_areas_to_render_and_reads()
50{
51 const bool is_rendering = context_.is_rendering();
53
54 rcti area;
55 for (eCompositorPriority priority : priorities_) {
56 for (NodeOperation *op : operations_) {
57 op->set_bnodetree(node_tree);
58 if (op->is_output_operation(is_rendering) && op->get_render_priority() == priority) {
59 get_output_render_area(op, area);
60 determine_areas_to_render(op, area);
61 determine_reads(op);
62 }
63 }
64 }
65}
66
67Vector<MemoryBuffer *> FullFrameExecutionModel::get_input_buffers(NodeOperation *op,
68 const int output_x,
69 const int output_y)
70{
71 const int num_inputs = op->get_number_of_input_sockets();
72 Vector<MemoryBuffer *> inputs_buffers(num_inputs);
73 for (int i = 0; i < num_inputs; i++) {
74 NodeOperation *input = op->get_input_operation(i);
75 const int offset_x = (input->get_canvas().xmin - op->get_canvas().xmin) + output_x;
76 const int offset_y = (input->get_canvas().ymin - op->get_canvas().ymin) + output_y;
77 MemoryBuffer *buf = active_buffers_.get_rendered_buffer(input);
78
79 rcti rect = buf->get_rect();
80 BLI_rcti_translate(&rect, offset_x, offset_y);
81 inputs_buffers[i] = new MemoryBuffer(
82 buf->get_buffer(), buf->get_num_channels(), rect, buf->is_a_single_elem());
83 }
84 return inputs_buffers;
85}
86
87MemoryBuffer *FullFrameExecutionModel::create_operation_buffer(NodeOperation *op,
88 const int output_x,
89 const int output_y)
90{
91 rcti rect;
93 &rect, output_x, output_x + op->get_width(), output_y, output_y + op->get_height());
94
95 const DataType data_type = op->get_output_socket(0)->get_data_type();
96 const bool is_a_single_elem = op->get_flags().is_constant_operation;
97 return new MemoryBuffer(data_type, rect, is_a_single_elem);
98}
99
100void FullFrameExecutionModel::render_operation(NodeOperation *op)
101{
102 /* Output has no offset for easier image algorithms implementation on operations. */
103 constexpr int output_x = 0;
104 constexpr int output_y = 0;
105
106 const timeit::TimePoint before_time = timeit::Clock::now();
107
108 const bool has_outputs = op->get_number_of_output_sockets() > 0;
109 MemoryBuffer *op_buf = has_outputs ? create_operation_buffer(op, output_x, output_y) : nullptr;
110 if (op->get_width() > 0 && op->get_height() > 0) {
111 Vector<MemoryBuffer *> input_bufs = get_input_buffers(op, output_x, output_y);
112 const int op_offset_x = output_x - op->get_canvas().xmin;
113 const int op_offset_y = output_y - op->get_canvas().ymin;
114 Vector<rcti> areas = active_buffers_.get_areas_to_render(op, op_offset_x, op_offset_y);
115 op->render(op_buf, areas, input_bufs);
117
118 for (MemoryBuffer *buf : input_bufs) {
119 delete buf;
120 }
121 }
122 /* Even if operation has no resolution set the empty buffer. It will be clipped with a
123 * TranslateOperation from convert resolutions if linked to an operation with resolution. */
124 active_buffers_.set_rendered_buffer(op, std::unique_ptr<MemoryBuffer>(op_buf));
125
126 operation_finished(op);
127
128 /* The operation may not come from any node. For example, it may have been added to convert data
129 * type. Do not accumulate time from its execution. */
130 const timeit::TimePoint after_time = timeit::Clock::now();
131 const bNodeInstanceKey node_instance_key = op->get_node_instance_key();
132 if (context_.get_profiler() && node_instance_key != bke::NODE_INSTANCE_KEY_NONE) {
133 context_.get_profiler()->set_node_evaluation_time(node_instance_key, after_time - before_time);
134 }
135}
136
137void FullFrameExecutionModel::render_operations()
138{
139 const bool is_rendering = context_.is_rendering();
140
142 for (eCompositorPriority priority : priorities_) {
143 for (NodeOperation *op : operations_) {
144 const bool has_size = op->get_width() > 0 && op->get_height() > 0;
145 const bool is_priority_output = op->is_output_operation(is_rendering) &&
146 op->get_render_priority() == priority;
147 if (is_priority_output && has_size) {
148 render_output_dependencies(op);
149 render_operation(op);
150 }
151 else if (is_priority_output && !has_size && op->is_active_viewer_output()) {
152 static_cast<ViewerOperation *>(op)->clear_display_buffer();
153 }
154 }
155 }
157}
158
164{
165 /* Get dependencies from outputs to inputs. */
166 Vector<NodeOperation *> dependencies;
167 Vector<NodeOperation *> next_outputs;
168 next_outputs.append(operation);
169 while (next_outputs.size() > 0) {
170 Vector<NodeOperation *> outputs(next_outputs);
171 next_outputs.clear();
172 for (NodeOperation *output : outputs) {
173 for (int i = 0; i < output->get_number_of_input_sockets(); i++) {
174 next_outputs.append(output->get_input_operation(i));
175 }
176 }
177 dependencies.extend(next_outputs);
178 }
179
180 /* Reverse to get dependencies from inputs to outputs. */
181 std::reverse(dependencies.begin(), dependencies.end());
182
183 return dependencies;
184}
185
186void FullFrameExecutionModel::render_output_dependencies(NodeOperation *output_op)
187{
188 BLI_assert(output_op->is_output_operation(context_.is_rendering()));
189 Vector<NodeOperation *> dependencies = get_operation_dependencies(output_op);
190 for (NodeOperation *op : dependencies) {
191 if (!active_buffers_.is_operation_rendered(op)) {
192 render_operation(op);
193 }
194 }
195}
196
197void FullFrameExecutionModel::determine_areas_to_render(NodeOperation *output_op,
198 const rcti &output_area)
199{
200 BLI_assert(output_op->is_output_operation(context_.is_rendering()));
201
202 Vector<std::pair<NodeOperation *, const rcti>> stack;
203 stack.append({output_op, output_area});
204 while (stack.size() > 0) {
205 std::pair<NodeOperation *, rcti> pair = stack.pop_last();
206 NodeOperation *operation = pair.first;
207 const rcti &render_area = pair.second;
208 if (BLI_rcti_is_empty(&render_area) ||
209 active_buffers_.is_area_registered(operation, render_area))
210 {
211 continue;
212 }
213
214 active_buffers_.register_area(operation, render_area);
215
216 const int num_inputs = operation->get_number_of_input_sockets();
217 for (int i = 0; i < num_inputs; i++) {
218 NodeOperation *input_op = operation->get_input_operation(i);
219 rcti input_area;
220 operation->get_area_of_interest(input_op, render_area, input_area);
221
222 /* Ensure area of interest is within operation bounds, cropping areas outside. */
223 BLI_rcti_isect(&input_area, &input_op->get_canvas(), &input_area);
224
225 stack.append({input_op, input_area});
226 }
227 }
228}
229
230void FullFrameExecutionModel::determine_reads(NodeOperation *output_op)
231{
232 BLI_assert(output_op->is_output_operation(context_.is_rendering()));
233
234 Vector<NodeOperation *> stack;
235 stack.append(output_op);
236 while (stack.size() > 0) {
237 NodeOperation *operation = stack.pop_last();
238 const int num_inputs = operation->get_number_of_input_sockets();
239 for (int i = 0; i < num_inputs; i++) {
240 NodeOperation *input_op = operation->get_input_operation(i);
241 if (!active_buffers_.has_registered_reads(input_op)) {
242 stack.append(input_op);
243 }
244 active_buffers_.register_read(input_op);
245 }
246 }
247}
248
249void FullFrameExecutionModel::get_output_render_area(NodeOperation *output_op, rcti &r_area)
250{
251 BLI_assert(output_op->is_output_operation(context_.is_rendering()));
252
253 /* By default return operation bounds (no border). */
254 rcti canvas = output_op->get_canvas();
255 r_area = canvas;
256
257 const bool has_viewer_border = border_.use_viewer_border &&
258 (output_op->get_flags().is_viewer_operation ||
259 output_op->get_flags().is_preview_operation);
260 const bool has_render_border = border_.use_render_border &&
261 output_op->get_flags().use_render_border;
262 if (has_viewer_border || has_render_border) {
263 /* Get border with normalized coordinates. */
264 const rctf *norm_border = has_viewer_border ? border_.viewer_border : border_.render_border;
265
266 /* Return denormalized border within canvas. */
267 const int w = output_op->get_width();
268 const int h = output_op->get_height();
269 r_area.xmin = canvas.xmin + norm_border->xmin * w;
270 r_area.xmax = canvas.xmin + norm_border->xmax * w;
271 r_area.ymin = canvas.ymin + norm_border->ymin * h;
272 r_area.ymax = canvas.ymin + norm_border->ymax * h;
273 }
274}
275
276void FullFrameExecutionModel::operation_finished(NodeOperation *operation)
277{
278 /* Report inputs reads so that buffers may be freed/reused. */
279 const int num_inputs = operation->get_number_of_input_sockets();
280 for (int i = 0; i < num_inputs; i++) {
281 active_buffers_.read_finished(operation->get_input_operation(i));
282 }
283
284 num_operations_finished_++;
285 update_progress_bar();
286}
287
288void FullFrameExecutionModel::update_progress_bar()
289{
291 if (tree) {
292 const float progress = num_operations_finished_ / float(operations_.size());
293 tree->runtime->progress(tree->runtime->prh, progress);
294
295 char buf[128];
296 SNPRINTF(buf,
297 RPT_("Compositing | Operation %i-%li"),
298 num_operations_finished_ + 1,
299 operations_.size());
300 tree->runtime->stats_draw(tree->runtime->sdh, buf);
301 }
302}
303
304} // namespace blender::compositor
#define BLI_assert(a)
Definition BLI_assert.h:50
void BLI_rcti_translate(struct rcti *rect, int x, int y)
Definition rct.c:560
void BLI_rcti_init(struct rcti *rect, int xmin, int xmax, int ymin, int ymax)
Definition rct.c:418
bool BLI_rcti_isect(const struct rcti *src1, const struct rcti *src2, struct rcti *dest)
bool BLI_rcti_is_empty(const struct rcti *rect)
#define SNPRINTF(dst, format,...)
Definition BLI_string.h:597
#define RPT_(msgid)
Read Guarded memory(de)allocation.
SIMD_FORCE_INLINE const btScalar & w() const
Return the w value.
Definition btQuadWord.h:119
int64_t size() const
void append(const T &value)
void extend(Span< T > array)
Overall context of the compositor.
const bNodeTree * get_bnodetree() const
get the bnodetree of the context
realtime_compositor::Profiler * get_profiler() const
get the profiler
bool is_rendering() const
get the rendering field of the context
static void operation_rendered(const NodeOperation *op, MemoryBuffer *render)
Definition COM_Debug.h:93
static void graphviz(const ExecutionSystem *system, StringRefNull name="")
Definition COM_Debug.cc:340
struct blender::compositor::ExecutionModel::@171 border_
the ExecutionSystem contains the whole compositor tree.
FullFrameExecutionModel(CompositorContext &context, SharedOperationBuffers &shared_buffers, Span< NodeOperation * > operations)
void execute(ExecutionSystem &exec_system) override
NodeOperation contains calculation logic.
void set_rendered_buffer(NodeOperation *op, std::unique_ptr< MemoryBuffer > buffer)
Vector< rcti > get_areas_to_render(NodeOperation *op, int offset_x, int offset_y)
bool is_area_registered(NodeOperation *op, const rcti &area_to_render)
MemoryBuffer * get_rendered_buffer(NodeOperation *op)
void register_area(NodeOperation *op, const rcti &area_to_render)
void set_node_evaluation_time(bNodeInstanceKey node_instance_key, timeit::Nanoseconds time)
Definition profiler.cc:23
KDTree_3d * tree
draw_view in_light_buf[] float
eCompositorPriority
Possible priority settings.
Definition COM_Enums.h:33
bNodeInstanceKey node_instance_key(bNodeInstanceKey parent_key, const bNodeTree *ntree, const bNode *node)
Definition node.cc:4041
const bNodeInstanceKey NODE_INSTANCE_KEY_NONE
Definition node.cc:4021
static Vector< NodeOperation * > get_operation_dependencies(NodeOperation *operation)
Clock::time_point TimePoint
Definition BLI_timeit.hh:15
static blender::bke::bNodeSocketTemplate outputs[]
bNodeTreeRuntimeHandle * runtime
static void start()
Start the execution this methods will start the WorkScheduler. Inside this method all threads are ini...
static void stop()
stop the execution All created thread by the start method are destroyed.
float xmax
float xmin
float ymax
float ymin
int ymin
int ymax
int xmin
int xmax