Blender V4.3
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
11#include <cstdarg>
12
13#include "BLI_dot_export.hh"
14#include "BLI_utildefines.h"
15
16#include "DNA_listBase.h"
17
18#include "DEG_depsgraph.hh"
20
21#include "intern/depsgraph.hh"
23
28
29#include <sstream>
30
31namespace deg = blender::deg;
32namespace dot = blender::dot;
33
34/* ****************** */
35/* Graphviz Debugging */
36
37namespace blender::deg {
38
39/* Only one should be enabled, defines whether graphviz nodes
40 * get colored by individual types or classes.
41 */
42#define COLOR_SCHEME_NODE_CLASS 1
43// #define COLOR_SCHEME_NODE_TYPE 2
44
45static const char *deg_debug_graphviz_fontname = "helvetica";
48static const int deg_debug_max_colors = 12;
49#ifdef COLOR_SCHEME_NODE_TYPE
50static const char *deg_debug_colors[] = {
51 "#a6cee3",
52 "#1f78b4",
53 "#b2df8a",
54 "#33a02c",
55 "#fb9a99",
56 "#e31a1c",
57 "#fdbf6f",
58 "#ff7f00",
59 "#cab2d6",
60 "#6a3d9a",
61 "#ffff99",
62 "#b15928",
63 "#ff00ff",
64};
65#endif
66static const char *deg_debug_colors_light[] = {
67 "#8dd3c7",
68 "#ffffb3",
69 "#bebada",
70 "#fb8072",
71 "#80b1d3",
72 "#fdb462",
73 "#b3de69",
74 "#fccde5",
75 "#d9d9d9",
76 "#bc80bd",
77 "#ccebc5",
78 "#ffed6f",
79 "#ff00ff",
80};
81
82#ifdef COLOR_SCHEME_NODE_TYPE
83static const int deg_debug_node_type_color_map[][2] = {
86
87 /* Outer Types */
94 {NodeType::CACHE, 9},
98 {-1, 0},
99};
100#endif
101
102static int deg_debug_node_color_index(const Node *node)
103{
104#ifdef COLOR_SCHEME_NODE_CLASS
105 /* Some special types. */
106 switch (node->type) {
107 case NodeType::ID_REF:
108 return 5;
109 case NodeType::OPERATION: {
110 OperationNode *op_node = (OperationNode *)node;
111 if (op_node->is_noop()) {
113 return 7;
114 }
115 return 8;
116 }
117 break;
118 }
119
120 default:
121 break;
122 }
123 /* Do others based on class. */
124 switch (node->get_class()) {
126 return 4;
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
151
152static void deg_debug_graphviz_legend_color(const char *name,
153 const char *color,
154 std::stringstream &ss)
155{
156
157 ss << "<TR>";
158 ss << "<TD>" << name << "</TD>";
159 ss << "<TD BGCOLOR=\"" << color << "\"></TD>";
160 ss << "</TR>";
161}
162
164{
165 dot::Node &legend_node = ctx.digraph.new_node("");
166 legend_node.attributes.set("rank", "sink");
167 legend_node.attributes.set("shape", "none");
168 legend_node.attributes.set("margin", 0);
169
170 std::stringstream ss;
171 ss << "<";
172 ss << R"(<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0" CELLPADDING="4">)";
173 ss << "<TR><TD COLSPAN=\"2\"><B>Legend</B></TD></TR>";
174
175#ifdef COLOR_SCHEME_NODE_CLASS
176 const char **colors = deg_debug_colors_light;
177 deg_debug_graphviz_legend_color("Operation", colors[4], ss);
178 deg_debug_graphviz_legend_color("Component", colors[1], ss);
179 deg_debug_graphviz_legend_color("ID Node", colors[5], ss);
180 deg_debug_graphviz_legend_color("NOOP", colors[8], ss);
181 deg_debug_graphviz_legend_color("Pinned OP", colors[7], ss);
182#endif
183
184#ifdef COLOR_SCHEME_NODE_TYPE
185 const int(*pair)[2];
186 for (pair = deg_debug_node_type_color_map; (*pair)[0] >= 0; pair++) {
187 DepsNodeFactory *nti = type_get_factory((NodeType)(*pair)[0]);
189 ctx, nti->tname().c_str(), deg_debug_colors_light[(*pair)[1] % deg_debug_max_colors], ss);
190 }
191#endif
192
193 ss << "</TABLE>";
194 ss << ">";
195 legend_node.attributes.set("label", ss.str());
196 legend_node.attributes.set("fontname", deg_debug_graphviz_fontname);
197}
198
200 const Node *node,
201 dot::Attributes &dot_attributes)
202{
203 const char *color_default = "black";
204 const char *color_modified = "orangered4";
205 const char *color_update = "dodgerblue3";
206 const char *color = color_default;
207 if (ctx.show_tags) {
208 if (node->get_class() == NodeClass::OPERATION) {
209 OperationNode *op_node = (OperationNode *)node;
210 if (op_node->flag & DEPSOP_FLAG_DIRECTLY_MODIFIED) {
211 color = color_modified;
212 }
213 else if (op_node->flag & DEPSOP_FLAG_NEEDS_UPDATE) {
214 color = color_update;
215 }
216 }
217 }
218 dot_attributes.set("color", color);
219}
220
222 const Node *node,
223 dot::Attributes &dot_attributes)
224{
225 float penwidth_default = 1.0f;
226 float penwidth_modified = 4.0f;
227 float penwidth_update = 4.0f;
228 float penwidth = penwidth_default;
229 if (ctx.show_tags) {
230 if (node->get_class() == NodeClass::OPERATION) {
231 OperationNode *op_node = (OperationNode *)node;
232 if (op_node->flag & DEPSOP_FLAG_DIRECTLY_MODIFIED) {
233 penwidth = penwidth_modified;
234 }
235 else if (op_node->flag & DEPSOP_FLAG_NEEDS_UPDATE) {
236 penwidth = penwidth_update;
237 }
238 }
239 }
240 dot_attributes.set("penwidth", penwidth);
241}
242
243static void deg_debug_graphviz_node_fillcolor(const Node *node, dot::Attributes &dot_attributes)
244{
245 const char *defaultcolor = "gainsboro";
246 int color_index = deg_debug_node_color_index(node);
247 const char *fillcolor = color_index < 0 ?
248 defaultcolor :
250 dot_attributes.set("fillcolor", fillcolor);
251}
252
254{
255 const char *color_default = "black";
256 const char *color_cyclic = "red4"; /* The color of crime scene. */
257 const char *color_godmode = "blue4"; /* The color of beautiful sky. */
258 const char *color = color_default;
259 if (rel->flag & RELATION_FLAG_CYCLIC) {
260 color = color_cyclic;
261 }
262 else if (rel->flag & RELATION_FLAG_GODMODE) {
263 color = color_godmode;
264 }
265 edge.attributes.set("color", color);
266}
267
269{
270 const char *style_default = "solid";
271 const char *style_no_flush = "dashed";
272 const char *style_flush_user_only = "dotted";
273 const char *style = style_default;
274 if (rel->flag & RELATION_FLAG_NO_FLUSH) {
275 style = style_no_flush;
276 }
278 style = style_flush_user_only;
279 }
280 edge.attributes.set("style", style);
281}
282
284{
285 const char *shape_default = "normal";
286 const char *shape_no_cow = "box";
287 const char *shape = shape_default;
288 if (rel->from->get_class() == NodeClass::OPERATION &&
290 {
291 OperationNode *op_from = (OperationNode *)rel->from;
292 OperationNode *op_to = (OperationNode *)rel->to;
293 if (op_from->owner->type == NodeType::COPY_ON_EVAL &&
294 /* The #ID::recalc flag depends on run-time state which is not valid at this point in time.
295 * Pass in all flags although there may be a better way to represent this. */
297 {
298 shape = shape_no_cow;
299 }
300 }
301 edge.attributes.set("arrowhead", shape);
302}
303
305 const Node *node,
306 dot::Attributes &dot_attributes)
307{
308 StringRef base_style = "filled"; /* default style */
309 if (ctx.show_tags) {
310 if (node->get_class() == NodeClass::OPERATION) {
311 OperationNode *op_node = (OperationNode *)node;
313 base_style = "striped";
314 }
315 }
316 }
317 switch (node->get_class()) {
319 dot_attributes.set("style", base_style);
320 break;
322 dot_attributes.set("style", base_style);
323 break;
325 dot_attributes.set("style", base_style + ",rounded");
326 break;
327 }
328}
329
331 const Node *node,
332 dot::Cluster *parent_cluster)
333{
334 string name = node->identifier();
335
336 dot::Node &dot_node = ctx.digraph.new_node(name);
337 ctx.nodes_map.add_new(node, &dot_node);
338 dot_node.set_parent_cluster(parent_cluster);
339 dot_node.attributes.set("fontname", deg_debug_graphviz_fontname);
341 dot_node.attributes.set("shape", "box");
342
343 deg_debug_graphviz_node_style(ctx, node, dot_node.attributes);
344 deg_debug_graphviz_node_color(ctx, node, dot_node.attributes);
346 deg_debug_graphviz_node_penwidth(ctx, node, dot_node.attributes);
347}
348
350 const Node *node,
351 dot::Cluster *parent_cluster)
352{
353 string name = node->identifier();
354 dot::Cluster &cluster = ctx.digraph.new_cluster(name);
355 cluster.set_parent_cluster(parent_cluster);
356 cluster.attributes.set("fontname", deg_debug_graphviz_fontname);
358 cluster.attributes.set("margin", 16);
359 deg_debug_graphviz_node_style(ctx, node, cluster.attributes);
360 deg_debug_graphviz_node_color(ctx, node, cluster.attributes);
363 /* dummy node, so we can add edges between clusters */
364 dot::Node &dot_node = ctx.digraph.new_node("");
365 dot_node.attributes.set("shape", "point");
366 dot_node.attributes.set("style", "invis");
367 dot_node.set_parent_cluster(&cluster);
368 ctx.nodes_map.add_new(node, &dot_node);
369 ctx.clusters_map.add_new(node, &cluster);
370 return cluster;
371}
372
373static void deg_debug_graphviz_graph_nodes(DotExportContext &ctx, const Depsgraph *graph);
374static void deg_debug_graphviz_graph_relations(DotExportContext &ctx, const Depsgraph *graph);
375
377 const Node *node,
378 dot::Cluster *parent_cluster)
379{
380 switch (node->type) {
381 case NodeType::ID_REF: {
382 const IDNode *id_node = (const IDNode *)node;
383 if (id_node->components.is_empty()) {
384 deg_debug_graphviz_node_single(ctx, node, parent_cluster);
385 }
386 else {
387 dot::Cluster &cluster = deg_debug_graphviz_node_cluster_create(ctx, node, parent_cluster);
388 for (const ComponentNode *comp : id_node->components.values()) {
389 deg_debug_graphviz_node(ctx, comp, &cluster);
390 }
391 }
392 break;
393 }
400 case NodeType::BONE:
402 case NodeType::CACHE:
414 case NodeType::AUDIO:
417 case NodeType::SCENE:
421 ComponentNode *comp_node = (ComponentNode *)node;
422 if (comp_node->operations.is_empty()) {
423 deg_debug_graphviz_node_single(ctx, node, parent_cluster);
424 }
425 else {
426 dot::Cluster &cluster = deg_debug_graphviz_node_cluster_create(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::Node &dot_tail = *ctx.nodes_map.lookup(tail);
451 dot::Node &dot_head = *ctx.nodes_map.lookup(head);
452
453 dot::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::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::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
509 dot::DirectedGraph digraph;
510 deg::DotExportContext ctx{false, digraph};
511
512 digraph.set_rankdir(dot::Attr_rankdir::LeftToRight);
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:1155
These structs are the foundation for all linked lists in the library system.
bool is_empty() const
void set(StringRef key, StringRef value)
void set_parent_cluster(Cluster *new_parent)
Definition dot_export.cc:48
bool contains(Node &node) const
std::string name() const
DirectedEdge & new_edge(NodePort from, NodePort to)
Definition dot_export.cc:41
std::string to_dot_string() const
Cluster & new_cluster(StringRef label="")
Definition dot_export.cc:25
Node & new_node(StringRef label)
Definition dot_export.cc:16
void set_rankdir(Attr_rankdir rankdir)
void set_parent_cluster(Cluster *cluster)
Definition dot_export.cc:68
Depsgraph * graph
std::string DEG_debug_graph_to_dot(const Depsgraph &graph, const blender::StringRef label)
const IDNode * id_node
const char * label
draw_view push_constant(Type::INT, "radiance_src") .push_constant(Type capture_info_buf storage_buf(1, Qualifier::READ, "ObjectBounds", "bounds_buf[]") .push_constant(Type draw_view int
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 float deg_debug_graphviz_node_label_size
static const char * deg_debug_colors_light[]
static void deg_debug_graphviz_node(DotExportContext &ctx, const Node *node, dot::Cluster *parent_cluster)
static const int deg_debug_max_colors
static float deg_debug_graphviz_graph_label_size
static void deg_debug_graphviz_graph_nodes(DotExportContext &ctx, const Depsgraph *graph)
DepsNodeFactory * type_get_factory(const NodeType type)
static void deg_debug_graphviz_node_single(DotExportContext &ctx, const Node *node, dot::Cluster *parent_cluster)
static void deg_debug_graphviz_node_fillcolor(const Node *node, dot::Attributes &dot_attributes)
static dot::Cluster & deg_debug_graphviz_node_cluster_create(DotExportContext &ctx, const Node *node, dot::Cluster *parent_cluster)
static void deg_debug_graphviz_relation_style(const Relation *rel, dot::DirectedEdge &edge)
static void deg_debug_graphviz_relation_arrowhead(const Relation *rel, dot::DirectedEdge &edge)
static void deg_debug_graphviz_legend_color(const char *name, const char *color, std::stringstream &ss)
static void deg_debug_graphviz_node_relations(DotExportContext &ctx, const Node *node)
static const char * deg_debug_graphviz_fontname
static void deg_debug_graphviz_relation_color(const Relation *rel, dot::DirectedEdge &edge)
static void deg_debug_graphviz_node_style(DotExportContext &ctx, const Node *node, dot::Attributes &dot_attributes)
static void deg_debug_graphviz_node_penwidth(DotExportContext &ctx, const Node *node, dot::Attributes &dot_attributes)
static void deg_debug_graphviz_node_color(DotExportContext &ctx, const Node *node, dot::Attributes &dot_attributes)
virtual bool need_tag_cow_before_update(const IDRecalcFlag)
Vector< OperationNode * > operations
Map< const Node *, dot::Cluster * > clusters_map
Map< const Node *, dot::Node * > nodes_map
Map< ComponentIDKey, ComponentNode * > components
virtual NodeClass get_class() const
Definition deg_node.cc:309