Blender V4.5
socket_usage_inference.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2024 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
5#include <regex>
6
12
13#include "DNA_anim_types.h"
14#include "DNA_material_types.h"
15#include "DNA_node_types.h"
16
20#include "BKE_node_runtime.hh"
22
23#include "ANIM_action.hh"
25
26#include "BLI_listbase.h"
27#include "BLI_stack.hh"
28
30
33 private:
35 ResourceScope scope_;
36 bke::ComputeContextCache compute_context_cache_;
37
39 const bNodeTree &root_tree_;
40
44 Stack<SocketInContext> usage_tasks_;
45 Stack<SocketInContext> value_tasks_;
46
51 Map<SocketInContext, bool> all_socket_usages_;
52
57 Map<SocketInContext, const void *> all_socket_values_;
58
63 Set<const bNodeSocket *> animated_sockets_;
64 Set<const bNodeTree *> trees_with_handled_animation_data_;
65
67 AlignedBuffer<1024, 8> scope_buffer_;
68
69 std::optional<Span<bool>> top_level_ignored_inputs_;
70
71 public:
73 const std::optional<Span<GPointer>> tree_input_values,
74 const std::optional<Span<bool>> top_level_ignored_inputs = std::nullopt)
75 : root_tree_(tree), top_level_ignored_inputs_(top_level_ignored_inputs)
76 {
77 scope_.allocator().provide_buffer(scope_buffer_);
78 root_tree_.ensure_topology_cache();
79 root_tree_.ensure_interface_cache();
80 this->ensure_animation_data_processed(root_tree_);
81
82 for (const bNode *node : root_tree_.group_input_nodes()) {
83 for (const int i : root_tree_.interface_inputs().index_range()) {
84 const bNodeSocket &socket = node->output_socket(i);
85 if (!socket.is_directly_linked()) {
86 /* This socket is not linked, hence it's value is never used. Thus we don't have to add
87 * it to #all_socket_values_. This optimization helps a lot when the node group has a
88 * very large number of inputs and group input nodes. */
89 continue;
90 }
91 const SocketInContext socket_in_context{nullptr, &socket};
92 const void *input_value = nullptr;
93 if (!this->treat_socket_as_unknown(socket_in_context)) {
94 if (tree_input_values.has_value()) {
95 input_value = (*tree_input_values)[i].get();
96 }
97 }
98 all_socket_values_.add_new(socket_in_context, input_value);
99 }
100 }
101 }
102
104 {
105 for (const bNode *node : root_tree_.all_nodes()) {
106 if (node->is_group_input()) {
107 /* Can skip these sockets, because they don't affect usage anyway, and there may be a lot
108 * of them. See #144756. */
109 continue;
110 }
111 for (const bNodeSocket *socket : node->output_sockets()) {
112 all_socket_usages_.add_new({nullptr, socket}, true);
113 }
114 }
115 }
116
117 bool is_group_input_used(const int input_i)
118 {
119 for (const bNode *node : root_tree_.group_input_nodes()) {
120 const SocketInContext socket{nullptr, &node->output_socket(input_i)};
121 if (this->is_socket_used(socket)) {
122 return true;
123 }
124 }
125 return false;
126 }
127
128 bool is_socket_used(const SocketInContext &socket)
129 {
130 const std::optional<bool> is_used = all_socket_usages_.lookup_try(socket);
131 if (is_used.has_value()) {
132 return *is_used;
133 }
134 if (socket->is_output() && !socket->is_directly_linked()) {
135 /* In this case we can return early because the socket can't be used if it's not linked. */
136 return false;
137 }
138 if (socket->owner_tree().has_available_link_cycle()) {
139 return false;
140 }
141
142 BLI_assert(usage_tasks_.is_empty());
143 usage_tasks_.push(socket);
144
145 while (!usage_tasks_.is_empty()) {
146 const SocketInContext &socket = usage_tasks_.peek();
147 this->usage_task(socket);
148 if (&socket == &usage_tasks_.peek()) {
149 /* The task is finished if it hasn't added any new task it depends on. */
150 usage_tasks_.pop();
151 }
152 }
153
154 return all_socket_usages_.lookup(socket);
155 }
156
157 const void *get_socket_value(const SocketInContext &socket)
158 {
159 const std::optional<const void *> value = all_socket_values_.lookup_try(socket);
160 if (value.has_value()) {
161 return *value;
162 }
163 if (socket->owner_tree().has_available_link_cycle()) {
164 return nullptr;
165 }
166
167 BLI_assert(value_tasks_.is_empty());
168 value_tasks_.push(socket);
169
170 while (!value_tasks_.is_empty()) {
171 const SocketInContext &socket = value_tasks_.peek();
172 this->value_task(socket);
173 if (&socket == &value_tasks_.peek()) {
174 /* The task is finished if it hasn't added any new task it depends on. */
175 value_tasks_.pop();
176 }
177 }
178
179 return all_socket_values_.lookup(socket);
180 }
181
182 private:
183 void usage_task(const SocketInContext &socket)
184 {
185 if (all_socket_usages_.contains(socket)) {
186 return;
187 }
188 const bNode &node = socket->owner_node();
189 if (!socket->is_available()) {
190 all_socket_usages_.add_new(socket, false);
191 return;
192 }
193 if (node.is_undefined() && !node.is_custom_group()) {
194 all_socket_usages_.add_new(socket, false);
195 return;
196 }
197 if (socket->is_input()) {
198 this->usage_task__input(socket);
199 }
200 else {
201 this->usage_task__output(socket);
202 }
203 }
204
205 void usage_task__input(const SocketInContext &socket)
206 {
207 const NodeInContext node = socket.owner_node();
208
209 if (node->is_muted()) {
210 this->usage_task__input__muted_node(socket);
211 return;
212 }
213
214 switch (node->type_legacy) {
215 case NODE_GROUP:
216 case NODE_CUSTOM_GROUP: {
217 this->usage_task__input__group_node(socket);
218 break;
219 }
220 case NODE_GROUP_OUTPUT: {
221 this->usage_task__input__group_output_node(socket);
222 break;
223 }
224 case GEO_NODE_SWITCH: {
225 this->usage_task__input__generic_switch(socket, switch__is_socket_selected);
226 break;
227 }
229 this->usage_task__input__generic_switch(socket, index_switch__is_socket_selected);
230 break;
231 }
233 this->usage_task__input__generic_switch(socket, menu_switch__is_socket_selected);
234 break;
235 }
236 case SH_NODE_MIX: {
237 this->usage_task__input__generic_switch(socket, mix_node__is_socket_selected);
238 break;
239 }
240 case SH_NODE_MIX_SHADER: {
241 this->usage_task__input__generic_switch(socket, shader_mix_node__is_socket_selected);
242 break;
243 }
245 this->usage_task__input__simulation_input_node(socket);
246 break;
247 }
249 this->usage_task__input__repeat_input_node(socket);
250 break;
251 }
253 this->usage_task__input__foreach_element_input_node(socket);
254 break;
255 }
257 this->usage_task__input__foreach_element_output_node(socket);
258 break;
259 }
261 this->usage_task__input__capture_attribute_node(socket);
262 break;
263 }
271 case TEX_NODE_OUTPUT: {
272 this->usage_task__input__output_node(socket);
273 break;
274 }
275 default: {
276 this->usage_task__input__fallback(socket);
277 break;
278 }
279 }
280 }
281
282 void usage_task__input__output_node(const SocketInContext &socket)
283 {
284 all_socket_usages_.add_new(socket, true);
285 }
286
291 void usage_task__input__generic_switch(
292 const SocketInContext &socket,
293 const FunctionRef<bool(const SocketInContext &socket, const void *condition)>
294 is_selected_socket)
295 {
296 const NodeInContext node = socket.owner_node();
297 BLI_assert(node->input_sockets().size() >= 1);
298 BLI_assert(node->output_sockets().size() >= 1);
299
300 if (socket->type == SOCK_CUSTOM && STREQ(socket->idname, "NodeSocketVirtual")) {
301 all_socket_usages_.add_new(socket, false);
302 return;
303 }
304 const SocketInContext output_socket{socket.context,
305 this->get_first_available_bsocket(node->output_sockets())};
306 const std::optional<bool> output_is_used = all_socket_usages_.lookup_try(output_socket);
307 if (!output_is_used.has_value()) {
308 this->push_usage_task(output_socket);
309 return;
310 }
311 if (!*output_is_used) {
312 all_socket_usages_.add_new(socket, false);
313 return;
314 }
315 const SocketInContext condition_socket{
316 socket.context, this->get_first_available_bsocket(node->input_sockets())};
317 if (socket == condition_socket) {
318 all_socket_usages_.add_new(socket, true);
319 return;
320 }
321 const void *condition_value = this->get_socket_value(condition_socket);
322 if (condition_value == nullptr) {
323 /* The exact condition value is unknown, so any input may be used. */
324 all_socket_usages_.add_new(socket, true);
325 return;
326 }
327 const bool is_used = is_selected_socket(socket, condition_value);
328 all_socket_usages_.add_new(socket, is_used);
329 }
330
331 const bNodeSocket *get_first_available_bsocket(const Span<const bNodeSocket *> sockets) const
332 {
333 for (const bNodeSocket *socket : sockets) {
334 if (socket->is_available()) {
335 return socket;
336 }
337 }
338 return nullptr;
339 }
340
341 void usage_task__input__group_node(const SocketInContext &socket)
342 {
343 const NodeInContext node = socket.owner_node();
344 const bNodeTree *group = reinterpret_cast<const bNodeTree *>(node->id);
345 if (!group || ID_MISSING(&group->id)) {
346 all_socket_usages_.add_new(socket, false);
347 return;
348 }
349 group->ensure_topology_cache();
350 if (group->has_available_link_cycle()) {
351 all_socket_usages_.add_new(socket, false);
352 return;
353 }
354 this->ensure_animation_data_processed(*group);
355
356 /* The group node input is used if any of the matching group inputs within the group is
357 * used. */
358 const ComputeContext &group_context = compute_context_cache_.for_group_node(
359 socket.context, node->identifier, &node->owner_tree());
360 Vector<const bNodeSocket *> dependent_sockets;
361 for (const bNode *group_input_node : group->group_input_nodes()) {
362 const bNodeSocket &group_input_socket = group_input_node->output_socket(socket->index());
363 if (group_input_socket.is_directly_linked()) {
364 /* Skip unlinked group inputs to avoid further unnecessary processing of them further down
365 * the line. */
366 dependent_sockets.append(&group_input_socket);
367 }
368 }
369 this->usage_task__with_dependent_sockets(socket, dependent_sockets, {}, &group_context);
370 }
371
372 void usage_task__input__group_output_node(const SocketInContext &socket)
373 {
374 const int output_i = socket->index();
375 if (socket.context == nullptr) {
376 /* This is a final output which is always used. */
377 all_socket_usages_.add_new(socket, true);
378 return;
379 }
380 /* The group output node is used if the matching output of the parent group node is used. */
381 const bke::GroupNodeComputeContext &group_context =
382 *static_cast<const bke::GroupNodeComputeContext *>(socket.context);
383 const bNodeSocket &group_node_output = group_context.node()->output_socket(output_i);
384 this->usage_task__with_dependent_sockets(
385 socket, {&group_node_output}, {}, group_context.parent());
386 }
387
388 void usage_task__output(const SocketInContext &socket)
389 {
390 /* An output socket is used if any of the sockets it is connected to is used. */
391 Vector<const bNodeSocket *> dependent_sockets;
392 for (const bNodeLink *link : socket->directly_linked_links()) {
393 if (link->is_used()) {
394 dependent_sockets.append(link->tosock);
395 }
396 }
397 this->usage_task__with_dependent_sockets(socket, dependent_sockets, {}, socket.context);
398 }
399
400 void usage_task__input__simulation_input_node(const SocketInContext &socket)
401 {
402 const NodeInContext node = socket.owner_node();
403 const bNodeTree &tree = socket->owner_tree();
404
405 const NodeGeometrySimulationInput &storage = *static_cast<const NodeGeometrySimulationInput *>(
406 node->storage);
407 const bNode *sim_output_node = tree.node_by_id(storage.output_node_id);
408 if (!sim_output_node) {
409 all_socket_usages_.add_new(socket, false);
410 return;
411 }
412 /* Simulation inputs are also used when any of the simulation outputs are used. */
413 Vector<const bNodeSocket *, 16> dependent_sockets;
414 dependent_sockets.extend(node->output_sockets());
415 dependent_sockets.extend(sim_output_node->output_sockets());
416 this->usage_task__with_dependent_sockets(socket, dependent_sockets, {}, socket.context);
417 }
418
419 void usage_task__input__repeat_input_node(const SocketInContext &socket)
420 {
421 const NodeInContext node = socket.owner_node();
422 const bNodeTree &tree = socket->owner_tree();
423
424 const NodeGeometryRepeatInput &storage = *static_cast<const NodeGeometryRepeatInput *>(
425 node->storage);
426 const bNode *repeat_output_node = tree.node_by_id(storage.output_node_id);
427 if (!repeat_output_node) {
428 all_socket_usages_.add_new(socket, false);
429 return;
430 }
431 /* Assume that all repeat inputs are used when any of the outputs are used. This check could
432 * become more precise in the future if necessary. */
433 Vector<const bNodeSocket *, 16> dependent_sockets;
434 dependent_sockets.extend(node->output_sockets());
435 dependent_sockets.extend(repeat_output_node->output_sockets());
436 this->usage_task__with_dependent_sockets(socket, dependent_sockets, {}, socket.context);
437 }
438
439 void usage_task__input__foreach_element_output_node(const SocketInContext &socket)
440 {
441 const NodeInContext node = socket.owner_node();
442 this->usage_task__with_dependent_sockets(
443 socket, {&node->output_by_identifier(socket->identifier)}, {}, socket.context);
444 }
445
446 void usage_task__input__capture_attribute_node(const SocketInContext &socket)
447 {
448 const NodeInContext node = socket.owner_node();
449 this->usage_task__with_dependent_sockets(
450 socket, {&node->output_socket(socket->index())}, {}, socket.context);
451 }
452
453 void usage_task__input__fallback(const SocketInContext &socket)
454 {
455 Vector<const bNodeSocket *> dependent_boolean_inputs;
456 /* For built-in nodes we assume that sockets in a panel with a panel-toggle are disabled when
457 * the panel is disabled. */
458 if (const SocketDeclaration *socket_decl = socket->runtime->declaration) {
459 for (const PanelDeclaration *panel_decl = socket_decl->parent; panel_decl;
460 panel_decl = panel_decl->parent)
461 {
462 if (const SocketDeclaration *panel_toggle_decl = panel_decl->panel_input_decl()) {
463 if (panel_toggle_decl != socket_decl) {
464 dependent_boolean_inputs.append(
465 &socket->owner_node().socket_by_decl(*panel_toggle_decl));
466 }
467 }
468 }
469 }
470 this->usage_task__with_dependent_sockets(
471 socket, socket->owner_node().output_sockets(), dependent_boolean_inputs, socket.context);
472 }
473
474 void usage_task__input__foreach_element_input_node(const SocketInContext &socket)
475 {
476 const NodeInContext node = socket.owner_node();
477 const bNodeTree &tree = socket->owner_tree();
478
480 *static_cast<const NodeGeometryForeachGeometryElementInput *>(node->storage);
481 const bNode *foreach_output_node = tree.node_by_id(storage.output_node_id);
482 if (!foreach_output_node) {
483 all_socket_usages_.add_new(socket, false);
484 return;
485 }
486 Vector<const bNodeSocket *, 16> dependent_sockets;
487 if (StringRef(socket->identifier).startswith("Input_")) {
488 dependent_sockets.append(&node->output_by_identifier(socket->identifier));
489 }
490 else {
491 /* The geometry and selection inputs are used whenever any of the zone outputs is used. */
492 dependent_sockets.extend(node->output_sockets());
493 dependent_sockets.extend(foreach_output_node->output_sockets());
494 }
495 this->usage_task__with_dependent_sockets(socket, dependent_sockets, {}, socket.context);
496 }
497
498 void usage_task__input__muted_node(const SocketInContext &socket)
499 {
500 const NodeInContext node = socket.owner_node();
501 Vector<const bNodeSocket *> dependent_sockets;
502 for (const bNodeLink &internal_link : node->internal_links()) {
503 if (internal_link.fromsock != socket.socket) {
504 continue;
505 }
506 dependent_sockets.append(internal_link.tosock);
507 }
508 this->usage_task__with_dependent_sockets(socket, dependent_sockets, {}, socket.context);
509 }
510
515 void usage_task__with_dependent_sockets(const SocketInContext &socket,
516 const Span<const bNodeSocket *> dependent_outputs,
517 const Span<const bNodeSocket *> condition_inputs,
518 const ComputeContext *dependent_socket_context)
519 {
520 /* Check if any of the dependent outputs are used. */
521 SocketInContext next_unknown_output;
522 bool any_output_used = false;
523 for (const bNodeSocket *dependent_socket_ptr : dependent_outputs) {
524 const SocketInContext dependent_socket{dependent_socket_context, dependent_socket_ptr};
525 const std::optional<bool> is_used = all_socket_usages_.lookup_try(dependent_socket);
526 if (!is_used.has_value() && !next_unknown_output) {
527 next_unknown_output = dependent_socket;
528 continue;
529 }
530 if (is_used.value_or(false)) {
531 any_output_used = true;
532 break;
533 }
534 }
535 if (next_unknown_output) {
536 /* Create a task that checks if the next dependent socket is used. Intentionally only create
537 * a task for the very next one and not for all, because that could potentially trigger a lot
538 * of unnecessary evaluations. */
539 this->push_usage_task(next_unknown_output);
540 return;
541 }
542 if (!any_output_used) {
543 all_socket_usages_.add_new(socket, false);
544 return;
545 }
546 bool all_condition_inputs_true = true;
547 for (const bNodeSocket *condition_input_ptr : condition_inputs) {
548 const SocketInContext condition_input{dependent_socket_context, condition_input_ptr};
549 const void *condition_value = this->get_socket_value(condition_input);
550 if (condition_value == nullptr) {
551 /* The condition is not known, so it may be true. */
552 continue;
553 }
554 BLI_assert(condition_input_ptr->type == SOCK_BOOLEAN);
555 const bool condition = *static_cast<const bool *>(condition_value);
556 if (!condition) {
557 all_condition_inputs_true = false;
558 break;
559 }
560 }
561 all_socket_usages_.add_new(socket, all_condition_inputs_true);
562 }
563
564 void value_task(const SocketInContext &socket)
565 {
566 if (all_socket_values_.contains(socket)) {
567 /* Task is done already. */
568 return;
569 }
570 const bNode &node = socket->owner_node();
571 if (node.is_undefined() && !node.is_custom_group()) {
572 all_socket_values_.add_new(socket, nullptr);
573 return;
574 }
575 const CPPType *base_type = socket->typeinfo->base_cpp_type;
576 if (!base_type) {
577 /* The socket type is unknown for some reason (maybe a socket type from the future?). */
578 all_socket_values_.add_new(socket, nullptr);
579 return;
580 }
581 if (socket->is_input()) {
582 this->value_task__input(socket);
583 }
584 else {
585 this->value_task__output(socket);
586 }
587 }
588
589 void value_task__output(const SocketInContext &socket)
590 {
591 const NodeInContext node = socket.owner_node();
592 if (node->is_muted()) {
593 this->value_task__output__muted_node(socket);
594 return;
595 }
596 switch (node->type_legacy) {
597 case NODE_GROUP:
598 case NODE_CUSTOM_GROUP: {
599 this->value_task__output__group_node(socket);
600 return;
601 }
602 case NODE_GROUP_INPUT: {
603 this->value_task__output__group_input_node(socket);
604 return;
605 }
606 case NODE_REROUTE: {
607 this->value_task__output__reroute_node(socket);
608 return;
609 }
610 case GEO_NODE_SWITCH: {
611 this->value_task__output__generic_switch(socket, switch__is_socket_selected);
612 return;
613 }
615 this->value_task__output__generic_switch(socket, index_switch__is_socket_selected);
616 return;
617 }
619 this->value_task__output__generic_switch(socket, menu_switch__is_socket_selected);
620 return;
621 }
622 case SH_NODE_MIX: {
623 this->value_task__output__generic_switch(socket, mix_node__is_socket_selected);
624 return;
625 }
626 case SH_NODE_MIX_SHADER: {
627 this->value_task__output__generic_switch(socket, shader_mix_node__is_socket_selected);
628 return;
629 }
630 case SH_NODE_MATH: {
631 this->value_task__output__float_math(socket);
632 return;
633 }
634 case SH_NODE_VECTOR_MATH: {
635 this->value_task__output__vector_math(socket);
636 return;
637 }
639 this->value_task__output__integer_math(socket);
640 return;
641 }
643 this->value_task__output__boolean_math(socket);
644 return;
645 }
646 default: {
647 if (node->typeinfo->build_multi_function) {
648 this->value_task__output__multi_function_node(socket);
649 return;
650 }
651 break;
652 }
653 }
654 /* If none of the above cases work, the socket value is set to null which means that it is
655 * unknown/dynamic. */
656 all_socket_values_.add_new(socket, nullptr);
657 }
658
659 void value_task__output__group_node(const SocketInContext &socket)
660 {
661 const NodeInContext node = socket.owner_node();
662 const bNodeTree *group = reinterpret_cast<const bNodeTree *>(node->id);
663 if (!group || ID_MISSING(&group->id)) {
664 all_socket_values_.add_new(socket, nullptr);
665 return;
666 }
667 group->ensure_topology_cache();
668 if (group->has_available_link_cycle()) {
669 all_socket_values_.add_new(socket, nullptr);
670 return;
671 }
672 this->ensure_animation_data_processed(*group);
673 const bNode *group_output_node = group->group_output_node();
674 if (!group_output_node) {
675 /* Can't compute the value if the group does not have an output node. */
676 all_socket_values_.add_new(socket, nullptr);
677 return;
678 }
679 const ComputeContext &group_context = compute_context_cache_.for_group_node(
680 socket.context, node->identifier, &node->owner_tree());
681 const SocketInContext socket_in_group{&group_context,
682 &group_output_node->input_socket(socket->index())};
683 const std::optional<const void *> value = all_socket_values_.lookup_try(socket_in_group);
684 if (!value.has_value()) {
685 this->push_value_task(socket_in_group);
686 return;
687 }
688 all_socket_values_.add_new(socket, *value);
689 }
690
691 void value_task__output__group_input_node(const SocketInContext &socket)
692 {
693 /* Group inputs for the root context should be initialized already. */
694 BLI_assert(socket.context != nullptr);
695
696 const bke::GroupNodeComputeContext &group_context =
697 *static_cast<const bke::GroupNodeComputeContext *>(socket.context);
698 const SocketInContext group_node_input{group_context.parent(),
699 &group_context.node()->input_socket(socket->index())};
700 const std::optional<const void *> value = all_socket_values_.lookup_try(group_node_input);
701 if (!value.has_value()) {
702 this->push_value_task(group_node_input);
703 return;
704 }
705 all_socket_values_.add_new(socket, *value);
706 }
707
708 void value_task__output__reroute_node(const SocketInContext &socket)
709 {
710 const SocketInContext input_socket = socket.owner_node().input_socket(0);
711 const std::optional<const void *> value = all_socket_values_.lookup_try(input_socket);
712 if (!value.has_value()) {
713 this->push_value_task(input_socket);
714 return;
715 }
716 all_socket_values_.add_new(socket, *value);
717 }
718
719 void value_task__output__float_math(const SocketInContext &socket)
720 {
721 const NodeInContext node = socket.owner_node();
722 const NodeMathOperation operation = NodeMathOperation(node->custom1);
723 switch (operation) {
724 case NODE_MATH_MULTIPLY: {
725 this->value_task__output__generic_eval(
726 socket, [&](const Span<const void *> inputs) -> std::optional<const void *> {
727 const std::optional<float> a = inputs[0] ? std::optional(*static_cast<const float *>(
728 inputs[0])) :
729 std::nullopt;
730 const std::optional<float> b = inputs[1] ? std::optional(*static_cast<const float *>(
731 inputs[1])) :
732 std::nullopt;
733 if (a == 0.0f || b == 0.0f) {
734 return &scope_.construct<float>(0.0f);
735 }
736 if (a.has_value() && b.has_value()) {
737 return &scope_.construct<float>(*a * *b);
738 }
739 return std::nullopt;
740 });
741 break;
742 }
743 default: {
744 this->value_task__output__multi_function_node(socket);
745 break;
746 }
747 }
748 }
749
750 void value_task__output__vector_math(const SocketInContext &socket)
751 {
752 const NodeInContext node = socket.owner_node();
753 const NodeVectorMathOperation operation = NodeVectorMathOperation(node->custom1);
754 switch (operation) {
756 this->value_task__output__generic_eval(
757 socket, [&](const Span<const void *> inputs) -> std::optional<const void *> {
758 const std::optional<float3> a = inputs[0] ?
759 std::optional(
760 *static_cast<const float3 *>(inputs[0])) :
761 std::nullopt;
762 const std::optional<float3> b = inputs[1] ?
763 std::optional(
764 *static_cast<const float3 *>(inputs[1])) :
765 std::nullopt;
766 if (a == float3(0.0f) || b == float3(0.0f)) {
767 return &scope_.construct<float3>(0.0f);
768 }
769 if (a.has_value() && b.has_value()) {
770 return &scope_.construct<float3>(*a * *b);
771 }
772 return std::nullopt;
773 });
774 break;
775 }
777 this->value_task__output__generic_eval(
778 socket, [&](const Span<const void *> inputs) -> std::optional<const void *> {
779 const std::optional<float3> a = inputs[0] ?
780 std::optional(
781 *static_cast<const float3 *>(inputs[0])) :
782 std::nullopt;
783 const std::optional<float> scale = inputs[3] ?
784 std::optional(
785 *static_cast<const float *>(inputs[3])) :
786 std::nullopt;
787 if (a == float3(0.0f) || scale == 0.0f) {
788 return &scope_.construct<float3>(0.0f);
789 }
790 if (a.has_value() && scale.has_value()) {
791 return &scope_.construct<float3>(*a * *scale);
792 }
793 return std::nullopt;
794 });
795 break;
796 }
797 default: {
798 this->value_task__output__multi_function_node(socket);
799 break;
800 }
801 }
802 }
803
804 void value_task__output__integer_math(const SocketInContext &socket)
805 {
806 const NodeInContext node = socket.owner_node();
807 const NodeIntegerMathOperation operation = NodeIntegerMathOperation(node->custom1);
808 switch (operation) {
810 this->value_task__output__generic_eval(
811 socket, [&](const Span<const void *> inputs) -> std::optional<const void *> {
812 const std::optional<int> a = inputs[0] ? std::optional(
813 *static_cast<const int *>(inputs[0])) :
814 std::nullopt;
815 const std::optional<int> b = inputs[1] ? std::optional(
816 *static_cast<const int *>(inputs[1])) :
817 std::nullopt;
818 if (a == 0 || b == 0) {
819 return &scope_.construct<int>(0);
820 }
821 if (a.has_value() && b.has_value()) {
822 return &scope_.construct<int>(*a * *b);
823 }
824 return std::nullopt;
825 });
826 break;
827 }
828 default: {
829 this->value_task__output__multi_function_node(socket);
830 break;
831 }
832 }
833 }
834
835 void value_task__output__boolean_math(const SocketInContext &socket)
836 {
837 const NodeInContext node = socket.owner_node();
838 const NodeBooleanMathOperation operation = NodeBooleanMathOperation(node->custom1);
839
840 const auto handle_binary_op =
841 [&](FunctionRef<std::optional<bool>(std::optional<bool>, std::optional<bool>)> fn) {
842 this->value_task__output__generic_eval(
843 socket, [&](const Span<const void *> inputs) -> std::optional<const void *> {
844 const std::optional<bool> a = inputs[0] ? std::optional(*static_cast<const bool *>(
845 inputs[0])) :
846 std::nullopt;
847 const std::optional<bool> b = inputs[1] ? std::optional(*static_cast<const bool *>(
848 inputs[1])) :
849 std::nullopt;
850 const std::optional<bool> result = fn(a, b);
851 if (result.has_value()) {
852 return &scope_.construct<bool>(*result);
853 }
854 return std::nullopt;
855 });
856 };
857 switch (operation) {
859 handle_binary_op(
860 [](const std::optional<bool> &a, const std::optional<bool> &b) -> std::optional<bool> {
861 if (a == false || b == false) {
862 return false;
863 }
864 if (a.has_value() && b.has_value()) {
865 return *a && *b;
866 }
867 return std::nullopt;
868 });
869 break;
870 }
872 handle_binary_op(
873 [](const std::optional<bool> &a, const std::optional<bool> &b) -> std::optional<bool> {
874 if (a == true || b == true) {
875 return true;
876 }
877 if (a.has_value() && b.has_value()) {
878 return *a || *b;
879 }
880 return std::nullopt;
881 });
882 break;
883 }
885 handle_binary_op(
886 [](const std::optional<bool> &a, const std::optional<bool> &b) -> std::optional<bool> {
887 if (a == false || b == false) {
888 return true;
889 }
890 if (a.has_value() && b.has_value()) {
891 return !(*a && *b);
892 }
893 return std::nullopt;
894 });
895 break;
896 }
898 handle_binary_op(
899 [](const std::optional<bool> &a, const std::optional<bool> &b) -> std::optional<bool> {
900 if (a == true || b == true) {
901 return false;
902 }
903 if (a.has_value() && b.has_value()) {
904 return !(*a || *b);
905 }
906 return std::nullopt;
907 });
908 break;
909 }
911 handle_binary_op(
912 [](const std::optional<bool> &a, const std::optional<bool> &b) -> std::optional<bool> {
913 if (a == false || b == true) {
914 return true;
915 }
916 if (a.has_value() && b.has_value()) {
917 return !*a || *b;
918 }
919 return std::nullopt;
920 });
921 break;
922 }
924 handle_binary_op(
925 [](const std::optional<bool> &a, const std::optional<bool> &b) -> std::optional<bool> {
926 if (a == false || b == true) {
927 return false;
928 }
929 if (a.has_value() && b.has_value()) {
930 return *a && !*b;
931 }
932 return std::nullopt;
933 });
934 break;
935 }
936 default: {
937 this->value_task__output__multi_function_node(socket);
938 break;
939 }
940 }
941 }
942
947 void value_task__output__generic_switch(
948 const SocketInContext &socket,
949 const FunctionRef<bool(const SocketInContext &socket, const void *condition)>
950 is_selected_socket)
951 {
952 const NodeInContext node = socket.owner_node();
953 BLI_assert(node->input_sockets().size() >= 1);
954 BLI_assert(node->output_sockets().size() >= 1);
955
956 const SocketInContext condition_socket{
957 socket.context, this->get_first_available_bsocket(node->input_sockets())};
958 const std::optional<const void *> condition_value = all_socket_values_.lookup_try(
959 condition_socket);
960 if (!condition_value.has_value()) {
961 this->push_value_task(condition_socket);
962 return;
963 }
964 if (!*condition_value) {
965 /* The condition value is not a simple static value, so the output is unknown. */
966 all_socket_values_.add_new(socket, nullptr);
967 return;
968 }
969 Vector<const bNodeSocket *> selected_inputs;
970 for (const int input_i :
971 node->input_sockets().index_range().drop_front(condition_socket->index() + 1))
972 {
973 const SocketInContext input_socket = node.input_socket(input_i);
974 if (!input_socket->is_available()) {
975 continue;
976 }
977 if (input_socket->type == SOCK_CUSTOM && STREQ(input_socket->idname, "NodeSocketVirtual")) {
978 continue;
979 }
980 const bool is_selected = is_selected_socket(input_socket, *condition_value);
981 if (is_selected) {
982 selected_inputs.append(input_socket.socket);
983 }
984 }
985 if (selected_inputs.is_empty()) {
986 all_socket_values_.add_new(socket, nullptr);
987 return;
988 }
989 if (selected_inputs.size() == 1) {
990 /* A single input is selected, so just pass through this value without regarding others. */
991 const SocketInContext selected_input{socket.context, selected_inputs[0]};
992 const std::optional<const void *> input_value = all_socket_values_.lookup_try(
993 selected_input);
994 if (!input_value.has_value()) {
995 this->push_value_task(selected_input);
996 return;
997 }
998 all_socket_values_.add_new(socket, *input_value);
999 return;
1000 }
1001
1002 /* Multiple inputs are selected. */
1003 if (node->typeinfo->build_multi_function) {
1004 /* Try to compute the output value from the multiple selected inputs. */
1005 this->value_task__output__multi_function_node(socket);
1006 return;
1007 }
1008 /* Can't compute the output value, so set it to be unknown. */
1009 all_socket_values_.add_new(socket, nullptr);
1010 }
1011
1012 void value_task__output__generic_eval(
1013 const SocketInContext &socket,
1014 const FunctionRef<std::optional<const void *>(Span<const void *> inputs)> eval_fn)
1015 {
1016 const NodeInContext node = socket.owner_node();
1017 const int inputs_num = node->input_sockets().size();
1018
1019 Array<const void *, 16> input_values(inputs_num, nullptr);
1020 std::optional<int> next_unknown_input_index;
1021 for (const int input_i : IndexRange(inputs_num)) {
1022 const SocketInContext input_socket = node.input_socket(input_i);
1023 if (!input_socket->is_available()) {
1024 continue;
1025 }
1026 const std::optional<const void *> input_value = all_socket_values_.lookup_try(input_socket);
1027 if (!input_value.has_value()) {
1028 next_unknown_input_index = input_i;
1029 break;
1030 }
1031 input_values[input_i] = *input_value;
1032 }
1033 const std::optional<const void *> output_value = eval_fn(input_values);
1034 if (output_value.has_value()) {
1035 /* Was able to compute the output value. */
1036 all_socket_values_.add_new(socket, *output_value);
1037 return;
1038 }
1039 if (!next_unknown_input_index.has_value()) {
1040 /* The output is still unknown even though we know as much about the inputs as possible
1041 * already. */
1042 all_socket_values_.add_new(socket, nullptr);
1043 return;
1044 }
1045 /* Request the next input socket. */
1046 const SocketInContext next_input = node.input_socket(*next_unknown_input_index);
1047 this->push_value_task(next_input);
1048 }
1049
1050 void value_task__output__multi_function_node(const SocketInContext &socket)
1051 {
1052 const NodeInContext node = socket.owner_node();
1053 const int inputs_num = node->input_sockets().size();
1054
1055 /* Gather all input values are return early if any of them is not known. */
1056 Vector<const void *> input_values(inputs_num);
1057 for (const int input_i : IndexRange(inputs_num)) {
1058 const SocketInContext input_socket = node.input_socket(input_i);
1059 const std::optional<const void *> input_value = all_socket_values_.lookup_try(input_socket);
1060 if (!input_value.has_value()) {
1061 this->push_value_task(input_socket);
1062 return;
1063 }
1064 if (*input_value == nullptr) {
1065 all_socket_values_.add_new(socket, nullptr);
1066 return;
1067 }
1068 input_values[input_i] = *input_value;
1069 }
1070
1071 /* Get the multi-function for the node. */
1072 NodeMultiFunctionBuilder builder{*node.node, node->owner_tree()};
1073 node->typeinfo->build_multi_function(builder);
1074 const mf::MultiFunction &fn = builder.function();
1075
1076 /* We only evaluate the node for a single value here. */
1077 const IndexMask mask(1);
1078
1079 /* Prepare parameters for the multi-function evaluation. */
1080 mf::ParamsBuilder params{fn, &mask};
1081 for (const int input_i : IndexRange(inputs_num)) {
1082 const SocketInContext input_socket = node.input_socket(input_i);
1083 if (!input_socket->is_available()) {
1084 continue;
1085 }
1086 params.add_readonly_single_input(
1087 GPointer(input_socket->typeinfo->base_cpp_type, input_values[input_i]));
1088 }
1089 for (const int output_i : node->output_sockets().index_range()) {
1090 const SocketInContext output_socket = node.output_socket(output_i);
1091 if (!output_socket->is_available()) {
1092 continue;
1093 }
1094 /* Allocate memory for the output value. */
1095 const CPPType &base_type = *output_socket->typeinfo->base_cpp_type;
1096 void *value = scope_.allocate_owned(base_type);
1097 params.add_uninitialized_single_output(GMutableSpan(base_type, value, 1));
1098 all_socket_values_.add_new(output_socket, value);
1099 }
1100 mf::ContextBuilder context;
1101 /* Actually evaluate the multi-function. The outputs will be written into the memory allocated
1102 * earlier, which has been added to #all_socket_values_ already. */
1103 fn.call(mask, params, context);
1104 }
1105
1106 void value_task__output__muted_node(const SocketInContext &socket)
1107 {
1108 const NodeInContext node = socket.owner_node();
1109
1110 SocketInContext input_socket;
1111 for (const bNodeLink &internal_link : node->internal_links()) {
1112 if (internal_link.tosock == socket.socket) {
1113 input_socket = SocketInContext{socket.context, internal_link.fromsock};
1114 break;
1115 }
1116 }
1117 if (!input_socket) {
1118 /* The output does not have an internal link to an input. */
1119 all_socket_values_.add_new(socket, nullptr);
1120 return;
1121 }
1122 const std::optional<const void *> input_value = all_socket_values_.lookup_try(input_socket);
1123 if (!input_value.has_value()) {
1124 this->push_value_task(input_socket);
1125 return;
1126 }
1127 const void *converted_value = this->convert_type_if_necessary(
1128 *input_value, *input_socket.socket, *socket.socket);
1129 all_socket_values_.add_new(socket, converted_value);
1130 }
1131
1132 void value_task__input(const SocketInContext &socket)
1133 {
1134 if (socket->is_multi_input()) {
1135 /* Can't know the single value of a multi-input. */
1136 all_socket_values_.add_new(socket, nullptr);
1137 return;
1138 }
1139 const bNodeLink *source_link = nullptr;
1140 const Span<const bNodeLink *> connected_links = socket->directly_linked_links();
1141 for (const bNodeLink *link : connected_links) {
1142 if (!link->is_used()) {
1143 continue;
1144 }
1145 if (link->fromnode->is_dangling_reroute()) {
1146 continue;
1147 }
1148 source_link = link;
1149 break;
1150 }
1151 if (!source_link) {
1152 this->value_task__input__unlinked(socket);
1153 return;
1154 }
1155 this->value_task__input__linked({socket.context, source_link->fromsock}, socket);
1156 }
1157
1158 void value_task__input__unlinked(const SocketInContext &socket)
1159 {
1160 if (this->treat_socket_as_unknown(socket)) {
1161 all_socket_values_.add_new(socket, nullptr);
1162 return;
1163 }
1164 if (animated_sockets_.contains(socket.socket)) {
1165 /* The value of animated sockets is not known statically. */
1166 all_socket_values_.add_new(socket, nullptr);
1167 return;
1168 }
1169 if (const SocketDeclaration *socket_decl = socket.socket->runtime->declaration) {
1170 if (socket_decl->input_field_type == InputSocketFieldType::Implicit) {
1171 /* Implicit fields inputs don't have a single static value. */
1172 all_socket_values_.add_new(socket, nullptr);
1173 return;
1174 }
1175 }
1176
1177 void *value_buffer = scope_.allocate_owned(*socket->typeinfo->base_cpp_type);
1178 socket->typeinfo->get_base_cpp_value(socket->default_value, value_buffer);
1179 all_socket_values_.add_new(socket, value_buffer);
1180 }
1181
1182 void value_task__input__linked(const SocketInContext &from_socket,
1183 const SocketInContext &to_socket)
1184 {
1185 const std::optional<const void *> from_value = all_socket_values_.lookup_try(from_socket);
1186 if (!from_value.has_value()) {
1187 this->push_value_task(from_socket);
1188 return;
1189 }
1190 const void *converted_value = this->convert_type_if_necessary(
1191 *from_value, *from_socket.socket, *to_socket.socket);
1192 all_socket_values_.add_new(to_socket, converted_value);
1193 }
1194
1195 const void *convert_type_if_necessary(const void *src,
1196 const bNodeSocket &from_socket,
1197 const bNodeSocket &to_socket)
1198 {
1199 if (!src) {
1200 return nullptr;
1201 }
1202 const CPPType *from_type = from_socket.typeinfo->base_cpp_type;
1203 const CPPType *to_type = to_socket.typeinfo->base_cpp_type;
1204 if (from_type == to_type) {
1205 return src;
1206 }
1207 if (!to_type) {
1208 return nullptr;
1209 }
1210 const bke::DataTypeConversions &conversions = bke::get_implicit_type_conversions();
1211 if (!conversions.is_convertible(*from_type, *to_type)) {
1212 return nullptr;
1213 }
1214 void *dst = scope_.allocate_owned(*to_type);
1215 conversions.convert_to_uninitialized(*from_type, *to_type, src, dst);
1216 return dst;
1217 }
1218
1219 static bool switch__is_socket_selected(const SocketInContext &socket, const void *condition)
1220 {
1221 const bool is_true = *static_cast<const bool *>(condition);
1222 const int selected_index = is_true ? 2 : 1;
1223 return socket->index() == selected_index;
1224 }
1225
1226 static bool index_switch__is_socket_selected(const SocketInContext &socket,
1227 const void *condition)
1228 {
1229 const int index = *static_cast<const int *>(condition);
1230 return socket->index() == index + 1;
1231 }
1232
1233 static bool menu_switch__is_socket_selected(const SocketInContext &socket, const void *condition)
1234 {
1235 const NodeMenuSwitch &storage = *static_cast<const NodeMenuSwitch *>(
1236 socket->owner_node().storage);
1237 const int menu_value = *static_cast<const int *>(condition);
1238 const NodeEnumItem &item = storage.enum_definition.items_array[socket->index() - 1];
1239 return menu_value == item.identifier;
1240 }
1241
1242 static bool mix_node__is_socket_selected(const SocketInContext &socket, const void *condition)
1243 {
1244 const NodeShaderMix &storage = *static_cast<const NodeShaderMix *>(
1245 socket.owner_node()->storage);
1246 if (storage.data_type == SOCK_RGBA && storage.blend_type != MA_RAMP_BLEND) {
1247 return true;
1248 }
1249
1250 const bool clamp_factor = storage.clamp_factor != 0;
1251 bool only_a = false;
1252 bool only_b = false;
1253 if (storage.data_type == SOCK_VECTOR && storage.factor_mode == NODE_MIX_MODE_NON_UNIFORM) {
1254 const float3 mix_factor = *static_cast<const float3 *>(condition);
1255 if (clamp_factor) {
1256 only_a = mix_factor.x <= 0.0f && mix_factor.y <= 0.0f && mix_factor.z <= 0.0f;
1257 only_b = mix_factor.x >= 1.0f && mix_factor.y >= 1.0f && mix_factor.z >= 1.0f;
1258 }
1259 else {
1260 only_a = float3{0.0f, 0.0f, 0.0f} == mix_factor;
1261 only_b = float3{1.0f, 1.0f, 1.0f} == mix_factor;
1262 }
1263 }
1264 else {
1265 const float mix_factor = *static_cast<const float *>(condition);
1266 if (clamp_factor) {
1267 only_a = mix_factor <= 0.0f;
1268 only_b = mix_factor >= 1.0f;
1269 }
1270 else {
1271 only_a = mix_factor == 0.0f;
1272 only_b = mix_factor == 1.0f;
1273 }
1274 }
1275 if (only_a) {
1276 if (STREQ(socket->name, "B")) {
1277 return false;
1278 }
1279 }
1280 if (only_b) {
1281 if (STREQ(socket->name, "A")) {
1282 return false;
1283 }
1284 }
1285 return true;
1286 }
1287
1288 static bool shader_mix_node__is_socket_selected(const SocketInContext &socket,
1289 const void *condition)
1290 {
1291 const float mix_factor = *static_cast<const float *>(condition);
1292 if (mix_factor == 0.0f) {
1293 if (STREQ(socket->identifier, "Shader_001")) {
1294 return false;
1295 }
1296 }
1297 else if (mix_factor == 1.0f) {
1298 if (STREQ(socket->identifier, "Shader")) {
1299 return false;
1300 }
1301 }
1302 return true;
1303 }
1304
1305 void push_usage_task(const SocketInContext &socket)
1306 {
1307 usage_tasks_.push(socket);
1308 }
1309
1310 void push_value_task(const SocketInContext &socket)
1311 {
1312 value_tasks_.push(socket);
1313 }
1314
1315 void ensure_animation_data_processed(const bNodeTree &tree)
1316 {
1317 if (!trees_with_handled_animation_data_.add(&tree)) {
1318 return;
1319 }
1320 if (!tree.adt) {
1321 return;
1322 }
1323
1324 static std::regex pattern(R"#(nodes\‍["(.*)"\‍].inputs\‍[(\d+)\‍].default_value)#");
1325 MultiValueMap<StringRef, int> animated_inputs_by_node_name;
1326 auto handle_rna_path = [&](const char *rna_path) {
1327 std::cmatch match;
1328 if (!std::regex_match(rna_path, match, pattern)) {
1329 return;
1330 }
1331 const StringRef node_name{match[1].first, match[1].second - match[1].first};
1332 const int socket_index = std::stoi(match[2]);
1333 animated_inputs_by_node_name.add(node_name, socket_index);
1334 };
1335
1336 /* Gather all inputs controlled by fcurves. */
1337 if (tree.adt->action) {
1339 tree.adt->action->wrap(), tree.adt->slot_handle, [&](const FCurve &fcurve) {
1340 handle_rna_path(fcurve.rna_path);
1341 });
1342 }
1343 /* Gather all inputs controlled by drivers. */
1344 LISTBASE_FOREACH (const FCurve *, driver, &tree.adt->drivers) {
1345 handle_rna_path(driver->rna_path);
1346 }
1347
1348 /* Actually find the #bNodeSocket for each controlled input. */
1349 if (!animated_inputs_by_node_name.is_empty()) {
1350 for (const bNode *node : tree.all_nodes()) {
1351 const Span<int> animated_inputs = animated_inputs_by_node_name.lookup(node->name);
1352 const Span<const bNodeSocket *> input_sockets = node->input_sockets();
1353 for (const int socket_index : animated_inputs) {
1354 if (socket_index < 0 || socket_index >= input_sockets.size()) {
1355 /* This can happen when the animation data is not immediately updated after a socket is
1356 * removed. */
1357 continue;
1358 }
1359 const bNodeSocket &socket = *input_sockets[socket_index];
1360 animated_sockets_.add(&socket);
1361 }
1362 }
1363 }
1364 }
1365
1366 bool treat_socket_as_unknown(const SocketInContext &socket) const
1367 {
1368 if (!top_level_ignored_inputs_.has_value()) {
1369 return false;
1370 }
1371 if (socket.context) {
1372 return false;
1373 }
1374 if (socket->is_output()) {
1375 return false;
1376 }
1377 return (*top_level_ignored_inputs_)[socket->index_in_all_inputs()];
1378 }
1379};
1380
1382{
1383 return socket.socket_type == StringRef("NodeSocketMenu");
1384}
1385
1387{
1388 return socket.type == SOCK_MENU;
1389}
1390
1392{
1393 tree.ensure_topology_cache();
1394 const Span<const bNodeSocket *> all_input_sockets = tree.all_input_sockets();
1395 Array<SocketUsage> all_usages(all_input_sockets.size());
1396
1397 {
1398 /* Find actual socket usages. */
1399 SocketUsageInferencer inferencer{tree, std::nullopt};
1401 for (const int i : all_input_sockets.index_range()) {
1402 const bNodeSocket &socket = *all_input_sockets[i];
1403 all_usages[i].is_used = inferencer.is_socket_used({nullptr, &socket});
1404 }
1405 }
1406
1407 /* Find input sockets that should be hidden. */
1408 Array<bool> only_controllers_used(all_input_sockets.size(), NoInitialization{});
1409 Array<bool> all_ignored_inputs(all_input_sockets.size(), true);
1410 threading::parallel_for(all_input_sockets.index_range(), 1024, [&](const IndexRange range) {
1411 for (const int i : range) {
1412 const bNodeSocket &socket = *all_input_sockets[i];
1413 only_controllers_used[i] = !input_may_affect_visibility(socket);
1414 }
1415 });
1416 SocketUsageInferencer inferencer_all_unknown{tree, std::nullopt, all_ignored_inputs};
1417 SocketUsageInferencer inferencer_only_controllers{tree, std::nullopt, only_controllers_used};
1418 inferencer_all_unknown.mark_top_level_node_outputs_as_used();
1419 inferencer_only_controllers.mark_top_level_node_outputs_as_used();
1420 for (const int i : all_input_sockets.index_range()) {
1421 if (all_usages[i].is_used) {
1422 /* Used inputs are always visible. */
1423 continue;
1424 }
1425 const SocketInContext socket{nullptr, all_input_sockets[i]};
1426 if (inferencer_only_controllers.is_socket_used((socket))) {
1427 /* The input should be visible if it's used if only visibility-controlling inputs are
1428 * considered. */
1429 continue;
1430 }
1431 if (!inferencer_all_unknown.is_socket_used(socket)) {
1432 /* The input should be visible if it's never used, regardless of any inputs. Its usage does
1433 * not depend on any visibility-controlling input. */
1434 continue;
1435 }
1436 all_usages[i].is_visible = false;
1437 }
1438
1439 return all_usages;
1440}
1441
1443 const Span<GPointer> group_input_values,
1444 const MutableSpan<SocketUsage> r_input_usages)
1445{
1446 SocketUsage default_usage;
1447 default_usage.is_used = false;
1448 default_usage.is_visible = true;
1449 r_input_usages.fill(default_usage);
1450
1451 {
1452 /* Detect actually used inputs. */
1453 SocketUsageInferencer inferencer{group, group_input_values};
1454 for (const bNode *node : group.group_input_nodes()) {
1455 for (const int i : group.interface_inputs().index_range()) {
1456 const bNodeSocket &socket = node->output_socket(i);
1457 r_input_usages[i].is_used |= inferencer.is_socket_used({nullptr, &socket});
1458 }
1459 }
1460 }
1461 if (std::all_of(r_input_usages.begin(), r_input_usages.end(), [](const SocketUsage &usage) {
1462 return usage.is_used;
1463 }))
1464 {
1465 /* If all inputs are used, there is no need to infer visibility because all inputs should be
1466 * visible. */
1467 return;
1468 }
1469 bool visibility_controlling_input_exists = false;
1470 Array<GPointer, 32> inputs_all_unknown(group_input_values.size());
1471 Array<GPointer, 32> inputs_only_controllers = group_input_values;
1472 for (const int i : group.interface_inputs().index_range()) {
1473 const bNodeTreeInterfaceSocket &io_socket = *group.interface_inputs()[i];
1474 if (input_may_affect_visibility(io_socket)) {
1475 visibility_controlling_input_exists = true;
1476 }
1477 else {
1478 inputs_only_controllers[i] = {};
1479 }
1480 }
1481 if (!visibility_controlling_input_exists) {
1482 /* If there is no visibility controller inputs, all inputs are always visible. */
1483 return;
1484 }
1485 SocketUsageInferencer inferencer_all_unknown{group, inputs_all_unknown};
1486 SocketUsageInferencer inferencer_only_controllers{group, inputs_only_controllers};
1487 for (const int i : group.interface_inputs().index_range()) {
1488 if (r_input_usages[i].is_used) {
1489 /* Used inputs are always visible. */
1490 continue;
1491 }
1492 if (inferencer_only_controllers.is_group_input_used(i)) {
1493 /* The input should be visible if it's used if only visibility-controlling inputs are
1494 * considered. */
1495 continue;
1496 }
1497 if (!inferencer_all_unknown.is_group_input_used(i)) {
1498 /* The input should be visible if it's never used, regardless of any inputs. Its usage does
1499 * not depend on any visibility-controlling input. */
1500 continue;
1501 }
1502 r_input_usages[i].is_visible = false;
1503 }
1504}
1505
1507 Span<const bNodeSocket *> input_sockets,
1508 MutableSpan<SocketUsage> r_input_usages)
1509{
1510 BLI_assert(group.interface_inputs().size() == input_sockets.size());
1511
1512 AlignedBuffer<1024, 8> allocator_buffer;
1513 LinearAllocator<> allocator;
1514 allocator.provide_buffer(allocator_buffer);
1515
1516 Array<GPointer> input_values(input_sockets.size());
1517 for (const int i : input_sockets.index_range()) {
1518 const bNodeSocket &socket = *input_sockets[i];
1519 if (socket.is_directly_linked()) {
1520 continue;
1521 }
1522
1523 const bke::bNodeSocketType &stype = *socket.typeinfo;
1524 const CPPType *base_type = stype.base_cpp_type;
1525 if (base_type == nullptr) {
1526 continue;
1527 }
1528 void *value = allocator.allocate(*base_type);
1529 stype.get_base_cpp_value(socket.default_value, value);
1530 input_values[i] = GPointer(base_type, value);
1531 }
1532
1533 infer_group_interface_inputs_usage(group, input_values, r_input_usages);
1534
1535 for (GPointer &value : input_values) {
1536 if (const void *data = value.get()) {
1537 value.type()->destruct(const_cast<void *>(data));
1538 }
1539 }
1540}
1541
1543 const PropertiesVectorSet &properties,
1544 MutableSpan<SocketUsage> r_input_usages)
1545{
1546 const int inputs_num = group.interface_inputs().size();
1547 Array<GPointer> input_values(inputs_num);
1548 ResourceScope scope;
1549 nodes::get_geometry_nodes_input_base_values(group, properties, scope, input_values);
1551 group, input_values, r_input_usages);
1552}
1553
1554} // namespace blender::nodes::socket_usage_inference
Functions and classes to work with Actions.
Functionality to iterate an Action in various ways.
#define NODE_REROUTE
Definition BKE_node.hh:798
#define NODE_CUSTOM_GROUP
Definition BKE_node.hh:801
#define NODE_GROUP_OUTPUT
Definition BKE_node.hh:800
#define NODE_GROUP
Definition BKE_node.hh:796
#define NODE_GROUP_INPUT
Definition BKE_node.hh:799
#define SH_NODE_MIX_SHADER
#define CMP_NODE_COMPOSITE
#define SH_NODE_OUTPUT_WORLD
#define GEO_NODE_MENU_SWITCH
#define GEO_NODE_FOREACH_GEOMETRY_ELEMENT_INPUT
#define FN_NODE_INTEGER_MATH
#define SH_NODE_VECTOR_MATH
#define GEO_NODE_FOREACH_GEOMETRY_ELEMENT_OUTPUT
#define SH_NODE_MATH
#define FN_NODE_BOOLEAN_MATH
#define GEO_NODE_SWITCH
#define SH_NODE_OUTPUT_MATERIAL
#define CMP_NODE_OUTPUT_FILE
#define SH_NODE_MIX
#define GEO_NODE_CAPTURE_ATTRIBUTE
#define TEX_NODE_OUTPUT
#define GEO_NODE_REPEAT_INPUT
#define GEO_NODE_SIMULATION_INPUT
#define SH_NODE_OUTPUT_LIGHT
#define SH_NODE_OUTPUT_AOV
#define SH_NODE_OUTPUT_LINESTYLE
#define GEO_NODE_INDEX_SWITCH
#define BLI_assert(a)
Definition BLI_assert.h:46
#define LISTBASE_FOREACH(type, var, list)
#define STREQ(a, b)
float[3] Vector
struct FCurve FCurve
@ MA_RAMP_BLEND
struct NodeMenuSwitch NodeMenuSwitch
NodeVectorMathOperation
@ NODE_VECTOR_MATH_MULTIPLY
@ NODE_VECTOR_MATH_SCALE
struct NodeGeometrySimulationInput NodeGeometrySimulationInput
NodeMathOperation
@ NODE_MATH_MULTIPLY
struct NodeGeometryForeachGeometryElementInput NodeGeometryForeachGeometryElementInput
struct NodeGeometryRepeatInput NodeGeometryRepeatInput
struct bNodeLink bNodeLink
@ NODE_MIX_MODE_NON_UNIFORM
struct NodeShaderMix NodeShaderMix
struct bNode bNode
@ SOCK_VECTOR
@ SOCK_BOOLEAN
@ SOCK_CUSTOM
@ SOCK_RGBA
@ SOCK_MENU
struct bNodeTree bNodeTree
NodeBooleanMathOperation
@ NODE_BOOLEAN_MATH_IMPLY
@ NODE_BOOLEAN_MATH_AND
@ NODE_BOOLEAN_MATH_NAND
@ NODE_BOOLEAN_MATH_OR
@ NODE_BOOLEAN_MATH_NIMPLY
@ NODE_BOOLEAN_MATH_NOR
struct NodeEnumItem NodeEnumItem
NodeIntegerMathOperation
@ NODE_INTEGER_MATH_MULTIPLY
struct bNodeSocket bNodeSocket
BMesh const char void * data
Span< Value > lookup(const Key &key) const
void add(const Key &key, const Value &value)
constexpr int64_t size() const
Definition BLI_span.hh:252
int64_t size() const
void append(const T &value)
bool is_empty() const
void extend(Span< T > array)
void provide_buffer(void *buffer, const int64_t size)
void * allocate(const int64_t size, const int64_t alignment)
void add_new(const Key &key, const Value &value)
Definition BLI_map.hh:265
bool contains(const Key &key) const
Definition BLI_map.hh:353
constexpr void fill(const T &value) const
Definition BLI_span.hh:517
constexpr T * end() const
Definition BLI_span.hh:548
constexpr T * begin() const
Definition BLI_span.hh:544
constexpr int64_t size() const
Definition BLI_span.hh:252
constexpr IndexRange index_range() const
Definition BLI_span.hh:401
KDTree_3d * tree
#define ID_MISSING(_id)
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
ccl_device_inline float2 mask(const MaskType mask, const float2 a)
void foreach_fcurve_in_action_slot(Action &action, slot_handle_t handle, FunctionRef< void(FCurve &fcurve)> callback)
const DataTypeConversions & get_implicit_type_conversions()
int context(const bContext *C, const char *member, bContextDataResult *result)
static Type to_type(const eGPUType type)
MatBase< T, NumCol, NumRow > scale(const MatBase< T, NumCol, NumRow > &mat, const VectorT &scale)
void infer_group_interface_inputs_usage(const bNodeTree &group, const Span< GPointer > group_input_values, const MutableSpan< SocketUsage > r_input_usages)
static bool input_may_affect_visibility(const bNodeTreeInterfaceSocket &socket)
Array< SocketUsage > infer_all_input_sockets_usage(const bNodeTree &tree)
CustomIDVectorSet< IDProperty *, IDPropNameGetter, 16 > PropertiesVectorSet
void get_geometry_nodes_input_base_values(const bNodeTree &btree, const PropertiesVectorSet &properties, ResourceScope &scope, MutableSpan< GPointer > r_values)
void parallel_for(const IndexRange range, const int64_t grain_size, const Function &function, const TaskSizeHints &size_hints=detail::TaskSizeHints_Static(1))
Definition BLI_task.hh:93
VecBase< float, 3 > float3
static blender::bke::bNodeSocketTemplate inputs[]
NodeEnumItem * items_array
NodeEnumDefinition enum_definition
bNodeSocketTypeHandle * typeinfo
void * default_value
bNodeTypeHandle * typeinfo
Defines a socket type.
Definition BKE_node.hh:152
SocketGetCPPValueFunction get_base_cpp_value
Definition BKE_node.hh:201
const blender::CPPType * base_cpp_type
Definition BKE_node.hh:199
SocketUsageInferencer(const bNodeTree &tree, const std::optional< Span< GPointer > > tree_input_values, const std::optional< Span< bool > > top_level_ignored_inputs=std::nullopt)
float z
Definition sky_float3.h:27
float y
Definition sky_float3.h:27
float x
Definition sky_float3.h:27
i
Definition text_draw.cc:230