Blender V5.0
deg_debug_relations_graphviz.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2014 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
10
11#include <cstdarg>
12
13#include "BLI_dot_export.hh"
14
15#include "DEG_depsgraph.hh"
17
18#include "intern/depsgraph.hh"
20
25
26#include <sstream>
27
28namespace deg = blender::deg;
30
31/* ****************** */
32/* Graphviz Debugging */
33
34namespace blender::deg {
35
36/* Only one should be enabled, defines whether graphviz nodes
37 * get colored by individual types or classes.
38 */
39#define COLOR_SCHEME_NODE_CLASS 1
40// #define COLOR_SCHEME_NODE_TYPE 2
41
42static const char *deg_debug_graphviz_fontname = "helvetica";
45static const int deg_debug_max_colors = 12;
46#ifdef COLOR_SCHEME_NODE_TYPE
47static const char *deg_debug_colors[] = {
48 "#a6cee3",
49 "#1f78b4",
50 "#b2df8a",
51 "#33a02c",
52 "#fb9a99",
53 "#e31a1c",
54 "#fdbf6f",
55 "#ff7f00",
56 "#cab2d6",
57 "#6a3d9a",
58 "#ffff99",
59 "#b15928",
60 "#ff00ff",
61};
62#endif
63static const char *deg_debug_colors_light[] = {
64 "#8dd3c7",
65 "#ffffb3",
66 "#bebada",
67 "#fb8072",
68 "#80b1d3",
69 "#fdb462",
70 "#b3de69",
71 "#fccde5",
72 "#d9d9d9",
73 "#bc80bd",
74 "#ccebc5",
75 "#ffed6f",
76 "#ff00ff",
77};
78
79#ifdef COLOR_SCHEME_NODE_TYPE
80static const int deg_debug_node_type_color_map[][2] = {
83
84 /* Outer Types */
91 {NodeType::CACHE, 9},
95 {-1, 0},
96};
97#endif
98
99static int deg_debug_node_color_index(const Node *node)
100{
101#ifdef COLOR_SCHEME_NODE_CLASS
102 /* Some special types. */
103 switch (node->type) {
104 case NodeType::ID_REF:
105 return 5;
106 case NodeType::OPERATION: {
107 OperationNode *op_node = (OperationNode *)node;
108 if (op_node->is_noop()) {
110 return 7;
111 }
112 return 8;
113 }
114 break;
115 }
116
117 default:
118 break;
119 }
120 /* Do others based on class. */
121 switch (node->get_class()) {
123 return 4;
125 return 1;
126 default:
127 return 9;
128 }
129#endif
130
131#ifdef COLOR_SCHEME_NODE_TYPE
132 const int (*pair)[2];
133 for (pair = deg_debug_node_type_color_map; (*pair)[0] >= 0; pair++) {
134 if ((*pair)[0] == node->type) {
135 return (*pair)[1];
136 }
137 }
138 return -1;
139#endif
140}
141
148
149static void deg_debug_graphviz_legend_color(const char *name,
150 const char *color,
151 std::stringstream &ss)
152{
153
154 ss << "<TR>";
155 ss << "<TD>" << name << "</TD>";
156 ss << "<TD BGCOLOR=\"" << color << "\"></TD>";
157 ss << "</TR>";
158}
159
161{
162 dot_export::Node &legend_node = ctx.digraph.new_node("");
163 legend_node.attributes.set("rank", "sink");
164 legend_node.attributes.set("shape", "none");
165 legend_node.attributes.set("margin", 0);
166
167 std::stringstream ss;
168 ss << "<";
169 ss << R"(<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0" CELLPADDING="4">)";
170 ss << "<TR><TD COLSPAN=\"2\"><B>Legend</B></TD></TR>";
171
172#ifdef COLOR_SCHEME_NODE_CLASS
173 const char **colors = deg_debug_colors_light;
174 deg_debug_graphviz_legend_color("Operation", colors[4], ss);
175 deg_debug_graphviz_legend_color("Component", colors[1], ss);
176 deg_debug_graphviz_legend_color("ID Node", colors[5], ss);
177 deg_debug_graphviz_legend_color("NOOP", colors[8], ss);
178 deg_debug_graphviz_legend_color("Pinned OP", colors[7], ss);
179#endif
180
181#ifdef COLOR_SCHEME_NODE_TYPE
182 const int (*pair)[2];
183 for (pair = deg_debug_node_type_color_map; (*pair)[0] >= 0; pair++) {
184 DepsNodeFactory *nti = type_get_factory((NodeType)(*pair)[0]);
186 ctx, nti->tname().c_str(), deg_debug_colors_light[(*pair)[1] % deg_debug_max_colors], ss);
187 }
188#endif
189
190 ss << "</TABLE>";
191 ss << ">";
192 legend_node.attributes.set("label", ss.str());
193 legend_node.attributes.set("fontname", deg_debug_graphviz_fontname);
194}
195
197 const Node *node,
198 dot_export::Attributes &dot_attributes)
199{
200 const char *color_default = "black";
201 const char *color_modified = "orangered4";
202 const char *color_update = "dodgerblue3";
203 const char *color = color_default;
204 if (ctx.show_tags) {
205 if (node->get_class() == NodeClass::OPERATION) {
206 OperationNode *op_node = (OperationNode *)node;
207 if (op_node->flag & DEPSOP_FLAG_DIRECTLY_MODIFIED) {
208 color = color_modified;
209 }
210 else if (op_node->flag & DEPSOP_FLAG_NEEDS_UPDATE) {
211 color = color_update;
212 }
213 }
214 }
215 dot_attributes.set("color", color);
216}
217
219 const Node *node,
220 dot_export::Attributes &dot_attributes)
221{
222 float penwidth_default = 1.0f;
223 float penwidth_modified = 4.0f;
224 float penwidth_update = 4.0f;
225 float penwidth = penwidth_default;
226 if (ctx.show_tags) {
227 if (node->get_class() == NodeClass::OPERATION) {
228 OperationNode *op_node = (OperationNode *)node;
229 if (op_node->flag & DEPSOP_FLAG_DIRECTLY_MODIFIED) {
230 penwidth = penwidth_modified;
231 }
232 else if (op_node->flag & DEPSOP_FLAG_NEEDS_UPDATE) {
233 penwidth = penwidth_update;
234 }
235 }
236 }
237 dot_attributes.set("penwidth", penwidth);
238}
239
241 dot_export::Attributes &dot_attributes)
242{
243 const char *defaultcolor = "gainsboro";
244 int color_index = deg_debug_node_color_index(node);
245 const char *fillcolor = color_index < 0 ?
246 defaultcolor :
248 dot_attributes.set("fillcolor", fillcolor);
249}
250
252{
253 const char *color_default = "black";
254 const char *color_cyclic = "red4"; /* The color of crime scene. */
255 const char *color_godmode = "blue4"; /* The color of beautiful sky. */
256 const char *color = color_default;
257 if (rel->flag & RELATION_FLAG_CYCLIC) {
258 color = color_cyclic;
259 }
260 else if (rel->flag & RELATION_FLAG_GODMODE) {
261 color = color_godmode;
262 }
263 edge.attributes.set("color", color);
264}
265
267{
268 const char *style_default = "solid";
269 const char *style_no_flush = "dashed";
270 const char *style_flush_user_only = "dotted";
271 const char *style = style_default;
272 if (rel->flag & RELATION_FLAG_NO_FLUSH) {
273 style = style_no_flush;
274 }
276 style = style_flush_user_only;
277 }
278 edge.attributes.set("style", style);
279}
280
283{
284 const char *shape_default = "normal";
285 const char *shape_no_cow = "box";
286 const char *shape = shape_default;
287 if (rel->from->get_class() == NodeClass::OPERATION &&
289 {
290 OperationNode *op_from = (OperationNode *)rel->from;
291 OperationNode *op_to = (OperationNode *)rel->to;
292 if (op_from->owner->type == NodeType::COPY_ON_EVAL &&
293 /* The #ID::recalc flag depends on run-time state which is not valid at this point in time.
294 * Pass in all flags although there may be a better way to represent this. */
296 {
297 shape = shape_no_cow;
298 }
299 }
300 edge.attributes.set("arrowhead", shape);
301}
302
304 const Node *node,
305 dot_export::Attributes &dot_attributes)
306{
307 StringRef base_style = "filled"; /* default style */
308 if (ctx.show_tags) {
309 if (node->get_class() == NodeClass::OPERATION) {
310 OperationNode *op_node = (OperationNode *)node;
312 base_style = "striped";
313 }
314 }
315 }
316 switch (node->get_class()) {
318 dot_attributes.set("style", base_style);
319 break;
321 dot_attributes.set("style", base_style);
322 break;
324 dot_attributes.set("style", base_style + ",rounded");
325 break;
326 }
327}
328
330 const Node *node,
331 dot_export::Cluster *parent_cluster)
332{
333 std::string name = node->identifier();
334
335 dot_export::Node &dot_node = ctx.digraph.new_node(name);
336 ctx.nodes_map.add_new(node, &dot_node);
337 dot_node.set_parent_cluster(parent_cluster);
338 dot_node.attributes.set("fontname", deg_debug_graphviz_fontname);
340 dot_node.attributes.set("shape", "box");
341
342 deg_debug_graphviz_node_style(ctx, node, dot_node.attributes);
343 deg_debug_graphviz_node_color(ctx, node, dot_node.attributes);
345 deg_debug_graphviz_node_penwidth(ctx, node, dot_node.attributes);
346}
347
349 DotExportContext &ctx, const Node *node, dot_export::Cluster *parent_cluster)
350{
351 std::string name = node->identifier();
353 cluster.set_parent_cluster(parent_cluster);
354 cluster.attributes.set("fontname", deg_debug_graphviz_fontname);
356 cluster.attributes.set("margin", 16);
357 deg_debug_graphviz_node_style(ctx, node, cluster.attributes);
358 deg_debug_graphviz_node_color(ctx, node, cluster.attributes);
361 /* dummy node, so we can add edges between clusters */
362 dot_export::Node &dot_node = ctx.digraph.new_node("");
363 dot_node.attributes.set("shape", "point");
364 dot_node.attributes.set("style", "invis");
365 dot_node.set_parent_cluster(&cluster);
366 ctx.nodes_map.add_new(node, &dot_node);
367 ctx.clusters_map.add_new(node, &cluster);
368 return cluster;
369}
370
371static void deg_debug_graphviz_graph_nodes(DotExportContext &ctx, const Depsgraph *graph);
372static void deg_debug_graphviz_graph_relations(DotExportContext &ctx, const Depsgraph *graph);
373
375 const Node *node,
376 dot_export::Cluster *parent_cluster)
377{
378 switch (node->type) {
379 case NodeType::ID_REF: {
380 const IDNode *id_node = (const IDNode *)node;
381 if (id_node->components.is_empty()) {
382 deg_debug_graphviz_node_single(ctx, node, parent_cluster);
383 }
384 else {
386 ctx, node, parent_cluster);
387 for (const ComponentNode *comp : id_node->components.values()) {
388 deg_debug_graphviz_node(ctx, comp, &cluster);
389 }
390 }
391 break;
392 }
399 case NodeType::BONE:
401 case NodeType::CACHE:
413 case NodeType::AUDIO:
416 case NodeType::SCENE:
420 ComponentNode *comp_node = (ComponentNode *)node;
421 if (comp_node->operations.is_empty()) {
422 deg_debug_graphviz_node_single(ctx, node, parent_cluster);
423 }
424 else {
426 ctx, node, parent_cluster);
427 for (Node *op_node : comp_node->operations) {
428 deg_debug_graphviz_node(ctx, op_node, &cluster);
429 }
430 }
431 break;
432 }
436 deg_debug_graphviz_node_single(ctx, node, parent_cluster);
437 break;
439 break;
440 }
441}
442
444{
445 for (Relation *rel : node->inlinks) {
446 float penwidth = 2.0f;
447
448 const Node *head = rel->to; /* same as node */
449 const Node *tail = rel->from;
450 dot_export::Node &dot_tail = *ctx.nodes_map.lookup(tail);
451 dot_export::Node &dot_head = *ctx.nodes_map.lookup(head);
452
453 dot_export::DirectedEdge &edge = ctx.digraph.new_edge(dot_tail, dot_head);
454
455 /* NOTE: without label an id seem necessary to avoid bugs in graphviz/dot. */
456 edge.attributes.set("id", rel->name);
460 edge.attributes.set("penwidth", penwidth);
461
462 /* NOTE: edge from node to our own cluster is not possible and gives graphviz
463 * warning, avoid this here by just linking directly to the invisible
464 * placeholder node. */
465 dot_export::Cluster *tail_cluster = ctx.clusters_map.lookup_default(tail, nullptr);
466 if (tail_cluster != nullptr && tail_cluster->contains(dot_head)) {
467 edge.attributes.set("ltail", tail_cluster->name());
468 }
469 dot_export::Cluster *head_cluster = ctx.clusters_map.lookup_default(head, nullptr);
470 if (head_cluster != nullptr && head_cluster->contains(dot_tail)) {
471 edge.attributes.set("lhead", head_cluster->name());
472 }
473 }
474}
475
477{
478 for (Node *node : graph->id_nodes) {
479 deg_debug_graphviz_node(ctx, node, nullptr);
480 }
481 TimeSourceNode *time_source = graph->find_time_source();
482 if (time_source != nullptr) {
483 deg_debug_graphviz_node(ctx, time_source, nullptr);
484 }
485}
486
488{
489 for (IDNode *id_node : graph->id_nodes) {
490 for (ComponentNode *comp_node : id_node->components.values()) {
491 for (OperationNode *op_node : comp_node->operations) {
493 }
494 }
495 }
496
497 TimeSourceNode *time_source = graph->find_time_source();
498 if (time_source != nullptr) {
499 deg_debug_graphviz_node_relations(ctx, time_source);
500 }
501}
502
503} // namespace blender::deg
504
505std::string DEG_debug_graph_to_dot(const Depsgraph &graph, const blender::StringRef label)
506{
507 const deg::Depsgraph &deg_graph = reinterpret_cast<const deg::Depsgraph &>(graph);
508
510 deg::DotExportContext ctx{false, digraph};
511
513 digraph.attributes.set("compound", "true");
514 digraph.attributes.set("labelloc", "t");
517 digraph.attributes.set("label", label);
518 digraph.attributes.set("splines", "ortho");
519 digraph.attributes.set("overlap", "scalexy");
520
523
525
526 return digraph.to_dot_string();
527}
@ ID_RECALC_ALL
Definition DNA_ID.h:1188
bool is_empty() const
void set(StringRef key, StringRef value)
bool contains(Node &node) const
void set_parent_cluster(Cluster *new_parent)
Definition dot_export.cc:52
std::string to_dot_string() const
DirectedEdge & new_edge(NodePort from, NodePort to)
Definition dot_export.cc:45
Cluster & new_cluster(StringRef label="")
Definition dot_export.cc:29
void set_rankdir(Attr_rankdir rankdir)
Node & new_node(StringRef label)
Definition dot_export.cc:20
void set_parent_cluster(Cluster *cluster)
Definition dot_export.cc:72
std::string DEG_debug_graph_to_dot(const Depsgraph &graph, const blender::StringRef label)
static void deg_debug_graphviz_node_color(DotExportContext &ctx, const Node *node, dot_export::Attributes &dot_attributes)
static int deg_debug_node_color_index(const Node *node)
static void deg_debug_graphviz_graph_relations(DotExportContext &ctx, const Depsgraph *graph)
static void deg_debug_graphviz_legend(DotExportContext &ctx)
static void deg_debug_graphviz_relation_color(const Relation *rel, dot_export::DirectedEdge &edge)
static void deg_debug_graphviz_relation_style(const Relation *rel, dot_export::DirectedEdge &edge)
static void deg_debug_graphviz_node_fillcolor(const Node *node, dot_export::Attributes &dot_attributes)
static void deg_debug_graphviz_node_penwidth(DotExportContext &ctx, const Node *node, dot_export::Attributes &dot_attributes)
static float deg_debug_graphviz_node_label_size
static const char * deg_debug_colors_light[]
static dot_export::Cluster & deg_debug_graphviz_node_cluster_create(DotExportContext &ctx, const Node *node, dot_export::Cluster *parent_cluster)
static const int deg_debug_max_colors
static float deg_debug_graphviz_graph_label_size
static void deg_debug_graphviz_relation_arrowhead(const Relation *rel, dot_export::DirectedEdge &edge)
static void deg_debug_graphviz_graph_nodes(DotExportContext &ctx, const Depsgraph *graph)
static void deg_debug_graphviz_node_single(DotExportContext &ctx, const Node *node, dot_export::Cluster *parent_cluster)
DepsNodeFactory * type_get_factory(const NodeType type)
static void deg_debug_graphviz_legend_color(const char *name, const char *color, std::stringstream &ss)
static void deg_debug_graphviz_node(DotExportContext &ctx, const Node *node, dot_export::Cluster *parent_cluster)
static void deg_debug_graphviz_node_relations(DotExportContext &ctx, const Node *node)
static const char * deg_debug_graphviz_fontname
static void deg_debug_graphviz_node_style(DotExportContext &ctx, const Node *node, dot_export::Attributes &dot_attributes)
const char * name
virtual bool need_tag_cow_before_update(const IDRecalcFlag)
Vector< OperationNode * > operations
TimeSourceNode * find_time_source() const
Definition depsgraph.cc:91
Map< const Node *, dot_export::Cluster * > clusters_map
Map< const Node *, dot_export::Node * > nodes_map
Map< ComponentIDKey, ComponentNode * > components
Relations inlinks
Definition deg_node.hh:182
virtual std::string identifier() const
Definition deg_node.cc:290
virtual NodeClass get_class() const
Definition deg_node.cc:295