Blender  V2.93
COM_Debug.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 "COM_Debug.h"
20 
21 #include <map>
22 #include <typeinfo>
23 #include <vector>
24 
25 extern "C" {
26 #include "BLI_fileops.h"
27 #include "BLI_path_util.h"
28 #include "BLI_string.h"
29 #include "BLI_sys_types.h"
30 
31 #include "BKE_appdir.h"
32 #include "BKE_node.h"
33 #include "DNA_node_types.h"
34 }
35 
36 #include "COM_ExecutionSystem.h"
37 #include "COM_Node.h"
38 
40 #include "COM_ViewerOperation.h"
42 
43 namespace blender::compositor {
44 
45 #ifdef COM_DEBUG
46 
47 int DebugInfo::m_file_index = 0;
48 DebugInfo::NodeNameMap DebugInfo::m_node_names;
49 DebugInfo::OpNameMap DebugInfo::m_op_names;
50 std::string DebugInfo::m_current_node_name;
51 std::string DebugInfo::m_current_op_name;
52 DebugInfo::GroupStateMap DebugInfo::m_group_states;
53 
54 std::string DebugInfo::node_name(const Node *node)
55 {
56  NodeNameMap::const_iterator it = m_node_names.find(node);
57  if (it != m_node_names.end()) {
58  return it->second;
59  }
60  return "";
61 }
62 
63 std::string DebugInfo::operation_name(const NodeOperation *op)
64 {
65  OpNameMap::const_iterator it = m_op_names.find(op);
66  if (it != m_op_names.end()) {
67  return it->second;
68  }
69  return "";
70 }
71 
73 {
74  m_op_names.clear();
75 }
76 
77 void DebugInfo::execute_started(const ExecutionSystem *system)
78 {
79  m_file_index = 1;
80  m_group_states.clear();
81  for (ExecutionGroup *execution_group : system->m_groups) {
82  m_group_states[execution_group] = EG_WAIT;
83  }
84 }
85 
86 void DebugInfo::node_added(const Node *node)
87 {
88  m_node_names[node] = std::string(node->getbNode() ? node->getbNode()->name : "");
89 }
90 
92 {
93  m_current_node_name = m_node_names[node];
94 }
95 
96 void DebugInfo::operation_added(const NodeOperation *operation)
97 {
98  m_op_names[operation] = m_current_node_name;
99 }
100 
101 void DebugInfo::operation_read_write_buffer(const NodeOperation *operation)
102 {
103  m_current_op_name = m_op_names[operation];
104 }
105 
106 void DebugInfo::execution_group_started(const ExecutionGroup *group)
107 {
108  m_group_states[group] = EG_RUNNING;
109 }
110 
111 void DebugInfo::execution_group_finished(const ExecutionGroup *group)
112 {
113  m_group_states[group] = EG_FINISHED;
114 }
115 
116 int DebugInfo::graphviz_operation(const ExecutionSystem *system,
117  NodeOperation *operation,
118  const ExecutionGroup *group,
119  char *str,
120  int maxlen)
121 {
122  int len = 0;
123 
124  std::string fillcolor = "gainsboro";
125  if (operation->get_flags().is_viewer_operation) {
126  const ViewerOperation *viewer = (const ViewerOperation *)operation;
127  if (viewer->isActiveViewerOutput()) {
128  fillcolor = "lightskyblue1";
129  }
130  else {
131  fillcolor = "lightskyblue3";
132  }
133  }
134  else if (operation->isOutputOperation(system->getContext().isRendering())) {
135  fillcolor = "dodgerblue1";
136  }
137  else if (operation->get_flags().is_set_operation) {
138  fillcolor = "khaki1";
139  }
140  else if (operation->get_flags().is_read_buffer_operation) {
141  fillcolor = "darkolivegreen3";
142  }
143  else if (operation->get_flags().is_write_buffer_operation) {
144  fillcolor = "darkorange";
145  }
146 
147  len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "// OPERATION: %p\r\n", operation);
148  if (group) {
149  len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "\"O_%p_%p\"", operation, group);
150  }
151  else {
152  len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "\"O_%p\"", operation);
153  }
154  len += snprintf(str + len,
155  maxlen > len ? maxlen - len : 0,
156  " [fillcolor=%s,style=filled,shape=record,label=\"{",
157  fillcolor.c_str());
158 
159  int totinputs = operation->getNumberOfInputSockets();
160  if (totinputs != 0) {
161  len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "{");
162  for (int k = 0; k < totinputs; k++) {
163  NodeOperationInput *socket = operation->getInputSocket(k);
164  if (k != 0) {
165  len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "|");
166  }
167  len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "<IN_%p>", socket);
168  switch (socket->getDataType()) {
169  case DataType::Value:
170  len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "Value");
171  break;
172  case DataType::Vector:
173  len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "Vector");
174  break;
175  case DataType::Color:
176  len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "Color");
177  break;
178  }
179  }
180  len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "}");
181  len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "|");
182  }
183 
184  len += snprintf(str + len,
185  maxlen > len ? maxlen - len : 0,
186  "%s\\n(%s)",
187  m_op_names[operation].c_str(),
188  typeid(*operation).name());
189 
190  len += snprintf(str + len,
191  maxlen > len ? maxlen - len : 0,
192  " (%u,%u)",
193  operation->getWidth(),
194  operation->getHeight());
195 
196  int totoutputs = operation->getNumberOfOutputSockets();
197  if (totoutputs != 0) {
198  len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "|");
199  len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "{");
200  for (int k = 0; k < totoutputs; k++) {
201  NodeOperationOutput *socket = operation->getOutputSocket(k);
202  if (k != 0) {
203  len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "|");
204  }
205  len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "<OUT_%p>", socket);
206  switch (socket->getDataType()) {
207  case DataType::Value:
208  len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "Value");
209  break;
210  case DataType::Vector:
211  len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "Vector");
212  break;
213  case DataType::Color:
214  len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "Color");
215  break;
216  }
217  }
218  len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "}");
219  }
220  len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "}\"]");
221  len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "\r\n");
222 
223  return len;
224 }
225 
226 int DebugInfo::graphviz_legend_color(const char *name, const char *color, char *str, int maxlen)
227 {
228  int len = 0;
229  len += snprintf(str + len,
230  maxlen > len ? maxlen - len : 0,
231  "<TR><TD>%s</TD><TD BGCOLOR=\"%s\"></TD></TR>\r\n",
232  name,
233  color);
234  return len;
235 }
236 
237 int DebugInfo::graphviz_legend_line(
238  const char * /*name*/, const char * /*color*/, const char * /*style*/, char *str, int maxlen)
239 {
240  /* XXX TODO */
241  int len = 0;
242  len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "\r\n");
243  return len;
244 }
245 
246 int DebugInfo::graphviz_legend_group(
247  const char *name, const char *color, const char * /*style*/, char *str, int maxlen)
248 {
249  int len = 0;
250  len += snprintf(str + len,
251  maxlen > len ? maxlen - len : 0,
252  "<TR><TD>%s</TD><TD CELLPADDING=\"4\"><TABLE BORDER=\"1\" CELLBORDER=\"0\" "
253  "CELLSPACING=\"0\" CELLPADDING=\"0\"><TR><TD "
254  "BGCOLOR=\"%s\"></TD></TR></TABLE></TD></TR>\r\n",
255  name,
256  color);
257  return len;
258 }
259 
260 int DebugInfo::graphviz_legend(char *str, int maxlen)
261 {
262  int len = 0;
263 
264  len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "{\r\n");
265  len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "rank = sink;\r\n");
266  len += snprintf(
267  str + len, maxlen > len ? maxlen - len : 0, "Legend [shape=none, margin=0, label=<\r\n");
268 
269  len += snprintf(
270  str + len,
271  maxlen > len ? maxlen - len : 0,
272  " <TABLE BORDER=\"0\" CELLBORDER=\"1\" CELLSPACING=\"0\" CELLPADDING=\"4\">\r\n");
273  len += snprintf(str + len,
274  maxlen > len ? maxlen - len : 0,
275  "<TR><TD COLSPAN=\"2\"><B>Legend</B></TD></TR>\r\n");
276 
277  len += graphviz_legend_color(
278  "NodeOperation", "gainsboro", str + len, maxlen > len ? maxlen - len : 0);
279  len += graphviz_legend_color(
280  "Output", "dodgerblue1", str + len, maxlen > len ? maxlen - len : 0);
281  len += graphviz_legend_color(
282  "Viewer", "lightskyblue3", str + len, maxlen > len ? maxlen - len : 0);
283  len += graphviz_legend_color(
284  "Active Viewer", "lightskyblue1", str + len, maxlen > len ? maxlen - len : 0);
285  len += graphviz_legend_color(
286  "Write Buffer", "darkorange", str + len, maxlen > len ? maxlen - len : 0);
287  len += graphviz_legend_color(
288  "Read Buffer", "darkolivegreen3", str + len, maxlen > len ? maxlen - len : 0);
289  len += graphviz_legend_color(
290  "Input Value", "khaki1", str + len, maxlen > len ? maxlen - len : 0);
291 
292  len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "<TR><TD></TD></TR>\r\n");
293 
294  len += graphviz_legend_group(
295  "Group Waiting", "white", "dashed", str + len, maxlen > len ? maxlen - len : 0);
296  len += graphviz_legend_group(
297  "Group Running", "firebrick1", "solid", str + len, maxlen > len ? maxlen - len : 0);
298  len += graphviz_legend_group(
299  "Group Finished", "chartreuse4", "solid", str + len, maxlen > len ? maxlen - len : 0);
300 
301  len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "</TABLE>\r\n");
302  len += snprintf(str + len, maxlen > len ? maxlen - len : 0, ">];\r\n");
303  len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "}\r\n");
304 
305  return len;
306 }
307 
308 bool DebugInfo::graphviz_system(const ExecutionSystem *system, char *str, int maxlen)
309 {
310  char strbuf[64];
311  int len = 0;
312 
313  len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "digraph compositorexecution {\r\n");
314  len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "ranksep=1.5\r\n");
315  len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "rankdir=LR\r\n");
316  len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "splines=false\r\n");
317 
318  std::map<NodeOperation *, std::vector<std::string>> op_groups;
319  int index = 0;
320  for (const ExecutionGroup *group : system->m_groups) {
321  len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "// GROUP: %d\r\n", index);
322  len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "subgraph cluster_%d{\r\n", index);
323  /* used as a check for executing group */
324  if (m_group_states[group] == EG_WAIT) {
325  len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "style=dashed\r\n");
326  }
327  else if (m_group_states[group] == EG_RUNNING) {
328  len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "style=filled\r\n");
329  len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "color=black\r\n");
330  len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "fillcolor=firebrick1\r\n");
331  }
332  else if (m_group_states[group] == EG_FINISHED) {
333  len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "style=filled\r\n");
334  len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "color=black\r\n");
335  len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "fillcolor=chartreuse4\r\n");
336  }
337 
338  for (NodeOperation *operation : group->m_operations) {
339 
340  sprintf(strbuf, "_%p", group);
341  op_groups[operation].push_back(std::string(strbuf));
342 
343  len += graphviz_operation(
344  system, operation, group, str + len, maxlen > len ? maxlen - len : 0);
345  }
346 
347  len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "}\r\n");
348  index++;
349  }
350 
351  /* operations not included in any group */
352  for (NodeOperation *operation : system->m_operations) {
353  if (op_groups.find(operation) != op_groups.end()) {
354  continue;
355  }
356 
357  op_groups[operation].push_back(std::string(""));
358 
359  len += graphviz_operation(
360  system, operation, nullptr, str + len, maxlen > len ? maxlen - len : 0);
361  }
362 
363  for (NodeOperation *operation : system->m_operations) {
364  if (operation->get_flags().is_read_buffer_operation) {
365  ReadBufferOperation *read = (ReadBufferOperation *)operation;
366  WriteBufferOperation *write = read->getMemoryProxy()->getWriteBufferOperation();
367  std::vector<std::string> &read_groups = op_groups[read];
368  std::vector<std::string> &write_groups = op_groups[write];
369 
370  for (int k = 0; k < write_groups.size(); k++) {
371  for (int l = 0; l < read_groups.size(); l++) {
372  len += snprintf(str + len,
373  maxlen > len ? maxlen - len : 0,
374  "\"O_%p%s\" -> \"O_%p%s\" [style=dotted]\r\n",
375  write,
376  write_groups[k].c_str(),
377  read,
378  read_groups[l].c_str());
379  }
380  }
381  }
382  }
383 
384  for (NodeOperation *op : system->m_operations) {
385  for (NodeOperationInput &to : op->m_inputs) {
386  NodeOperationOutput *from = to.getLink();
387 
388  if (!from) {
389  continue;
390  }
391 
392  std::string color;
393  switch (from->getDataType()) {
394  case DataType::Value:
395  color = "gray";
396  break;
397  case DataType::Vector:
398  color = "blue";
399  break;
400  case DataType::Color:
401  color = "orange";
402  break;
403  }
404 
405  NodeOperation *to_op = &to.getOperation();
406  NodeOperation *from_op = &from->getOperation();
407  std::vector<std::string> &from_groups = op_groups[from_op];
408  std::vector<std::string> &to_groups = op_groups[to_op];
409 
410  len += snprintf(str + len,
411  maxlen > len ? maxlen - len : 0,
412  "// CONNECTION: %p.%p -> %p.%p\r\n",
413  from_op,
414  from,
415  to_op,
416  &to);
417  for (int k = 0; k < from_groups.size(); k++) {
418  for (int l = 0; l < to_groups.size(); l++) {
419  len += snprintf(str + len,
420  maxlen > len ? maxlen - len : 0,
421  R"("O_%p%s":"OUT_%p":e -> "O_%p%s":"IN_%p":w)",
422  from_op,
423  from_groups[k].c_str(),
424  from,
425  to_op,
426  to_groups[l].c_str(),
427  &to);
428  len += snprintf(
429  str + len, maxlen > len ? maxlen - len : 0, " [color=%s]", color.c_str());
430  len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "\r\n");
431  }
432  }
433  }
434  }
435 
436  len += graphviz_legend(str + len, maxlen > len ? maxlen - len : 0);
437 
438  len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "}\r\n");
439 
440  return (len < maxlen);
441 }
442 
443 void DebugInfo::graphviz(const ExecutionSystem *system)
444 {
445  char str[1000000];
446  if (graphviz_system(system, str, sizeof(str) - 1)) {
447  char basename[FILE_MAX];
448  char filename[FILE_MAX];
449 
450  BLI_snprintf(basename, sizeof(basename), "compositor_%d.dot", m_file_index);
451  BLI_join_dirfile(filename, sizeof(filename), BKE_tempdir_session(), basename);
452  m_file_index++;
453 
454  std::cout << "Writing compositor debug to: " << filename << "\n";
455 
456  FILE *fp = BLI_fopen(filename, "wb");
457  fputs(str, fp);
458  fclose(fp);
459  }
460 }
461 
462 #else
463 
464 std::string DebugInfo::node_name(const Node * /*node*/)
465 {
466  return "";
467 }
468 std::string DebugInfo::operation_name(const NodeOperation * /*op*/)
469 {
470  return "";
471 }
473 {
474 }
476 {
477 }
478 void DebugInfo::node_added(const Node * /*node*/)
479 {
480 }
481 void DebugInfo::node_to_operations(const Node * /*node*/)
482 {
483 }
484 void DebugInfo::operation_added(const NodeOperation * /*operation*/)
485 {
486 }
488 {
489 }
491 {
492 }
494 {
495 }
496 void DebugInfo::graphviz(const ExecutionSystem * /*system*/)
497 {
498 }
499 
500 #endif
501 
502 } // namespace blender::compositor
File and directory operations.
FILE * BLI_fopen(const char *filename, const char *mode) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition: fileops.c:1003
#define FILE_MAX
void BLI_join_dirfile(char *__restrict dst, const size_t maxlen, const char *__restrict dir, const char *__restrict file) ATTR_NONNULL()
Definition: path_util.c:1737
size_t BLI_snprintf(char *__restrict dst, size_t maxncpy, const char *__restrict format,...) ATTR_NONNULL(1
#define snprintf
Definition: BLI_winstuff.h:69
ATTR_WARN_UNUSED_RESULT const BMLoop * l
static std::string node_name(const Node *node)
Definition: COM_Debug.cc:464
static void node_added(const Node *node)
Definition: COM_Debug.cc:478
static void operation_read_write_buffer(const NodeOperation *operation)
Definition: COM_Debug.cc:487
static void execute_started(const ExecutionSystem *system)
Definition: COM_Debug.cc:475
static void execution_group_started(const ExecutionGroup *group)
Definition: COM_Debug.cc:490
static std::string operation_name(const NodeOperation *op)
Definition: COM_Debug.cc:468
static void operation_added(const NodeOperation *operation)
Definition: COM_Debug.cc:484
std::map< const Node *, std::string > NodeNameMap
Definition: COM_Debug.h:37
std::map< const NodeOperation *, std::string > OpNameMap
Definition: COM_Debug.h:38
static void graphviz(const ExecutionSystem *system)
Definition: COM_Debug.cc:496
std::map< const ExecutionGroup *, GroupState > GroupStateMap
Definition: COM_Debug.h:39
static void node_to_operations(const Node *node)
Definition: COM_Debug.cc:481
static void execution_group_finished(const ExecutionGroup *group)
Definition: COM_Debug.cc:493
Class ExecutionGroup is a group of Operations that are executed as one. This grouping is used to comb...
the ExecutionSystem contains the whole compositor tree.
NodeOperation contains calculation logic.
static char * basename(char *string)
Definition: datatoc.c:33
OperationNode * node
StackEntry * from
#define str(s)
@ Vector
Vector data type.
void * BKE_tempdir_session
Definition: node.h:98
uint len