Blender  V2.93
deg_debug_relations_graphviz.cc
Go to the documentation of this file.
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * The Original Code is Copyright (C) 2014 Blender Foundation.
17  * All rights reserved.
18  */
19 
26 #include <cstdarg>
27 
28 #include "BLI_dot_export.hh"
29 #include "BLI_utildefines.h"
30 
31 #include "DNA_listBase.h"
32 
33 #include "DEG_depsgraph.h"
34 #include "DEG_depsgraph_debug.h"
35 
36 #include "intern/depsgraph.h"
38 
43 
44 namespace deg = blender::deg;
45 namespace dot = blender::dot;
46 
47 /* ****************** */
48 /* Graphviz Debugging */
49 
50 namespace blender::deg {
51 
52 /* Only one should be enabled, defines whether graphviz nodes
53  * get colored by individual types or classes.
54  */
55 #define COLOR_SCHEME_NODE_CLASS 1
56 //#define COLOR_SCHEME_NODE_TYPE 2
57 
58 static const char *deg_debug_graphviz_fontname = "helvetica";
61 static const int deg_debug_max_colors = 12;
62 #ifdef COLOR_SCHEME_NODE_TYPE
63 static const char *deg_debug_colors[] = {
64  "#a6cee3",
65  "#1f78b4",
66  "#b2df8a",
67  "#33a02c",
68  "#fb9a99",
69  "#e31a1c",
70  "#fdbf6f",
71  "#ff7f00",
72  "#cab2d6",
73  "#6a3d9a",
74  "#ffff99",
75  "#b15928",
76  "#ff00ff",
77 };
78 #endif
79 static const char *deg_debug_colors_light[] = {
80  "#8dd3c7",
81  "#ffffb3",
82  "#bebada",
83  "#fb8072",
84  "#80b1d3",
85  "#fdb462",
86  "#b3de69",
87  "#fccde5",
88  "#d9d9d9",
89  "#bc80bd",
90  "#ccebc5",
91  "#ffed6f",
92  "#ff00ff",
93 };
94 
95 #ifdef COLOR_SCHEME_NODE_TYPE
96 static const int deg_debug_node_type_color_map[][2] = {
98  {NodeType::ID_REF, 1},
99 
100  /* Outer Types */
102  {NodeType::PROXY, 3},
103  {NodeType::ANIMATION, 4},
104  {NodeType::TRANSFORM, 5},
105  {NodeType::GEOMETRY, 6},
106  {NodeType::SEQUENCER, 7},
107  {NodeType::SHADING, 8},
109  {NodeType::CACHE, 10},
110  {NodeType::POINT_CACHE, 11},
113  {-1, 0},
114 };
115 #endif
116 
118 {
119 #ifdef COLOR_SCHEME_NODE_CLASS
120  /* Some special types. */
121  switch (node->type) {
122  case NodeType::ID_REF:
123  return 5;
124  case NodeType::OPERATION: {
125  OperationNode *op_node = (OperationNode *)node;
126  if (op_node->is_noop()) {
127  if (op_node->flag & OperationFlag::DEPSOP_FLAG_PINNED) {
128  return 7;
129  }
130  return 8;
131  }
132  break;
133  }
134 
135  default:
136  break;
137  }
138  /* Do others based on class. */
139  switch (node->get_class()) {
141  return 4;
143  return 1;
144  default:
145  return 9;
146  }
147 #endif
148 
149 #ifdef COLOR_SCHEME_NODE_TYPE
150  const int(*pair)[2];
151  for (pair = deg_debug_node_type_color_map; (*pair)[0] >= 0; pair++) {
152  if ((*pair)[0] == node->type) {
153  return (*pair)[1];
154  }
155  }
156  return -1;
157 #endif
158 }
159 
161  bool show_tags;
165 };
166 
167 static void deg_debug_graphviz_legend_color(const char *name,
168  const char *color,
169  std::stringstream &ss)
170 {
171 
172  ss << "<TR>";
173  ss << "<TD>" << name << "</TD>";
174  ss << "<TD BGCOLOR=\"" << color << "\"></TD>";
175  ss << "</TR>";
176 }
177 
179 {
180  dot::Node &legend_node = ctx.digraph.new_node("");
181  legend_node.attributes.set("rank", "sink");
182  legend_node.attributes.set("shape", "none");
183  legend_node.attributes.set("margin", 0);
184 
185  std::stringstream ss;
186  ss << "<";
187  ss << R"(<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0" CELLPADDING="4">)";
188  ss << "<TR><TD COLSPAN=\"2\"><B>Legend</B></TD></TR>";
189 
190 #ifdef COLOR_SCHEME_NODE_CLASS
191  const char **colors = deg_debug_colors_light;
192  deg_debug_graphviz_legend_color("Operation", colors[4], ss);
193  deg_debug_graphviz_legend_color("Component", colors[1], ss);
194  deg_debug_graphviz_legend_color("ID Node", colors[5], ss);
195  deg_debug_graphviz_legend_color("NOOP", colors[8], ss);
196  deg_debug_graphviz_legend_color("Pinned OP", colors[7], ss);
197 #endif
198 
199 #ifdef COLOR_SCHEME_NODE_TYPE
200  const int(*pair)[2];
201  for (pair = deg_debug_node_type_color_map; (*pair)[0] >= 0; pair++) {
202  DepsNodeFactory *nti = type_get_factory((NodeType)(*pair)[0]);
204  ctx, nti->tname().c_str(), deg_debug_colors_light[(*pair)[1] % deg_debug_max_colors], ss);
205  }
206 #endif
207 
208  ss << "</TABLE>";
209  ss << ">";
210  legend_node.attributes.set("label", ss.str());
211  legend_node.attributes.set("fontname", deg_debug_graphviz_fontname);
212 }
213 
215  const Node *node,
216  dot::Attributes &dot_attributes)
217 {
218  const char *color_default = "black";
219  const char *color_modified = "orangered4";
220  const char *color_update = "dodgerblue3";
221  const char *color = color_default;
222  if (ctx.show_tags) {
223  if (node->get_class() == NodeClass::OPERATION) {
224  OperationNode *op_node = (OperationNode *)node;
225  if (op_node->flag & DEPSOP_FLAG_DIRECTLY_MODIFIED) {
226  color = color_modified;
227  }
228  else if (op_node->flag & DEPSOP_FLAG_NEEDS_UPDATE) {
229  color = color_update;
230  }
231  }
232  }
233  dot_attributes.set("color", color);
234 }
235 
237  const Node *node,
238  dot::Attributes &dot_attributes)
239 {
240  float penwidth_default = 1.0f;
241  float penwidth_modified = 4.0f;
242  float penwidth_update = 4.0f;
243  float penwidth = penwidth_default;
244  if (ctx.show_tags) {
245  if (node->get_class() == NodeClass::OPERATION) {
246  OperationNode *op_node = (OperationNode *)node;
247  if (op_node->flag & DEPSOP_FLAG_DIRECTLY_MODIFIED) {
248  penwidth = penwidth_modified;
249  }
250  else if (op_node->flag & DEPSOP_FLAG_NEEDS_UPDATE) {
251  penwidth = penwidth_update;
252  }
253  }
254  }
255  dot_attributes.set("penwidth", penwidth);
256 }
257 
258 static void deg_debug_graphviz_node_fillcolor(const Node *node, dot::Attributes &dot_attributes)
259 {
260  const char *defaultcolor = "gainsboro";
261  int color_index = deg_debug_node_color_index(node);
262  const char *fillcolor = color_index < 0 ?
263  defaultcolor :
265  dot_attributes.set("fillcolor", fillcolor);
266 }
267 
269 {
270  const char *color_default = "black";
271  const char *color_cyclic = "red4"; /* The color of crime scene. */
272  const char *color_godmode = "blue4"; /* The color of beautiful sky. */
273  const char *color = color_default;
274  if (rel->flag & RELATION_FLAG_CYCLIC) {
275  color = color_cyclic;
276  }
277  else if (rel->flag & RELATION_FLAG_GODMODE) {
278  color = color_godmode;
279  }
280  edge.attributes.set("color", color);
281 }
282 
284 {
285  const char *style_default = "solid";
286  const char *style_no_flush = "dashed";
287  const char *style_flush_user_only = "dotted";
288  const char *style = style_default;
289  if (rel->flag & RELATION_FLAG_NO_FLUSH) {
290  style = style_no_flush;
291  }
293  style = style_flush_user_only;
294  }
295  edge.attributes.set("style", style);
296 }
297 
299 {
300  const char *shape_default = "normal";
301  const char *shape_no_cow = "box";
302  const char *shape = shape_default;
303  if (rel->from->get_class() == NodeClass::OPERATION &&
304  rel->to->get_class() == NodeClass::OPERATION) {
305  OperationNode *op_from = (OperationNode *)rel->from;
306  OperationNode *op_to = (OperationNode *)rel->to;
307  if (op_from->owner->type == NodeType::COPY_ON_WRITE &&
308  !op_to->owner->need_tag_cow_before_update()) {
309  shape = shape_no_cow;
310  }
311  }
312  edge.attributes.set("arrowhead", shape);
313 }
314 
316  const Node *node,
317  dot::Attributes &dot_attributes)
318 {
319  StringRef base_style = "filled"; /* default style */
320  if (ctx.show_tags) {
321  if (node->get_class() == NodeClass::OPERATION) {
322  OperationNode *op_node = (OperationNode *)node;
324  base_style = "striped";
325  }
326  }
327  }
328  switch (node->get_class()) {
329  case NodeClass::GENERIC:
330  dot_attributes.set("style", base_style);
331  break;
333  dot_attributes.set("style", base_style);
334  break;
336  dot_attributes.set("style", base_style + ",rounded");
337  break;
338  }
339 }
340 
342  const Node *node,
343  dot::Cluster *parent_cluster)
344 {
345  string name = node->identifier();
346 
347  dot::Node &dot_node = ctx.digraph.new_node(name);
348  ctx.nodes_map.add_new(node, &dot_node);
349  dot_node.set_parent_cluster(parent_cluster);
350  dot_node.attributes.set("fontname", deg_debug_graphviz_fontname);
351  dot_node.attributes.set("frontsize", deg_debug_graphviz_node_label_size);
352  dot_node.attributes.set("shape", "box");
353 
358 }
359 
361  const Node *node,
362  dot::Cluster *parent_cluster)
363 {
364  string name = node->identifier();
365  dot::Cluster &cluster = ctx.digraph.new_cluster(name);
366  cluster.set_parent_cluster(parent_cluster);
367  cluster.attributes.set("fontname", deg_debug_graphviz_fontname);
369  cluster.attributes.set("margin", 16);
374  /* dummy node, so we can add edges between clusters */
375  dot::Node &dot_node = ctx.digraph.new_node("");
376  dot_node.attributes.set("shape", "point");
377  dot_node.attributes.set("style", "invis");
378  dot_node.set_parent_cluster(&cluster);
379  ctx.nodes_map.add_new(node, &dot_node);
380  ctx.clusters_map.add_new(node, &cluster);
381  return cluster;
382 }
383 
384 static void deg_debug_graphviz_graph_nodes(DotExportContext &ctx, const Depsgraph *graph);
385 static void deg_debug_graphviz_graph_relations(DotExportContext &ctx, const Depsgraph *graph);
386 
388  const Node *node,
389  dot::Cluster *parent_cluster)
390 {
391  switch (node->type) {
392  case NodeType::ID_REF: {
393  const IDNode *id_node = (const IDNode *)node;
394  if (id_node->components.is_empty()) {
395  deg_debug_graphviz_node_single(ctx, node, parent_cluster);
396  }
397  else {
398  dot::Cluster &cluster = deg_debug_graphviz_node_cluster_create(ctx, node, parent_cluster);
399  for (const ComponentNode *comp : id_node->components.values()) {
400  deg_debug_graphviz_node(ctx, comp, &cluster);
401  }
402  }
403  break;
404  }
406  case NodeType::ANIMATION:
407  case NodeType::TRANSFORM:
408  case NodeType::PROXY:
409  case NodeType::GEOMETRY:
410  case NodeType::SEQUENCER:
411  case NodeType::EVAL_POSE:
412  case NodeType::BONE:
413  case NodeType::SHADING:
415  case NodeType::CACHE:
424  case NodeType::DUPLI:
426  case NodeType::AUDIO:
427  case NodeType::ARMATURE:
429  case NodeType::SIMULATION: {
430  ComponentNode *comp_node = (ComponentNode *)node;
431  if (comp_node->operations.is_empty()) {
432  deg_debug_graphviz_node_single(ctx, node, parent_cluster);
433  }
434  else {
435  dot::Cluster &cluster = deg_debug_graphviz_node_cluster_create(ctx, node, parent_cluster);
436  for (Node *op_node : comp_node->operations) {
437  deg_debug_graphviz_node(ctx, op_node, &cluster);
438  }
439  }
440  break;
441  }
442  case NodeType::UNDEFINED:
444  case NodeType::OPERATION:
445  deg_debug_graphviz_node_single(ctx, node, parent_cluster);
446  break;
447  case NodeType::NUM_TYPES:
448  break;
449  }
450 }
451 
453 {
454  for (Relation *rel : node->inlinks) {
455  float penwidth = 2.0f;
456 
457  const Node *head = rel->to; /* same as node */
458  const Node *tail = rel->from;
459  dot::Node &dot_tail = *ctx.nodes_map.lookup(tail);
460  dot::Node &dot_head = *ctx.nodes_map.lookup(head);
461 
462  dot::DirectedEdge &edge = ctx.digraph.new_edge(dot_tail, dot_head);
463 
464  /* Note: without label an id seem necessary to avoid bugs in graphviz/dot */
465  edge.attributes.set("id", rel->name);
469  edge.attributes.set("penwidth", penwidth);
470 
471  /* NOTE: edge from node to own cluster is not possible and gives graphviz
472  * warning, avoid this here by just linking directly to the invisible
473  * placeholder node. */
474  dot::Cluster *tail_cluster = ctx.clusters_map.lookup_default(tail, nullptr);
475  if (tail_cluster != nullptr && tail_cluster->contains(dot_head)) {
476  edge.attributes.set("ltail", tail_cluster->name());
477  }
478  dot::Cluster *head_cluster = ctx.clusters_map.lookup_default(head, nullptr);
479  if (head_cluster != nullptr && head_cluster->contains(dot_tail)) {
480  edge.attributes.set("lhead", head_cluster->name());
481  }
482  }
483 }
484 
486 {
487  for (Node *node : graph->id_nodes) {
488  deg_debug_graphviz_node(ctx, node, nullptr);
489  }
490  TimeSourceNode *time_source = graph->find_time_source();
491  if (time_source != nullptr) {
492  deg_debug_graphviz_node(ctx, time_source, nullptr);
493  }
494 }
495 
497 {
498  for (IDNode *id_node : graph->id_nodes) {
499  for (ComponentNode *comp_node : id_node->components.values()) {
500  for (OperationNode *op_node : comp_node->operations) {
501  deg_debug_graphviz_node_relations(ctx, op_node);
502  }
503  }
504  }
505 
506  TimeSourceNode *time_source = graph->find_time_source();
507  if (time_source != nullptr) {
508  deg_debug_graphviz_node_relations(ctx, time_source);
509  }
510 }
511 
512 } // namespace blender::deg
513 
514 void DEG_debug_relations_graphviz(const Depsgraph *graph, FILE *fp, const char *label)
515 {
516  if (!graph) {
517  return;
518  }
519 
520  const deg::Depsgraph *deg_graph = reinterpret_cast<const deg::Depsgraph *>(graph);
521 
522  dot::DirectedGraph digraph;
523  deg::DotExportContext ctx{false, digraph};
524 
525  digraph.set_rankdir(dot::Attr_rankdir::LeftToRight);
526  digraph.attributes.set("compound", "true");
527  digraph.attributes.set("labelloc", "t");
529  digraph.attributes.set("fontname", deg::deg_debug_graphviz_fontname);
530  digraph.attributes.set("label", label);
531  digraph.attributes.set("splines", "ortho");
532  digraph.attributes.set("overlap", "scalexy");
533 
534  deg::deg_debug_graphviz_graph_nodes(ctx, deg_graph);
536 
538 
539  std::string dot_string = digraph.to_dot_string();
540  fprintf(fp, "%s", dot_string.c_str());
541 }
struct Depsgraph Depsgraph
Definition: DEG_depsgraph.h:51
These structs are the foundation for all linked lists in the library system.
bool is_empty() const
Definition: BLI_vector.hh:674
void set(StringRef key, StringRef value)
void set_parent_cluster(Cluster *new_parent)
Definition: dot_export.cc:58
bool contains(Node &node) const
Definition: dot_export.cc:120
std::string name() const
DirectedEdge & new_edge(NodePort from, NodePort to)
Definition: dot_export.cc:51
std::string to_dot_string() const
Definition: dot_export.cc:135
Cluster & new_cluster(StringRef label="")
Definition: dot_export.cc:35
Node & new_node(StringRef label)
Definition: dot_export.cc:26
void set_rankdir(Attr_rankdir rankdir)
void set_parent_cluster(Cluster *cluster)
Definition: dot_export.cc:78
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)
Vector< OperationNode * > operations
TimeSourceNode * find_time_source() const
Definition: depsgraph.cc:102
IDDepsNodes id_nodes
Definition: depsgraph.h:103
Map< const Node *, dot::Cluster * > clusters_map
Map< const Node *, dot::Node * > nodes_map
Map< ComponentIDKey, ComponentNode * > components
Definition: deg_node_id.h:96
Relations inlinks
Definition: deg_node.h:175
virtual NodeClass get_class() const
Definition: deg_node.cc:314
virtual string identifier() const override
ccl_device_inline float dot(const float2 &a, const float2 &b)