Blender V5.0
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 <optional>
6#include <regex>
7
9#include "NOD_menu_value.hh"
10#include "NOD_multi_function.hh"
14
15#include "DNA_anim_types.h"
16#include "DNA_material_types.h"
17#include "DNA_node_types.h"
18
22#include "BKE_node_runtime.hh"
24
25#include "ANIM_action.hh"
27
28#include "BLI_listbase.h"
29#include "BLI_stack.hh"
30
32
35 private:
36 friend InputSocketUsageParams;
37
38 bke::ComputeContextCache &compute_context_cache_;
39
41 SocketValueInferencer &value_inferencer_;
42
44 const bNodeTree &root_tree_;
45
49 Stack<SocketInContext> usage_tasks_;
50
55 Map<SocketInContext, bool> all_socket_usages_;
56
60 Stack<SocketInContext> disabled_output_tasks_;
61
65 Map<SocketInContext, bool> all_socket_disable_states_;
66
73 bool ignore_top_level_node_muting_ = false;
74
75 public:
77
79 SocketValueInferencer &value_inferencer,
80 bke::ComputeContextCache &compute_context_cache,
81 const bool ignore_top_level_node_muting)
82 : compute_context_cache_(compute_context_cache),
83 value_inferencer_(value_inferencer),
84 root_tree_(tree),
85 ignore_top_level_node_muting_(ignore_top_level_node_muting)
86 {
87 root_tree_.ensure_topology_cache();
88 root_tree_.ensure_interface_cache();
89 }
90
92 {
93 for (const bNode *node : root_tree_.all_nodes()) {
94 if (node->is_group_input()) {
95 /* Can skip these sockets, because they don't affect usage anyway, and there may be a lot
96 * of them. See #144756. */
97 continue;
98 }
99 for (const bNodeSocket *socket : node->output_sockets()) {
100 all_socket_usages_.add_new({nullptr, socket}, true);
101 }
102 }
103 }
104
105 bool is_group_input_used(const int input_i)
106 {
107 for (const bNode *node : root_tree_.group_input_nodes()) {
108 const bNodeSocket &socket = node->output_socket(input_i);
109 if (!socket.is_directly_linked()) {
110 continue;
111 }
112 const SocketInContext socket_ctx{nullptr, &socket};
113 if (this->is_socket_used(socket_ctx)) {
114 return true;
115 }
116 }
117 return false;
118 }
119
120 bool is_socket_used(const SocketInContext &socket)
121 {
122 const std::optional<bool> is_used = all_socket_usages_.lookup_try(socket);
123 if (is_used.has_value()) {
124 return *is_used;
125 }
126 if (socket->is_output() && !socket->is_directly_linked()) {
127 /* In this case we can return early because the socket can't be used if it's not linked. */
128 return false;
129 }
130 if (socket->owner_tree().has_available_link_cycle()) {
131 return false;
132 }
133
134 BLI_assert(usage_tasks_.is_empty());
135 usage_tasks_.push(socket);
136
137 while (!usage_tasks_.is_empty()) {
138 const SocketInContext &socket = usage_tasks_.peek();
139 this->usage_task(socket);
140 if (&socket == &usage_tasks_.peek()) {
141 /* The task is finished if it hasn't added any new task it depends on. */
142 usage_tasks_.pop();
143 }
144 }
145
146 return all_socket_usages_.lookup(socket);
147 }
148
150 {
151 return value_inferencer_.get_socket_value(socket);
152 }
153
154 bool is_disabled_group_output(const int output_i)
155 {
156 const bNode *group_output_node = root_tree_.group_output_node();
157 if (!group_output_node) {
158 return true;
159 }
160 const SocketInContext socket{nullptr, &group_output_node->input_socket(output_i)};
161 return this->is_disabled_output(socket);
162 }
163
165 {
166 const std::optional<bool> is_disabled = all_socket_disable_states_.lookup_try(socket);
167 if (is_disabled.has_value()) {
168 return *is_disabled;
169 }
170 if (socket->owner_tree().has_available_link_cycle()) {
171 return true;
172 }
173 BLI_assert(disabled_output_tasks_.is_empty());
174 disabled_output_tasks_.push(socket);
175
176 while (!disabled_output_tasks_.is_empty()) {
177 const SocketInContext &socket = disabled_output_tasks_.peek();
178 this->disabled_output_task(socket);
179 if (&socket == &disabled_output_tasks_.peek()) {
180 /* The task is finished if it hasn't added any new task it depends on. */
181 disabled_output_tasks_.pop();
182 }
183 }
184 return all_socket_disable_states_.lookup(socket);
185 }
186
187 private:
188 void usage_task(const SocketInContext &socket)
189 {
190 if (all_socket_usages_.contains(socket)) {
191 return;
192 }
193 const bNode &node = socket->owner_node();
194 if (!socket->is_available()) {
195 all_socket_usages_.add_new(socket, false);
196 return;
197 }
198 if (node.is_undefined() && !node.is_custom_group()) {
199 all_socket_usages_.add_new(socket, false);
200 return;
201 }
202 if (socket->is_input()) {
203 this->usage_task__input(socket);
204 }
205 else {
206 this->usage_task__output(socket);
207 }
208 }
209
210 void usage_task__input(const SocketInContext &socket)
211 {
212 const NodeInContext node = socket.owner_node();
213
214 if (node->is_muted()) {
215 const bool is_top_level = socket.context == nullptr;
216 if (!this->ignore_top_level_node_muting_ || !is_top_level) {
217 this->usage_task__input__muted_node(socket);
218 return;
219 }
220 }
221
222 switch (node->type_legacy) {
223 case NODE_GROUP:
224 case NODE_CUSTOM_GROUP: {
225 this->usage_task__input__group_node(socket);
226 break;
227 }
228 case NODE_GROUP_OUTPUT: {
229 this->usage_task__input__group_output_node(socket);
230 break;
231 }
232 case GEO_NODE_SWITCH: {
233 this->usage_task__input__generic_switch(
235 break;
236 }
238 this->usage_task__input__generic_switch(
240 break;
241 }
243 if (socket->index() == 0) {
244 this->usage_task__input__fallback(socket);
245 }
246 else {
247 this->usage_task__input__generic_switch(
249 }
250 break;
251 }
252 case SH_NODE_MIX: {
253 this->usage_task__input__generic_switch(
255 break;
256 }
257 case SH_NODE_MIX_SHADER: {
258 this->usage_task__input__generic_switch(
260 break;
261 }
263 this->usage_task__input__simulation_input_node(socket);
264 break;
265 }
267 this->usage_task__input__repeat_input_node(socket);
268 break;
269 }
271 this->usage_task__input__foreach_element_input_node(socket);
272 break;
273 }
275 this->usage_task__input__foreach_element_output_node(socket);
276 break;
277 }
279 this->usage_task__input__capture_attribute_node(socket);
280 break;
281 }
288 case TEX_NODE_OUTPUT: {
289 this->usage_task__input__output_node(socket);
290 break;
291 }
292 default: {
293 if (node->is_type("NodeEnableOutput")) {
294 this->usage_task__input__enable_output(socket);
295 break;
296 }
297 this->usage_task__input__fallback(socket);
298 break;
299 }
300 }
301 }
302
303 void usage_task__input__output_node(const SocketInContext &socket)
304 {
305 all_socket_usages_.add_new(socket, true);
306 }
307
312 void usage_task__input__generic_switch(
313 const SocketInContext &socket,
314 const FunctionRef<bool(const SocketInContext &socket, const InferenceValue &condition)>
315 is_selected_socket)
316 {
317 const NodeInContext node = socket.owner_node();
318 BLI_assert(node->input_sockets().size() >= 1);
319 BLI_assert(node->output_sockets().size() >= 1);
320
321 if (socket->type == SOCK_CUSTOM && STREQ(socket->idname, "NodeSocketVirtual")) {
322 all_socket_usages_.add_new(socket, false);
323 return;
324 }
325 const SocketInContext output_socket{socket.context,
326 get_first_available_bsocket(node->output_sockets())};
327 const std::optional<bool> output_is_used = all_socket_usages_.lookup_try(output_socket);
328 if (!output_is_used.has_value()) {
329 this->push_usage_task(output_socket);
330 return;
331 }
332 if (!*output_is_used) {
333 all_socket_usages_.add_new(socket, false);
334 return;
335 }
336 const SocketInContext condition_socket{socket.context,
337 get_first_available_bsocket(node->input_sockets())};
338 if (socket == condition_socket) {
339 all_socket_usages_.add_new(socket, true);
340 return;
341 }
342 const InferenceValue condition_value = this->get_socket_value(condition_socket);
343 if (condition_value.is_unknown()) {
344 /* The exact condition value is unknown, so any input may be used. */
345 all_socket_usages_.add_new(socket, true);
346 return;
347 }
348 const bool is_used = is_selected_socket(socket, condition_value);
349 all_socket_usages_.add_new(socket, is_used);
350 }
351
352 void usage_task__input__group_node(const SocketInContext &socket)
353 {
354 const NodeInContext node = socket.owner_node();
355 const bNodeTree *group = reinterpret_cast<const bNodeTree *>(node->id);
356 if (!group || ID_MISSING(&group->id)) {
357 all_socket_usages_.add_new(socket, false);
358 return;
359 }
360 group->ensure_topology_cache();
361 if (group->has_available_link_cycle()) {
362 all_socket_usages_.add_new(socket, false);
363 return;
364 }
365
366 /* The group node input is used if any of the matching group inputs within the group is
367 * used. */
368 const ComputeContext &group_context = compute_context_cache_.for_group_node(
369 socket.context, node->identifier, &node->owner_tree());
370 Vector<const bNodeSocket *> dependent_sockets;
371 for (const bNode *group_input_node : group->group_input_nodes()) {
372 const bNodeSocket &group_input_socket = group_input_node->output_socket(socket->index());
373 if (group_input_socket.is_directly_linked()) {
374 /* Skip unlinked group inputs to avoid further unnecessary processing of them further down
375 * the line. */
376 dependent_sockets.append(&group_input_socket);
377 }
378 }
379 this->usage_task__with_dependent_sockets(socket, dependent_sockets, {}, &group_context);
380 }
381
382 void usage_task__input__group_output_node(const SocketInContext &socket)
383 {
384 const int output_i = socket->index();
385 if (socket.context == nullptr) {
386 /* This is a final output which is always used. */
387 all_socket_usages_.add_new(socket, true);
388 return;
389 }
390 /* The group output node is used if the matching output of the parent group node is used. */
391 const bke::GroupNodeComputeContext &group_context =
392 *static_cast<const bke::GroupNodeComputeContext *>(socket.context);
393 const bNodeSocket &group_node_output = group_context.node()->output_socket(output_i);
394 this->usage_task__with_dependent_sockets(
395 socket, {&group_node_output}, {}, group_context.parent());
396 }
397
398 void usage_task__output(const SocketInContext &socket)
399 {
400 /* An output socket is used if any of the sockets it is connected to is used. */
401 Vector<const bNodeSocket *> dependent_sockets;
402 for (const bNodeLink *link : socket->directly_linked_links()) {
403 if (link->is_used()) {
404 dependent_sockets.append(link->tosock);
405 }
406 }
407 this->usage_task__with_dependent_sockets(socket, dependent_sockets, {}, socket.context);
408 }
409
410 void usage_task__input__simulation_input_node(const SocketInContext &socket)
411 {
412 const NodeInContext node = socket.owner_node();
413 const bNodeTree &tree = socket->owner_tree();
414
415 const NodeGeometrySimulationInput &storage = *static_cast<const NodeGeometrySimulationInput *>(
416 node->storage);
417 const bNode *sim_output_node = tree.node_by_id(storage.output_node_id);
418 if (!sim_output_node) {
419 all_socket_usages_.add_new(socket, false);
420 return;
421 }
422 /* Simulation inputs are also used when any of the simulation outputs are used. */
423 Vector<const bNodeSocket *, 16> dependent_sockets;
424 dependent_sockets.extend(node->output_sockets());
425 dependent_sockets.extend(sim_output_node->output_sockets());
426 this->usage_task__with_dependent_sockets(socket, dependent_sockets, {}, socket.context);
427 }
428
429 void usage_task__input__repeat_input_node(const SocketInContext &socket)
430 {
431 const NodeInContext node = socket.owner_node();
432 const bNodeTree &tree = socket->owner_tree();
433
434 const NodeGeometryRepeatInput &storage = *static_cast<const NodeGeometryRepeatInput *>(
435 node->storage);
436 const bNode *repeat_output_node = tree.node_by_id(storage.output_node_id);
437 if (!repeat_output_node) {
438 all_socket_usages_.add_new(socket, false);
439 return;
440 }
441 /* Assume that all repeat inputs are used when any of the outputs are used. This check could
442 * become more precise in the future if necessary. */
443 Vector<const bNodeSocket *, 16> dependent_sockets;
444 dependent_sockets.extend(node->output_sockets());
445 dependent_sockets.extend(repeat_output_node->output_sockets());
446 this->usage_task__with_dependent_sockets(socket, dependent_sockets, {}, socket.context);
447 }
448
449 void usage_task__input__foreach_element_output_node(const SocketInContext &socket)
450 {
451 const NodeInContext node = socket.owner_node();
452 this->usage_task__with_dependent_sockets(
453 socket, {node->output_by_identifier(socket->identifier)}, {}, socket.context);
454 }
455
456 void usage_task__input__capture_attribute_node(const SocketInContext &socket)
457 {
458 const NodeInContext node = socket.owner_node();
459 this->usage_task__with_dependent_sockets(
460 socket, {&node->output_socket(socket->index())}, {}, socket.context);
461 }
462
463 void usage_task__input__enable_output(const SocketInContext &socket)
464 {
465 const NodeInContext node = socket.owner_node();
466 const SocketInContext enable_socket = node.input_socket(0);
467 const SocketInContext output_socket = node.output_socket(0);
468 if (socket == enable_socket) {
469 this->usage_task__with_dependent_sockets(socket, {&*output_socket}, {}, socket.context);
470 }
471 else {
472 this->usage_task__with_dependent_sockets(
473 socket, {&*output_socket}, {&*enable_socket}, socket.context);
474 }
475 }
476
477 void usage_task__input__fallback(const SocketInContext &socket)
478 {
479 const SocketDeclaration *socket_decl = socket->runtime->declaration;
480 if (!socket_decl) {
481 all_socket_usages_.add_new(socket, true);
482 return;
483 }
484 if (!socket_decl->usage_inference_fn) {
485 this->usage_task__with_dependent_sockets(
486 socket, socket->owner_node().output_sockets(), {}, socket.context);
487 return;
488 }
489 InputSocketUsageParams params{
490 *owner_, socket.context, socket->owner_tree(), socket->owner_node(), *socket};
491 const std::optional<bool> is_used = (*socket_decl->usage_inference_fn)(params);
492 if (!is_used.has_value()) {
493 /* Some value was requested, come back later when that value is available. */
494 return;
495 }
496 all_socket_usages_.add_new(socket, *is_used);
497 }
498
499 void usage_task__input__foreach_element_input_node(const SocketInContext &socket)
500 {
501 const NodeInContext node = socket.owner_node();
502 const bNodeTree &tree = socket->owner_tree();
503
505 *static_cast<const NodeGeometryForeachGeometryElementInput *>(node->storage);
506 const bNode *foreach_output_node = tree.node_by_id(storage.output_node_id);
507 if (!foreach_output_node) {
508 all_socket_usages_.add_new(socket, false);
509 return;
510 }
511 Vector<const bNodeSocket *, 16> dependent_sockets;
512 if (StringRef(socket->identifier).startswith("Input_")) {
513 dependent_sockets.append(node->output_by_identifier(socket->identifier));
514 }
515 else {
516 /* The geometry and selection inputs are used whenever any of the zone outputs is used. */
517 dependent_sockets.extend(node->output_sockets());
518 dependent_sockets.extend(foreach_output_node->output_sockets());
519 }
520 this->usage_task__with_dependent_sockets(socket, dependent_sockets, {}, socket.context);
521 }
522
523 void usage_task__input__muted_node(const SocketInContext &socket)
524 {
525 const NodeInContext node = socket.owner_node();
526 Vector<const bNodeSocket *> dependent_sockets;
527 for (const bNodeLink &internal_link : node->internal_links()) {
528 if (internal_link.fromsock != socket.socket) {
529 continue;
530 }
531 dependent_sockets.append(internal_link.tosock);
532 }
533 this->usage_task__with_dependent_sockets(socket, dependent_sockets, {}, socket.context);
534 }
535
540 void usage_task__with_dependent_sockets(const SocketInContext &socket,
541 const Span<const bNodeSocket *> dependent_outputs,
542 const Span<const bNodeSocket *> condition_inputs,
543 const ComputeContext *dependent_socket_context)
544 {
545 /* Check if any of the dependent outputs are used. */
546 SocketInContext next_unknown_socket;
547 bool any_output_used = false;
548 for (const bNodeSocket *dependent_socket_ptr : dependent_outputs) {
549 const SocketInContext dependent_socket{dependent_socket_context, dependent_socket_ptr};
550 const std::optional<bool> is_used = all_socket_usages_.lookup_try(dependent_socket);
551 if (!is_used.has_value()) {
552 if (dependent_socket_ptr->is_output() && !dependent_socket_ptr->is_directly_linked()) {
553 continue;
554 }
555 if (!next_unknown_socket) {
556 next_unknown_socket = dependent_socket;
557 continue;
558 }
559 }
560 if (is_used.value_or(false)) {
561 any_output_used = true;
562 break;
563 }
564 }
565 if (next_unknown_socket) {
566 /* Create a task that checks if the next dependent socket is used. Intentionally only create
567 * a task for the very next one and not for all, because that could potentially trigger a lot
568 * of unnecessary evaluations. */
569 this->push_usage_task(next_unknown_socket);
570 return;
571 }
572 if (!any_output_used) {
573 all_socket_usages_.add_new(socket, false);
574 return;
575 }
576 bool all_condition_inputs_true = true;
577 for (const bNodeSocket *condition_input_ptr : condition_inputs) {
578 const SocketInContext condition_input{dependent_socket_context, condition_input_ptr};
579 const InferenceValue condition_value = this->get_socket_value(condition_input);
580 if (!condition_value.is_primitive_value()) {
581 /* The condition is not known, so it may be true. */
582 continue;
583 }
584 BLI_assert(condition_input_ptr->type == SOCK_BOOLEAN);
585 if (!condition_value.get_primitive<bool>()) {
586 all_condition_inputs_true = false;
587 break;
588 }
589 }
590 all_socket_usages_.add_new(socket, all_condition_inputs_true);
591 }
592
593 void push_usage_task(const SocketInContext &socket)
594 {
595 usage_tasks_.push(socket);
596 }
597
598 void disabled_output_task(const SocketInContext &socket)
599 {
600 if (all_socket_disable_states_.contains(socket)) {
601 return;
602 }
603 const bNode &node = socket->owner_node();
604 if (!socket->is_available()) {
605 all_socket_disable_states_.add_new(socket, true);
606 return;
607 }
608 if (node.is_undefined() && !node.is_custom_group()) {
609 all_socket_disable_states_.add_new(socket, true);
610 return;
611 }
612 if (socket->is_input()) {
613 this->disabled_output_task__input(socket);
614 }
615 else {
616 this->disabled_output_task__output(socket);
617 }
618 }
619
620 void disabled_output_task__input(const SocketInContext &socket)
621 {
622 const Span<const bNodeLink *> links = socket->directly_linked_links();
623 const bNodeLink *single_link = links.size() == 1 && links[0]->is_used() ? links[0] : nullptr;
624 if (links.size() != 1 || !links[0]->is_used()) {
625 /* The socket is not linked, thus it is not disabled. */
626 all_socket_disable_states_.add_new(socket, false);
627 return;
628 }
629 const SocketInContext origin_socket{socket.context, single_link->fromsock};
630 this->disabled_output_task__with_origin_socket(socket, origin_socket);
631 }
632
633 void disabled_output_task__output(const SocketInContext &socket)
634 {
635 const NodeInContext node = socket.owner_node();
636 if (node->is_muted()) {
637 const bool is_top_level = socket.context == nullptr;
638 if (!this->ignore_top_level_node_muting_ || !is_top_level) {
639 this->disabled_output_task__output__muted_node(socket);
640 return;
641 }
642 }
643
644 switch (node->type_legacy) {
645 case NODE_GROUP:
646 case NODE_CUSTOM_GROUP: {
647 this->disabled_output_task__output__group_node(socket);
648 break;
649 }
650 case NODE_REROUTE: {
651 this->disabled_output_task__with_origin_socket(socket, node.input_socket(0));
652 break;
653 }
654 default: {
655 if (node->is_type("NodeEnableOutput")) {
656 this->disabled_output_task__output__enable_output_node(socket);
657 break;
658 }
659 /* By default, all output sockets are enabled unless they are explicitly disabled by some
660 * rule above. */
661 all_socket_disable_states_.add_new(socket, false);
662 break;
663 }
664 }
665 }
666
667 void disabled_output_task__output__muted_node(const SocketInContext &socket)
668 {
669 const NodeInContext node = socket.owner_node();
670 for (const bNodeLink &internal_link : node->internal_links()) {
671 if (internal_link.tosock != socket.socket) {
672 continue;
673 }
674 this->disabled_output_task__with_origin_socket(socket,
675 {socket.context, internal_link.fromsock});
676 return;
677 }
678 all_socket_disable_states_.add_new(socket, false);
679 }
680
681 void disabled_output_task__output__group_node(const SocketInContext &socket)
682 {
683 const NodeInContext node = socket.owner_node();
684 const bNodeTree *group = reinterpret_cast<const bNodeTree *>(node->id);
685 if (!group || ID_MISSING(&group->id)) {
686 all_socket_disable_states_.add_new(socket, false);
687 return;
688 }
689 group->ensure_topology_cache();
690 if (group->has_available_link_cycle()) {
691 all_socket_disable_states_.add_new(socket, false);
692 return;
693 }
694 const bNode *group_output_node = group->group_output_node();
695 if (!group_output_node) {
696 all_socket_disable_states_.add_new(socket, false);
697 return;
698 }
699 const ComputeContext &group_context = compute_context_cache_.for_group_node(
700 socket.context, node->identifier, &node->owner_tree());
701 const SocketInContext origin_socket{&group_context,
702 &group_output_node->input_socket(socket->index())};
703 this->disabled_output_task__with_origin_socket(socket, origin_socket);
704 }
705
706 void disabled_output_task__output__enable_output_node(const SocketInContext &socket)
707 {
708 const NodeInContext node = socket.owner_node();
709 const SocketInContext enable_socket = node.input_socket(0);
710 const InferenceValue enable_value = this->get_socket_value(enable_socket);
711 const std::optional<bool> is_enabled_opt = enable_value.get_if_primitive<bool>();
712 const bool is_enabled = is_enabled_opt.value_or(true);
713 all_socket_disable_states_.add_new(socket, !is_enabled);
714 }
715
716 void disabled_output_task__with_origin_socket(const SocketInContext &socket,
717 const SocketInContext &origin_socket)
718 {
719 const std::optional<bool> is_disabled = all_socket_disable_states_.lookup_try(origin_socket);
720 if (is_disabled.has_value()) {
721 all_socket_disable_states_.add_new(socket, *is_disabled);
722 return;
723 }
724 this->push_disabled_output_task(origin_socket);
725 }
726
727 void push_disabled_output_task(const SocketInContext &socket)
728 {
729 disabled_output_tasks_.push(socket);
730 }
731
732 static const bNodeSocket *get_first_available_bsocket(const Span<const bNodeSocket *> sockets)
733 {
734 for (const bNodeSocket *socket : sockets) {
735 if (socket->is_available()) {
736 return socket;
737 }
738 }
739 return nullptr;
740 }
741};
742
744 ResourceScope &scope,
745 SocketValueInferencer &value_inferencer,
746 bke::ComputeContextCache &compute_context_cache,
747 const bool ignore_top_level_node_muting)
748 : impl_(scope.construct<SocketUsageInferencerImpl>(
749 tree, value_inferencer, compute_context_cache, ignore_top_level_node_muting))
750{
751 impl_.owner_ = this;
752}
753
755{
756 return socket.socket_type == StringRef("NodeSocketMenu");
757}
758
759static bool input_may_affect_visibility(const bNodeSocket &socket)
760{
761 return socket.type == SOCK_MENU;
762}
763
765{
766 tree.ensure_topology_cache();
767 const Span<const bNodeSocket *> all_input_sockets = tree.all_input_sockets();
768 const Span<const bNodeSocket *> all_output_sockets = tree.all_output_sockets();
769 Array<SocketUsage> all_usages(tree.all_sockets().size());
770
771 if (tree.has_available_link_cycle()) {
772 return all_usages;
773 }
774
775 ResourceScope scope;
776 bke::ComputeContextCache compute_context_cache;
777
778 const bool ignore_top_level_node_muting = true;
779
780 {
781 /* Find actual socket usages. */
782 SocketValueInferencer value_inferencer{tree, scope, compute_context_cache};
783 SocketUsageInferencer usage_inferencer{
784 tree, scope, value_inferencer, compute_context_cache, ignore_top_level_node_muting};
785 usage_inferencer.mark_top_level_node_outputs_as_used();
786 for (const bNodeSocket *socket : all_input_sockets) {
787 all_usages[socket->index_in_tree()].is_used = usage_inferencer.is_socket_used(
788 {nullptr, socket});
789 }
790 }
791
792 /* Find input sockets that should be hidden. */
793 Array<bool> only_controllers_used(all_input_sockets.size(), NoInitialization{});
794 Array<bool> all_ignored_inputs(all_input_sockets.size(), true);
795 threading::parallel_for(all_input_sockets.index_range(), 1024, [&](const IndexRange range) {
796 for (const int i : range) {
797 const bNodeSocket &socket = *all_input_sockets[i];
798 only_controllers_used[i] = !input_may_affect_visibility(socket);
799 }
800 });
801 SocketValueInferencer value_inferencer_all_unknown{
802 tree, scope, compute_context_cache, nullptr, all_ignored_inputs};
803 SocketUsageInferencer usage_inferencer_all_unknown{tree,
804 scope,
805 value_inferencer_all_unknown,
806 compute_context_cache,
807 ignore_top_level_node_muting};
808 SocketValueInferencer value_inferencer_only_controllers{
809 tree, scope, compute_context_cache, nullptr, only_controllers_used};
810 SocketUsageInferencer usage_inferencer_only_controllers{tree,
811 scope,
812 value_inferencer_only_controllers,
813 compute_context_cache,
814 ignore_top_level_node_muting};
815 usage_inferencer_all_unknown.mark_top_level_node_outputs_as_used();
816 usage_inferencer_only_controllers.mark_top_level_node_outputs_as_used();
817 for (const bNodeSocket *socket : all_input_sockets) {
818 SocketUsage &usage = all_usages[socket->index_in_tree()];
819 if (usage.is_used) {
820 /* Used inputs are always visible. */
821 continue;
822 }
823 const SocketInContext socket_ctx{nullptr, socket};
824 if (usage_inferencer_only_controllers.is_socket_used(socket_ctx)) {
825 /* The input should be visible if it's used if only visibility-controlling inputs are
826 * considered. */
827 continue;
828 }
829 if (!usage_inferencer_all_unknown.is_socket_used(socket_ctx)) {
830 /* The input should be visible if it's never used, regardless of any inputs. Its usage does
831 * not depend on any visibility-controlling input. */
832 continue;
833 }
834 usage.is_visible = false;
835 }
836 for (const bNodeSocket *socket : all_output_sockets) {
837 const bNode &node = socket->owner_node();
838 if (node.is_group_input()) {
839 continue;
840 }
841 const SocketInContext socket_ctx{nullptr, socket};
842 if (usage_inferencer_only_controllers.is_disabled_output(socket_ctx)) {
843 SocketUsage &usage = all_usages[socket->index_in_tree()];
844 usage.is_visible = false;
845 }
846 }
847
848 return all_usages;
849}
850
852 const Span<InferenceValue> group_input_values,
853 const MutableSpan<SocketUsage> r_input_usages,
854 const std::optional<MutableSpan<SocketUsage>> r_output_usages)
855{
856 SocketUsage default_usage;
857 default_usage.is_used = false;
858 default_usage.is_visible = true;
859 r_input_usages.fill(default_usage);
860 if (r_output_usages) {
861 r_output_usages->fill({true, true});
862 }
863
864 ResourceScope scope;
865 bke::ComputeContextCache compute_context_cache;
866
867 {
868 /* Detect actually used inputs. */
869 const auto get_input_value = [&](const int group_input_i) {
870 return group_input_values[group_input_i];
871 };
872 SocketValueInferencer value_inferencer{group, scope, compute_context_cache, get_input_value};
873 SocketUsageInferencer usage_inferencer{group, scope, value_inferencer, compute_context_cache};
874 for (const int i : group.interface_inputs().index_range()) {
875 r_input_usages[i].is_used |= usage_inferencer.is_group_input_used(i);
876 }
877 }
878 bool visibility_controlling_input_exists = false;
879 for (const int i : group.interface_inputs().index_range()) {
880 const bNodeTreeInterfaceSocket &io_socket = *group.interface_inputs()[i];
881 if (input_may_affect_visibility(io_socket)) {
882 visibility_controlling_input_exists = true;
883 }
884 }
885 if (!visibility_controlling_input_exists) {
886 /* If there is no visibility controller inputs, all inputs are always visible. */
887 return;
888 }
889 SocketValueInferencer value_inferencer_all_unknown{group, scope, compute_context_cache};
890 SocketUsageInferencer usage_inferencer_all_unknown{
891 group, scope, value_inferencer_all_unknown, compute_context_cache};
892 const auto get_only_controllers_input_value = [&](const int group_input_i) {
893 const bNodeTreeInterfaceSocket &io_socket = *group.interface_inputs()[group_input_i];
894 if (input_may_affect_visibility(io_socket)) {
895 return group_input_values[group_input_i];
896 }
898 };
899 SocketValueInferencer value_inferencer_only_controllers{
900 group, scope, compute_context_cache, get_only_controllers_input_value};
901 SocketUsageInferencer usage_inferencer_only_controllers{
902 group, scope, value_inferencer_only_controllers, compute_context_cache};
903 for (const int i : group.interface_inputs().index_range()) {
904 if (r_input_usages[i].is_used) {
905 /* Used inputs are always visible. */
906 continue;
907 }
908 if (usage_inferencer_only_controllers.is_group_input_used(i)) {
909 /* The input should be visible if it's used if only visibility-controlling inputs are
910 * considered. */
911 continue;
912 }
913 if (!usage_inferencer_all_unknown.is_group_input_used(i)) {
914 /* The input should be visible if it's never used, regardless of any inputs. Its usage does
915 * not depend on any visibility-controlling input. */
916 continue;
917 }
918 r_input_usages[i].is_visible = false;
919 }
920 if (r_output_usages) {
921 for (const int i : group.interface_outputs().index_range()) {
922 if (usage_inferencer_only_controllers.is_disabled_group_output(i)) {
923 SocketUsage &usage = (*r_output_usages)[i];
924 usage.is_used = false;
925 usage.is_visible = false;
926 }
927 }
928 }
929}
930
932 Span<const bNodeSocket *> input_sockets,
933 MutableSpan<SocketUsage> r_input_usages)
934{
935 BLI_assert(group.interface_inputs().size() == input_sockets.size());
936
937 AlignedBuffer<1024, 8> allocator_buffer;
938 ResourceScope scope;
939 scope.allocator().provide_buffer(allocator_buffer);
940
941 Array<InferenceValue> input_values(input_sockets.size(), InferenceValue::Unknown());
942 for (const int i : input_sockets.index_range()) {
943 const bNodeSocket &socket = *input_sockets[i];
944 if (socket.is_directly_linked()) {
945 continue;
946 }
947
948 const bke::bNodeSocketType &stype = *socket.typeinfo;
949 const CPPType *base_type = stype.base_cpp_type;
950 if (base_type == nullptr) {
951 continue;
952 }
953 void *value = scope.allocate_owned(*base_type);
954 stype.get_base_cpp_value(socket.default_value, value);
955 input_values[i] = InferenceValue::from_primitive(value);
956 }
957
958 infer_group_interface_usage(group, input_values, r_input_usages, {}); // TODO
959}
960
962 const IDProperty *properties,
963 MutableSpan<SocketUsage> r_input_usages,
964 std::optional<MutableSpan<SocketUsage>> r_output_usages)
965{
966 ResourceScope scope;
967 const Vector<InferenceValue> group_input_values =
968 nodes::get_geometry_nodes_input_inference_values(group, properties, scope);
970 group, group_input_values, r_input_usages, r_output_usages);
971}
972
974 const ComputeContext *compute_context,
975 const bNodeTree &tree,
976 const bNode &node,
977 const bNodeSocket &socket)
978 : inferencer_(inferencer),
979 compute_context_(compute_context),
980 tree(tree),
981 node(node),
983{
984}
985
987{
988 const SocketInContext input_socket{compute_context_, this->node.input_by_identifier(identifier)};
989 return inferencer_.impl_.get_socket_value(input_socket);
990}
991
993{
994 const bNodeSocket *first_missing = nullptr;
995 for (const bNodeSocket *output_socket : this->node.output_sockets()) {
996 if (const std::optional<bool> is_used = inferencer_.impl_.all_socket_usages_.lookup_try(
997 {compute_context_, output_socket}))
998 {
999 if (*is_used) {
1000 return true;
1001 }
1002 }
1003 else {
1004 first_missing = output_socket;
1005 }
1006 }
1007 if (first_missing) {
1008 inferencer_.impl_.push_usage_task({compute_context_, first_missing});
1009 return std::nullopt;
1010 }
1011 return false;
1012}
1013
1015 const int enum_value) const
1016{
1017 BLI_assert(this->node.input_by_identifier(identifier)->type == SOCK_MENU);
1018 const InferenceValue value = this->get_input(identifier);
1019 if (!value.is_primitive_value()) {
1020 /* The value is unknown, so it may be the requested enum value. */
1021 return true;
1022 }
1023 return value.get_primitive<MenuValue>().value == enum_value;
1024}
1025
1027{
1028 impl_.mark_top_level_node_outputs_as_used();
1029}
1030
1032{
1033 return impl_.is_group_input_used(input_i);
1034}
1035
1037{
1038 return impl_.is_socket_used(socket);
1039}
1040
1042{
1043 return impl_.is_disabled_group_output(output_i);
1044}
1045
1047{
1048 return impl_.is_disabled_output(socket);
1049}
1050
1051} // 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:813
#define NODE_CUSTOM_GROUP
Definition BKE_node.hh:816
#define NODE_GROUP_OUTPUT
Definition BKE_node.hh:815
#define NODE_GROUP
Definition BKE_node.hh:811
#define SH_NODE_MIX_SHADER
#define SH_NODE_OUTPUT_WORLD
#define GEO_NODE_MENU_SWITCH
#define GEO_NODE_FOREACH_GEOMETRY_ELEMENT_INPUT
#define GEO_NODE_FOREACH_GEOMETRY_ELEMENT_OUTPUT
#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 STREQ(a, b)
#define ID_MISSING(_id)
Definition DNA_ID.h:692
struct NodeGeometrySimulationInput NodeGeometrySimulationInput
struct NodeGeometryForeachGeometryElementInput NodeGeometryForeachGeometryElementInput
struct NodeGeometryRepeatInput NodeGeometryRepeatInput
struct bNodeLink bNodeLink
struct bNode bNode
@ SOCK_BOOLEAN
@ SOCK_CUSTOM
@ SOCK_MENU
struct bNodeTree bNodeTree
struct bNodeSocket bNodeSocket
static bool is_disabled
std::unique_ptr< InputSocketUsageInferenceFn > usage_inference_fn
constexpr int64_t size() const
Definition BLI_span.hh:252
void append(const T &value)
void extend(Span< T > array)
void provide_buffer(void *buffer, const int64_t size)
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
void * allocate_owned(const CPPType &type)
LinearAllocator & allocator()
constexpr int64_t size() const
Definition BLI_span.hh:252
constexpr IndexRange index_range() const
Definition BLI_span.hh:401
static InferenceValue from_primitive(const void *value)
bool menu_input_may_be(StringRef identifier, int enum_value) const
InputSocketUsageParams(SocketUsageInferencer &inferencer, const ComputeContext *compute_context, const bNodeTree &tree, const bNode &node, const bNodeSocket &socket)
SocketUsageInferencerImpl(const bNodeTree &tree, SocketValueInferencer &value_inferencer, bke::ComputeContextCache &compute_context_cache, const bool ignore_top_level_node_muting)
SocketUsageInferencer(const bNodeTree &tree, ResourceScope &scope, SocketValueInferencer &value_inferencer, bke::ComputeContextCache &compute_context_cache, bool ignore_top_level_node_muting=false)
KDTree_3d * tree
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
bool is_enabled(const Sculpt &sd, const Object &object, const Brush *br)
Array< SocketUsage > infer_all_sockets_usage(const bNodeTree &tree)
static bool input_may_affect_visibility(const bNodeTreeInterfaceSocket &socket)
void infer_group_interface_inputs_usage(const bNodeTree &group, Span< const bNodeSocket * > input_sockets, MutableSpan< SocketUsage > r_input_usages)
void infer_group_interface_usage(const bNodeTree &group, const Span< InferenceValue > group_input_values, const MutableSpan< SocketUsage > r_input_usages, const std::optional< MutableSpan< SocketUsage > > r_output_usages)
bool is_socket_selected__shader_mix_node(const SocketInContext &socket, const InferenceValue &condition)
bool is_socket_selected__mix_node(const SocketInContext &socket, const InferenceValue &condition)
bool is_socket_selected__menu_switch(const SocketInContext &socket, const InferenceValue &condition)
bool is_socket_selected__index_switch(const SocketInContext &socket, const InferenceValue &condition)
bool is_socket_selected__switch(const SocketInContext &socket, const InferenceValue &condition)
Vector< InferenceValue > get_geometry_nodes_input_inference_values(const bNodeTree &btree, const IDProperty *properties, ResourceScope &scope)
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
static bool is_disabled(const Scene *, ModifierData *md, bool)
bNodeSocketTypeHandle * typeinfo
void * default_value
Defines a socket type.
Definition BKE_node.hh:158
SocketGetCPPValueFunction get_base_cpp_value
Definition BKE_node.hh:207
const blender::CPPType * base_cpp_type
Definition BKE_node.hh:205
i
Definition text_draw.cc:230