Blender  V3.3
COM_NodeOperation.cc
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later
2  * Copyright 2011 Blender Foundation. */
3 
4 #include <cstdio>
5 
6 #include "COM_BufferOperation.h"
7 #include "COM_ExecutionSystem.h"
9 
10 #include "COM_NodeOperation.h" /* own include */
11 
12 namespace blender::compositor {
13 
14 /*******************
15  **** NodeOperation ****
16  *******************/
17 
19 {
20  canvas_input_index_ = 0;
22  btree_ = nullptr;
23 }
24 
26 {
27  BLI_assert(outputs_.size() > 0 && get_output_socket()->get_data_type() == DataType::Value);
28  return *get_constant_elem_default(&default_value);
29 }
30 
31 const float *NodeOperation::get_constant_elem_default(const float *default_elem)
32 {
33  BLI_assert(outputs_.size() > 0);
34  if (get_flags().is_constant_operation) {
35  return static_cast<ConstantOperation *>(this)->get_constant_elem();
36  }
37 
38  return default_elem;
39 }
40 
41 std::optional<NodeOperationHash> NodeOperation::generate_hash()
42 {
43  params_hash_ = get_default_hash_2(canvas_.xmin, canvas_.xmax);
44 
45  /* Hash subclasses params. */
46  is_hash_output_params_implemented_ = true;
48  if (!is_hash_output_params_implemented_) {
49  return std::nullopt;
50  }
51 
53  if (outputs_.size() > 0) {
54  BLI_assert(outputs_.size() == 1);
55  hash_param(this->get_output_socket()->get_data_type());
56  }
58  hash.params_hash_ = params_hash_;
59 
60  hash.parents_hash_ = 0;
61  for (NodeOperationInput &socket : inputs_) {
62  if (!socket.is_connected()) {
63  continue;
64  }
65 
66  NodeOperation &input = socket.get_link()->get_operation();
67  const bool is_constant = input.get_flags().is_constant_operation;
68  combine_hashes(hash.parents_hash_, get_default_hash(is_constant));
69  if (is_constant) {
70  const float *elem = ((ConstantOperation *)&input)->get_constant_elem();
71  const int num_channels = COM_data_type_num_channels(socket.get_data_type());
72  for (const int i : IndexRange(num_channels)) {
73  combine_hashes(hash.parents_hash_, get_default_hash(elem[i]));
74  }
75  }
76  else {
77  combine_hashes(hash.parents_hash_, get_default_hash(input.get_id()));
78  }
79  }
80 
81  hash.type_hash_ = typeid(*this).hash_code();
82  hash.operation_ = this;
83 
84  return hash;
85 }
86 
88 {
89  return &outputs_[index];
90 }
91 
93 {
94  return &inputs_[index];
95 }
96 
98 {
99  inputs_.append(NodeOperationInput(this, datatype, resize_mode));
100 }
101 
103 {
104  outputs_.append(NodeOperationOutput(this, datatype));
105 }
106 
107 void NodeOperation::determine_canvas(const rcti &preferred_area, rcti &r_area)
108 {
109  unsigned int used_canvas_index = 0;
110  if (canvas_input_index_ == RESOLUTION_INPUT_ANY) {
111  for (NodeOperationInput &input : inputs_) {
112  rcti any_area = COM_AREA_NONE;
113  const bool determined = input.determine_canvas(preferred_area, any_area);
114  if (determined) {
115  r_area = any_area;
116  break;
117  }
118  used_canvas_index += 1;
119  }
120  }
121  else if (canvas_input_index_ < inputs_.size()) {
122  NodeOperationInput &input = inputs_[canvas_input_index_];
123  input.determine_canvas(preferred_area, r_area);
124  used_canvas_index = canvas_input_index_;
125  }
126 
127  if (modify_determined_canvas_fn_) {
128  modify_determined_canvas_fn_(r_area);
129  }
130 
131  rcti unused_area = COM_AREA_NONE;
132  const rcti &local_preferred_area = r_area;
133  for (unsigned int index = 0; index < inputs_.size(); index++) {
134  if (index == used_canvas_index) {
135  continue;
136  }
137  NodeOperationInput &input = inputs_[index];
138  if (input.is_connected()) {
139  input.determine_canvas(local_preferred_area, unused_area);
140  }
141  }
142 }
143 
145 {
146  this->canvas_input_index_ = index;
147 }
148 
150 {
151  /* Pass. */
152 }
154 {
155  /* pass */
156 }
157 
159 {
160  BLI_mutex_init(&mutex_);
161 }
162 
164 {
165  BLI_mutex_lock(&mutex_);
166 }
167 
169 {
170  BLI_mutex_unlock(&mutex_);
171 }
172 
174 {
175  BLI_mutex_end(&mutex_);
176 }
177 
179 {
180  /* pass */
181 }
182 
183 void NodeOperation::set_canvas(const rcti &canvas_area)
184 {
185  canvas_ = canvas_area;
186  flags_.is_canvas_set = true;
187 }
188 
190 {
191  return canvas_;
192 }
193 
195 {
196  BLI_assert(inputs_.size() == 0);
197  flags_.is_canvas_set = false;
198 }
199 
201 {
202  return this->get_input_socket(index)->get_reader();
203 }
204 
206 {
208  if (input && input->is_connected()) {
209  return &input->get_link()->get_operation();
210  }
211 
212  return nullptr;
213 }
214 
216  ReadBufferOperation *read_operation,
217  rcti *output)
218 {
219  if (inputs_.size() == 0) {
220  BLI_rcti_init(output, input->xmin, input->xmax, input->ymin, input->ymax);
221  return false;
222  }
223 
224  rcti temp_output;
225  bool first = true;
226  for (int i = 0; i < get_number_of_input_sockets(); i++) {
227  NodeOperation *input_operation = this->get_input_operation(i);
228  if (input_operation && input_operation->determine_depending_area_of_interest(
229  input, read_operation, &temp_output)) {
230  if (first) {
231  output->xmin = temp_output.xmin;
232  output->ymin = temp_output.ymin;
233  output->xmax = temp_output.xmax;
234  output->ymax = temp_output.ymax;
235  first = false;
236  }
237  else {
238  output->xmin = MIN2(output->xmin, temp_output.xmin);
239  output->ymin = MIN2(output->ymin, temp_output.ymin);
240  output->xmax = MAX2(output->xmax, temp_output.xmax);
241  output->ymax = MAX2(output->ymax, temp_output.ymax);
242  }
243  }
244  }
245  return !first;
246 }
247 
248 /* -------------------------------------------------------------------- */
252 void NodeOperation::get_area_of_interest(const int input_idx,
253  const rcti &output_area,
254  rcti &r_input_area)
255 {
256  if (get_flags().is_fullframe_operation) {
257  r_input_area = output_area;
258  }
259  else {
260  /* Non full-frame operations never implement this method. To ensure correctness assume
261  * whole area is used. */
262  NodeOperation *input_op = get_input_operation(input_idx);
263  r_input_area = input_op->get_canvas();
264  }
265 }
266 
268  const rcti &output_area,
269  rcti &r_input_area)
270 {
271  for (int i = 0; i < get_number_of_input_sockets(); i++) {
272  if (input_op == get_input_operation(i)) {
273  get_area_of_interest(i, output_area, r_input_area);
274  return;
275  }
276  }
277  BLI_assert_msg(0, "input_op is not an input operation.");
278 }
279 
281  Span<rcti> areas,
282  Span<MemoryBuffer *> inputs_bufs)
283 {
284  if (get_flags().is_fullframe_operation) {
285  render_full_frame(output_buf, areas, inputs_bufs);
286  }
287  else {
288  render_full_frame_fallback(output_buf, areas, inputs_bufs);
289  }
290 }
291 
292 void NodeOperation::render_full_frame(MemoryBuffer *output_buf,
293  Span<rcti> areas,
294  Span<MemoryBuffer *> inputs_bufs)
295 {
296  init_execution();
297  for (const rcti &area : areas) {
298  update_memory_buffer(output_buf, area, inputs_bufs);
299  }
301 }
302 
303 void NodeOperation::render_full_frame_fallback(MemoryBuffer *output_buf,
304  Span<rcti> areas,
305  Span<MemoryBuffer *> inputs_bufs)
306 {
307  Vector<NodeOperationOutput *> orig_input_links = replace_inputs_with_buffers(inputs_bufs);
308 
309  init_execution();
311  if (!is_output_operation && output_buf->is_a_single_elem()) {
312  float *output_elem = output_buf->get_elem(0, 0);
313  read_sampled(output_elem, 0, 0, PixelSampler::Nearest);
314  }
315  else {
316  for (const rcti &rect : areas) {
317  exec_system_->execute_work(rect, [=](const rcti &split_rect) {
318  rcti tile_rect = split_rect;
319  if (is_output_operation) {
320  execute_region(&tile_rect, 0);
321  }
322  else {
323  render_tile(output_buf, &tile_rect);
324  }
325  });
326  }
327  }
329 
330  remove_buffers_and_restore_original_inputs(orig_input_links);
331 }
332 
333 void NodeOperation::render_tile(MemoryBuffer *output_buf, rcti *tile_rect)
334 {
335  const bool is_complex = get_flags().complex;
336  void *tile_data = is_complex ? initialize_tile_data(tile_rect) : nullptr;
337  const int elem_stride = output_buf->elem_stride;
338  for (int y = tile_rect->ymin; y < tile_rect->ymax; y++) {
339  float *output_elem = output_buf->get_elem(tile_rect->xmin, y);
340  if (is_complex) {
341  for (int x = tile_rect->xmin; x < tile_rect->xmax; x++) {
342  read(output_elem, x, y, tile_data);
343  output_elem += elem_stride;
344  }
345  }
346  else {
347  for (int x = tile_rect->xmin; x < tile_rect->xmax; x++) {
348  read_sampled(output_elem, x, y, PixelSampler::Nearest);
349  output_elem += elem_stride;
350  }
351  }
352  }
353  if (tile_data) {
354  deinitialize_tile_data(tile_rect, tile_data);
355  }
356 }
357 
358 Vector<NodeOperationOutput *> NodeOperation::replace_inputs_with_buffers(
359  Span<MemoryBuffer *> inputs_bufs)
360 {
361  BLI_assert(inputs_bufs.size() == get_number_of_input_sockets());
362  Vector<NodeOperationOutput *> orig_links(inputs_bufs.size());
363  for (int i = 0; i < inputs_bufs.size(); i++) {
364  NodeOperationInput *input_socket = get_input_socket(i);
365  BufferOperation *buffer_op = new BufferOperation(inputs_bufs[i],
366  input_socket->get_data_type());
367  orig_links[i] = input_socket->get_link();
368  input_socket->set_link(buffer_op->get_output_socket());
369  buffer_op->init_execution();
370  }
371  return orig_links;
372 }
373 
374 void NodeOperation::remove_buffers_and_restore_original_inputs(
375  Span<NodeOperationOutput *> original_inputs_links)
376 {
377  BLI_assert(original_inputs_links.size() == get_number_of_input_sockets());
378  for (int i = 0; i < original_inputs_links.size(); i++) {
379  NodeOperation *buffer_op = get_input_operation(i);
380  BLI_assert(buffer_op != nullptr);
381  BLI_assert(typeid(*buffer_op) == typeid(BufferOperation));
382  buffer_op->deinit_execution();
383  NodeOperationInput *input_socket = get_input_socket(i);
384  input_socket->set_link(original_inputs_links[i]);
385  delete buffer_op;
386  }
387 }
388 
391 /*****************
392  **** OpInput ****
393  *****************/
394 
396  DataType datatype,
397  ResizeMode resize_mode)
398  : operation_(op), datatype_(datatype), resize_mode_(resize_mode), link_(nullptr)
399 {
400 }
401 
403 {
404  if (is_connected()) {
405  return &link_->get_operation();
406  }
407 
408  return nullptr;
409 }
410 
411 bool NodeOperationInput::determine_canvas(const rcti &preferred_area, rcti &r_area)
412 {
413  if (link_) {
414  link_->determine_canvas(preferred_area, r_area);
415  return !BLI_rcti_is_empty(&r_area);
416  }
417  return false;
418 }
419 
420 /******************
421  **** OpOutput ****
422  ******************/
423 
425  : operation_(op), datatype_(datatype)
426 {
427 }
428 
429 void NodeOperationOutput::determine_canvas(const rcti &preferred_area, rcti &r_area)
430 {
431  NodeOperation &operation = get_operation();
432  if (operation.get_flags().is_canvas_set) {
433  r_area = operation.get_canvas();
434  }
435  else {
436  operation.determine_canvas(preferred_area, r_area);
437  if (!BLI_rcti_is_empty(&r_area)) {
438  operation.set_canvas(r_area);
439  }
440  }
441 }
442 
443 std::ostream &operator<<(std::ostream &os, const NodeOperationFlags &node_operation_flags)
444 {
445  if (node_operation_flags.complex) {
446  os << "complex,";
447  }
448  if (node_operation_flags.open_cl) {
449  os << "open_cl,";
450  }
451  if (node_operation_flags.single_threaded) {
452  os << "single_threaded,";
453  }
454  if (node_operation_flags.use_render_border) {
455  os << "render_border,";
456  }
457  if (node_operation_flags.use_viewer_border) {
458  os << "view_border,";
459  }
460  if (node_operation_flags.is_canvas_set) {
461  os << "canvas_set,";
462  }
463  if (node_operation_flags.is_set_operation) {
464  os << "set_operation,";
465  }
466  if (node_operation_flags.is_write_buffer_operation) {
467  os << "write_buffer,";
468  }
469  if (node_operation_flags.is_read_buffer_operation) {
470  os << "read_buffer,";
471  }
472  if (node_operation_flags.is_proxy_operation) {
473  os << "proxy,";
474  }
475  if (node_operation_flags.is_viewer_operation) {
476  os << "viewer,";
477  }
478  if (node_operation_flags.is_preview_operation) {
479  os << "preview,";
480  }
481  if (!node_operation_flags.use_datatype_conversion) {
482  os << "no_conversion,";
483  }
484  if (node_operation_flags.is_fullframe_operation) {
485  os << "full_frame,";
486  }
487  if (node_operation_flags.is_constant_operation) {
488  os << "contant_operation,";
489  }
490  if (node_operation_flags.can_be_constant) {
491  os << "can_be_constant,";
492  }
493 
494  return os;
495 }
496 
497 std::ostream &operator<<(std::ostream &os, const NodeOperation &node_operation)
498 {
499  NodeOperationFlags flags = node_operation.get_flags();
500  os << "NodeOperation(";
501  os << "id=" << node_operation.get_id();
502  if (!node_operation.get_name().empty()) {
503  os << ",name=" << node_operation.get_name();
504  }
505  os << ",flags={" << flags << "}";
506  if (flags.is_read_buffer_operation) {
507  const ReadBufferOperation *read_operation = (const ReadBufferOperation *)&node_operation;
508  const MemoryProxy *proxy = read_operation->get_memory_proxy();
509  if (proxy) {
510  const WriteBufferOperation *write_operation = proxy->get_write_buffer_operation();
511  if (write_operation) {
512  os << ",write=" << (NodeOperation &)*write_operation;
513  }
514  }
515  }
516  os << ")";
517 
518  return os;
519 }
520 
521 } // namespace blender::compositor
#define BLI_assert(a)
Definition: BLI_assert.h:46
#define BLI_assert_msg(a, msg)
Definition: BLI_assert.h:53
void BLI_rcti_init(struct rcti *rect, int xmin, int xmax, int ymin, int ymax)
Definition: rct.c:417
bool BLI_rcti_is_empty(const struct rcti *rect)
void BLI_mutex_end(ThreadMutex *mutex)
Definition: threads.cc:388
void BLI_mutex_init(ThreadMutex *mutex)
Definition: threads.cc:368
void BLI_mutex_lock(ThreadMutex *mutex)
Definition: threads.cc:373
void BLI_mutex_unlock(ThreadMutex *mutex)
Definition: threads.cc:378
#define MAX2(a, b)
#define MIN2(a, b)
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum const void *lists _GL_VOID_RET _GL_VOID const GLdouble *equation _GL_VOID_RET _GL_VOID GLdouble GLdouble blue _GL_VOID_RET _GL_VOID GLfloat GLfloat blue _GL_VOID_RET _GL_VOID GLint GLint blue _GL_VOID_RET _GL_VOID GLshort GLshort blue _GL_VOID_RET _GL_VOID GLubyte GLubyte blue _GL_VOID_RET _GL_VOID GLuint GLuint blue _GL_VOID_RET _GL_VOID GLushort GLushort blue _GL_VOID_RET _GL_VOID GLbyte GLbyte GLbyte alpha _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble alpha _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat alpha _GL_VOID_RET _GL_VOID GLint GLint GLint alpha _GL_VOID_RET _GL_VOID GLshort GLshort GLshort alpha _GL_VOID_RET _GL_VOID GLubyte GLubyte GLubyte alpha _GL_VOID_RET _GL_VOID GLuint GLuint GLuint alpha _GL_VOID_RET _GL_VOID GLushort GLushort GLushort alpha _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLint y
void execute_work(const rcti &work_rect, std::function< void(const rcti &split_rect)> work_func)
a MemoryBuffer contains access to the data of a chunk
A MemoryProxy is a unique identifier for a memory buffer. A single MemoryProxy is used among all chun...
WriteBufferOperation * get_write_buffer_operation() const
get the WriteBufferOperation that is responsible for writing to this MemoryProxy
bool determine_canvas(const rcti &preferred_area, rcti &r_area)
NodeOperationInput(NodeOperation *op, DataType datatype, ResizeMode resize_mode=ResizeMode::Center)
NodeOperationOutput(NodeOperation *op, DataType datatype)
void determine_canvas(const rcti &preferred_area, rcti &r_area)
NodeOperation contains calculation logic.
static void combine_hashes(size_t &combined, size_t other)
void set_canvas(const rcti &canvas_area)
void add_output_socket(DataType datatype)
unsigned int get_number_of_output_sockets() const
virtual bool is_output_operation(bool) const
is_output_operation determines whether this operation is an output of the ExecutionSystem during rend...
virtual void update_memory_buffer(MemoryBuffer *UNUSED(output), const rcti &UNUSED(area), Span< MemoryBuffer * > UNUSED(inputs))
const NodeOperationFlags get_flags() const
SocketReader * get_input_socket_reader(unsigned int index)
virtual void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area)
Get input operation area being read by this operation on rendering given output area.
std::optional< NodeOperationHash > generate_hash()
float get_constant_value_default(float default_value)
unsigned int get_number_of_input_sockets() const
NodeOperationOutput * get_output_socket(unsigned int index=0)
NodeOperation * get_input_operation(int index)
const float * get_constant_elem_default(const float *default_elem)
NodeOperationInput * get_input_socket(unsigned int index)
virtual void execute_region(rcti *, unsigned int)
when a chunk is executed by a CPUDevice, this method is called
void read(float result[4], int x, int y, void *chunk_data)
virtual bool determine_depending_area_of_interest(rcti *input, ReadBufferOperation *read_operation, rcti *output)
virtual void deinitialize_tile_data(rcti *, void *)
void read_sampled(float result[4], float x, float y, PixelSampler sampler)
void add_input_socket(DataType datatype, ResizeMode resize_mode=ResizeMode::Center)
const std::string get_name() const
void set_canvas_input_index(unsigned int index)
set the index of the input socket that will determine the canvas of this operation
void hash_params(T1 param1, T2 param2)
virtual void * initialize_tile_data(rcti *)
void render(MemoryBuffer *output_buf, Span< rcti > areas, Span< MemoryBuffer * > inputs_bufs)
virtual void determine_canvas(const rcti &preferred_area, rcti &r_area)
DataType
possible data types for sockets
Definition: COM_defines.h:30
ResizeMode
Resize modes of inputsockets How are the input and working resolutions matched.
ccl_global KernelShaderEvalInput ccl_global float * output
ccl_global KernelShaderEvalInput * input
static constexpr unsigned int RESOLUTION_INPUT_ANY
static void area(int d1, int d2, int e1, int e2, float weights[2])
constexpr int COM_data_type_num_channels(const DataType datatype)
Definition: COM_defines.h:42
constexpr rcti COM_AREA_NONE
Definition: COM_defines.h:112
std::ostream & operator<<(std::ostream &os, const eCompositorPriority &priority)
Definition: COM_Enums.cc:26
uint64_t get_default_hash(const T &v)
Definition: BLI_hash.hh:218
uint64_t get_default_hash_2(const T1 &v1, const T2 &v2)
Definition: BLI_hash.hh:223
#define hash
Definition: noise.c:153
int ymin
Definition: DNA_vec_types.h:64
int ymax
Definition: DNA_vec_types.h:64
int xmin
Definition: DNA_vec_types.h:63
int xmax
Definition: DNA_vec_types.h:63