15744ff614f6933858a147478b0c12e5e2abb4ad
[blender.git] / source / blender / depsgraph / intern / debug / deg_debug_relations_graphviz.cc
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * The Original Code is Copyright (C) 2014 Blender Foundation.
19  * All rights reserved.
20  *
21  * Original Author: Lukas Toenne
22  * Contributor(s): None Yet
23  *
24  * ***** END GPL LICENSE BLOCK *****
25  */
26
27 /** \file blender/depsgraph/intern/debug/deg_debug_relations_graphviz.cc
28  *  \ingroup depsgraph
29  *
30  * Implementation of tools for debugging the depsgraph
31  */
32
33 #include "BLI_utildefines.h"
34 #include "BLI_ghash.h"
35
36 extern "C" {
37 #include "DNA_listBase.h"
38 }  /* extern "C" */
39
40 #include "DEG_depsgraph.h"
41 #include "DEG_depsgraph_debug.h"
42
43 #include "intern/depsgraph_intern.h"
44 #include "intern/nodes/deg_node_id.h"
45 #include "intern/nodes/deg_node_time.h"
46
47 #include "util/deg_util_foreach.h"
48
49 /* ****************** */
50 /* Graphviz Debugging */
51
52 namespace DEG {
53
54 #define NL "\r\n"
55
56 /* Only one should be enabled, defines whether graphviz nodes
57  * get colored by individual types or classes.
58  */
59 #define COLOR_SCHEME_NODE_CLASS 1
60 //#define COLOR_SCHEME_NODE_TYPE  2
61
62 static const char *deg_debug_graphviz_fontname = "helvetica";
63 static float deg_debug_graphviz_graph_label_size = 20.0f;
64 static float deg_debug_graphviz_node_label_size = 14.0f;
65 static const int deg_debug_max_colors = 12;
66 #ifdef COLOR_SCHEME_NODE_TYPE
67 static const char *deg_debug_colors[] = {
68     "#a6cee3", "#1f78b4", "#b2df8a",
69     "#33a02c", "#fb9a99", "#e31a1c",
70     "#fdbf6f", "#ff7f00", "#cab2d6",
71     "#6a3d9a", "#ffff99", "#b15928",
72     "#ff00ff",
73 };
74 #endif
75 static const char *deg_debug_colors_light[] = {
76     "#8dd3c7", "#ffffb3", "#bebada",
77     "#fb8072", "#80b1d3", "#fdb462",
78     "#b3de69", "#fccde5", "#d9d9d9",
79     "#bc80bd", "#ccebc5", "#ffed6f",
80     "#ff00ff",
81 };
82
83 #ifdef COLOR_SCHEME_NODE_TYPE
84 static const int deg_debug_node_type_color_map[][2] = {
85     {DEG_NODE_TYPE_TIMESOURCE,   0},
86     {DEG_NODE_TYPE_ID_REF,       1},
87
88     /* Outer Types */
89     {DEG_NODE_TYPE_PARAMETERS,         2},
90     {DEG_NODE_TYPE_PROXY,              3},
91     {DEG_NODE_TYPE_ANIMATION,          4},
92     {DEG_NODE_TYPE_TRANSFORM,          5},
93     {DEG_NODE_TYPE_GEOMETRY,           6},
94     {DEG_NODE_TYPE_SEQUENCER,          7},
95     {DEG_NODE_TYPE_SHADING,            8},
96     {DEG_NODE_TYPE_SHADING_PARAMETERS, 9},
97     {DEG_NODE_TYPE_CACHE,              10},
98     {DEG_NODE_TYPE_POINT_CACHE,        11},
99     {DEG_NODE_TYPE_LAYER_COLLECTIONS,  12},
100     {DEG_NODE_TYPE_COPY_ON_WRITE,      13},
101     {-1,                               0}
102 };
103 #endif
104
105 static int deg_debug_node_color_index(const DepsNode *node)
106 {
107 #ifdef COLOR_SCHEME_NODE_CLASS
108         /* Some special types. */
109         switch (node->type) {
110                 case DEG_NODE_TYPE_ID_REF:
111                         return 5;
112                 case DEG_NODE_TYPE_OPERATION:
113                 {
114                         OperationDepsNode *op_node = (OperationDepsNode *)node;
115                         if (op_node->is_noop())
116                                 return 8;
117                         break;
118                 }
119
120                 default:
121                         break;
122         }
123         /* Do others based on class. */
124         switch (node->get_class()) {
125                 case DEG_NODE_CLASS_OPERATION:
126                         return 4;
127                 case DEG_NODE_CLASS_COMPONENT:
128                         return 1;
129                 default:
130                         return 9;
131         }
132 #endif
133
134 #ifdef COLOR_SCHEME_NODE_TYPE
135         const int (*pair)[2];
136         for (pair = deg_debug_node_type_color_map; (*pair)[0] >= 0; ++pair) {
137                 if ((*pair)[0] == node->type) {
138                         return (*pair)[1];
139                 }
140         }
141         return -1;
142 #endif
143 }
144
145 struct DebugContext {
146         FILE *file;
147         bool show_tags;
148 };
149
150 static void deg_debug_fprintf(const DebugContext &ctx, const char *fmt, ...) ATTR_PRINTF_FORMAT(2, 3);
151 static void deg_debug_fprintf(const DebugContext &ctx, const char *fmt, ...)
152 {
153         va_list args;
154         va_start(args, fmt);
155         vfprintf(ctx.file, fmt, args);
156         va_end(args);
157 }
158
159 static void deg_debug_graphviz_legend_color(const DebugContext &ctx,
160                                             const char *name,
161                                             const char *color)
162 {
163         deg_debug_fprintf(ctx, "<TR>");
164         deg_debug_fprintf(ctx, "<TD>%s</TD>", name);
165         deg_debug_fprintf(ctx, "<TD BGCOLOR=\"%s\"></TD>", color);
166         deg_debug_fprintf(ctx, "</TR>" NL);
167 }
168
169 static void deg_debug_graphviz_legend(const DebugContext &ctx)
170 {
171         deg_debug_fprintf(ctx, "{" NL);
172         deg_debug_fprintf(ctx, "rank = sink;" NL);
173         deg_debug_fprintf(ctx, "Legend [shape=none, margin=0, label=<" NL);
174         deg_debug_fprintf(ctx, "  <TABLE BORDER=\"0\" CELLBORDER=\"1\" CELLSPACING=\"0\" CELLPADDING=\"4\">" NL);
175         deg_debug_fprintf(ctx, "<TR><TD COLSPAN=\"2\"><B>Legend</B></TD></TR>" NL);
176
177 #ifdef COLOR_SCHEME_NODE_CLASS
178         const char **colors = deg_debug_colors_light;
179         deg_debug_graphviz_legend_color(ctx, "Operation", colors[4]);
180         deg_debug_graphviz_legend_color(ctx, "Component", colors[1]);
181         deg_debug_graphviz_legend_color(ctx, "ID Node", colors[5]);
182         deg_debug_graphviz_legend_color(ctx, "NOOP", colors[8]);
183 #endif
184
185 #ifdef COLOR_SCHEME_NODE_TYPE
186         const int (*pair)[2];
187         for (pair = deg_debug_node_type_color_map; (*pair)[0] >= 0; ++pair) {
188                 DepsNodeFactory *nti = deg_type_get_factory((eDepsNode_Type)(*pair)[0]);
189                 deg_debug_graphviz_legend_color(ctx,
190                                                 nti->tname().c_str(),
191                                                 deg_debug_colors_light[(*pair)[1] % deg_debug_max_colors]);
192         }
193 #endif
194
195         deg_debug_fprintf(ctx, "</TABLE>" NL);
196         deg_debug_fprintf(ctx, ">" NL);
197         deg_debug_fprintf(ctx, ",fontname=\"%s\"", deg_debug_graphviz_fontname);
198         deg_debug_fprintf(ctx, "];" NL);
199         deg_debug_fprintf(ctx, "}" NL);
200 }
201
202 static void deg_debug_graphviz_node_color(const DebugContext &ctx,
203                                           const DepsNode *node)
204 {
205         const char *color_default = "black";
206         const char *color_modified = "orangered4";
207         const char *color_update = "dodgerblue3";
208         const char *color = color_default;
209         if (ctx.show_tags) {
210                 if (node->get_class() == DEG_NODE_CLASS_OPERATION) {
211                         OperationDepsNode *op_node = (OperationDepsNode *)node;
212                         if (op_node->flag & DEPSOP_FLAG_DIRECTLY_MODIFIED) {
213                                 color = color_modified;
214                         }
215                         else if (op_node->flag & DEPSOP_FLAG_NEEDS_UPDATE) {
216                                 color = color_update;
217                         }
218                 }
219         }
220         deg_debug_fprintf(ctx, "\"%s\"", color);
221 }
222
223 static void deg_debug_graphviz_node_penwidth(const DebugContext &ctx,
224                                              const DepsNode *node)
225 {
226         float penwidth_default = 1.0f;
227         float penwidth_modified = 4.0f;
228         float penwidth_update = 4.0f;
229         float penwidth = penwidth_default;
230         if (ctx.show_tags) {
231                 if (node->get_class() == DEG_NODE_CLASS_OPERATION) {
232                         OperationDepsNode *op_node = (OperationDepsNode *)node;
233                         if (op_node->flag & DEPSOP_FLAG_DIRECTLY_MODIFIED) {
234                                 penwidth = penwidth_modified;
235                         }
236                         else if (op_node->flag & DEPSOP_FLAG_NEEDS_UPDATE) {
237                                 penwidth = penwidth_update;
238                         }
239                 }
240         }
241         deg_debug_fprintf(ctx, "\"%f\"", penwidth);
242 }
243
244 static void deg_debug_graphviz_node_fillcolor(const DebugContext &ctx,
245                                               const DepsNode *node)
246 {
247         const char *defaultcolor = "gainsboro";
248         int color_index = deg_debug_node_color_index(node);
249         const char *fillcolor = color_index < 0 ? defaultcolor : deg_debug_colors_light[color_index % deg_debug_max_colors];
250         deg_debug_fprintf(ctx, "\"%s\"", fillcolor);
251 }
252
253 static void deg_debug_graphviz_relation_color(const DebugContext &ctx,
254                                               const DepsRelation *rel)
255 {
256         const char *color_default = "black";
257         const char *color_cyclic = "red4";  /* The color of crime scene. */
258         const char *color_godmode = "blue4";  /* The color of beautiful sky. */
259         const char *color = color_default;
260         if (rel->flag & DEPSREL_FLAG_CYCLIC) {
261                 color = color_cyclic;
262         }
263         else if (rel->flag & DEPSREL_FLAG_GODMODE) {
264                 color = color_godmode;
265         }
266         deg_debug_fprintf(ctx, "%s", color);
267 }
268
269 static void deg_debug_graphviz_relation_style(const DebugContext &ctx,
270                                               const DepsRelation *rel)
271 {
272         const char *style_default = "solid";
273         const char *style_no_flush = "dashed";
274         const char *style_flush_user_only = "dotted";
275         const char *style = style_default;
276         if (rel->flag & DEPSREL_FLAG_NO_FLUSH) {
277                 style = style_no_flush;
278         }
279         if (rel->flag & DEPSREL_FLAG_FLUSH_USER_EDIT_ONLY) {
280                 style = style_flush_user_only;
281         }
282         deg_debug_fprintf(ctx, "%s", style);
283 }
284
285 static void deg_debug_graphviz_relation_arrowhead(const DebugContext &ctx,
286                                                   const DepsRelation *rel)
287 {
288         const char *shape_default = "normal";
289         const char *shape_no_cow = "box";
290         const char *shape = shape_default;
291         if (rel->from->get_class() == DEG_NODE_CLASS_OPERATION &&
292             rel->to->get_class() == DEG_NODE_CLASS_OPERATION)
293         {
294                 OperationDepsNode *op_from = (OperationDepsNode *)rel->from;
295                 OperationDepsNode *op_to = (OperationDepsNode *)rel->to;
296                 if (op_from->owner->type == DEG_NODE_TYPE_COPY_ON_WRITE &&
297                     !op_to->owner->need_tag_cow_before_update())
298                 {
299                         shape = shape_no_cow;
300                 }
301         }
302         deg_debug_fprintf(ctx, "%s", shape);
303 }
304
305 static void deg_debug_graphviz_node_style(const DebugContext &ctx, const DepsNode *node)
306 {
307         const char *base_style = "filled"; /* default style */
308         if (ctx.show_tags) {
309                 if (node->get_class() == DEG_NODE_CLASS_OPERATION) {
310                         OperationDepsNode *op_node = (OperationDepsNode *)node;
311                         if (op_node->flag & (DEPSOP_FLAG_DIRECTLY_MODIFIED | DEPSOP_FLAG_NEEDS_UPDATE)) {
312                                 base_style = "striped";
313                         }
314                 }
315         }
316         switch (node->get_class()) {
317                 case DEG_NODE_CLASS_GENERIC:
318                         deg_debug_fprintf(ctx, "\"%s\"", base_style);
319                         break;
320                 case DEG_NODE_CLASS_COMPONENT:
321                         deg_debug_fprintf(ctx, "\"%s\"", base_style);
322                         break;
323                 case DEG_NODE_CLASS_OPERATION:
324                         deg_debug_fprintf(ctx, "\"%s,rounded\"", base_style);
325                         break;
326         }
327 }
328
329 static void deg_debug_graphviz_node_single(const DebugContext &ctx,
330                                            const DepsNode *node)
331 {
332         const char *shape = "box";
333         string name = node->identifier();
334         deg_debug_fprintf(ctx, "// %s\n", name.c_str());
335         deg_debug_fprintf(ctx, "\"node_%p\"", node);
336         deg_debug_fprintf(ctx, "[");
337 //      deg_debug_fprintf(ctx, "label=<<B>%s</B>>", name);
338         deg_debug_fprintf(ctx, "label=<%s>", name.c_str());
339         deg_debug_fprintf(ctx, ",fontname=\"%s\"", deg_debug_graphviz_fontname);
340         deg_debug_fprintf(ctx, ",fontsize=%f", deg_debug_graphviz_node_label_size);
341         deg_debug_fprintf(ctx, ",shape=%s", shape);
342         deg_debug_fprintf(ctx, ",style="); deg_debug_graphviz_node_style(ctx, node);
343         deg_debug_fprintf(ctx, ",color="); deg_debug_graphviz_node_color(ctx, node);
344         deg_debug_fprintf(ctx, ",fillcolor="); deg_debug_graphviz_node_fillcolor(ctx, node);
345         deg_debug_fprintf(ctx, ",penwidth="); deg_debug_graphviz_node_penwidth(ctx, node);
346         deg_debug_fprintf(ctx, "];" NL);
347         deg_debug_fprintf(ctx, NL);
348 }
349
350 static void deg_debug_graphviz_node_cluster_begin(const DebugContext &ctx,
351                                                   const DepsNode *node)
352 {
353         string name = node->identifier();
354         deg_debug_fprintf(ctx, "// %s\n", name.c_str());
355         deg_debug_fprintf(ctx, "subgraph \"cluster_%p\" {" NL, node);
356 //      deg_debug_fprintf(ctx, "label=<<B>%s</B>>;" NL, name);
357         deg_debug_fprintf(ctx, "label=<%s>;" NL, name.c_str());
358         deg_debug_fprintf(ctx, "fontname=\"%s\";" NL, deg_debug_graphviz_fontname);
359         deg_debug_fprintf(ctx, "fontsize=%f;" NL, deg_debug_graphviz_node_label_size);
360         deg_debug_fprintf(ctx, "margin=\"%d\";" NL, 16);
361         deg_debug_fprintf(ctx, "style="); deg_debug_graphviz_node_style(ctx, node); deg_debug_fprintf(ctx, ";" NL);
362         deg_debug_fprintf(ctx, "color="); deg_debug_graphviz_node_color(ctx, node); deg_debug_fprintf(ctx, ";" NL);
363         deg_debug_fprintf(ctx, "fillcolor="); deg_debug_graphviz_node_fillcolor(ctx, node); deg_debug_fprintf(ctx, ";" NL);
364         deg_debug_fprintf(ctx, "penwidth="); deg_debug_graphviz_node_penwidth(ctx, node); deg_debug_fprintf(ctx, ";" NL);
365         /* dummy node, so we can add edges between clusters */
366         deg_debug_fprintf(ctx, "\"node_%p\"", node);
367         deg_debug_fprintf(ctx, "[");
368         deg_debug_fprintf(ctx, "shape=%s", "point");
369         deg_debug_fprintf(ctx, ",style=%s", "invis");
370         deg_debug_fprintf(ctx, "];" NL);
371         deg_debug_fprintf(ctx, NL);
372 }
373
374 static void deg_debug_graphviz_node_cluster_end(const DebugContext &ctx)
375 {
376         deg_debug_fprintf(ctx, "}" NL);
377         deg_debug_fprintf(ctx, NL);
378 }
379
380 static void deg_debug_graphviz_graph_nodes(const DebugContext &ctx,
381                                            const Depsgraph *graph);
382 static void deg_debug_graphviz_graph_relations(const DebugContext &ctx,
383                                                const Depsgraph *graph);
384
385 static void deg_debug_graphviz_node(const DebugContext &ctx,
386                                     const DepsNode *node)
387 {
388         switch (node->type) {
389                 case DEG_NODE_TYPE_ID_REF:
390                 {
391                         const IDDepsNode *id_node = (const IDDepsNode *)node;
392                         if (BLI_ghash_len(id_node->components) == 0) {
393                                 deg_debug_graphviz_node_single(ctx, node);
394                         }
395                         else {
396                                 deg_debug_graphviz_node_cluster_begin(ctx, node);
397                                 GHASH_FOREACH_BEGIN(const ComponentDepsNode *, comp, id_node->components)
398                                 {
399                                         deg_debug_graphviz_node(ctx, comp);
400                                 }
401                                 GHASH_FOREACH_END();
402                                 deg_debug_graphviz_node_cluster_end(ctx);
403                         }
404                         break;
405                 }
406                 case DEG_NODE_TYPE_PARAMETERS:
407                 case DEG_NODE_TYPE_ANIMATION:
408                 case DEG_NODE_TYPE_TRANSFORM:
409                 case DEG_NODE_TYPE_PROXY:
410                 case DEG_NODE_TYPE_GEOMETRY:
411                 case DEG_NODE_TYPE_SEQUENCER:
412                 case DEG_NODE_TYPE_EVAL_POSE:
413                 case DEG_NODE_TYPE_BONE:
414                 case DEG_NODE_TYPE_SHADING:
415                 case DEG_NODE_TYPE_SHADING_PARAMETERS:
416                 case DEG_NODE_TYPE_CACHE:
417                 case DEG_NODE_TYPE_POINT_CACHE:
418                 case DEG_NODE_TYPE_LAYER_COLLECTIONS:
419                 case DEG_NODE_TYPE_EVAL_PARTICLES:
420                 case DEG_NODE_TYPE_COPY_ON_WRITE:
421                 case DEG_NODE_TYPE_OBJECT_FROM_LAYER:
422                 case DEG_NODE_TYPE_BATCH_CACHE:
423                 case DEG_NODE_TYPE_DUPLI:
424                 case DEG_NODE_TYPE_SYNCHRONIZE:
425                 case DEG_NODE_TYPE_GENERIC_DATABLOCK:
426                 {
427                         ComponentDepsNode *comp_node = (ComponentDepsNode *)node;
428                         if (!comp_node->operations.empty()) {
429                                 deg_debug_graphviz_node_cluster_begin(ctx, node);
430                                 foreach (DepsNode *op_node, comp_node->operations) {
431                                         deg_debug_graphviz_node(ctx, op_node);
432                                 }
433                                 deg_debug_graphviz_node_cluster_end(ctx);
434                         }
435                         else {
436                                 deg_debug_graphviz_node_single(ctx, node);
437                         }
438                         break;
439                 }
440                 case DEG_NODE_TYPE_UNDEFINED:
441                 case DEG_NODE_TYPE_TIMESOURCE:
442                 case DEG_NODE_TYPE_OPERATION:
443                         deg_debug_graphviz_node_single(ctx, node);
444                         break;
445                 case NUM_DEG_NODE_TYPES:
446                         break;
447         }
448 }
449
450 static bool deg_debug_graphviz_is_cluster(const DepsNode *node)
451 {
452         switch (node->type) {
453                 case DEG_NODE_TYPE_ID_REF:
454                 {
455                         const IDDepsNode *id_node = (const IDDepsNode *)node;
456                         return BLI_ghash_len(id_node->components) > 0;
457                 }
458                 case DEG_NODE_TYPE_PARAMETERS:
459                 case DEG_NODE_TYPE_ANIMATION:
460                 case DEG_NODE_TYPE_TRANSFORM:
461                 case DEG_NODE_TYPE_PROXY:
462                 case DEG_NODE_TYPE_GEOMETRY:
463                 case DEG_NODE_TYPE_SEQUENCER:
464                 case DEG_NODE_TYPE_EVAL_POSE:
465                 case DEG_NODE_TYPE_BONE:
466                 {
467                         ComponentDepsNode *comp_node = (ComponentDepsNode *)node;
468                         return !comp_node->operations.empty();
469                 }
470                 default:
471                         return false;
472         }
473 }
474
475 static bool deg_debug_graphviz_is_owner(const DepsNode *node,
476                                         const DepsNode *other)
477 {
478         switch (node->get_class()) {
479                 case DEG_NODE_CLASS_COMPONENT:
480                 {
481                         ComponentDepsNode *comp_node = (ComponentDepsNode *)node;
482                         if (comp_node->owner == other)
483                                 return true;
484                         break;
485                 }
486                 case DEG_NODE_CLASS_OPERATION:
487                 {
488                         OperationDepsNode *op_node = (OperationDepsNode *)node;
489                         if (op_node->owner == other)
490                                 return true;
491                         else if (op_node->owner->owner == other)
492                                 return true;
493                         break;
494                 }
495                 default: break;
496         }
497         return false;
498 }
499
500 static void deg_debug_graphviz_node_relations(const DebugContext &ctx,
501                                               const DepsNode *node)
502 {
503         foreach (DepsRelation *rel, node->inlinks) {
504                 float penwidth = 2.0f;
505
506                 const DepsNode *tail = rel->to; /* same as node */
507                 const DepsNode *head = rel->from;
508                 deg_debug_fprintf(ctx, "// %s -> %s\n",
509                                  head->identifier().c_str(),
510                                  tail->identifier().c_str());
511                 deg_debug_fprintf(ctx, "\"node_%p\"", head);
512                 deg_debug_fprintf(ctx, " -> ");
513                 deg_debug_fprintf(ctx, "\"node_%p\"", tail);
514
515                 deg_debug_fprintf(ctx, "[");
516                 /* Note: without label an id seem necessary to avoid bugs in graphviz/dot */
517                 deg_debug_fprintf(ctx, "id=\"%s\"", rel->name);
518                 // deg_debug_fprintf(ctx, "label=\"%s\"", rel->name);
519                 deg_debug_fprintf(ctx, ",color=");
520                 deg_debug_graphviz_relation_color(ctx, rel);
521                 deg_debug_fprintf(ctx, ",style=");
522                 deg_debug_graphviz_relation_style(ctx, rel);
523                 deg_debug_fprintf(ctx, ",arrowhead=");
524                 deg_debug_graphviz_relation_arrowhead(ctx, rel);
525                 deg_debug_fprintf(ctx, ",penwidth=\"%f\"", penwidth);
526                 /* NOTE: edge from node to own cluster is not possible and gives graphviz
527                  * warning, avoid this here by just linking directly to the invisible
528                  * placeholder node
529                  */
530                 if (deg_debug_graphviz_is_cluster(tail) &&
531                     !deg_debug_graphviz_is_owner(head, tail))
532                 {
533                         deg_debug_fprintf(ctx, ",ltail=\"cluster_%p\"", tail);
534                 }
535                 if (deg_debug_graphviz_is_cluster(head) &&
536                     !deg_debug_graphviz_is_owner(tail, head))
537                 {
538                         deg_debug_fprintf(ctx, ",lhead=\"cluster_%p\"", head);
539                 }
540                 deg_debug_fprintf(ctx, "];" NL);
541                 deg_debug_fprintf(ctx, NL);
542         }
543 }
544
545 static void deg_debug_graphviz_graph_nodes(const DebugContext &ctx,
546                                            const Depsgraph *graph)
547 {
548         foreach (DepsNode *node, graph->id_nodes) {
549                 deg_debug_graphviz_node(ctx, node);
550         }
551         TimeSourceDepsNode *time_source = graph->find_time_source();
552         if (time_source != NULL) {
553                 deg_debug_graphviz_node(ctx, time_source);
554         }
555 }
556
557 static void deg_debug_graphviz_graph_relations(const DebugContext &ctx,
558                                                const Depsgraph *graph)
559 {
560         foreach (IDDepsNode *id_node, graph->id_nodes) {
561                 GHASH_FOREACH_BEGIN(ComponentDepsNode *, comp_node, id_node->components)
562                 {
563                         foreach (OperationDepsNode *op_node, comp_node->operations) {
564                                 deg_debug_graphviz_node_relations(ctx, op_node);
565                         }
566                 }
567                 GHASH_FOREACH_END();
568         }
569
570         TimeSourceDepsNode *time_source = graph->find_time_source();
571         if (time_source != NULL) {
572                 deg_debug_graphviz_node_relations(ctx, time_source);
573         }
574 }
575
576 }  // namespace DEG
577
578 void DEG_debug_relations_graphviz(const Depsgraph *graph,
579                                   FILE *f,
580                                   const char *label)
581 {
582         if (!graph) {
583                 return;
584         }
585
586         const DEG::Depsgraph *deg_graph = reinterpret_cast<const DEG::Depsgraph *>(graph);
587
588         DEG::DebugContext ctx;
589         ctx.file = f;
590
591         DEG::deg_debug_fprintf(ctx, "digraph depgraph {" NL);
592         DEG::deg_debug_fprintf(ctx, "rankdir=LR;" NL);
593         DEG::deg_debug_fprintf(ctx, "graph [");
594         DEG::deg_debug_fprintf(ctx, "compound=true");
595         DEG::deg_debug_fprintf(ctx, ",labelloc=\"t\"");
596         DEG::deg_debug_fprintf(ctx, ",fontsize=%f", DEG::deg_debug_graphviz_graph_label_size);
597         DEG::deg_debug_fprintf(ctx, ",fontname=\"%s\"", DEG::deg_debug_graphviz_fontname);
598         DEG::deg_debug_fprintf(ctx, ",label=\"%s\"", label);
599         DEG::deg_debug_fprintf(ctx, ",splines=ortho");
600         DEG::deg_debug_fprintf(ctx, ",overlap=scalexy"); // XXX: only when using neato
601         DEG::deg_debug_fprintf(ctx, "];" NL);
602
603         DEG::deg_debug_graphviz_graph_nodes(ctx, deg_graph);
604         DEG::deg_debug_graphviz_graph_relations(ctx, deg_graph);
605
606         DEG::deg_debug_graphviz_legend(ctx);
607
608         DEG::deg_debug_fprintf(ctx, "}" NL);
609 }
610
611 #undef NL