Blender  V3.3
COM_NodeOperationBuilder.cc
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later
2  * Copyright 2013 Blender Foundation. */
3 
4 #include <set>
5 
6 #include "BLI_multi_value_map.hh"
7 
8 #include "COM_Converter.h"
9 #include "COM_Debug.h"
10 
11 #include "COM_ExecutionGroup.h"
12 #include "COM_PreviewOperation.h"
14 #include "COM_SetColorOperation.h"
15 #include "COM_SetValueOperation.h"
16 #include "COM_SetVectorOperation.h"
17 #include "COM_ViewerOperation.h"
19 
20 #include "COM_ConstantFolder.h"
21 #include "COM_NodeOperationBuilder.h" /* own include */
22 
23 namespace blender::compositor {
24 
26  bNodeTree *b_nodetree,
27  ExecutionSystem *system)
28  : context_(context), exec_system_(system), current_node_(nullptr), active_viewer_(nullptr)
29 {
30  graph_.from_bNodeTree(*context, b_nodetree);
31 }
32 
34 {
35  /* interface handle for nodes */
36  NodeConverter converter(this);
37 
38  for (Node *node : graph_.nodes()) {
39  current_node_ = node;
40 
42  node->convert_to_operations(converter, *context_);
43  }
44 
45  current_node_ = nullptr;
46 
47  /* The input map constructed by nodes maps operation inputs to node inputs.
48  * Inverting yields a map of node inputs to all connected operation inputs,
49  * so multiple operations can use the same node input.
50  */
52  for (Map<NodeOperationInput *, NodeInput *>::MutableItem item : input_map_.items()) {
53  inverse_input_map.add(item.value, item.key);
54  }
55 
56  for (const NodeGraph::Link &link : graph_.links()) {
57  NodeOutput *from = link.from;
58  NodeInput *to = link.to;
59 
60  NodeOperationOutput *op_from = output_map_.lookup_default(from, nullptr);
61 
62  const blender::Span<NodeOperationInput *> op_to_list = inverse_input_map.lookup(to);
63  if (!op_from || op_to_list.is_empty()) {
64  /* XXX allow this? error/debug message? */
65  // BLI_assert(false);
66  /* XXX NOTE: this can happen with certain nodes (e.g. OutputFile)
67  * which only generate operations in certain circumstances (rendering)
68  * just let this pass silently for now ...
69  */
70  continue;
71  }
72 
73  for (NodeOperationInput *op_to : op_to_list) {
74  add_link(op_from, op_to);
75  }
76  }
77 
79 
81 
83 
85  save_graphviz("compositor_prior_folding");
86  ConstantFolder folder(*this);
87  folder.fold_operations();
88  }
89 
91 
92  save_graphviz("compositor_prior_merging");
93  merge_equal_operations();
94 
95  if (context_->get_execution_model() == eExecutionModel::Tiled) {
96  /* surround complex ops with read/write buffer */
98  }
99 
100  /* links not available from here on */
101  /* XXX make links_ a local variable to avoid confusion! */
102  links_.clear();
103 
105 
106  /* ensure topological (link-based) order of nodes */
107  // sort_operations(); /* not needed yet. */
108 
109  if (context_->get_execution_model() == eExecutionModel::Tiled) {
110  /* create execution groups */
112  }
113 
114  /* transfer resulting operations to the system */
115  system->set_operations(operations_, groups_);
116 }
117 
119 {
120  operation->set_id(operations_.size());
121  operations_.append(operation);
122  if (current_node_) {
123  operation->set_name(current_node_->get_bnode()->name);
124  }
125  operation->set_execution_model(context_->get_execution_model());
126  operation->set_execution_system(exec_system_);
127 }
128 
130  ConstantOperation *constant_operation)
131 {
132  BLI_assert(constant_operation->get_number_of_input_sockets() == 0);
133  unlink_inputs_and_relink_outputs(operation, constant_operation);
134  add_operation(constant_operation);
135 }
136 
137 void NodeOperationBuilder::unlink_inputs_and_relink_outputs(NodeOperation *unlinked_op,
138  NodeOperation *linked_op)
139 {
140  int i = 0;
141  while (i < links_.size()) {
142  Link &link = links_[i];
143  if (&link.to()->get_operation() == unlinked_op) {
144  link.to()->set_link(nullptr);
145  links_.remove(i);
146  continue;
147  }
148 
149  if (&link.from()->get_operation() == unlinked_op) {
150  link.to()->set_link(linked_op->get_output_socket());
151  links_[i] = Link(linked_op->get_output_socket(), link.to());
152  }
153  i++;
154  }
155 }
156 
158  NodeOperationInput *operation_socket)
159 {
160  BLI_assert(current_node_);
161  BLI_assert(node_socket->get_node() == current_node_);
162 
163  /* NOTE: this maps operation sockets to node sockets.
164  * for resolving links the map will be inverted first in convert_to_operations,
165  * to get a list of links for each node input socket.
166  */
167  input_map_.add_new(operation_socket, node_socket);
168 }
169 
171  NodeOperationOutput *operation_socket)
172 {
173  BLI_assert(current_node_);
174  BLI_assert(node_socket->get_node() == current_node_);
175 
176  output_map_.add_new(node_socket, operation_socket);
177 }
178 
180 {
181  if (to->is_connected()) {
182  return;
183  }
184 
185  links_.append(Link(from, to));
186 
187  /* register with the input */
188  to->set_link(from);
189 }
190 
192 {
193  int index = 0;
194  for (Link &link : links_) {
195  if (link.to() == to) {
196  /* unregister with the input */
197  to->set_link(nullptr);
198 
199  links_.remove(index);
200  return;
201  }
202  index++;
203  }
204 }
205 
206 PreviewOperation *NodeOperationBuilder::make_preview_operation() const
207 {
208  BLI_assert(current_node_);
209 
210  if (!(current_node_->get_bnode()->flag & NODE_PREVIEW)) {
211  return nullptr;
212  }
213  /* previews only in the active group */
214  if (!current_node_->is_in_active_group()) {
215  return nullptr;
216  }
217  /* do not calculate previews of hidden nodes */
218  if (current_node_->get_bnode()->flag & NODE_HIDDEN) {
219  return nullptr;
220  }
221 
222  bNodeInstanceHash *previews = context_->get_preview_hash();
223  if (previews) {
224  Scene *scene = context_->get_scene();
225  PreviewOperation *operation = new PreviewOperation(&scene->view_settings,
227  current_node_->get_bnode()->preview_xsize,
228  current_node_->get_bnode()->preview_ysize);
229  operation->set_bnodetree(context_->get_bnodetree());
230  operation->verify_preview(previews, current_node_->get_instance_key());
231  return operation;
232  }
233 
234  return nullptr;
235 }
236 
238 {
239  PreviewOperation *operation = make_preview_operation();
240  if (operation) {
241  add_operation(operation);
242 
243  add_link(output, operation->get_input_socket(0));
244  }
245 }
246 
248 {
249  PreviewOperation *operation = make_preview_operation();
250  if (operation) {
251  add_operation(operation);
252 
253  map_input_socket(input, operation->get_input_socket(0));
254  }
255 }
256 
258 {
259  if (active_viewer_) {
260  if (current_node_->is_in_active_group()) {
261  /* deactivate previous viewer */
262  active_viewer_->set_active(false);
263 
264  active_viewer_ = viewer;
265  viewer->set_active(true);
266  }
267  }
268  else {
269  if (current_node_->get_bnodetree() == context_->get_bnodetree()) {
270  active_viewer_ = viewer;
271  viewer->set_active(true);
272  }
273  }
274 }
275 
276 /****************************
277  **** Optimization Steps ****
278  ****************************/
279 
281 {
282  Vector<Link> convert_links;
283  for (const Link &link : links_) {
284  /* proxy operations can skip data type conversion */
285  NodeOperation *from_op = &link.from()->get_operation();
286  NodeOperation *to_op = &link.to()->get_operation();
287  if (!(from_op->get_flags().use_datatype_conversion ||
289  continue;
290  }
291 
292  if (link.from()->get_data_type() != link.to()->get_data_type()) {
293  convert_links.append(link);
294  }
295  }
296  for (const Link &link : convert_links) {
297  NodeOperation *converter = COM_convert_data_type(*link.from(), *link.to());
298  if (converter) {
299  add_operation(converter);
300 
301  remove_input_link(link.to());
302  add_link(link.from(), converter->get_input_socket(0));
303  add_link(converter->get_output_socket(0), link.to());
304  }
305  }
306 }
307 
309 {
310  /* NOTE: unconnected inputs cached first to avoid modifying
311  * operations_ while iterating over it
312  */
313  Vector<NodeOperationInput *> pending_inputs;
314  for (NodeOperation *op : operations_) {
315  for (int k = 0; k < op->get_number_of_input_sockets(); ++k) {
316  NodeOperationInput *input = op->get_input_socket(k);
317  if (!input->is_connected()) {
318  pending_inputs.append(input);
319  }
320  }
321  }
322  for (NodeOperationInput *input : pending_inputs) {
323  add_input_constant_value(input, input_map_.lookup_default(input, nullptr));
324  }
325 }
326 
328  const NodeInput *node_input)
329 {
330  switch (input->get_data_type()) {
331  case DataType::Value: {
332  float value;
333  if (node_input && node_input->get_bnode_socket()) {
334  value = node_input->get_editor_value_float();
335  }
336  else {
337  value = 0.0f;
338  }
339 
341  op->set_value(value);
342  add_operation(op);
344  break;
345  }
346  case DataType::Color: {
347  float value[4];
348  if (node_input && node_input->get_bnode_socket()) {
349  node_input->get_editor_value_color(value);
350  }
351  else {
352  zero_v4(value);
353  }
354 
356  op->set_channels(value);
357  add_operation(op);
359  break;
360  }
361  case DataType::Vector: {
362  float value[3];
363  if (node_input && node_input->get_bnode_socket()) {
364  node_input->get_editor_value_vector(value);
365  }
366  else {
367  zero_v3(value);
368  }
369 
371  op->set_vector(value);
372  add_operation(op);
374  break;
375  }
376  }
377 }
378 
380 {
381  Vector<Link> proxy_links;
382  for (const Link &link : links_) {
383  /* don't replace links from proxy to proxy, since we may need them for replacing others! */
384  if (link.from()->get_operation().get_flags().is_proxy_operation &&
385  !link.to()->get_operation().get_flags().is_proxy_operation) {
386  proxy_links.append(link);
387  }
388  }
389 
390  for (const Link &link : proxy_links) {
391  NodeOperationInput *to = link.to();
392  NodeOperationOutput *from = link.from();
393  do {
394  /* walk upstream bypassing the proxy operation */
395  from = from->get_operation().get_input_socket(0)->get_link();
396  } while (from && from->get_operation().get_flags().is_proxy_operation);
397 
398  remove_input_link(to);
399  /* we may not have a final proxy input link,
400  * in that case it just gets dropped
401  */
402  if (from) {
403  add_link(from, to);
404  }
405  }
406 }
407 
409 {
410  /* Determine all canvas areas of the operations. */
411  const rcti &preferred_area = COM_AREA_NONE;
412  for (NodeOperation *op : operations_) {
413  if (op->is_output_operation(context_->is_rendering()) &&
414  !op->get_flags().is_preview_operation) {
415  rcti canvas = COM_AREA_NONE;
416  op->determine_canvas(preferred_area, canvas);
417  op->set_canvas(canvas);
418  }
419  }
420 
421  for (NodeOperation *op : operations_) {
422  if (op->is_output_operation(context_->is_rendering()) &&
423  op->get_flags().is_preview_operation) {
424  rcti canvas = COM_AREA_NONE;
425  op->determine_canvas(preferred_area, canvas);
426  op->set_canvas(canvas);
427  }
428  }
429 
430  /* Convert operation canvases when needed. */
431  {
432  Vector<Link> convert_links;
433  for (const Link &link : links_) {
434  if (link.to()->get_resize_mode() != ResizeMode::None) {
435  const rcti &from_canvas = link.from()->get_operation().get_canvas();
436  const rcti &to_canvas = link.to()->get_operation().get_canvas();
437 
438  bool needs_conversion;
439  if (link.to()->get_resize_mode() == ResizeMode::Align) {
440  needs_conversion = from_canvas.xmin != to_canvas.xmin ||
441  from_canvas.ymin != to_canvas.ymin;
442  }
443  else {
444  needs_conversion = !BLI_rcti_compare(&from_canvas, &to_canvas);
445  }
446 
447  if (needs_conversion) {
448  convert_links.append(link);
449  }
450  }
451  }
452  for (const Link &link : convert_links) {
453  COM_convert_canvas(*this, link.from(), link.to());
454  }
455  }
456 }
457 
459 {
461  for (NodeOperation *op : operations) {
462  std::optional<NodeOperationHash> hash = op->generate_hash();
463  if (hash) {
464  hashes.append(std::move(*hash));
465  }
466  }
467  return hashes;
468 }
469 
470 void NodeOperationBuilder::merge_equal_operations()
471 {
472  bool check_for_next_merge = true;
473  while (check_for_next_merge) {
474  /* Re-generate hashes with any change. */
475  Vector<NodeOperationHash> hashes = generate_hashes(operations_);
476 
477  /* Make hashes be consecutive when they are equal. */
478  std::sort(hashes.begin(), hashes.end());
479 
480  bool any_merged = false;
481  const NodeOperationHash *prev_hash = nullptr;
482  for (const NodeOperationHash &hash : hashes) {
483  if (prev_hash && *prev_hash == hash) {
484  merge_equal_operations(prev_hash->get_operation(), hash.get_operation());
485  any_merged = true;
486  }
487  prev_hash = &hash;
488  }
489 
490  check_for_next_merge = any_merged;
491  }
492 }
493 
494 void NodeOperationBuilder::merge_equal_operations(NodeOperation *from, NodeOperation *into)
495 {
496  unlink_inputs_and_relink_outputs(from, into);
497  operations_.remove_first_occurrence_and_reorder(from);
498  delete from;
499 }
500 
503 {
505  for (const Link &link : links_) {
506  if (link.from() == output) {
507  inputs.append(link.to());
508  }
509  }
510  return inputs;
511 }
512 
515 {
516  for (const Link &link : links_) {
517  if (link.from() == output) {
518  NodeOperation &op = link.to()->get_operation();
520  return (WriteBufferOperation *)(&op);
521  }
522  }
523  }
524  return nullptr;
525 }
526 
529 {
530  if (!input->is_connected()) {
531  return;
532  }
533 
534  NodeOperationOutput *output = input->get_link();
535  if (output->get_operation().get_flags().is_read_buffer_operation) {
536  /* input is already buffered, no need to add another */
537  return;
538  }
539 
540  /* this link will be replaced below */
542 
543  /* check of other end already has write operation, otherwise add a new one */
545  if (!writeoperation) {
546  writeoperation = new WriteBufferOperation(output->get_data_type());
547  writeoperation->set_bnodetree(context_->get_bnodetree());
548  add_operation(writeoperation);
549 
550  add_link(output, writeoperation->get_input_socket(0));
551 
552  writeoperation->read_resolution_from_input_socket();
553  }
554 
555  /* add readbuffer op for the input */
556  ReadBufferOperation *readoperation = new ReadBufferOperation(output->get_data_type());
557  readoperation->set_memory_proxy(writeoperation->get_memory_proxy());
558  this->add_operation(readoperation);
559 
560  add_link(readoperation->get_output_socket(), input);
561 
562  readoperation->read_resolution_from_write_buffer();
563 }
564 
567 {
568  /* cache connected sockets, so we can safely remove links first before replacing them */
570  if (targets.is_empty()) {
571  return;
572  }
573 
574  WriteBufferOperation *write_operation = nullptr;
575  for (NodeOperationInput *target : targets) {
576  /* try to find existing write buffer operation */
577  if (target->get_operation().get_flags().is_write_buffer_operation) {
578  BLI_assert(write_operation == nullptr); /* there should only be one write op connected */
579  write_operation = (WriteBufferOperation *)(&target->get_operation());
580  }
581  else {
582  /* remove all links to other nodes */
583  remove_input_link(target);
584  }
585  }
586 
587  /* if no write buffer operation exists yet, create a new one */
588  if (!write_operation) {
589  write_operation = new WriteBufferOperation(operation->get_output_socket()->get_data_type());
590  write_operation->set_bnodetree(context_->get_bnodetree());
591  add_operation(write_operation);
592 
593  add_link(output, write_operation->get_input_socket(0));
594  }
595 
596  write_operation->read_resolution_from_input_socket();
597 
598  /* add readbuffer op for every former connected input */
599  for (NodeOperationInput *target : targets) {
600  if (&target->get_operation() == write_operation) {
601  continue; /* skip existing write op links */
602  }
603 
604  ReadBufferOperation *readoperation = new ReadBufferOperation(
605  operation->get_output_socket()->get_data_type());
606  readoperation->set_memory_proxy(write_operation->get_memory_proxy());
607  add_operation(readoperation);
608 
609  add_link(readoperation->get_output_socket(), target);
610 
611  readoperation->read_resolution_from_write_buffer();
612  }
613 }
614 
616 {
617  /* NOTE: complex ops and get cached here first, since adding operations
618  * will invalidate iterators over the main operations_
619  */
620  Vector<NodeOperation *> complex_ops;
621  for (NodeOperation *operation : operations_) {
622  if (operation->get_flags().complex) {
623  complex_ops.append(operation);
624  }
625  }
626 
627  for (NodeOperation *op : complex_ops) {
629 
630  for (int index = 0; index < op->get_number_of_input_sockets(); index++) {
631  add_input_buffers(op, op->get_input_socket(index));
632  }
633 
634  for (int index = 0; index < op->get_number_of_output_sockets(); index++) {
635  add_output_buffers(op, op->get_output_socket(index));
636  }
637  }
638 }
639 
640 using Tags = std::set<NodeOperation *>;
641 
643 {
644  if (reachable.find(op) != reachable.end()) {
645  return;
646  }
647  reachable.insert(op);
648 
649  for (int i = 0; i < op->get_number_of_input_sockets(); i++) {
651  if (input->is_connected()) {
652  find_reachable_operations_recursive(reachable, &input->get_link()->get_operation());
653  }
654  }
655 
656  /* associated write-buffer operations are executed as well */
658  ReadBufferOperation *read_op = (ReadBufferOperation *)op;
659  MemoryProxy *memproxy = read_op->get_memory_proxy();
661  }
662 }
663 
665 {
666  Tags reachable;
667  for (NodeOperation *op : operations_) {
668  /* output operations are primary executed operations */
669  if (op->is_output_operation(context_->is_rendering())) {
671  }
672  }
673 
674  /* delete unreachable operations */
675  Vector<NodeOperation *> reachable_ops;
676  for (NodeOperation *op : operations_) {
677  if (reachable.find(op) != reachable.end()) {
678  reachable_ops.append(op);
679  }
680  else {
681  delete op;
682  }
683  }
684  /* finally replace the operations list with the pruned list */
685  operations_ = reachable_ops;
686 }
687 
688 /* topological (depth-first) sorting of operations */
690  Tags &visited,
691  NodeOperation *op)
692 {
693  if (visited.find(op) != visited.end()) {
694  return;
695  }
696  visited.insert(op);
697 
698  for (int i = 0; i < op->get_number_of_input_sockets(); i++) {
700  if (input->is_connected()) {
701  sort_operations_recursive(sorted, visited, &input->get_link()->get_operation());
702  }
703  }
704 
705  sorted.append(op);
706 }
707 
709 {
711  sorted.reserve(operations_.size());
712  Tags visited;
713 
714  for (NodeOperation *operation : operations_) {
715  sort_operations_recursive(sorted, visited, operation);
716  }
717 
718  operations_ = sorted;
719 }
720 
722 {
723  if (visited.find(op) != visited.end()) {
724  return;
725  }
726  visited.insert(op);
727 
728  if (!group->add_operation(op)) {
729  return;
730  }
731 
732  /* add all eligible input ops to the group */
733  for (int i = 0; i < op->get_number_of_input_sockets(); i++) {
735  if (input->is_connected()) {
736  add_group_operations_recursive(visited, &input->get_link()->get_operation(), group);
737  }
738  }
739 }
740 
742 {
743  ExecutionGroup *group = new ExecutionGroup(groups_.size());
744  groups_.append(group);
745 
746  Tags visited;
748 
749  return group;
750 }
751 
753 {
754  for (NodeOperation *op : operations_) {
755  if (op->is_output_operation(context_->is_rendering())) {
756  ExecutionGroup *group = make_group(op);
757  group->set_output_execution_group(true);
758  }
759 
760  /* add new groups for associated memory proxies where needed */
761  if (op->get_flags().is_read_buffer_operation) {
762  ReadBufferOperation *read_op = (ReadBufferOperation *)op;
763  MemoryProxy *memproxy = read_op->get_memory_proxy();
764 
765  if (memproxy->get_executor() == nullptr) {
767  memproxy->set_executor(group);
768  }
769  }
770  }
771 }
772 
773 void NodeOperationBuilder::save_graphviz(StringRefNull name)
774 {
775  if (COM_EXPORT_GRAPHVIZ) {
776  exec_system_->set_operations(operations_, groups_);
777  DebugInfo::graphviz(exec_system_, name);
778  }
779 }
780 
781 std::ostream &operator<<(std::ostream &os, const NodeOperationBuilder &builder)
782 {
783  os << "# Builder start\n";
784  os << "digraph G {\n";
785  os << " rankdir=LR;\n";
786  os << " node [shape=box];\n";
787  for (const NodeOperation *operation : builder.get_operations()) {
788  os << " op" << operation->get_id() << " [label=\"" << *operation << "\"];\n";
789  }
790 
791  os << "\n";
792  for (const NodeOperationBuilder::Link &link : builder.get_links()) {
793  os << " op" << link.from()->get_operation().get_id() << " -> op"
794  << link.to()->get_operation().get_id() << ";\n";
795  }
796  for (const NodeOperation *operation : builder.get_operations()) {
797  if (operation->get_flags().is_read_buffer_operation) {
798  const ReadBufferOperation &read_operation = static_cast<const ReadBufferOperation &>(
799  *operation);
800  const WriteBufferOperation &write_operation =
801  *read_operation.get_memory_proxy()->get_write_buffer_operation();
802  os << " op" << write_operation.get_id() << " -> op" << read_operation.get_id() << ";\n";
803  }
804  }
805 
806  os << "}\n";
807  os << "# Builder end\n";
808  return os;
809 }
810 
811 std::ostream &operator<<(std::ostream &os, const NodeOperationBuilder::Link &link)
812 {
813  os << link.from()->get_operation().get_id() << " -> " << link.to()->get_operation().get_id();
814  return os;
815 }
816 
817 } // namespace blender::compositor
#define BLI_assert(a)
Definition: BLI_assert.h:46
MINLINE void zero_v4(float r[4])
MINLINE void zero_v3(float r[3])
bool BLI_rcti_compare(const struct rcti *rect_a, const struct rcti *rect_b)
struct Link Link
#define NODE_HIDDEN
#define NODE_PREVIEW
void sort(btMatrix3x3 &U, btVector3 &sigma, btMatrix3x3 &V, int t)
Helper function of 3X3 SVD for sorting singular values.
Span< Value > lookup(const Key &key) const
void add(const Key &key, const Value &value)
constexpr bool is_empty() const
Definition: BLI_span.hh:248
void append(const T &value)
Definition: BLI_vector.hh:433
bool is_empty() const
Definition: BLI_vector.hh:706
void reserve(const int64_t min_capacity)
Definition: BLI_vector.hh:340
Overall context of the compositor.
bNodeInstanceHash * get_preview_hash() const
get the preview image hash table
bool is_rendering() const
get the rendering field of the context
const bNodeTree * get_bnodetree() const
get the bnodetree of the context
static void node_to_operations(const Node *node)
Definition: COM_Debug.h:81
static void operation_read_write_buffer(const NodeOperation *operation)
Definition: COM_Debug.h:95
static void graphviz(const ExecutionSystem *system, StringRefNull name="")
Definition: COM_Debug.cc:414
Class ExecutionGroup is a group of Operations that are executed as one. This grouping is used to comb...
bool add_operation(NodeOperation *operation)
add an operation to this ExecutionGroup
void set_output_execution_group(bool is_output)
set whether this ExecutionGroup is an output
the ExecutionSystem contains the whole compositor tree.
void set_operations(const Vector< NodeOperation * > &operations, const Vector< ExecutionGroup * > &groups)
A MemoryProxy is a unique identifier for a memory buffer. A single MemoryProxy is used among all chun...
void set_executor(ExecutionGroup *executor)
set the ExecutionGroup that can be scheduled to calculate a certain chunk.
ExecutionGroup * get_executor() const
get the ExecutionGroup that can be scheduled to calculate a certain chunk.
WriteBufferOperation * get_write_buffer_operation() const
get the WriteBufferOperation that is responsible for writing to this MemoryProxy
void from_bNodeTree(const CompositorContext &context, bNodeTree *tree)
const Vector< Node * > & nodes() const
Definition: COM_NodeGraph.h:41
const Vector< Link > & links() const
Definition: COM_NodeGraph.h:45
NodeInput are sockets that can receive data/input.
Definition: COM_Node.h:190
void get_editor_value_color(float *value) const
Definition: COM_Node.cc:142
bNodeSocket * get_bnode_socket() const
Definition: COM_Node.h:214
float get_editor_value_float() const
Definition: COM_Node.cc:135
void get_editor_value_vector(float *value) const
Definition: COM_Node.cc:149
void map_input_socket(NodeInput *node_socket, NodeOperationInput *operation_socket)
void convert_to_operations(ExecutionSystem *system)
WriteBufferOperation * find_attached_write_buffer_operation(NodeOperationOutput *output) const
void add_preview(NodeOperationOutput *output)
void map_output_socket(NodeOutput *node_socket, NodeOperationOutput *operation_socket)
void replace_operation_with_constant(NodeOperation *operation, ConstantOperation *constant_operation)
void add_input_constant_value(NodeOperationInput *input, const NodeInput *node_input)
void add_output_buffers(NodeOperation *operation, NodeOperationOutput *output)
Vector< NodeOperationInput * > cache_output_links(NodeOperationOutput *output) const
NodeOperationBuilder(const CompositorContext *context, bNodeTree *b_nodetree, ExecutionSystem *system)
void add_input_buffers(NodeOperation *operation, NodeOperationInput *input)
ExecutionGroup * make_group(NodeOperation *op)
const Vector< NodeOperation * > & get_operations() const
const CompositorContext & context() const
void add_link(NodeOperationOutput *from, NodeOperationInput *to)
void set_link(NodeOperationOutput *link)
NodeOperation contains calculation logic.
void set_bnodetree(const bNodeTree *tree)
void set_name(const std::string name)
const NodeOperationFlags get_flags() const
unsigned int get_number_of_input_sockets() const
NodeOperationOutput * get_output_socket(unsigned int index=0)
NodeOperationInput * get_input_socket(unsigned int index)
void set_execution_system(ExecutionSystem *system)
void set_execution_model(const eExecutionModel model)
NodeOutput are sockets that can send data/input.
Definition: COM_Node.h:238
bNodeTree * get_bnodetree() const
get the reference to the SDNA bNodeTree struct
Definition: COM_Node.h:72
bNodeInstanceKey get_instance_key() const
Definition: COM_Node.h:160
bNode * get_bnode() const
get the reference to the SDNA bNode struct
Definition: COM_Node.h:64
bool is_in_active_group() const
Is this node part of the active group the active group is the group that is currently being edited....
Definition: COM_Node.h:140
void set_memory_proxy(MemoryProxy *memory_proxy)
OperationNode * node
StackEntry * from
Scene scene
Set< ComponentNode * > visited
@ Vector
Vector data type.
ccl_global KernelShaderEvalInput ccl_global float * output
ccl_global KernelShaderEvalInput * input
static void find_reachable_operations_recursive(Tags &reachable, NodeOperation *op)
NodeOperation * COM_convert_data_type(const NodeOperationOutput &from, const NodeOperationInput &to)
This function will add a date-type conversion rule when the to-socket does not support the from-socke...
static constexpr bool COM_EXPORT_GRAPHVIZ
Definition: COM_Debug.h:17
static Vector< NodeOperationHash > generate_hashes(Span< NodeOperation * > operations)
void COM_convert_canvas(NodeOperationBuilder &builder, NodeOperationOutput *from_socket, NodeOperationInput *to_socket)
This function will add a resolution rule based on the settings of the NodeInput.
static void add_group_operations_recursive(Tags &visited, NodeOperation *op, ExecutionGroup *group)
std::set< NodeOperation * > Tags
constexpr rcti COM_AREA_NONE
Definition: COM_defines.h:112
static void sort_operations_recursive(Vector< NodeOperation * > &sorted, Tags &visited, NodeOperation *op)
std::ostream & operator<<(std::ostream &os, const eCompositorPriority &priority)
Definition: COM_Enums.cc:26
static bNodeSocketTemplate inputs[]
#define hash
Definition: noise.c:153
ColorManagedViewSettings view_settings
ColorManagedDisplaySettings display_settings
char name[64]
short preview_ysize
short preview_xsize
int ymin
Definition: DNA_vec_types.h:64
int xmin
Definition: DNA_vec_types.h:63