Blender V4.5
node_tree_structure_type_inferencing.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2025 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
5#include "BLI_array_utils.hh"
6#include "BLI_stack.hh"
7#include "BLI_utildefines.h"
8
9#include "BKE_node.hh"
11#include "BKE_node_runtime.hh"
12
14#include "DNA_node_types.h"
15
17
19
20using nodes::StructureType;
21namespace aal = nodes::anonymous_attribute_lifetime;
22
24{
25 const Span<const bNodeSocket *> input_sockets = node.input_sockets();
26 const Span<const bNodeSocket *> output_sockets = node.output_sockets();
27
29 node_interface.inputs.reinitialize(input_sockets.size());
30 node_interface.outputs.reinitialize(output_sockets.size());
31
32 if (node.is_undefined() || !node.declaration() || node.declaration()->skip_updating_sockets) {
33 node_interface.inputs.fill(StructureType::Dynamic);
34 node_interface.outputs.fill(
36 return node_interface;
37 }
38 if (node.is_reroute()) {
39 node_interface.inputs.first() = StructureType::Dynamic;
40 node_interface.outputs.first() = {StructureType::Dynamic, {0}};
41 return node_interface;
42 }
43
44 for (const int i : input_sockets.index_range()) {
45 const nodes::SocketDeclaration &decl = *input_sockets[i]->runtime->declaration;
46 node_interface.inputs[i] = decl.structure_type;
47 }
48
49 for (const int output : output_sockets.index_range()) {
50 const nodes::SocketDeclaration &decl = *output_sockets[output]->runtime->declaration;
52 dependency.type = decl.structure_type;
53 if (dependency.type != StructureType::Dynamic) {
54 continue;
55 }
56
57 /* Currently the input sockets that influence the field status of an output are the same as the
58 * sockets that influence its structure type. Reuse that for the propagation of structure type
59 * until there is a more generic format of intra-node dependencies. */
60 switch (decl.output_field_dependency.field_type()) {
62 break;
64 break;
66 dependency.linked_inputs.reinitialize(input_sockets.size());
68 break;
71 break;
72 }
73 }
74
75 return node_interface;
76}
77
79{
80 const Span<const bNode *> nodes = tree.all_nodes();
82 for (const int i : nodes.index_range()) {
83 interfaces[i] = calc_node_interface(*nodes[i]);
84 }
85 return interfaces;
86}
87
88enum class DataRequirement : int8_t { None, Field, Single, Grid, Invalid };
89
91{
92 if (a == b) {
93 return a;
94 }
95 if (a == DataRequirement::None) {
96 return b;
97 }
98 if (b == DataRequirement::None) {
99 return a;
100 }
103 {
104 /* Single beats field, because fields can accept single values too. */
106 }
108}
109
111 MutableSpan<DataRequirement> input_requirements)
112{
113 const Span<const bNodeSocket *> input_sockets = tree.all_input_sockets();
114 for (const int i : input_sockets.index_range()) {
115 const bNodeSocket &socket = *input_sockets[i];
116 const nodes::SocketDeclaration *declaration = socket.runtime->declaration;
117 if (!declaration) {
118 input_requirements[i] = DataRequirement::None;
119 continue;
120 }
121 switch (declaration->structure_type) {
122 case StructureType::Dynamic: {
123 input_requirements[i] = DataRequirement::None;
124 break;
125 }
126 case StructureType::Single: {
127 input_requirements[i] = DataRequirement::Single;
128 break;
129 }
130 case StructureType::Grid: {
131 input_requirements[i] = DataRequirement::Grid;
132 break;
133 }
134 case StructureType::Field: {
135 input_requirements[i] = DataRequirement::Field;
136 break;
137 }
138 }
139 }
140}
141
143 const bNodeSocket &output_socket, const Span<DataRequirement> input_requirements)
144{
146 if (!output_socket.is_available()) {
147 return requirement;
148 }
149 for (const bNodeSocket *socket : output_socket.directly_linked_sockets()) {
150 if (!socket->is_available()) {
151 continue;
152 }
153 requirement = merge(requirement, input_requirements[socket->index_in_all_inputs()]);
154 }
155 return requirement;
156}
157
159 const Span<DataRequirement> input_requirements,
160 nodes::StructureTypeInterface &derived_interface)
161{
162 /* Merge usages from all group input nodes. */
163 Array<DataRequirement> interface_requirements(tree.interface_inputs().size(),
165 for (const bNode *node : tree.group_input_nodes()) {
166 const Span<const bNodeSocket *> output_sockets = node->output_sockets();
167 for (const int i : output_sockets.index_range().drop_back(1)) {
168 const bNodeSocket &output = *output_sockets[i];
169 interface_requirements[i] = merge(
170 interface_requirements[i], calc_output_socket_requirement(output, input_requirements));
171 }
172 }
173
174 /* Build derived interface structure types from group input nodes. */
175 for (const int i : tree.interface_inputs().index_range()) {
176 const bNodeTreeInterfaceSocket &io_socket = *tree.interface_inputs()[i];
178 derived_interface.inputs[i] = StructureType(io_socket.structure_type);
179 continue;
180 }
181
182 const DataRequirement requirement = interface_requirements[i];
183 switch (requirement) {
185 derived_interface.inputs[i] = StructureType::Dynamic;
186 break;
188 derived_interface.inputs[i] = StructureType::Field;
189 break;
191 derived_interface.inputs[i] = StructureType::Single;
192 break;
194 derived_interface.inputs[i] = StructureType::Grid;
195 break;
197 derived_interface.inputs[i] = StructureType::Dynamic;
198 break;
199 }
200 }
201}
202
203enum class ZoneInOutChange {
204 None = 0,
205 In = (1 << 1),
206 Out = (1 << 2),
207};
209
211 const bNode &input_node,
212 const bNode &output_node,
213 MutableSpan<DataRequirement> input_requirements)
214{
216 for (const int i : output_node.output_sockets().index_range()) {
217 /* First input node output is Delta Time which does not appear in the output node outputs. */
218 const bNodeSocket &socket_input = input_node.input_socket(i);
219 const bNodeSocket &socket_output = output_node.output_socket(i);
220 const DataRequirement new_value = merge(
221 input_requirements[socket_input.index_in_all_inputs()],
222 calc_output_socket_requirement(socket_output, input_requirements));
223 if (input_requirements[socket_input.index_in_all_inputs()] != new_value) {
224 input_requirements[socket_input.index_in_all_inputs()] = new_value;
225 change |= ZoneInOutChange::In;
226 }
227 if (input_requirements[socket_input.index_in_all_inputs()] != new_value) {
228 input_requirements[socket_input.index_in_all_inputs()] = new_value;
229 change |= ZoneInOutChange::In;
230 }
231 }
232 return change;
233}
234
236 const bNode &input_node,
237 const bNode &output_node,
238 MutableSpan<DataRequirement> input_requirements)
239{
241 for (const int i : output_node.output_sockets().index_range()) {
242 const bNodeSocket &socket_input = input_node.input_socket(i + 1);
243 const bNodeSocket &socket_output = output_node.output_socket(i);
244 const DataRequirement new_value = merge(
245 input_requirements[socket_input.index_in_all_inputs()],
246 calc_output_socket_requirement(socket_output, input_requirements));
247 if (input_requirements[socket_input.index_in_all_inputs()] != new_value) {
248 input_requirements[socket_input.index_in_all_inputs()] = new_value;
249 change |= ZoneInOutChange::In;
250 }
251 if (input_requirements[socket_input.index_in_all_inputs()] != new_value) {
252 input_requirements[socket_input.index_in_all_inputs()] = new_value;
253 change |= ZoneInOutChange::In;
254 }
255 }
256 return change;
257}
258
260 const bNode &node,
261 MutableSpan<DataRequirement> input_requirements)
262{
263 /* Sync field state between zone nodes and schedule another pass if necessary. */
264 switch (node.type_legacy) {
266 const auto &data = *static_cast<const NodeGeometrySimulationInput *>(node.storage);
267 if (const bNode *output_node = tree.node_by_id(data.output_node_id)) {
269 node, *output_node, input_requirements);
271 return true;
272 }
273 }
274 return false;
275 }
277 for (const bNode *input_node : tree.nodes_by_type("GeometryNodeSimulationInput")) {
278 const auto &data = *static_cast<const NodeGeometrySimulationInput *>(input_node->storage);
279 if (node.identifier == data.output_node_id) {
281 *input_node, node, input_requirements);
282 if ((change & ZoneInOutChange::In) != ZoneInOutChange::None) {
283 return true;
284 }
285 }
286 }
287 return false;
288 }
290 const auto &data = *static_cast<const NodeGeometryRepeatInput *>(node.storage);
291 if (const bNode *output_node = tree.node_by_id(data.output_node_id)) {
293 node, *output_node, input_requirements);
295 return true;
296 }
297 }
298 return false;
299 }
301 for (const bNode *input_node : tree.nodes_by_type("GeometryNodeRepeatInput")) {
302 const auto &data = *static_cast<const NodeGeometryRepeatInput *>(input_node->storage);
303 if (node.identifier == data.output_node_id) {
305 *input_node, node, input_requirements);
306 if ((change & ZoneInOutChange::In) != ZoneInOutChange::None) {
307 return true;
308 }
309 }
310 }
311 return false;
312 }
313 }
314
315 return false;
316}
317
319 const Span<nodes::StructureTypeInterface> node_interfaces,
320 MutableSpan<DataRequirement> input_requirements)
321{
322 while (true) {
323 bool need_update = false;
324
325 for (const bNode *node : tree.toposort_right_to_left()) {
326 const Span<const bNodeSocket *> input_sockets = node->input_sockets();
327 const Span<const bNodeSocket *> output_sockets = node->output_sockets();
328 const nodes::StructureTypeInterface &node_interface = node_interfaces[node->index()];
329
330 for (const int output : node_interface.outputs.index_range()) {
331 const bNodeSocket &output_socket = *output_sockets[output];
332 DataRequirement output_requirement = DataRequirement::None;
333 for (const bNodeSocket *socket : output_socket.directly_linked_sockets()) {
334 if (!socket->is_available()) {
335 continue;
336 }
337 output_requirement = merge(output_requirement,
338 input_requirements[socket->index_in_all_inputs()]);
339 }
340
341 /* When a data requirement could be provided by multiple node inputs (i.e. only a single
342 * node input involved in a math operation has to be a volume grid for the output to be a
343 * grid), it's better to not propagate the data requirement than incorrectly saying that
344 * all of the inputs have it. */
345 Vector<int, 8> inputs_with_links;
346 for (const int input : node_interface.outputs[output].linked_inputs) {
347 const bNodeSocket &input_socket = *input_sockets[input];
348 if (input_socket.is_directly_linked()) {
349 inputs_with_links.append(input_socket.index_in_all_inputs());
350 }
351 }
352 if (inputs_with_links.size() == 1) {
353 input_requirements[inputs_with_links.first()] = output_requirement;
354 }
355 else {
356 for (const int input : inputs_with_links) {
357 input_requirements[input] = DataRequirement::None;
358 }
359 }
360 }
361
362 /* Find reverse dependencies and resolve conflicts, which may require another pass. */
363 if (propagate_zone_data_requirements(tree, *node, input_requirements)) {
364 need_update = true;
365 }
366 }
367
368 if (!need_update) {
369 break;
370 }
371 }
372}
373
374static StructureType left_to_right_merge(const StructureType a, const StructureType b)
375{
376 if (a == b) {
377 return a;
378 }
379 if ((a == StructureType::Dynamic && b == StructureType::Single) ||
380 (a == StructureType::Single && b == StructureType::Dynamic))
381 {
382 return StructureType::Dynamic;
383 }
384 if ((a == StructureType::Dynamic && b == StructureType::Field) ||
385 (a == StructureType::Field && b == StructureType::Dynamic))
386 {
387 return StructureType::Field;
388 }
389 if ((a == StructureType::Dynamic && b == StructureType::Grid) ||
390 (a == StructureType::Grid && b == StructureType::Dynamic))
391 {
392 return StructureType::Grid;
393 }
394 if ((a == StructureType::Field && b == StructureType::Grid) ||
395 (a == StructureType::Grid && b == StructureType::Field))
396 {
397 return StructureType::Grid;
398 }
399 if ((a == StructureType::Single && b == StructureType::Field) ||
400 (a == StructureType::Field && b == StructureType::Single))
401 {
402 return StructureType::Field;
403 }
404 if ((a == StructureType::Single && b == StructureType::Grid) ||
405 (a == StructureType::Grid && b == StructureType::Single))
406 {
407 return StructureType::Grid;
408 }
409 /* Invalid combination. */
410 return a;
411}
412
414 const bNode &output_node,
415 MutableSpan<StructureType> structure_types)
416{
418 for (const int i : output_node.output_sockets().index_range()) {
419 /* First input node output is Delta Time which does not appear in the output node outputs. */
420 const bNodeSocket &input = input_node.output_socket(i + 1);
421 const bNodeSocket &output = output_node.output_socket(i);
422 const StructureType new_value = left_to_right_merge(structure_types[input.index_in_tree()],
423 structure_types[output.index_in_tree()]);
424 if (structure_types[input.index_in_tree()] != new_value) {
425 structure_types[input.index_in_tree()] = new_value;
426 change |= ZoneInOutChange::In;
427 }
428 if (structure_types[output.index_in_tree()] != new_value) {
429 structure_types[output.index_in_tree()] = new_value;
430 change |= ZoneInOutChange::Out;
431 }
432 }
433 return change;
434}
435
437 const bNode &output_node,
438 MutableSpan<StructureType> structure_types)
439{
441 for (const int i : output_node.output_sockets().index_range()) {
442 const bNodeSocket &input = input_node.output_socket(i + 1);
443 const bNodeSocket &output = output_node.output_socket(i);
444 const StructureType new_value = left_to_right_merge(structure_types[input.index_in_tree()],
445 structure_types[output.index_in_tree()]);
446 if (structure_types[input.index_in_tree()] != new_value) {
447 structure_types[input.index_in_tree()] = new_value;
448 change |= ZoneInOutChange::In;
449 }
450 if (structure_types[output.index_in_tree()] != new_value) {
451 structure_types[output.index_in_tree()] = new_value;
452 change |= ZoneInOutChange::Out;
453 }
454 }
455 return change;
456}
457
459 const bNode &node,
460 MutableSpan<StructureType> structure_types)
461{
462 /* Sync field state between zone nodes and schedule another pass if necessary. */
463 switch (node.type_legacy) {
465 const auto &data = *static_cast<const NodeGeometrySimulationInput *>(node.storage);
466 if (const bNode *output_node = tree.node_by_id(data.output_node_id)) {
468 node, *output_node, structure_types);
470 return true;
471 }
472 }
473 return false;
474 }
476 for (const bNode *input_node : tree.nodes_by_type("GeometryNodeSimulationInput")) {
477 const auto &data = *static_cast<const NodeGeometrySimulationInput *>(input_node->storage);
478 if (node.identifier == data.output_node_id) {
480 *input_node, node, structure_types);
481 if ((change & ZoneInOutChange::In) != ZoneInOutChange::None) {
482 return true;
483 }
484 }
485 }
486 return false;
487 }
489 const auto &data = *static_cast<const NodeGeometryRepeatInput *>(node.storage);
490 if (const bNode *output_node = tree.node_by_id(data.output_node_id)) {
492 node, *output_node, structure_types);
494 return true;
495 }
496 }
497 return false;
498 }
500 for (const bNode *input_node : tree.nodes_by_type("GeometryNodeRepeatInput")) {
501 const auto &data = *static_cast<const NodeGeometryRepeatInput *>(input_node->storage);
502 if (node.identifier == data.output_node_id) {
504 *input_node, node, structure_types);
505 if ((change & ZoneInOutChange::In) != ZoneInOutChange::None) {
506 return true;
507 }
508 }
509 }
510 return false;
511 }
512 }
513
514 return false;
515}
516
518 const nodes::SocketDeclaration &declaration)
519{
521 return StructureType::Field;
522 }
523 return StructureType::Single;
524}
525
527 const Span<nodes::StructureTypeInterface> node_interfaces,
528 const Span<StructureType> group_input_structure_types,
529 MutableSpan<StructureType> structure_types)
530{
531 for (const bNodeSocket *input : tree.all_input_sockets()) {
532 if (input->owner_node().is_undefined()) {
533 continue;
534 }
535 if (!input->is_directly_linked()) {
536 if (const nodes::SocketDeclaration *declaration = input->runtime->declaration) {
537 structure_types[input->index_in_tree()] = get_unconnected_input_structure_type(
538 *declaration);
539 }
540 }
541 }
542 while (true) {
543 bool need_update = false;
544 for (const bNode *node : tree.toposort_left_to_right()) {
545 if (node->is_undefined()) {
546 continue;
547 }
548 const Span<const bNodeSocket *> input_sockets = node->input_sockets();
549 const Span<const bNodeSocket *> output_sockets = node->output_sockets();
550 if (node->is_group_input()) {
551 for (const int i : output_sockets.index_range().drop_back(1)) {
552 structure_types[output_sockets[i]->index_in_tree()] = group_input_structure_types[i];
553 }
554 continue;
555 }
556
557 for (const bNodeSocket *input : input_sockets) {
558 if (!input->is_available()) {
559 continue;
560 }
561
562 std::optional<StructureType> input_type;
563 for (const bNodeLink *link : input->directly_linked_links()) {
564 if (!link->is_used()) {
565 continue;
566 }
567 const StructureType new_type = structure_types[link->fromsock->index_in_tree()];
568 if (input_type) {
569 input_type = left_to_right_merge(*input_type, new_type);
570 }
571 else {
572 input_type = new_type;
573 }
574 }
575 if (input_type) {
576 structure_types[input->index_in_tree()] = *input_type;
577 }
578 }
579
580 const nodes::StructureTypeInterface &node_interface = node_interfaces[node->index()];
581
582 for (const int output_index : node_interface.outputs.index_range()) {
583 const bNodeSocket &output = *output_sockets[output_index];
584 if (!output.is_available() || !output.runtime->declaration) {
585 continue;
586 }
587 const nodes::SocketDeclaration &declaration = *output.runtime->declaration;
588
589 std::optional<StructureType> output_type;
590 for (const int input_index : node_interface.outputs[output_index].linked_inputs) {
591 const bNodeSocket &input = node->input_socket(input_index);
592 if (!input.is_available()) {
593 continue;
594 }
595 const StructureType new_type = structure_types[input.index_in_tree()];
596 if (output_type) {
597 output_type = left_to_right_merge(*output_type, new_type);
598 }
599 else {
600 output_type = new_type;
601 }
602 }
603 structure_types[output.index_in_tree()] = output_type.value_or(declaration.structure_type);
604 }
605
606 if (propagate_zone_status(tree, *node, structure_types)) {
607 need_update = true;
608 }
609 }
610
611 if (!need_update) {
612 break;
613 }
614 }
615}
616
618 const bNodeSocket &group_output, const Span<nodes::StructureTypeInterface> interface_by_node)
619{
620 /* Use a Set instead of an array indexed by socket because we may only look at a few sockets. */
621 Set<const bNodeSocket *> handled_sockets;
622 Stack<const bNodeSocket *> sockets_to_check;
623
624 handled_sockets.add(&group_output);
625 sockets_to_check.push(&group_output);
626
627 Vector<int> group_inputs;
628
629 while (!sockets_to_check.is_empty()) {
630 const bNodeSocket *input_socket = sockets_to_check.pop();
631 if (!input_socket->is_directly_linked()) {
632 continue;
633 }
634
635 for (const bNodeSocket *origin_socket : input_socket->directly_linked_sockets()) {
636 const bNode &origin_node = origin_socket->owner_node();
637 if (origin_node.is_group_input()) {
638 group_inputs.append_non_duplicates(origin_socket->index());
639 continue;
640 }
641
642 const nodes::StructureTypeInterface &node_interface = interface_by_node[origin_node.index()];
643 for (const int input_index : node_interface.outputs[origin_socket->index()].linked_inputs) {
644 const bNodeSocket &input = origin_node.input_socket(input_index);
645 if (!input.is_available()) {
646 continue;
647 }
648 if (handled_sockets.add(&input)) {
649 sockets_to_check.push(&input);
650 }
651 }
652 }
653 }
654
655 return group_inputs;
656}
657
659 const bNodeTree &tree,
660 const Span<nodes::StructureTypeInterface> interface_by_node,
661 const Span<StructureType> structure_types,
663{
664 const bNode *group_output_node = tree.group_output_node();
665 if (!group_output_node) {
667 output.type = StructureType::Dynamic;
668 }
669 return;
670 }
671
672 const Span<const bNodeTreeInterfaceSocket *> interface_outputs = tree.interface_outputs();
673 const Span<const bNodeSocket *> sockets = group_output_node->input_sockets().drop_back(1);
674 for (const int i : sockets.index_range()) {
675 if (interface_outputs[i]->structure_type != NODE_INTERFACE_SOCKET_STRUCTURE_TYPE_AUTO) {
676 interface.outputs[i] = {StructureType(interface_outputs[i]->structure_type), {}};
677 continue;
678 }
679 /* Update derived interface output structure types from output node socket usages. */
680 interface.outputs[i].type = structure_types[sockets[i]->index_in_tree()];
681 if (interface.outputs[i].type == StructureType::Dynamic) {
682 const Vector<int> linked_inputs = find_dynamic_output_linked_inputs(*sockets[i],
683 interface_by_node);
684 interface.outputs[i] = {StructureType::Dynamic, linked_inputs.as_span()};
685 }
686 }
687}
688
689static std::unique_ptr<nodes::StructureTypeInterface> calc_structure_type_interface(
690 const bNodeTree &tree)
691{
692 tree.ensure_topology_cache();
693 tree.ensure_interface_cache();
694
695 auto derived_interface = std::make_unique<nodes::StructureTypeInterface>();
696 derived_interface->inputs.reinitialize(tree.interface_inputs().size());
697 derived_interface->outputs.reinitialize(tree.interface_outputs().size());
698 if (tree.has_available_link_cycle()) {
699 derived_interface->inputs.fill(StructureType::Dynamic);
700 derived_interface->outputs.fill({StructureType::Dynamic, {}});
701 return derived_interface;
702 }
703
705
706 Array<DataRequirement> data_requirements(tree.all_input_sockets().size());
707 Array<StructureType> structure_types(tree.all_sockets().size(), StructureType::Dynamic);
708
709 init_input_requirements(tree, data_requirements);
710 propagate_right_to_left(tree, node_interfaces, data_requirements);
711 store_group_input_structure_types(tree, data_requirements, *derived_interface);
712 propagate_left_to_right(tree, node_interfaces, derived_interface->inputs, structure_types);
713 store_group_output_structure_types(tree, node_interfaces, structure_types, *derived_interface);
714
715 return derived_interface;
716}
717
719{
720 std::unique_ptr<nodes::StructureTypeInterface> new_interface = calc_structure_type_interface(
721 tree);
722 if (tree.runtime->structure_type_interface &&
723 *tree.runtime->structure_type_interface == *new_interface)
724 {
725 return false;
726 }
727 tree.runtime->structure_type_interface = std::move(new_interface);
728 return true;
729}
730
731} // namespace blender::bke::node_structure_type_inferencing
#define GEO_NODE_SIMULATION_OUTPUT
#define GEO_NODE_REPEAT_OUTPUT
#define GEO_NODE_REPEAT_INPUT
#define GEO_NODE_SIMULATION_INPUT
#define ENUM_OPERATORS(_type, _max)
@ NODE_INTERFACE_SOCKET_STRUCTURE_TYPE_AUTO
BMesh const char void * data
MutableSpan< T > as_mutable_span()
Definition BLI_array.hh:237
void reinitialize(const int64_t new_size)
Definition BLI_array.hh:398
constexpr IndexRange drop_back(int64_t n) const
bool add(const Key &key)
Definition BLI_set.hh:248
constexpr int64_t size() const
Definition BLI_span.hh:252
constexpr IndexRange index_range() const
Definition BLI_span.hh:401
bool is_empty() const
Definition BLI_stack.hh:308
void push(const T &value)
Definition BLI_stack.hh:213
int64_t size() const
void append(const T &value)
void append_non_duplicates(const T &value)
Span< T > as_span() const
const T & first() const
OutputSocketFieldType field_type() const
KDTree_3d * tree
#define input
#define output
void fill_index_range(MutableSpan< T > span, const T start=0)
static void store_group_input_structure_types(const bNodeTree &tree, const Span< DataRequirement > input_requirements, nodes::StructureTypeInterface &derived_interface)
static void propagate_right_to_left(const bNodeTree &tree, const Span< nodes::StructureTypeInterface > node_interfaces, MutableSpan< DataRequirement > input_requirements)
static DataRequirement merge(const DataRequirement a, const DataRequirement b)
static void propagate_left_to_right(const bNodeTree &tree, const Span< nodes::StructureTypeInterface > node_interfaces, const Span< StructureType > group_input_structure_types, MutableSpan< StructureType > structure_types)
static StructureType left_to_right_merge(const StructureType a, const StructureType b)
static ZoneInOutChange simulation_zone_requirements_propagate(const bNode &input_node, const bNode &output_node, MutableSpan< DataRequirement > input_requirements)
static bool propagate_zone_status(const bNodeTree &tree, const bNode &node, MutableSpan< StructureType > structure_types)
static ZoneInOutChange simulation_zone_status_propagate(const bNode &input_node, const bNode &output_node, MutableSpan< StructureType > structure_types)
static ZoneInOutChange repeat_zone_requirements_propagate(const bNode &input_node, const bNode &output_node, MutableSpan< DataRequirement > input_requirements)
static void init_input_requirements(const bNodeTree &tree, MutableSpan< DataRequirement > input_requirements)
static DataRequirement calc_output_socket_requirement(const bNodeSocket &output_socket, const Span< DataRequirement > input_requirements)
static Vector< int > find_dynamic_output_linked_inputs(const bNodeSocket &group_output, const Span< nodes::StructureTypeInterface > interface_by_node)
static nodes::StructureTypeInterface calc_node_interface(const bNode &node)
static bool propagate_zone_data_requirements(const bNodeTree &tree, const bNode &node, MutableSpan< DataRequirement > input_requirements)
static ZoneInOutChange repeat_zone_status_propagate(const bNode &input_node, const bNode &output_node, MutableSpan< StructureType > structure_types)
static StructureType get_unconnected_input_structure_type(const nodes::SocketDeclaration &declaration)
static void store_group_output_structure_types(const bNodeTree &tree, const Span< nodes::StructureTypeInterface > interface_by_node, const Span< StructureType > structure_types, nodes::StructureTypeInterface &interface)
static Array< nodes::StructureTypeInterface > calc_node_interfaces(const bNodeTree &tree)
static std::unique_ptr< nodes::StructureTypeInterface > calc_structure_type_interface(const bNodeTree &tree)
bNodeSocketRuntimeHandle * runtime
int16_t type_legacy
void * storage
int32_t identifier
i
Definition text_draw.cc:230