Blender V4.3
COM_Debug.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2013 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
5#include <iostream>
6
7#include "COM_Debug.h"
8
9#include "BLI_assert.h"
10#include "BLI_fileops.h"
11#include "BLI_path_utils.hh"
12#include "BLI_string.h"
13
14#include "BKE_appdir.hh"
15#include "IMB_imbuf.hh"
16#include "IMB_imbuf_types.hh"
17
19#include "COM_ViewerOperation.h"
20
21namespace blender::compositor {
22
23int DebugInfo::file_index_ = 0;
24DebugInfo::NodeNameMap DebugInfo::node_names_;
25DebugInfo::OpNameMap DebugInfo::op_names_;
26std::string DebugInfo::current_node_name_;
27std::string DebugInfo::current_op_name_;
28
29static std::string operation_class_name(const NodeOperation *op)
30{
31 std::string full_name = typeid(*op).name();
32 /* The typeid name is implementation defined, but it is typically a full C++ name that is either
33 * mangled or demangled. In case it was demangled, remove the namespaces, but if it was mangled,
34 * return the entire name, since there is no easy way to demangle it. */
35 size_t pos = full_name.find_last_of(':');
36 if (pos == std::string::npos) {
37 return full_name;
38 }
39 return full_name.substr(pos + 1);
40}
41
42std::string DebugInfo::node_name(const Node *node)
43{
44 NodeNameMap::const_iterator it = node_names_.find(node);
45 if (it != node_names_.end()) {
46 return it->second;
47 }
48 return "";
49}
50
52{
53 OpNameMap::const_iterator it = op_names_.find(op);
54 if (it != op_names_.end()) {
55 return it->second;
56 }
57 return "";
58}
59
61 NodeOperation *operation,
62 char *str,
63 int maxlen)
64{
65 int len = 0;
66
67 std::string fillcolor = "gainsboro";
68 if (operation->get_flags().is_viewer_operation) {
69 const ViewerOperation *viewer = (const ViewerOperation *)operation;
70 if (viewer->is_active_viewer_output()) {
71 fillcolor = "lightskyblue1";
72 }
73 else {
74 fillcolor = "lightskyblue3";
75 }
76 }
77 else if (operation->is_output_operation(system->get_context().is_rendering())) {
78 fillcolor = "dodgerblue1";
79 }
80 else if (operation->get_flags().is_constant_operation) {
81 fillcolor = "khaki1";
82 }
83
84 len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "// OPERATION: %p\r\n", operation);
85 len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "\"O_%p\"", operation);
86 len += snprintf(str + len,
87 maxlen > len ? maxlen - len : 0,
88 " [fillcolor=%s,style=filled,shape=record,label=\"{",
89 fillcolor.c_str());
90
91 int totinputs = operation->get_number_of_input_sockets();
92 if (totinputs != 0) {
93 len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "{");
94 for (int k = 0; k < totinputs; k++) {
95 NodeOperationInput *socket = operation->get_input_socket(k);
96 if (k != 0) {
97 len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "|");
98 }
99 len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "<IN_%p>", socket);
100 switch (socket->get_data_type()) {
101 case DataType::Value:
102 len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "Value");
103 break;
104 case DataType::Vector:
105 len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "Vector");
106 break;
107 case DataType::Color:
108 len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "Color");
109 break;
110 case DataType::Float2:
111 /* An internal type that needn't be handled. */
113 break;
114 }
115 }
116 len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "}");
117 len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "|");
118 }
119
121 std::string op_node_name = operation->get_name();
122 if (!op_node_name.empty()) {
123 len += snprintf(
124 str + len, maxlen > len ? maxlen - len : 0, "%s\\n", (op_node_name + " Node").c_str());
125 }
126 }
127
128 len += snprintf(str + len,
129 maxlen > len ? maxlen - len : 0,
130 "%s\\n",
131 operation_class_name(operation).c_str());
132
133 len += snprintf(str + len,
134 maxlen > len ? maxlen - len : 0,
135 "#%d (%i,%i) (%u,%u)",
136 operation->get_id(),
137 operation->get_canvas().xmin,
138 operation->get_canvas().ymin,
139 operation->get_width(),
140 operation->get_height());
141
142 int totoutputs = operation->get_number_of_output_sockets();
143 if (totoutputs != 0) {
144 len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "|");
145 len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "{");
146 for (int k = 0; k < totoutputs; k++) {
147 NodeOperationOutput *socket = operation->get_output_socket(k);
148 if (k != 0) {
149 len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "|");
150 }
151 len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "<OUT_%p>", socket);
152 switch (socket->get_data_type()) {
153 case DataType::Value: {
154 ConstantOperation *constant = operation->get_flags().is_constant_operation ?
155 static_cast<ConstantOperation *>(operation) :
156 nullptr;
157 if (constant && constant->can_get_constant_elem()) {
158 const float value = *constant->get_constant_elem();
159 len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "Value\\n%12.4g", value);
160 }
161 else {
162 len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "Value");
163 }
164 break;
165 }
166 case DataType::Vector: {
167 len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "Vector");
168 break;
169 }
170 case DataType::Color: {
171 len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "Color");
172 break;
173 }
174 case DataType::Float2:
175 /* An internal type that needn't be handled. */
177 break;
178 }
179 }
180 len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "}");
181 }
182 len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "}\"]");
183 len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "\r\n");
184
185 return len;
186}
187
188int DebugInfo::graphviz_legend_color(const char *name, const char *color, char *str, int maxlen)
189{
190 int len = 0;
191 len += snprintf(str + len,
192 maxlen > len ? maxlen - len : 0,
193 "<TR><TD>%s</TD><TD BGCOLOR=\"%s\"></TD></TR>\r\n",
194 name,
195 color);
196 return len;
197}
198
200 const char * /*name*/, const char * /*color*/, const char * /*style*/, char *str, int maxlen)
201{
202 /* XXX TODO */
203 int len = 0;
204 len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "\r\n");
205 return len;
206}
207
209 const char *name, const char *color, const char * /*style*/, char *str, int maxlen)
210{
211 int len = 0;
212 len += snprintf(str + len,
213 maxlen > len ? maxlen - len : 0,
214 "<TR><TD>%s</TD><TD CELLPADDING=\"4\"><TABLE BORDER=\"1\" CELLBORDER=\"0\" "
215 "CELLSPACING=\"0\" CELLPADDING=\"0\"><TR><TD "
216 "BGCOLOR=\"%s\"></TD></TR></TABLE></TD></TR>\r\n",
217 name,
218 color);
219 return len;
220}
221
222int DebugInfo::graphviz_legend(char *str, int maxlen)
223{
224 int len = 0;
225
226 len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "{\r\n");
227 len += snprintf(
228 str + len, maxlen > len ? maxlen - len : 0, "Legend [shape=none, margin=0, label=<\r\n");
229
230 len += snprintf(
231 str + len,
232 maxlen > len ? maxlen - len : 0,
233 " <TABLE BORDER=\"0\" CELLBORDER=\"1\" CELLSPACING=\"0\" CELLPADDING=\"4\">\r\n");
234 len += snprintf(str + len,
235 maxlen > len ? maxlen - len : 0,
236 "<TR><TD COLSPAN=\"2\"><B>Legend</B></TD></TR>\r\n");
237
239 "NodeOperation", "gainsboro", str + len, maxlen > len ? maxlen - len : 0);
241 "Output", "dodgerblue1", str + len, maxlen > len ? maxlen - len : 0);
243 "Viewer", "lightskyblue3", str + len, maxlen > len ? maxlen - len : 0);
245 "Active Viewer", "lightskyblue1", str + len, maxlen > len ? maxlen - len : 0);
247 "Input Value", "khaki1", str + len, maxlen > len ? maxlen - len : 0);
248
249 len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "</TABLE>\r\n");
250 len += snprintf(str + len, maxlen > len ? maxlen - len : 0, ">];\r\n");
251 len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "}\r\n");
252
253 return len;
254}
255
256bool DebugInfo::graphviz_system(const ExecutionSystem *system, char *str, int maxlen)
257{
258 int len = 0;
259
260 len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "digraph compositorexecution {\r\n");
261 len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "ranksep=1.5\r\n");
262 len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "rankdir=LR\r\n");
263 len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "splines=false\r\n");
264
265 std::map<NodeOperation *, std::vector<std::string>> op_groups;
266
267 for (NodeOperation *operation : system->operations_) {
268 if (op_groups.find(operation) != op_groups.end()) {
269 continue;
270 }
271
272 op_groups[operation].push_back(std::string(""));
273
274 len += graphviz_operation(system, operation, str + len, maxlen > len ? maxlen - len : 0);
275 }
276
277 for (NodeOperation *op : system->operations_) {
278 for (NodeOperationInput &to : op->inputs_) {
279 NodeOperationOutput *from = to.get_link();
280
281 if (!from) {
282 continue;
283 }
284
285 std::string color;
286 switch (from->get_data_type()) {
287 case DataType::Value:
288 color = "gray";
289 break;
290 case DataType::Vector:
291 color = "blue";
292 break;
293 case DataType::Color:
294 color = "orange";
295 break;
296 case DataType::Float2:
297 /* An internal type that needn't be handled. */
299 break;
300 }
301
302 NodeOperation *to_op = &to.get_operation();
303 NodeOperation *from_op = &from->get_operation();
304 std::vector<std::string> &from_groups = op_groups[from_op];
305 std::vector<std::string> &to_groups = op_groups[to_op];
306
307 len += snprintf(str + len,
308 maxlen > len ? maxlen - len : 0,
309 "// CONNECTION: %p.%p -> %p.%p\r\n",
310 from_op,
311 from,
312 to_op,
313 &to);
314 for (int k = 0; k < from_groups.size(); k++) {
315 for (int l = 0; l < to_groups.size(); l++) {
316 len += snprintf(str + len,
317 maxlen > len ? maxlen - len : 0,
318 R"("O_%p%s":"OUT_%p":e -> "O_%p%s":"IN_%p":w)",
319 from_op,
320 from_groups[k].c_str(),
321 from,
322 to_op,
323 to_groups[l].c_str(),
324 &to);
325 len += snprintf(
326 str + len, maxlen > len ? maxlen - len : 0, " [color=%s]", color.c_str());
327 len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "\r\n");
328 }
329 }
330 }
331 }
332
333 len += graphviz_legend(str + len, maxlen > len ? maxlen - len : 0);
334
335 len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "}\r\n");
336
337 return (len < maxlen);
338}
339
341{
342 if (!COM_EXPORT_GRAPHVIZ) {
343 return;
344 }
345 const int max_textlength = 1000000;
346 char *str = (char *)MEM_mallocN(max_textlength, __func__);
347 if (graphviz_system(system, str, max_textlength - 1)) {
348 char basename[FILE_MAX];
349 char filepath[FILE_MAX];
350
351 if (name.is_empty()) {
352 SNPRINTF(basename, "compositor_%d.dot", file_index_);
353 }
354 else {
355 STRNCPY(basename, (name + ".dot").c_str());
356 }
357 BLI_path_join(filepath, sizeof(filepath), BKE_tempdir_session(), basename);
358 file_index_++;
359
360 std::cout << "Writing compositor debug to: " << filepath << "\n";
361
362 FILE *fp = BLI_fopen(filepath, "wb");
363 fputs(str, fp);
364 fclose(fp);
365 }
366 MEM_freeN(str);
367}
368
369static std::string get_operations_export_dir()
370{
371 return std::string(BKE_tempdir_session()) + "COM_operations" + SEP_STR;
372}
373
375{
376 const int width = render->get_width();
377 const int height = render->get_height();
378 const int num_channels = render->get_num_channels();
379
380 ImBuf *ibuf = IMB_allocImBuf(width, height, 8 * num_channels, IB_rectfloat);
381 MemoryBuffer mem_ibuf(ibuf->float_buffer.data, 4, width, height);
382 mem_ibuf.copy_from(render, render->get_rect(), 0, num_channels, 0);
383
384 const std::string file_name = operation_class_name(op) + "_" + std::to_string(op->get_id()) +
385 ".png";
386 const std::string filepath = get_operations_export_dir() + file_name;
387 BLI_file_ensure_parent_dir_exists(filepath.c_str());
388 IMB_saveiff(ibuf, filepath.c_str(), ibuf->flags);
389 IMB_freeImBuf(ibuf);
390}
391
393{
394 const std::string dir = get_operations_export_dir();
395 if (BLI_exists(dir.c_str())) {
396 direntry *file_list;
397 int file_list_num = BLI_filelist_dir_contents(dir.c_str(), &file_list);
398 for (int i = 0; i < file_list_num; i++) {
399 direntry *file = &file_list[i];
400 const eFileAttributes file_attrs = BLI_file_attributes(file->path);
401 if (file_attrs & FILE_ATTR_ANY_LINK) {
402 continue;
403 }
404
405 if (BLI_is_file(file->path) && BLI_path_extension_check(file->path, ".png")) {
406 BLI_delete(file->path, false, false);
407 }
408 }
409 BLI_filelist_free(file_list, file_list_num);
410 }
411}
412
413} // namespace blender::compositor
#define BLI_assert_unreachable()
Definition BLI_assert.h:97
File and directory operations.
eFileAttributes BLI_file_attributes(const char *path)
Definition storage.cc:226
int BLI_exists(const char *path) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition storage.cc:350
#define FILE_ATTR_ANY_LINK
FILE * BLI_fopen(const char *filepath, const char *mode) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
unsigned int BLI_filelist_dir_contents(const char *dirname, struct direntry **r_filelist)
int BLI_delete(const char *path, bool dir, bool recursive) ATTR_NONNULL()
bool BLI_is_file(const char *path) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition storage.cc:438
void BLI_filelist_free(struct direntry *filelist, unsigned int nrentries)
eFileAttributes
bool BLI_file_ensure_parent_dir_exists(const char *filepath) ATTR_NONNULL(1)
Definition fileops_c.cc:429
#define FILE_MAX
#define BLI_path_join(...)
bool BLI_path_extension_check(const char *path, const char *ext) ATTR_NONNULL(1
#define STRNCPY(dst, src)
Definition BLI_string.h:593
#define SNPRINTF(dst, format,...)
Definition BLI_string.h:597
Contains defines and structs used throughout the imbuf module.
@ IB_rectfloat
ATTR_WARN_UNUSED_RESULT const BMLoop * l
bool is_rendering() const
get the rendering field of the context
virtual const float * get_constant_elem()=0
static std::string node_name(const Node *node)
Definition COM_Debug.cc:42
static void delete_operation_exports()
Definition COM_Debug.cc:392
static int graphviz_operation(const ExecutionSystem *system, NodeOperation *operation, char *str, int maxlen)
Definition COM_Debug.cc:60
static std::string operation_name(const NodeOperation *op)
Definition COM_Debug.cc:51
static int graphviz_legend_line(const char *name, const char *color, const char *style, char *str, int maxlen)
Definition COM_Debug.cc:199
static bool graphviz_system(const ExecutionSystem *system, char *str, int maxlen)
Definition COM_Debug.cc:256
std::map< const Node *, std::string > NodeNameMap
Definition COM_Debug.h:30
static void export_operation(const NodeOperation *op, MemoryBuffer *render)
Definition COM_Debug.cc:374
std::map< const NodeOperation *, std::string > OpNameMap
Definition COM_Debug.h:31
static int graphviz_legend(char *str, int maxlen)
Definition COM_Debug.cc:222
static int graphviz_legend_color(const char *name, const char *color, char *str, int maxlen)
Definition COM_Debug.cc:188
static void graphviz(const ExecutionSystem *system, StringRefNull name="")
Definition COM_Debug.cc:340
static int graphviz_legend_group(const char *name, const char *color, const char *style, char *str, int maxlen)
Definition COM_Debug.cc:208
the ExecutionSystem contains the whole compositor tree.
const CompositorContext & get_context() const
get the reference to the compositor context
a MemoryBuffer contains access to the data
void copy_from(const MemoryBuffer *src, const rcti &area)
NodeOperation contains calculation logic.
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...
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)
const std::string get_name() const
bool is_active_viewer_output() const override
is this operation the active viewer output user can select an ViewerNode to be active (the result of ...
StackEntry * from
int len
#define str(s)
struct ImBuf * IMB_allocImBuf(unsigned int, unsigned int, unsigned char, unsigned int)
bool IMB_saveiff(struct ImBuf *, const char *, int)
void IMB_freeImBuf(ImBuf *)
@ Vector
Vector data type.
@ Float2
Float2 data type.
void *(* MEM_mallocN)(size_t len, const char *str)
Definition mallocn.cc:44
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
static constexpr bool COM_EXPORT_GRAPHVIZ
Definition COM_Debug.h:18
static std::string get_operations_export_dir()
Definition COM_Debug.cc:369
static std::string operation_class_name(const NodeOperation *op)
Definition COM_Debug.cc:29
static constexpr bool COM_GRAPHVIZ_SHOW_NODE_NAME
Definition COM_Debug.h:19
ImBufFloatBuffer float_buffer
int ymin
int xmin
void * BKE_tempdir_session
Definition stubs.c:38
#define SEP_STR
Definition unit.cc:39