Blender  V2.93
COM_NodeOperationBuilder.cc
Go to the documentation of this file.
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * Copyright 2013, Blender Foundation.
17  */
18 
19 #include "BLI_multi_value_map.hh"
20 #include "BLI_utildefines.h"
21 
22 #include "COM_Converter.h"
23 #include "COM_Debug.h"
24 #include "COM_ExecutionSystem.h"
25 #include "COM_Node.h"
26 #include "COM_NodeConverter.h"
27 #include "COM_SocketProxyNode.h"
28 
29 #include "COM_NodeOperation.h"
30 #include "COM_PreviewOperation.h"
32 #include "COM_SetColorOperation.h"
33 #include "COM_SetValueOperation.h"
34 #include "COM_SetVectorOperation.h"
36 #include "COM_ViewerOperation.h"
38 
39 #include "COM_NodeOperationBuilder.h" /* own include */
40 
41 namespace blender::compositor {
42 
44  : m_context(context), m_current_node(nullptr), m_active_viewer(nullptr)
45 {
46  m_graph.from_bNodeTree(*context, b_nodetree);
47 }
48 
50 {
51  /* interface handle for nodes */
52  NodeConverter converter(this);
53 
54  for (Node *node : m_graph.nodes()) {
55  m_current_node = node;
56 
58  node->convertToOperations(converter, *m_context);
59  }
60 
61  m_current_node = nullptr;
62 
63  /* The input map constructed by nodes maps operation inputs to node inputs.
64  * Inverting yields a map of node inputs to all connected operation inputs,
65  * so multiple operations can use the same node input.
66  */
68  for (Map<NodeOperationInput *, NodeInput *>::MutableItem item : m_input_map.items()) {
69  inverse_input_map.add(item.value, item.key);
70  }
71 
72  for (const NodeGraph::Link &link : m_graph.links()) {
73  NodeOutput *from = link.from;
74  NodeInput *to = link.to;
75 
76  NodeOperationOutput *op_from = m_output_map.lookup_default(from, nullptr);
77 
78  const blender::Span<NodeOperationInput *> op_to_list = inverse_input_map.lookup(to);
79  if (!op_from || op_to_list.is_empty()) {
80  /* XXX allow this? error/debug message? */
81  // BLI_assert(false);
82  /* XXX note: this can happen with certain nodes (e.g. OutputFile)
83  * which only generate operations in certain circumstances (rendering)
84  * just let this pass silently for now ...
85  */
86  continue;
87  }
88 
89  for (NodeOperationInput *op_to : op_to_list) {
90  addLink(op_from, op_to);
91  }
92  }
93 
95 
97 
99 
101 
102  /* surround complex ops with read/write buffer */
104 
105  /* links not available from here on */
106  /* XXX make m_links a local variable to avoid confusion! */
107  m_links.clear();
108 
110 
111  /* ensure topological (link-based) order of nodes */
112  /*sort_operations();*/ /* not needed yet */
113 
114  /* create execution groups */
116 
117  /* transfer resulting operations to the system */
118  system->set_operations(m_operations, m_groups);
119 }
120 
122 {
123  operation->set_id(m_operations.size());
124  m_operations.append(operation);
125  if (m_current_node) {
126  operation->set_name(m_current_node->getbNode()->name);
127  }
128 }
129 
131  NodeOperationInput *operation_socket)
132 {
133  BLI_assert(m_current_node);
134  BLI_assert(node_socket->getNode() == m_current_node);
135 
136  /* note: this maps operation sockets to node sockets.
137  * for resolving links the map will be inverted first in convertToOperations,
138  * to get a list of links for each node input socket.
139  */
140  m_input_map.add_new(operation_socket, node_socket);
141 }
142 
144  NodeOperationOutput *operation_socket)
145 {
146  BLI_assert(m_current_node);
147  BLI_assert(node_socket->getNode() == m_current_node);
148 
149  m_output_map.add_new(node_socket, operation_socket);
150 }
151 
153 {
154  if (to->isConnected()) {
155  return;
156  }
157 
158  m_links.append(Link(from, to));
159 
160  /* register with the input */
161  to->setLink(from);
162 }
163 
165 {
166  int index = 0;
167  for (Link &link : m_links) {
168  if (link.to() == to) {
169  /* unregister with the input */
170  to->setLink(nullptr);
171 
172  m_links.remove(index);
173  return;
174  }
175  index++;
176  }
177 }
178 
179 PreviewOperation *NodeOperationBuilder::make_preview_operation() const
180 {
181  BLI_assert(m_current_node);
182 
183  if (!(m_current_node->getbNode()->flag & NODE_PREVIEW)) {
184  return nullptr;
185  }
186  /* previews only in the active group */
187  if (!m_current_node->isInActiveGroup()) {
188  return nullptr;
189  }
190  /* do not calculate previews of hidden nodes */
191  if (m_current_node->getbNode()->flag & NODE_HIDDEN) {
192  return nullptr;
193  }
194 
195  bNodeInstanceHash *previews = m_context->getPreviewHash();
196  if (previews) {
197  PreviewOperation *operation = new PreviewOperation(m_context->getViewSettings(),
198  m_context->getDisplaySettings(),
199  m_current_node->getbNode()->preview_xsize,
200  m_current_node->getbNode()->preview_ysize);
201  operation->setbNodeTree(m_context->getbNodeTree());
202  operation->verifyPreview(previews, m_current_node->getInstanceKey());
203  return operation;
204  }
205 
206  return nullptr;
207 }
208 
210 {
211  PreviewOperation *operation = make_preview_operation();
212  if (operation) {
213  addOperation(operation);
214 
215  addLink(output, operation->getInputSocket(0));
216  }
217 }
218 
220 {
221  PreviewOperation *operation = make_preview_operation();
222  if (operation) {
223  addOperation(operation);
224 
225  mapInputSocket(input, operation->getInputSocket(0));
226  }
227 }
228 
230 {
231  if (m_active_viewer) {
232  if (m_current_node->isInActiveGroup()) {
233  /* deactivate previous viewer */
234  m_active_viewer->setActive(false);
235 
236  m_active_viewer = viewer;
237  viewer->setActive(true);
238  }
239  }
240  else {
241  if (m_current_node->getbNodeTree() == m_context->getbNodeTree()) {
242  m_active_viewer = viewer;
243  viewer->setActive(true);
244  }
245  }
246 }
247 
248 /****************************
249  **** Optimization Steps ****
250  ****************************/
251 
253 {
254  Vector<Link> convert_links;
255  for (const Link &link : m_links) {
256  /* proxy operations can skip data type conversion */
257  NodeOperation *from_op = &link.from()->getOperation();
258  NodeOperation *to_op = &link.to()->getOperation();
259  if (!(from_op->get_flags().use_datatype_conversion ||
261  continue;
262  }
263 
264  if (link.from()->getDataType() != link.to()->getDataType()) {
265  convert_links.append(link);
266  }
267  }
268  for (const Link &link : convert_links) {
269  NodeOperation *converter = COM_convert_data_type(*link.from(), *link.to());
270  if (converter) {
271  addOperation(converter);
272 
273  removeInputLink(link.to());
274  addLink(link.from(), converter->getInputSocket(0));
275  addLink(converter->getOutputSocket(0), link.to());
276  }
277  }
278 }
279 
281 {
282  /* Note: unconnected inputs cached first to avoid modifying
283  * m_operations while iterating over it
284  */
285  Vector<NodeOperationInput *> pending_inputs;
286  for (NodeOperation *op : m_operations) {
287  for (int k = 0; k < op->getNumberOfInputSockets(); ++k) {
288  NodeOperationInput *input = op->getInputSocket(k);
289  if (!input->isConnected()) {
290  pending_inputs.append(input);
291  }
292  }
293  }
294  for (NodeOperationInput *input : pending_inputs) {
295  add_input_constant_value(input, m_input_map.lookup_default(input, nullptr));
296  }
297 }
298 
300  const NodeInput *node_input)
301 {
302  switch (input->getDataType()) {
303  case DataType::Value: {
304  float value;
305  if (node_input && node_input->getbNodeSocket()) {
306  value = node_input->getEditorValueFloat();
307  }
308  else {
309  value = 0.0f;
310  }
311 
313  op->setValue(value);
314  addOperation(op);
315  addLink(op->getOutputSocket(), input);
316  break;
317  }
318  case DataType::Color: {
319  float value[4];
320  if (node_input && node_input->getbNodeSocket()) {
321  node_input->getEditorValueColor(value);
322  }
323  else {
324  zero_v4(value);
325  }
326 
328  op->setChannels(value);
329  addOperation(op);
330  addLink(op->getOutputSocket(), input);
331  break;
332  }
333  case DataType::Vector: {
334  float value[3];
335  if (node_input && node_input->getbNodeSocket()) {
336  node_input->getEditorValueVector(value);
337  }
338  else {
339  zero_v3(value);
340  }
341 
343  op->setVector(value);
344  addOperation(op);
345  addLink(op->getOutputSocket(), input);
346  break;
347  }
348  }
349 }
350 
352 {
353  Vector<Link> proxy_links;
354  for (const Link &link : m_links) {
355  /* don't replace links from proxy to proxy, since we may need them for replacing others! */
356  if (link.from()->getOperation().get_flags().is_proxy_operation &&
357  !link.to()->getOperation().get_flags().is_proxy_operation) {
358  proxy_links.append(link);
359  }
360  }
361 
362  for (const Link &link : proxy_links) {
363  NodeOperationInput *to = link.to();
364  NodeOperationOutput *from = link.from();
365  do {
366  /* walk upstream bypassing the proxy operation */
367  from = from->getOperation().getInputSocket(0)->getLink();
368  } while (from && from->getOperation().get_flags().is_proxy_operation);
369 
370  removeInputLink(to);
371  /* we may not have a final proxy input link,
372  * in that case it just gets dropped
373  */
374  if (from) {
375  addLink(from, to);
376  }
377  }
378 }
379 
381 {
382  /* determine all resolutions of the operations (Width/Height) */
383  for (NodeOperation *op : m_operations) {
384  if (op->isOutputOperation(m_context->isRendering()) && !op->get_flags().is_preview_operation) {
385  unsigned int resolution[2] = {0, 0};
386  unsigned int preferredResolution[2] = {0, 0};
387  op->determineResolution(resolution, preferredResolution);
388  op->setResolution(resolution);
389  }
390  }
391 
392  for (NodeOperation *op : m_operations) {
393  if (op->isOutputOperation(m_context->isRendering()) && op->get_flags().is_preview_operation) {
394  unsigned int resolution[2] = {0, 0};
395  unsigned int preferredResolution[2] = {0, 0};
396  op->determineResolution(resolution, preferredResolution);
397  op->setResolution(resolution);
398  }
399  }
400 
401  /* add convert resolution operations when needed */
402  {
403  Vector<Link> convert_links;
404  for (const Link &link : m_links) {
405  if (link.to()->getResizeMode() != ResizeMode::None) {
406  NodeOperation &from_op = link.from()->getOperation();
407  NodeOperation &to_op = link.to()->getOperation();
408  if (from_op.getWidth() != to_op.getWidth() || from_op.getHeight() != to_op.getHeight()) {
409  convert_links.append(link);
410  }
411  }
412  }
413  for (const Link &link : convert_links) {
414  COM_convert_resolution(*this, link.from(), link.to());
415  }
416  }
417 }
418 
421 {
423  for (const Link &link : m_links) {
424  if (link.from() == output) {
425  inputs.append(link.to());
426  }
427  }
428  return inputs;
429 }
430 
433 {
434  for (const Link &link : m_links) {
435  if (link.from() == output) {
436  NodeOperation &op = link.to()->getOperation();
438  return (WriteBufferOperation *)(&op);
439  }
440  }
441  }
442  return nullptr;
443 }
444 
446  NodeOperationInput *input)
447 {
448  if (!input->isConnected()) {
449  return;
450  }
451 
452  NodeOperationOutput *output = input->getLink();
453  if (output->getOperation().get_flags().is_read_buffer_operation) {
454  /* input is already buffered, no need to add another */
455  return;
456  }
457 
458  /* this link will be replaced below */
459  removeInputLink(input);
460 
461  /* check of other end already has write operation, otherwise add a new one */
463  if (!writeoperation) {
464  writeoperation = new WriteBufferOperation(output->getDataType());
465  writeoperation->setbNodeTree(m_context->getbNodeTree());
466  addOperation(writeoperation);
467 
468  addLink(output, writeoperation->getInputSocket(0));
469 
470  writeoperation->readResolutionFromInputSocket();
471  }
472 
473  /* add readbuffer op for the input */
474  ReadBufferOperation *readoperation = new ReadBufferOperation(output->getDataType());
475  readoperation->setMemoryProxy(writeoperation->getMemoryProxy());
476  this->addOperation(readoperation);
477 
478  addLink(readoperation->getOutputSocket(), input);
479 
480  readoperation->readResolutionFromWriteBuffer();
481 }
482 
485 {
486  /* cache connected sockets, so we can safely remove links first before replacing them */
488  if (targets.is_empty()) {
489  return;
490  }
491 
492  WriteBufferOperation *writeOperation = nullptr;
493  for (NodeOperationInput *target : targets) {
494  /* try to find existing write buffer operation */
495  if (target->getOperation().get_flags().is_write_buffer_operation) {
496  BLI_assert(writeOperation == nullptr); /* there should only be one write op connected */
497  writeOperation = (WriteBufferOperation *)(&target->getOperation());
498  }
499  else {
500  /* remove all links to other nodes */
501  removeInputLink(target);
502  }
503  }
504 
505  /* if no write buffer operation exists yet, create a new one */
506  if (!writeOperation) {
507  writeOperation = new WriteBufferOperation(operation->getOutputSocket()->getDataType());
508  writeOperation->setbNodeTree(m_context->getbNodeTree());
509  addOperation(writeOperation);
510 
511  addLink(output, writeOperation->getInputSocket(0));
512  }
513 
514  writeOperation->readResolutionFromInputSocket();
515 
516  /* add readbuffer op for every former connected input */
517  for (NodeOperationInput *target : targets) {
518  if (&target->getOperation() == writeOperation) {
519  continue; /* skip existing write op links */
520  }
521 
522  ReadBufferOperation *readoperation = new ReadBufferOperation(
523  operation->getOutputSocket()->getDataType());
524  readoperation->setMemoryProxy(writeOperation->getMemoryProxy());
525  addOperation(readoperation);
526 
527  addLink(readoperation->getOutputSocket(), target);
528 
529  readoperation->readResolutionFromWriteBuffer();
530  }
531 }
532 
534 {
535  /* note: complex ops and get cached here first, since adding operations
536  * will invalidate iterators over the main m_operations
537  */
538  Vector<NodeOperation *> complex_ops;
539  for (NodeOperation *operation : m_operations) {
540  if (operation->get_flags().complex) {
541  complex_ops.append(operation);
542  }
543  }
544 
545  for (NodeOperation *op : complex_ops) {
547 
548  for (int index = 0; index < op->getNumberOfInputSockets(); index++) {
549  add_input_buffers(op, op->getInputSocket(index));
550  }
551 
552  for (int index = 0; index < op->getNumberOfOutputSockets(); index++) {
553  add_output_buffers(op, op->getOutputSocket(index));
554  }
555  }
556 }
557 
558 using Tags = std::set<NodeOperation *>;
559 
561 {
562  if (reachable.find(op) != reachable.end()) {
563  return;
564  }
565  reachable.insert(op);
566 
567  for (int i = 0; i < op->getNumberOfInputSockets(); i++) {
568  NodeOperationInput *input = op->getInputSocket(i);
569  if (input->isConnected()) {
571  }
572  }
573 
574  /* associated write-buffer operations are executed as well */
576  ReadBufferOperation *read_op = (ReadBufferOperation *)op;
577  MemoryProxy *memproxy = read_op->getMemoryProxy();
579  }
580 }
581 
583 {
584  Tags reachable;
585  for (NodeOperation *op : m_operations) {
586  /* output operations are primary executed operations */
587  if (op->isOutputOperation(m_context->isRendering())) {
589  }
590  }
591 
592  /* delete unreachable operations */
593  Vector<NodeOperation *> reachable_ops;
594  for (NodeOperation *op : m_operations) {
595  if (reachable.find(op) != reachable.end()) {
596  reachable_ops.append(op);
597  }
598  else {
599  delete op;
600  }
601  }
602  /* finally replace the operations list with the pruned list */
603  m_operations = reachable_ops;
604 }
605 
606 /* topological (depth-first) sorting of operations */
608  Tags &visited,
609  NodeOperation *op)
610 {
611  if (visited.find(op) != visited.end()) {
612  return;
613  }
614  visited.insert(op);
615 
616  for (int i = 0; i < op->getNumberOfInputSockets(); i++) {
617  NodeOperationInput *input = op->getInputSocket(i);
618  if (input->isConnected()) {
619  sort_operations_recursive(sorted, visited, &input->getLink()->getOperation());
620  }
621  }
622 
623  sorted.append(op);
624 }
625 
627 {
629  sorted.reserve(m_operations.size());
630  Tags visited;
631 
632  for (NodeOperation *operation : m_operations) {
633  sort_operations_recursive(sorted, visited, operation);
634  }
635 
636  m_operations = sorted;
637 }
638 
640 {
641  if (visited.find(op) != visited.end()) {
642  return;
643  }
644  visited.insert(op);
645 
646  if (!group->addOperation(op)) {
647  return;
648  }
649 
650  /* add all eligible input ops to the group */
651  for (int i = 0; i < op->getNumberOfInputSockets(); i++) {
652  NodeOperationInput *input = op->getInputSocket(i);
653  if (input->isConnected()) {
655  }
656  }
657 }
658 
660 {
661  ExecutionGroup *group = new ExecutionGroup(this->m_groups.size());
662  m_groups.append(group);
663 
664  Tags visited;
666 
667  return group;
668 }
669 
671 {
672  for (NodeOperation *op : m_operations) {
673  if (op->isOutputOperation(m_context->isRendering())) {
674  ExecutionGroup *group = make_group(op);
675  group->setOutputExecutionGroup(true);
676  }
677 
678  /* add new groups for associated memory proxies where needed */
679  if (op->get_flags().is_read_buffer_operation) {
680  ReadBufferOperation *read_op = (ReadBufferOperation *)op;
681  MemoryProxy *memproxy = read_op->getMemoryProxy();
682 
683  if (memproxy->getExecutor() == nullptr) {
684  ExecutionGroup *group = make_group(memproxy->getWriteBufferOperation());
685  memproxy->setExecutor(group);
686  }
687  }
688  }
689 }
690 
692 std::ostream &operator<<(std::ostream &os, const NodeOperationBuilder &builder)
693 {
694  os << "# Builder start\n";
695  os << "digraph G {\n";
696  os << " rankdir=LR;\n";
697  os << " node [shape=box];\n";
698  for (const NodeOperation *operation : builder.get_operations()) {
699  os << " op" << operation->get_id() << " [label=\"" << *operation << "\"];\n";
700  }
701 
702  os << "\n";
703  for (const NodeOperationBuilder::Link &link : builder.get_links()) {
704  os << " op" << link.from()->getOperation().get_id() << " -> op"
705  << link.to()->getOperation().get_id() << ";\n";
706  }
707  for (const NodeOperation *operation : builder.get_operations()) {
708  if (operation->get_flags().is_read_buffer_operation) {
709  const ReadBufferOperation &read_operation = static_cast<const ReadBufferOperation &>(
710  *operation);
711  const WriteBufferOperation &write_operation =
712  *read_operation.getMemoryProxy()->getWriteBufferOperation();
713  os << " op" << write_operation.get_id() << " -> op" << read_operation.get_id() << ";\n";
714  }
715  }
716 
717  os << "}\n";
718  os << "# Builder end\n";
719  return os;
720 }
721 
722 std::ostream &operator<<(std::ostream &os, const NodeOperationBuilder::Link &link)
723 {
724  os << link.from()->getOperation().get_id() << " -> " << link.to()->getOperation().get_id();
725  return os;
726 }
727 
728 } // namespace blender::compositor
#define BLI_assert(a)
Definition: BLI_assert.h:58
MINLINE void zero_v4(float r[4])
MINLINE void zero_v3(float r[3])
struct Link Link
#define NODE_HIDDEN
#define NODE_PREVIEW
#define output
Span< Value > lookup(const Key &key) const
void add(const Key &key, const Value &value)
constexpr bool is_empty() const
Definition: BLI_span.hh:262
void append(const T &value)
Definition: BLI_vector.hh:438
bool is_empty() const
Definition: BLI_vector.hh:674
void reserve(const int64_t min_capacity)
Definition: BLI_vector.hh:355
Overall context of the compositor.
bNodeInstanceHash * getPreviewHash() const
get the preview image hash table
bool isRendering() const
get the rendering field of the context
const bNodeTree * getbNodeTree() const
get the bnodetree of the context
const ColorManagedDisplaySettings * getDisplaySettings() const
get display settings of color color management
const ColorManagedViewSettings * getViewSettings() const
get view settings of color color management
static void operation_read_write_buffer(const NodeOperation *operation)
Definition: COM_Debug.cc:487
static void node_to_operations(const Node *node)
Definition: COM_Debug.cc:481
Class ExecutionGroup is a group of Operations that are executed as one. This grouping is used to comb...
void setOutputExecutionGroup(bool is_output)
set whether this ExecutionGroup is an output
bool addOperation(NodeOperation *operation)
add an operation to this ExecutionGroup
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 setExecutor(ExecutionGroup *executor)
set the ExecutionGroup that can be scheduled to calculate a certain chunk.
ExecutionGroup * getExecutor() const
get the ExecutionGroup that can be scheduled to calculate a certain chunk.
WriteBufferOperation * getWriteBufferOperation() 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:61
const Vector< Link > & links() const
Definition: COM_NodeGraph.h:65
NodeInput are sockets that can receive data/input.
Definition: COM_Node.h:210
void getEditorValueColor(float *value) const
Definition: COM_Node.cc:166
void getEditorValueVector(float *value) const
Definition: COM_Node.cc:173
bNodeSocket * getbNodeSocket() const
Definition: COM_Node.h:234
float getEditorValueFloat() const
Definition: COM_Node.cc:159
WriteBufferOperation * find_attached_write_buffer_operation(NodeOperationOutput *output) const
void addPreview(NodeOperationOutput *output)
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
void add_input_buffers(NodeOperation *operation, NodeOperationInput *input)
void mapOutputSocket(NodeOutput *node_socket, NodeOperationOutput *operation_socket)
void mapInputSocket(NodeInput *node_socket, NodeOperationInput *operation_socket)
void addLink(NodeOperationOutput *from, NodeOperationInput *to)
ExecutionGroup * make_group(NodeOperation *op)
const Vector< NodeOperation * > & get_operations() const
const CompositorContext & context() const
NodeOperationBuilder(const CompositorContext *context, bNodeTree *b_nodetree)
NodeOperationOutput * getLink() const
void setLink(NodeOperationOutput *link)
NodeOperation contains calculation logic.
void set_name(const std::string name)
NodeOperationInput * getInputSocket(unsigned int index)
const NodeOperationFlags get_flags() const
unsigned int getNumberOfInputSockets() const
NodeOperationOutput * getOutputSocket(unsigned int index=0)
void setbNodeTree(const bNodeTree *tree)
NodeOutput are sockets that can send data/input.
Definition: COM_Node.h:258
bNode * getbNode() const
get the reference to the SDNA bNode struct
Definition: COM_Node.h:82
bNodeInstanceKey getInstanceKey() const
Definition: COM_Node.h:180
bNodeTree * getbNodeTree() const
get the reference to the SDNA bNodeTree struct
Definition: COM_Node.h:90
bool isInActiveGroup() const
Is this node part of the active group the active group is the group that is currently being edited....
Definition: COM_Node.h:160
void setMemoryProxy(MemoryProxy *memoryProxy)
OperationNode * node
StackEntry * from
Set< ComponentNode * > visited
@ Vector
Vector data type.
@ None
The bottom left of the input image is the bottom left of the working area of the node,...
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 datetype conversion rule when the to-socket does not support the from-socket...
static void add_group_operations_recursive(Tags &visited, NodeOperation *op, ExecutionGroup *group)
void COM_convert_resolution(NodeOperationBuilder &builder, NodeOperationOutput *fromSocket, NodeOperationInput *toSocket)
This function will add a resolution rule based on the settings of the NodeInput.
std::set< NodeOperation * > Tags
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:23
static bNodeSocketTemplate inputs[]
char name[64]
short preview_ysize
short preview_xsize