Blender  V3.3
deg_debug_relations_graphviz.cc
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later
2  * Copyright 2014 Blender Foundation. All rights reserved. */
3 
10 #include <cstdarg>
11 
12 #include "BLI_dot_export.hh"
13 #include "BLI_utildefines.h"
14 
15 #include "DNA_listBase.h"
16 
17 #include "DEG_depsgraph.h"
18 #include "DEG_depsgraph_debug.h"
19 
20 #include "intern/depsgraph.h"
22 
27 
28 namespace deg = blender::deg;
29 namespace dot = blender::dot;
30 
31 /* ****************** */
32 /* Graphviz Debugging */
33 
34 namespace 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 
42 static const char *deg_debug_graphviz_fontname = "helvetica";
45 static const int deg_debug_max_colors = 12;
46 #ifdef COLOR_SCHEME_NODE_TYPE
47 static 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
63 static 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
80 static const int deg_debug_node_type_color_map[][2] = {
82  {NodeType::ID_REF, 1},
83 
84  /* Outer Types */
88  {NodeType::GEOMETRY, 6},
90  {NodeType::SHADING, 8},
91  {NodeType::CACHE, 9},
95  {-1, 0},
96 };
97 #endif
98 
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()) {
109  if (op_node->flag & OperationFlag::DEPSOP_FLAG_PINNED) {
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 
143  bool show_tags;
147 };
148 
149 static 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::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::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::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 
240 static void deg_debug_graphviz_node_fillcolor(const Node *node, dot::Attributes &dot_attributes)
241 {
242  const char *defaultcolor = "gainsboro";
243  int color_index = deg_debug_node_color_index(node);
244  const char *fillcolor = color_index < 0 ?
245  defaultcolor :
247  dot_attributes.set("fillcolor", fillcolor);
248 }
249 
251 {
252  const char *color_default = "black";
253  const char *color_cyclic = "red4"; /* The color of crime scene. */
254  const char *color_godmode = "blue4"; /* The color of beautiful sky. */
255  const char *color = color_default;
256  if (rel->flag & RELATION_FLAG_CYCLIC) {
257  color = color_cyclic;
258  }
259  else if (rel->flag & RELATION_FLAG_GODMODE) {
260  color = color_godmode;
261  }
262  edge.attributes.set("color", color);
263 }
264 
266 {
267  const char *style_default = "solid";
268  const char *style_no_flush = "dashed";
269  const char *style_flush_user_only = "dotted";
270  const char *style = style_default;
271  if (rel->flag & RELATION_FLAG_NO_FLUSH) {
272  style = style_no_flush;
273  }
275  style = style_flush_user_only;
276  }
277  edge.attributes.set("style", style);
278 }
279 
281 {
282  const char *shape_default = "normal";
283  const char *shape_no_cow = "box";
284  const char *shape = shape_default;
285  if (rel->from->get_class() == NodeClass::OPERATION &&
286  rel->to->get_class() == NodeClass::OPERATION) {
287  OperationNode *op_from = (OperationNode *)rel->from;
288  OperationNode *op_to = (OperationNode *)rel->to;
289  if (op_from->owner->type == NodeType::COPY_ON_WRITE &&
290  !op_to->owner->need_tag_cow_before_update()) {
291  shape = shape_no_cow;
292  }
293  }
294  edge.attributes.set("arrowhead", shape);
295 }
296 
298  const Node *node,
299  dot::Attributes &dot_attributes)
300 {
301  StringRef base_style = "filled"; /* default style */
302  if (ctx.show_tags) {
303  if (node->get_class() == NodeClass::OPERATION) {
304  OperationNode *op_node = (OperationNode *)node;
306  base_style = "striped";
307  }
308  }
309  }
310  switch (node->get_class()) {
311  case NodeClass::GENERIC:
312  dot_attributes.set("style", base_style);
313  break;
315  dot_attributes.set("style", base_style);
316  break;
318  dot_attributes.set("style", base_style + ",rounded");
319  break;
320  }
321 }
322 
324  const Node *node,
325  dot::Cluster *parent_cluster)
326 {
327  string name = node->identifier();
328 
329  dot::Node &dot_node = ctx.digraph.new_node(name);
330  ctx.nodes_map.add_new(node, &dot_node);
331  dot_node.set_parent_cluster(parent_cluster);
332  dot_node.attributes.set("fontname", deg_debug_graphviz_fontname);
333  dot_node.attributes.set("frontsize", deg_debug_graphviz_node_label_size);
334  dot_node.attributes.set("shape", "box");
335 
340 }
341 
343  const Node *node,
344  dot::Cluster *parent_cluster)
345 {
346  string name = node->identifier();
347  dot::Cluster &cluster = ctx.digraph.new_cluster(name);
348  cluster.set_parent_cluster(parent_cluster);
349  cluster.attributes.set("fontname", deg_debug_graphviz_fontname);
351  cluster.attributes.set("margin", 16);
356  /* dummy node, so we can add edges between clusters */
357  dot::Node &dot_node = ctx.digraph.new_node("");
358  dot_node.attributes.set("shape", "point");
359  dot_node.attributes.set("style", "invis");
360  dot_node.set_parent_cluster(&cluster);
361  ctx.nodes_map.add_new(node, &dot_node);
362  ctx.clusters_map.add_new(node, &cluster);
363  return cluster;
364 }
365 
366 static void deg_debug_graphviz_graph_nodes(DotExportContext &ctx, const Depsgraph *graph);
367 static void deg_debug_graphviz_graph_relations(DotExportContext &ctx, const Depsgraph *graph);
368 
370  const Node *node,
371  dot::Cluster *parent_cluster)
372 {
373  switch (node->type) {
374  case NodeType::ID_REF: {
375  const IDNode *id_node = (const IDNode *)node;
376  if (id_node->components.is_empty()) {
377  deg_debug_graphviz_node_single(ctx, node, parent_cluster);
378  }
379  else {
380  dot::Cluster &cluster = deg_debug_graphviz_node_cluster_create(ctx, node, parent_cluster);
381  for (const ComponentNode *comp : id_node->components.values()) {
382  deg_debug_graphviz_node(ctx, comp, &cluster);
383  }
384  }
385  break;
386  }
388  case NodeType::ANIMATION:
389  case NodeType::TRANSFORM:
390  case NodeType::GEOMETRY:
391  case NodeType::SEQUENCER:
392  case NodeType::EVAL_POSE:
393  case NodeType::BONE:
394  case NodeType::SHADING:
395  case NodeType::CACHE:
404  case NodeType::DUPLI:
406  case NodeType::AUDIO:
407  case NodeType::ARMATURE:
411  case NodeType::SIMULATION: {
412  ComponentNode *comp_node = (ComponentNode *)node;
413  if (comp_node->operations.is_empty()) {
414  deg_debug_graphviz_node_single(ctx, node, parent_cluster);
415  }
416  else {
417  dot::Cluster &cluster = deg_debug_graphviz_node_cluster_create(ctx, node, parent_cluster);
418  for (Node *op_node : comp_node->operations) {
419  deg_debug_graphviz_node(ctx, op_node, &cluster);
420  }
421  }
422  break;
423  }
424  case NodeType::UNDEFINED:
426  case NodeType::OPERATION:
427  deg_debug_graphviz_node_single(ctx, node, parent_cluster);
428  break;
429  case NodeType::NUM_TYPES:
430  break;
431  }
432 }
433 
435 {
436  for (Relation *rel : node->inlinks) {
437  float penwidth = 2.0f;
438 
439  const Node *head = rel->to; /* same as node */
440  const Node *tail = rel->from;
441  dot::Node &dot_tail = *ctx.nodes_map.lookup(tail);
442  dot::Node &dot_head = *ctx.nodes_map.lookup(head);
443 
444  dot::DirectedEdge &edge = ctx.digraph.new_edge(dot_tail, dot_head);
445 
446  /* NOTE: without label an id seem necessary to avoid bugs in graphviz/dot. */
447  edge.attributes.set("id", rel->name);
451  edge.attributes.set("penwidth", penwidth);
452 
453  /* NOTE: edge from node to own cluster is not possible and gives graphviz
454  * warning, avoid this here by just linking directly to the invisible
455  * placeholder node. */
456  dot::Cluster *tail_cluster = ctx.clusters_map.lookup_default(tail, nullptr);
457  if (tail_cluster != nullptr && tail_cluster->contains(dot_head)) {
458  edge.attributes.set("ltail", tail_cluster->name());
459  }
460  dot::Cluster *head_cluster = ctx.clusters_map.lookup_default(head, nullptr);
461  if (head_cluster != nullptr && head_cluster->contains(dot_tail)) {
462  edge.attributes.set("lhead", head_cluster->name());
463  }
464  }
465 }
466 
468 {
469  for (Node *node : graph->id_nodes) {
470  deg_debug_graphviz_node(ctx, node, nullptr);
471  }
472  TimeSourceNode *time_source = graph->find_time_source();
473  if (time_source != nullptr) {
474  deg_debug_graphviz_node(ctx, time_source, nullptr);
475  }
476 }
477 
479 {
480  for (IDNode *id_node : graph->id_nodes) {
481  for (ComponentNode *comp_node : id_node->components.values()) {
482  for (OperationNode *op_node : comp_node->operations) {
483  deg_debug_graphviz_node_relations(ctx, op_node);
484  }
485  }
486  }
487 
488  TimeSourceNode *time_source = graph->find_time_source();
489  if (time_source != nullptr) {
490  deg_debug_graphviz_node_relations(ctx, time_source);
491  }
492 }
493 
494 } // namespace blender::deg
495 
496 void DEG_debug_relations_graphviz(const Depsgraph *graph, FILE *fp, const char *label)
497 {
498  if (!graph) {
499  return;
500  }
501 
502  const deg::Depsgraph *deg_graph = reinterpret_cast<const deg::Depsgraph *>(graph);
503 
504  dot::DirectedGraph digraph;
505  deg::DotExportContext ctx{false, digraph};
506 
507  digraph.set_rankdir(dot::Attr_rankdir::LeftToRight);
508  digraph.attributes.set("compound", "true");
509  digraph.attributes.set("labelloc", "t");
511  digraph.attributes.set("fontname", deg::deg_debug_graphviz_fontname);
512  digraph.attributes.set("label", label);
513  digraph.attributes.set("splines", "ortho");
514  digraph.attributes.set("overlap", "scalexy");
515 
516  deg::deg_debug_graphviz_graph_nodes(ctx, deg_graph);
518 
520 
521  std::string dot_string = digraph.to_dot_string();
522  fprintf(fp, "%s", dot_string.c_str());
523 }
struct Depsgraph Depsgraph
Definition: DEG_depsgraph.h:35
These structs are the foundation for all linked lists in the library system.
Group Output data from inside of a node group A color picker Mix two input colors RGB to Convert a color s luminance to a grayscale value Generate a normal vector and a dot product Bright Control the brightness and contrast of the input color Vector Map an input vectors to used to fine tune the interpolation of the input Camera Retrieve information about the camera and how it relates to the current shading point s position Clamp a value between a minimum and a maximum Vector Perform vector math operation Invert a color
bool is_empty() const
Definition: BLI_vector.hh:706
void set(StringRef key, StringRef value)
void set_parent_cluster(Cluster *new_parent)
Definition: dot_export.cc:44
bool contains(Node &node) const
Definition: dot_export.cc:106
std::string name() const
DirectedEdge & new_edge(NodePort from, NodePort to)
Definition: dot_export.cc:37
std::string to_dot_string() const
Definition: dot_export.cc:121
Cluster & new_cluster(StringRef label="")
Definition: dot_export.cc:21
Node & new_node(StringRef label)
Definition: dot_export.cc:12
void set_rankdir(Attr_rankdir rankdir)
void set_parent_cluster(Cluster *cluster)
Definition: dot_export.cc:64
OperationNode * node
Depsgraph * graph
void DEG_debug_relations_graphviz(const Depsgraph *graph, FILE *fp, const char *label)
const IDNode * id_node
const char * label
static int deg_debug_node_color_index(const Node *node)
@ RELATION_FLAG_FLUSH_USER_EDIT_ONLY
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 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 dot::Cluster & deg_debug_graphviz_node_cluster_create(DotExportContext &ctx, const Node *node, dot::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_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)
T dot(const vec_base< T, Size > &a, const vec_base< T, Size > &b)
Vector< OperationNode * > operations
TimeSourceNode * find_time_source() const
Definition: depsgraph.cc:91
IDDepsNodes id_nodes
Definition: depsgraph.h:86
Map< const Node *, dot::Cluster * > clusters_map
Map< const Node *, dot::Node * > nodes_map
Map< ComponentIDKey, ComponentNode * > components
Definition: deg_node_id.h:80
Relations inlinks
Definition: deg_node.h:173
virtual NodeClass get_class() const
Definition: deg_node.cc:300
virtual string identifier() const override