Blender  V3.3
derived_node_tree.cc
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 
4 
5 #include "BLI_dot_export.hh"
6 
7 namespace blender::nodes {
8 
10 {
11  /* Construct all possible contexts immediately. This is significantly cheaper than inlining all
12  * node groups. If it still becomes a performance issue in the future, contexts could be
13  * constructed lazily when they are needed. */
14  root_context_ = &this->construct_context_recursively(nullptr, nullptr, btree, node_tree_refs);
15 }
16 
17 DTreeContext &DerivedNodeTree::construct_context_recursively(DTreeContext *parent_context,
18  const NodeRef *parent_node,
19  bNodeTree &btree,
20  NodeTreeRefMap &node_tree_refs)
21 {
22  DTreeContext &context = *allocator_.construct<DTreeContext>().release();
23  context.parent_context_ = parent_context;
24  context.parent_node_ = parent_node;
25  context.derived_tree_ = this;
26  context.tree_ = &get_tree_ref_from_map(node_tree_refs, btree);
27  used_node_tree_refs_.add(context.tree_);
28 
29  for (const NodeRef *node : context.tree_->nodes()) {
30  if (node->is_group_node()) {
31  bNode *bnode = node->bnode();
32  bNodeTree *child_btree = reinterpret_cast<bNodeTree *>(bnode->id);
33  if (child_btree != nullptr) {
34  DTreeContext &child = this->construct_context_recursively(
35  &context, node, *child_btree, node_tree_refs);
36  context.children_.add_new(node, &child);
37  }
38  }
39  }
40 
41  return context;
42 }
43 
45 {
46  /* Has to be destructed manually, because the context info is allocated in a linear allocator. */
47  this->destruct_context_recursively(root_context_);
48 }
49 
50 void DerivedNodeTree::destruct_context_recursively(DTreeContext *context)
51 {
52  for (DTreeContext *child : context->children_.values()) {
53  this->destruct_context_recursively(child);
54  }
55  context->~DTreeContext();
56 }
57 
59 {
60  for (const NodeTreeRef *tree_ref : used_node_tree_refs_) {
61  if (tree_ref->has_link_cycles()) {
62  return true;
63  }
64  }
65  return false;
66 }
67 
69 {
70  for (const NodeTreeRef *tree_ref : used_node_tree_refs_) {
71  if (tree_ref->has_undefined_nodes_or_sockets()) {
72  return true;
73  }
74  }
75  return false;
76 }
77 
79 {
80  this->foreach_node_in_context_recursive(*root_context_, callback);
81 }
82 
83 void DerivedNodeTree::foreach_node_in_context_recursive(const DTreeContext &context,
84  FunctionRef<void(DNode)> callback) const
85 {
86  for (const NodeRef *node_ref : context.tree_->nodes()) {
87  callback(DNode(&context, node_ref));
88  }
89  for (const DTreeContext *child_context : context.children_.values()) {
90  this->foreach_node_in_context_recursive(*child_context, callback);
91  }
92 }
93 
95 {
96  BLI_assert(*this);
98  BLI_assert(socket_ref_->index() < socket_ref_->node().inputs().size() - 1);
99 
100  const DTreeContext *parent_context = context_->parent_context();
101  const NodeRef *parent_node = context_->parent_node();
102  BLI_assert(parent_context != nullptr);
103  BLI_assert(parent_node != nullptr);
104 
105  const int socket_index = socket_ref_->index();
106  return {parent_context, &parent_node->output(socket_index)};
107 }
108 
110 {
111  BLI_assert(*this);
113 
114  const DTreeContext *child_context = context_->child_context(socket_ref_->node());
115  BLI_assert(child_context != nullptr);
116 
117  const NodeTreeRef &child_tree = child_context->tree();
118  Span<const NodeRef *> group_input_nodes = child_tree.nodes_by_type("NodeGroupInput");
119  const int socket_index = socket_ref_->index();
120  Vector<DOutputSocket> sockets;
121  for (const NodeRef *group_input_node : group_input_nodes) {
122  sockets.append(DOutputSocket(child_context, &group_input_node->output(socket_index)));
123  }
124  return sockets;
125 }
126 
128 {
129  BLI_assert(*this);
131  BLI_assert(socket_ref_->index() < socket_ref_->node().outputs().size() - 1);
132 
133  const DTreeContext *parent_context = context_->parent_context();
134  const NodeRef *parent_node = context_->parent_node();
135  BLI_assert(parent_context != nullptr);
136  BLI_assert(parent_node != nullptr);
137 
138  const int socket_index = socket_ref_->index();
139  return {parent_context, &parent_node->input(socket_index)};
140 }
141 
143 {
144  BLI_assert(*this);
146 
147  const DTreeContext *child_context = context_->child_context(socket_ref_->node());
148  if (child_context == nullptr) {
149  /* Can happen when the group node references a non-existent group (e.g. when the group is
150  * linked but the original file is not found). */
151  return {};
152  }
153 
154  const NodeTreeRef &child_tree = child_context->tree();
155  Span<const NodeRef *> group_output_nodes = child_tree.nodes_by_type("NodeGroupOutput");
156  const int socket_index = socket_ref_->index();
157  for (const NodeRef *group_output_node : group_output_nodes) {
158  if (group_output_node->bnode()->flag & NODE_DO_OUTPUT || group_output_nodes.size() == 1) {
159  return {child_context, &group_output_node->input(socket_index)};
160  }
161  }
162  return {};
163 }
164 
166 {
167  BLI_assert(*this);
168  for (const OutputSocketRef *linked_socket : socket_ref_->as_input().logically_linked_sockets()) {
169  const NodeRef &linked_node = linked_socket->node();
170  DOutputSocket linked_dsocket{context_, linked_socket};
171 
172  if (linked_node.is_group_input_node()) {
173  if (context_->is_root()) {
174  /* This is a group input in the root node group. */
175  origin_fn(linked_dsocket);
176  }
177  else {
178  DInputSocket socket_in_parent_group = linked_dsocket.get_corresponding_group_node_input();
179  if (socket_in_parent_group->is_logically_linked()) {
180  /* Follow the links coming into the corresponding socket on the parent group node. */
181  socket_in_parent_group.foreach_origin_socket(origin_fn);
182  }
183  else {
184  /* The corresponding input on the parent group node is not connected. Therefore, we use
185  * the value of that input socket directly. */
186  origin_fn(socket_in_parent_group);
187  }
188  }
189  }
190  else if (linked_node.is_group_node()) {
191  DInputSocket socket_in_group = linked_dsocket.get_active_corresponding_group_output_socket();
192  if (socket_in_group) {
193  if (socket_in_group->is_logically_linked()) {
194  /* Follow the links coming into the group output node of the child node group. */
195  socket_in_group.foreach_origin_socket(origin_fn);
196  }
197  else {
198  /* The output of the child node group is not connected, so we have to get the value from
199  * that socket. */
200  origin_fn(socket_in_group);
201  }
202  }
203  }
204  else {
205  /* The normal case: just use the value of a linked output socket. */
206  origin_fn(linked_dsocket);
207  }
208  }
209 }
210 
212 {
213  TargetSocketPathInfo path_info;
214  this->foreach_target_socket(target_fn, path_info);
215 }
216 
217 void DOutputSocket::foreach_target_socket(ForeachTargetSocketFn target_fn,
218  TargetSocketPathInfo &path_info) const
219 {
220  for (const LinkRef *link : socket_ref_->as_output().directly_linked_links()) {
221  if (link->is_muted()) {
222  continue;
223  }
224  const DInputSocket &linked_socket{context_, &link->to()};
225  if (!linked_socket->is_available()) {
226  continue;
227  }
228  const DNode linked_node = linked_socket.node();
229  if (linked_node->is_reroute_node()) {
230  const DInputSocket reroute_input = linked_socket;
231  const DOutputSocket reroute_output = linked_node.output(0);
232  path_info.sockets.append(reroute_input);
233  path_info.sockets.append(reroute_output);
234  reroute_output.foreach_target_socket(target_fn, path_info);
235  path_info.sockets.pop_last();
236  path_info.sockets.pop_last();
237  }
238  else if (linked_node->is_muted()) {
239  for (const InternalLinkRef *internal_link : linked_node->internal_links()) {
240  if (&internal_link->from() != linked_socket.socket_ref()) {
241  continue;
242  }
243  /* The internal link only forwards the first incoming link. */
244  if (linked_socket->is_multi_input_socket()) {
245  if (linked_socket->directly_linked_links()[0] != link) {
246  continue;
247  }
248  }
249  const DInputSocket mute_input = linked_socket;
250  const DOutputSocket mute_output{context_, &internal_link->to()};
251  path_info.sockets.append(mute_input);
252  path_info.sockets.append(mute_output);
253  mute_output.foreach_target_socket(target_fn, path_info);
254  path_info.sockets.pop_last();
255  path_info.sockets.pop_last();
256  }
257  }
258  else if (linked_node->is_group_output_node()) {
259  if (linked_node.node_ref() != context_->tree().group_output_node()) {
260  continue;
261  }
262  if (context_->is_root()) {
263  /* This is a group output in the root node group. */
264  path_info.sockets.append(linked_socket);
265  target_fn(linked_socket, path_info);
266  path_info.sockets.pop_last();
267  }
268  else {
269  /* Follow the links going out of the group node in the parent node group. */
270  const DOutputSocket socket_in_parent_group =
271  linked_socket.get_corresponding_group_node_output();
272  path_info.sockets.append(linked_socket);
273  path_info.sockets.append(socket_in_parent_group);
274  socket_in_parent_group.foreach_target_socket(target_fn, path_info);
275  path_info.sockets.pop_last();
276  path_info.sockets.pop_last();
277  }
278  }
279  else if (linked_node->is_group_node()) {
280  /* Follow the links within the nested node group. */
281  path_info.sockets.append(linked_socket);
282  const Vector<DOutputSocket> sockets_in_group =
283  linked_socket.get_corresponding_group_input_sockets();
284  for (const DOutputSocket &socket_in_group : sockets_in_group) {
285  path_info.sockets.append(socket_in_group);
286  socket_in_group.foreach_target_socket(target_fn, path_info);
287  path_info.sockets.pop_last();
288  }
289  path_info.sockets.pop_last();
290  }
291  else {
292  /* The normal case: just use the linked input socket as target. */
293  path_info.sockets.append(linked_socket);
294  target_fn(linked_socket, path_info);
295  path_info.sockets.pop_last();
296  }
297  }
298 }
299 
300 /* Each nested node group gets its own cluster. Just as node groups, clusters can be nested. */
302  dot::DirectedGraph &digraph,
303  const DTreeContext *context,
305 {
306  return dot_clusters.lookup_or_add_cb(context, [&]() -> dot::Cluster * {
307  const DTreeContext *parent_context = context->parent_context();
308  if (parent_context == nullptr) {
309  return nullptr;
310  }
311  dot::Cluster *parent_cluster = get_dot_cluster_for_context(
312  digraph, parent_context, dot_clusters);
313  std::string cluster_name = context->tree().name() + " / " + context->parent_node()->name();
314  dot::Cluster &cluster = digraph.new_cluster(cluster_name);
315  cluster.set_parent_cluster(parent_cluster);
316  return &cluster;
317  });
318 }
319 
320 std::string DerivedNodeTree::to_dot() const
321 {
322  dot::DirectedGraph digraph;
323  digraph.set_rankdir(dot::Attr_rankdir::LeftToRight);
324 
326  Map<DInputSocket, dot::NodePort> dot_input_sockets;
327  Map<DOutputSocket, dot::NodePort> dot_output_sockets;
328 
329  this->foreach_node([&](DNode node) {
330  /* Ignore nodes that should not show up in the final output. */
331  if (node->is_muted() || node->is_group_node() || node->is_reroute_node() || node->is_frame()) {
332  return;
333  }
334  if (!node.context()->is_root()) {
335  if (node->is_group_input_node() || node->is_group_output_node()) {
336  return;
337  }
338  }
339 
340  dot::Cluster *cluster = get_dot_cluster_for_context(digraph, node.context(), dot_clusters);
341 
342  dot::Node &dot_node = digraph.new_node("");
343  dot_node.set_parent_cluster(cluster);
344  dot_node.set_background_color("white");
345 
346  Vector<std::string> input_names;
347  Vector<std::string> output_names;
348  for (const InputSocketRef *socket : node->inputs()) {
349  if (socket->is_available()) {
350  input_names.append(socket->name());
351  }
352  }
353  for (const OutputSocketRef *socket : node->outputs()) {
354  if (socket->is_available()) {
355  output_names.append(socket->name());
356  }
357  }
358 
359  dot::NodeWithSocketsRef dot_node_with_sockets = dot::NodeWithSocketsRef(
360  dot_node, node->name(), input_names, output_names);
361 
362  int input_index = 0;
363  for (const InputSocketRef *socket : node->inputs()) {
364  if (socket->is_available()) {
365  dot_input_sockets.add_new(DInputSocket{node.context(), socket},
366  dot_node_with_sockets.input(input_index));
367  input_index++;
368  }
369  }
370  int output_index = 0;
371  for (const OutputSocketRef *socket : node->outputs()) {
372  if (socket->is_available()) {
373  dot_output_sockets.add_new(DOutputSocket{node.context(), socket},
374  dot_node_with_sockets.output(output_index));
375  output_index++;
376  }
377  }
378  });
379 
380  /* Floating inputs are used for example to visualize unlinked group node inputs. */
381  Map<DSocket, dot::Node *> dot_floating_inputs;
382 
383  for (const auto item : dot_input_sockets.items()) {
384  DInputSocket to_socket = item.key;
385  dot::NodePort dot_to_port = item.value;
386  to_socket.foreach_origin_socket([&](DSocket from_socket) {
387  if (from_socket->is_output()) {
388  dot::NodePort *dot_from_port = dot_output_sockets.lookup_ptr(DOutputSocket(from_socket));
389  if (dot_from_port != nullptr) {
390  digraph.new_edge(*dot_from_port, dot_to_port);
391  return;
392  }
393  }
394  dot::Node &dot_node = *dot_floating_inputs.lookup_or_add_cb(from_socket, [&]() {
395  dot::Node &dot_node = digraph.new_node(from_socket->name());
396  dot_node.set_background_color("white");
397  dot_node.set_shape(dot::Attr_shape::Ellipse);
398  dot_node.set_parent_cluster(
399  get_dot_cluster_for_context(digraph, from_socket.context(), dot_clusters));
400  return &dot_node;
401  });
402  digraph.new_edge(dot_node, dot_to_port);
403  });
404  }
405 
406  digraph.set_random_cluster_bgcolors();
407 
408  return digraph.to_dot_string();
409 }
410 
411 } // namespace blender::nodes
#define BLI_assert(a)
Definition: BLI_assert.h:46
#define NODE_DO_OUTPUT
destruct_ptr< T > construct(Args &&...args)
Value & lookup_or_add_cb(const Key &key, const CreateValueF &create_value)
Definition: BLI_map.hh:561
void add_new(const Key &key, const Value &value)
Definition: BLI_map.hh:220
constexpr int64_t size() const
Definition: BLI_span.hh:240
void append(const T &value)
Definition: BLI_vector.hh:433
void set_parent_cluster(Cluster *new_parent)
Definition: dot_export.cc:44
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)
NodePort output(int index) const
NodePort input(int index) const
void set_shape(Attr_shape shape)
void set_background_color(StringRef name)
void set_parent_cluster(Cluster *cluster)
Definition: dot_export.cc:64
void foreach_origin_socket(FunctionRef< void(DSocket)> origin_fn) const
DOutputSocket get_corresponding_group_node_output() const
Vector< DOutputSocket, 4 > get_corresponding_group_input_sockets() const
DInputSocket get_corresponding_group_node_input() const
DInputSocket get_active_corresponding_group_output_socket() const
void foreach_target_socket(ForeachTargetSocketFn target_fn) const
const DTreeContext * context() const
const DTreeContext * context_
const DTreeContext * parent_context() const
const DTreeContext * child_context(const NodeRef &node) const
const NodeRef * parent_node() const
const NodeTreeRef & tree() const
DerivedNodeTree(bNodeTree &btree, NodeTreeRefMap &node_tree_refs)
void foreach_node(FunctionRef< void(DNode)> callback) const
Span< const OutputSocketRef * > logically_linked_sockets() const
const OutputSocketRef & output(int index) const
Span< const InputSocketRef * > inputs() const
const InputSocketRef & input(int index) const
Span< const OutputSocketRef * > outputs() const
const NodeRef * group_output_node() const
Span< const NodeRef * > nodes_by_type(StringRefNull idname) const
const NodeRef & node() const
const OutputSocketRef & as_output() const
const InputSocketRef & as_input() const
Span< const LinkRef * > directly_linked_links() const
StringRefNull name() const
OperationNode * node
DEGForeachIDComponentCallback callback
const NodeTreeRef & get_tree_ref_from_map(NodeTreeRefMap &node_tree_refs, bNodeTree &btree)
static dot::Cluster * get_dot_cluster_for_context(dot::DirectedGraph &digraph, const DTreeContext *context, Map< const DTreeContext *, dot::Cluster * > &dot_clusters)
struct ID * id