Blender V5.0
derived_node_tree.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
6
7#include "BLI_dot_export.hh"
8
9namespace blender::nodes {
10
12{
13 /* Construct all possible contexts immediately. This is significantly cheaper than inlining all
14 * node groups. If it still becomes a performance issue in the future, contexts could be
15 * constructed lazily when they are needed. */
16 root_context_ = &this->construct_context_recursively(
17 nullptr, nullptr, btree, bke::NODE_INSTANCE_KEY_BASE);
18}
19
20DTreeContext &DerivedNodeTree::construct_context_recursively(DTreeContext *parent_context,
21 const bNode *parent_node,
22 const bNodeTree &btree,
23 const bNodeInstanceKey instance_key)
24{
25 btree.ensure_topology_cache();
26 DTreeContext &context = *allocator_.construct<DTreeContext>().release();
27 context.parent_context_ = parent_context;
28 context.parent_node_ = parent_node;
29 context.derived_tree_ = this;
30 context.btree_ = &btree;
31 context.instance_key_ = instance_key;
32 used_btrees_.add(context.btree_);
33
34 for (const bNode *bnode : context.btree_->all_nodes()) {
35 if (bnode->is_group()) {
36 bNodeTree *child_btree = reinterpret_cast<bNodeTree *>(bnode->id);
37 if (child_btree != nullptr) {
38 const bNodeInstanceKey child_key = bke::node_instance_key(instance_key, &btree, bnode);
39 DTreeContext &child = this->construct_context_recursively(
40 &context, bnode, *child_btree, child_key);
41 context.children_.add_new(bnode, &child);
42 }
43 }
44 }
45
46 return context;
47}
48
50{
51 /* Has to be destructed manually, because the context info is allocated in a linear allocator. */
52 this->destruct_context_recursively(root_context_);
53}
54
55void DerivedNodeTree::destruct_context_recursively(DTreeContext *context)
56{
57 for (DTreeContext *child : context->children_.values()) {
58 this->destruct_context_recursively(child);
59 }
60 context->~DTreeContext();
61}
62
64{
65 for (const bNodeTree *btree : used_btrees_) {
66 if (btree->has_available_link_cycle()) {
67 return true;
68 }
69 }
70 return false;
71}
72
74{
75 for (const bNodeTree *btree : used_btrees_) {
76 if (btree->has_undefined_nodes_or_sockets()) {
77 return true;
78 }
79 }
80 return false;
81}
82
84{
85 this->foreach_node_in_context_recursive(*root_context_, callback);
86}
87
88void DerivedNodeTree::foreach_node_in_context_recursive(const DTreeContext &context,
89 FunctionRef<void(DNode)> callback) const
90{
91 for (const bNode *bnode : context.btree_->all_nodes()) {
92 callback(DNode(&context, bnode));
93 }
94 for (const DTreeContext *child_context : context.children_.values()) {
95 this->foreach_node_in_context_recursive(*child_context, callback);
96 }
97}
98
103
105{
106 BLI_assert(*this);
107 BLI_assert(bsocket_->owner_node().is_group_output());
108 BLI_assert(bsocket_->index() < bsocket_->owner_node().input_sockets().size() - 1);
109
110 const DTreeContext *parent_context = context_->parent_context();
111 const bNode *parent_node = context_->parent_node();
112 BLI_assert(parent_context != nullptr);
113 BLI_assert(parent_node != nullptr);
114
115 const int socket_index = bsocket_->index();
116 return {parent_context, &parent_node->output_socket(socket_index)};
117}
118
120{
121 BLI_assert(*this);
122 BLI_assert(bsocket_->owner_node().is_group());
123
124 const DTreeContext *child_context = context_->child_context(bsocket_->owner_node());
125 BLI_assert(child_context != nullptr);
126
127 const bNodeTree &child_tree = child_context->btree();
128 Span<const bNode *> group_input_nodes = child_tree.group_input_nodes();
129 const int socket_index = bsocket_->index();
130 Vector<DOutputSocket> sockets;
131 for (const bNode *group_input_node : group_input_nodes) {
132 sockets.append(DOutputSocket(child_context, &group_input_node->output_socket(socket_index)));
133 }
134 return sockets;
135}
136
138{
139 BLI_assert(*this);
140 BLI_assert(bsocket_->owner_node().is_group_input());
141 BLI_assert(bsocket_->index() < bsocket_->owner_node().output_sockets().size() - 1);
142
143 const DTreeContext *parent_context = context_->parent_context();
144 const bNode *parent_node = context_->parent_node();
145 BLI_assert(parent_context != nullptr);
146 BLI_assert(parent_node != nullptr);
147
148 const int socket_index = bsocket_->index();
149 return {parent_context, &parent_node->input_socket(socket_index)};
150}
151
153{
154 BLI_assert(*this);
155 BLI_assert(bsocket_->owner_node().is_group());
156
157 const DTreeContext *child_context = context_->child_context(bsocket_->owner_node());
158 if (child_context == nullptr) {
159 /* Can happen when the group node references a non-existent group (e.g. when the group is
160 * linked but the original file is not found). */
161 return {};
162 }
163
164 const bNodeTree &child_tree = child_context->btree();
165 Span<const bNode *> group_output_nodes = child_tree.nodes_by_type("NodeGroupOutput");
166 const int socket_index = bsocket_->index();
167 for (const bNode *group_output_node : group_output_nodes) {
168 if (group_output_node->flag & NODE_DO_OUTPUT || group_output_nodes.size() == 1) {
169 return {child_context, &group_output_node->input_socket(socket_index)};
170 }
171 }
172 return {};
173}
174
176{
177 BLI_assert(*this);
178 for (const bNodeSocket *linked_socket : bsocket_->logically_linked_sockets()) {
179 const bNode &linked_node = linked_socket->owner_node();
180 DOutputSocket linked_dsocket{context_, linked_socket};
181
182 if (linked_node.is_group_input()) {
183 if (context_->is_root()) {
184 /* This is a group input in the root node group. */
185 origin_fn(linked_dsocket);
186 }
187 else {
188 DInputSocket socket_in_parent_group = linked_dsocket.get_corresponding_group_node_input();
189 if (socket_in_parent_group->is_logically_linked()) {
190 /* Follow the links coming into the corresponding socket on the parent group node. */
191 socket_in_parent_group.foreach_origin_socket(origin_fn);
192 }
193 else {
194 /* The corresponding input on the parent group node is not connected. Therefore, we use
195 * the value of that input socket directly. */
196 origin_fn(socket_in_parent_group);
197 }
198 }
199 }
200 else if (linked_node.is_group()) {
201 DInputSocket socket_in_group = linked_dsocket.get_active_corresponding_group_output_socket();
202 if (socket_in_group) {
203 if (socket_in_group->is_logically_linked()) {
204 /* Follow the links coming into the group output node of the child node group. */
205 socket_in_group.foreach_origin_socket(origin_fn);
206 }
207 else {
208 /* The output of the child node group is not connected, so we have to get the value from
209 * that socket. */
210 origin_fn(socket_in_group);
211 }
212 }
213 }
214 else {
215 /* The normal case: just use the value of a linked output socket. */
216 origin_fn(linked_dsocket);
217 }
218 }
219}
220
222{
223 TargetSocketPathInfo path_info;
224 this->foreach_target_socket(target_fn, path_info);
225}
226
227void DOutputSocket::foreach_target_socket(ForeachTargetSocketFn target_fn,
228 TargetSocketPathInfo &path_info) const
229{
230 for (const bNodeLink *link : bsocket_->directly_linked_links()) {
231 if (link->is_muted()) {
232 continue;
233 }
234 const DInputSocket &linked_socket{context_, link->tosock};
235 if (!linked_socket->is_available()) {
236 continue;
237 }
238 const DNode linked_node = linked_socket.node();
239 if (linked_node->is_reroute()) {
240 const DInputSocket reroute_input = linked_socket;
241 const DOutputSocket reroute_output = linked_node.output(0);
242 path_info.sockets.append(reroute_input);
243 path_info.sockets.append(reroute_output);
244 reroute_output.foreach_target_socket(target_fn, path_info);
245 path_info.sockets.pop_last();
246 path_info.sockets.pop_last();
247 }
248 else if (linked_node->is_muted()) {
249 for (const bNodeLink &internal_link : linked_node->internal_links()) {
250 if (internal_link.fromsock != linked_socket.bsocket()) {
251 continue;
252 }
253 /* The internal link only forwards the first incoming link. */
254 if (linked_socket->is_multi_input()) {
255 if (linked_socket->directly_linked_links()[0] != link) {
256 continue;
257 }
258 }
259 const DInputSocket mute_input = linked_socket;
260 const DOutputSocket mute_output{context_, internal_link.tosock};
261 path_info.sockets.append(mute_input);
262 path_info.sockets.append(mute_output);
263 mute_output.foreach_target_socket(target_fn, path_info);
264 path_info.sockets.pop_last();
265 path_info.sockets.pop_last();
266 }
267 }
268 else if (linked_node->is_group_output()) {
269 if (linked_node.bnode() != context_->btree().group_output_node()) {
270 continue;
271 }
272 if (context_->is_root()) {
273 /* This is a group output in the root node group. */
274 path_info.sockets.append(linked_socket);
275 target_fn(linked_socket, path_info);
276 path_info.sockets.pop_last();
277 }
278 else {
279 /* Follow the links going out of the group node in the parent node group. */
280 const DOutputSocket socket_in_parent_group =
281 linked_socket.get_corresponding_group_node_output();
282 path_info.sockets.append(linked_socket);
283 path_info.sockets.append(socket_in_parent_group);
284 socket_in_parent_group.foreach_target_socket(target_fn, path_info);
285 path_info.sockets.pop_last();
286 path_info.sockets.pop_last();
287 }
288 }
289 else if (linked_node->is_group()) {
290 /* Follow the links within the nested node group. */
291 path_info.sockets.append(linked_socket);
292 const Vector<DOutputSocket> sockets_in_group =
293 linked_socket.get_corresponding_group_input_sockets();
294 for (const DOutputSocket &socket_in_group : sockets_in_group) {
295 path_info.sockets.append(socket_in_group);
296 socket_in_group.foreach_target_socket(target_fn, path_info);
297 path_info.sockets.pop_last();
298 }
299 path_info.sockets.pop_last();
300 }
301 else {
302 /* The normal case: just use the linked input socket as target. */
303 path_info.sockets.append(linked_socket);
304 target_fn(linked_socket, path_info);
305 path_info.sockets.pop_last();
306 }
307 }
308}
309
310/* Find the active context from the given context and its descendants contexts. The active context
311 * is the one whose node instance key matches the active_viewer_key stored in the root node tree.
312 * The instance key of each context is computed by calling node_instance_key given the key of
313 * the parent as well as the group node making the context. */
315{
316 const bNodeInstanceKey key = context->instance_key();
317
318 /* The instance key of the given context matches the active viewer instance key, so this is the
319 * active context, return it. */
320 if (key.value == context->derived_tree().root_context().btree().active_viewer_key.value) {
321 return context;
322 }
323
324 /* For each of the group nodes, compute their instance key and contexts and call this function
325 * recursively. */
326 for (const bNode *group_node : context->btree().group_nodes()) {
327 /* No valid context exists for node groups without node trees. */
328 if (!group_node->id) {
329 continue;
330 }
331 const DTreeContext *child_context = context->child_context(*group_node);
332 const DTreeContext *found_context = find_active_context_recursive(child_context);
333
334 /* If the found context is null, that means neither the child context nor one of its descendant
335 * contexts is active. */
336 if (!found_context) {
337 continue;
338 }
339
340 /* Otherwise, we have found our active context, return it. */
341 return found_context;
342 }
343
344 /* Neither the given context nor one of its descendant contexts is active, so return null. */
345 return nullptr;
346}
347
349{
350 /* If the active viewer key is bke::NODE_INSTANCE_KEY_NONE, that means it is not yet initialized
351 * and we return the root context in that case. See the find_active_context_recursive function.
352 */
353 if (root_context().btree().active_viewer_key.value == bke::NODE_INSTANCE_KEY_NONE.value) {
354 return root_context();
355 }
356
358 if (found_context == nullptr) {
359 /* There should always be a valid active context. */
361 return root_context();
362 }
363
364 return *found_context;
365}
366
367/* Each nested node group gets its own cluster. Just as node groups, clusters can be nested. */
370 const DTreeContext *context,
372{
373 return dot_clusters.lookup_or_add_cb(context, [&]() -> dot_export::Cluster * {
374 const DTreeContext *parent_context = context->parent_context();
375 if (parent_context == nullptr) {
376 return nullptr;
377 }
379 digraph, parent_context, dot_clusters);
380 std::string cluster_name = StringRef(context->btree().id.name + 2) + " / " +
381 context->parent_node()->name;
382 dot_export::Cluster &cluster = digraph.new_cluster(cluster_name);
383 cluster.set_parent_cluster(parent_cluster);
384 return &cluster;
385 });
386}
387
388std::string DerivedNodeTree::to_dot() const
389{
390 namespace dot = dot_export;
391
394
398
399 this->foreach_node([&](DNode node) {
400 /* Ignore nodes that should not show up in the final output. */
401 if (node->is_muted() || node->is_group() || node->is_reroute() || node->is_frame()) {
402 return;
403 }
404 if (!node.context()->is_root()) {
405 if (node->is_group_input() || node->is_group_output()) {
406 return;
407 }
408 }
409
411 digraph, node.context(), dot_clusters);
412
413 dot_export::Node &dot_node = digraph.new_node("");
414 dot_node.set_parent_cluster(cluster);
415 dot_node.set_background_color("white");
416
417 dot_export::NodeWithSockets dot_node_with_sockets;
418 for (const bNodeSocket *socket : node->input_sockets()) {
419 if (socket->is_available()) {
420 dot_node_with_sockets.add_input(socket->name);
421 }
422 }
423 for (const bNodeSocket *socket : node->output_sockets()) {
424 if (socket->is_available()) {
425 dot_node_with_sockets.add_output(socket->name);
426 }
427 }
428
430 dot_node, dot_node_with_sockets);
431
432 int input_index = 0;
433 for (const bNodeSocket *socket : node->input_sockets()) {
434 if (socket->is_available()) {
435 dot_input_sockets.add_new(DInputSocket{node.context(), socket},
436 dot_node_with_sockets_ref.input(input_index));
437 input_index++;
438 }
439 }
440 int output_index = 0;
441 for (const bNodeSocket *socket : node->output_sockets()) {
442 if (socket->is_available()) {
443 dot_output_sockets.add_new(DOutputSocket{node.context(), socket},
444 dot_node_with_sockets_ref.output(output_index));
445 output_index++;
446 }
447 }
448 });
449
450 /* Floating inputs are used for example to visualize unlinked group node inputs. */
451 Map<DSocket, dot_export::Node *> dot_floating_inputs;
452
453 for (const auto item : dot_input_sockets.items()) {
454 DInputSocket to_socket = item.key;
455 dot_export::NodePort dot_to_port = item.value;
456 to_socket.foreach_origin_socket([&](DSocket from_socket) {
457 if (from_socket->is_output()) {
458 dot_export::NodePort *dot_from_port = dot_output_sockets.lookup_ptr(
459 DOutputSocket(from_socket));
460 if (dot_from_port != nullptr) {
461 digraph.new_edge(*dot_from_port, dot_to_port);
462 return;
463 }
464 }
465 dot_export::Node &dot_node = *dot_floating_inputs.lookup_or_add_cb(from_socket, [&]() {
466 dot_export::Node &dot_node = digraph.new_node(from_socket->name);
467 dot_node.set_background_color("white");
469 dot_node.set_parent_cluster(
470 get_dot_cluster_for_context(digraph, from_socket.context(), dot_clusters));
471 return &dot_node;
472 });
473 digraph.new_edge(dot_node, dot_to_port);
474 });
475 }
476
478
479 return digraph.to_dot_string();
480}
481
482} // namespace blender::nodes
#define BLI_assert_unreachable()
Definition BLI_assert.h:93
#define BLI_assert(a)
Definition BLI_assert.h:46
@ NODE_DO_OUTPUT
struct bNodeLink bNodeLink
bool add(const Key &key)
destruct_ptr< T > construct(Args &&...args)
const Value * lookup_ptr(const Key &key) const
Definition BLI_map.hh:508
Value & lookup_or_add_cb(const Key &key, const CreateValueF &create_value)
Definition BLI_map.hh:620
void add_new(const Key &key, const Value &value)
Definition BLI_map.hh:265
ItemIterator items() const &
Definition BLI_map.hh:902
constexpr int64_t size() const
Definition BLI_span.hh:252
void append(const T &value)
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_shape(Attr_shape shape)
void set_parent_cluster(Cluster *cluster)
Definition dot_export.cc:72
void set_background_color(StringRef name)
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
bNodeInstanceKey instance_key() const
const DTreeContext * context() const
const bNode * bnode() const
FunctionRef< void(DInputSocket, const TargetSocketPathInfo &path_info)> ForeachTargetSocketFn
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 bNodeTree & btree() const
const DTreeContext & active_context() const
DerivedNodeTree(const bNodeTree &btree)
const DTreeContext & root_context() const
void foreach_node(FunctionRef< void(DNode)> callback) const
dot(value.rgb, luminance_coefficients)") DEFINE_VALUE("REDUCE(lhs
const bNodeInstanceKey NODE_INSTANCE_KEY_BASE
Definition node.cc:4846
bNodeInstanceKey node_instance_key(bNodeInstanceKey parent_key, const bNodeTree *ntree, const bNode *node)
Definition node.cc:4867
const bNodeInstanceKey NODE_INSTANCE_KEY_NONE
Definition node.cc:4847
DOutputSocket::TargetSocketPathInfo TargetSocketPathInfo
Definition utilities.cc:25
int context(const bContext *C, const char *member, bContextDataResult *result)
static const DTreeContext * find_active_context_recursive(const DTreeContext *context)
static dot_export::Cluster * get_dot_cluster_for_context(dot_export::DirectedGraph &digraph, const DTreeContext *context, Map< const DTreeContext *, dot_export::Cluster * > &dot_clusters)
unsigned int value
Input & add_input(std::string name)
Output & add_output(std::string name)