From 3aa674966ff509c0514b7f486e1d480daa908590 Mon Sep 17 00:00:00 2001 From: Omar Emara Date: Wed, 15 Oct 2025 15:57:56 +0200 Subject: [PATCH] Nodes: Support disabling outputs in built-in nodes This patch adds support for disabling outputs in built-in nodes based on menu inputs using a custom usage_inference callback. This just essentially calls usage_inference_fn for outputs as well, while it was only being called for inputs. Additionally, the usage_by_menu methods were adjusted to only consider outputs if it is being called on inputs. Some types were renamed to be more general, and not just for inputs. Pull Request: https://projects.blender.org/blender/blender/pulls/148132 --- source/blender/nodes/NOD_node_declaration.hh | 16 +++---- .../nodes/NOD_socket_usage_inference.hh | 14 +++--- .../nodes/NOD_socket_usage_inference_fwd.hh | 2 +- .../nodes/node_composite_channel_matte.cc | 8 ++-- .../blender/nodes/intern/node_declaration.cc | 48 ++++++++++--------- .../nodes/intern/socket_usage_inference.cc | 37 +++++++++----- 6 files changed, 72 insertions(+), 53 deletions(-) diff --git a/source/blender/nodes/NOD_node_declaration.hh b/source/blender/nodes/NOD_node_declaration.hh index 06b4e982a68..e791002f24b 100644 --- a/source/blender/nodes/NOD_node_declaration.hh +++ b/source/blender/nodes/NOD_node_declaration.hh @@ -197,8 +197,8 @@ struct CustomSocketDrawParams { }; using CustomSocketDrawFn = std::function; -using InputSocketUsageInferenceFn = std::function( - const socket_usage_inference::InputSocketUsageParams ¶ms)>; +using SocketUsageInferenceFn = + std::function(const socket_usage_inference::SocketUsageParams ¶ms)>; /** * Describes a single input or output socket. This is subclassed for different socket types. @@ -267,10 +267,10 @@ class SocketDeclaration : public ItemDeclaration { */ std::unique_ptr custom_draw_fn; /** - * Determines whether this input socket is used based on other input values and based on which - * outputs are used. + * Determines whether this socket is used based on other input values and based on which outputs + * are used. */ - std::unique_ptr usage_inference_fn; + std::unique_ptr usage_inference_fn; friend NodeDeclarationBuilder; friend class BaseSocketDeclarationBuilder; @@ -427,10 +427,10 @@ class BaseSocketDeclarationBuilder { BaseSocketDeclarationBuilder &custom_draw(CustomSocketDrawFn fn); /** - * Provide a function that determines whether this input socket is used based on other input - * values and based on which outputs are used. + * Provide a function that determines whether this socket is used based on other input values and + * based on which outputs are used. */ - BaseSocketDeclarationBuilder &usage_inference(InputSocketUsageInferenceFn fn); + BaseSocketDeclarationBuilder &usage_inference(SocketUsageInferenceFn fn); /** * Utility method for the case when the node has a single menu input and this socket is only used diff --git a/source/blender/nodes/NOD_socket_usage_inference.hh b/source/blender/nodes/NOD_socket_usage_inference.hh index 113ffbbacd5..a98e8af2200 100644 --- a/source/blender/nodes/NOD_socket_usage_inference.hh +++ b/source/blender/nodes/NOD_socket_usage_inference.hh @@ -30,7 +30,7 @@ class SocketUsageInferencer { private: SocketUsageInferencerImpl &impl_; - friend class InputSocketUsageParams; + friend class SocketUsageParams; public: SocketUsageInferencer(const bNodeTree &tree, @@ -51,7 +51,7 @@ class SocketUsageInferencer { void mark_top_level_node_outputs_as_used(); }; -class InputSocketUsageParams { +class SocketUsageParams { private: SocketUsageInferencer &inferencer_; const ComputeContext *compute_context_ = nullptr; @@ -61,11 +61,11 @@ class InputSocketUsageParams { const bNode &node; const bNodeSocket &socket; - InputSocketUsageParams(SocketUsageInferencer &inferencer, - const ComputeContext *compute_context, - const bNodeTree &tree, - const bNode &node, - const bNodeSocket &socket); + SocketUsageParams(SocketUsageInferencer &inferencer, + const ComputeContext *compute_context, + const bNodeTree &tree, + const bNode &node, + const bNodeSocket &socket); /** * Get an the statically known input value for the given socket identifier. The value may be diff --git a/source/blender/nodes/NOD_socket_usage_inference_fwd.hh b/source/blender/nodes/NOD_socket_usage_inference_fwd.hh index 3320b999708..dd1f4f2da16 100644 --- a/source/blender/nodes/NOD_socket_usage_inference_fwd.hh +++ b/source/blender/nodes/NOD_socket_usage_inference_fwd.hh @@ -6,7 +6,7 @@ namespace blender::nodes::socket_usage_inference { -class InputSocketUsageParams; +class SocketUsageParams; struct SocketUsage { bool is_used = true; diff --git a/source/blender/nodes/composite/nodes/node_composite_channel_matte.cc b/source/blender/nodes/composite/nodes/node_composite_channel_matte.cc index 24425f85641..dc7486ce3d7 100644 --- a/source/blender/nodes/composite/nodes/node_composite_channel_matte.cc +++ b/source/blender/nodes/composite/nodes/node_composite_channel_matte.cc @@ -181,7 +181,7 @@ static void cmp_node_channel_matte_declare(NodeDeclarationBuilder &b) CMP_NODE_CHANNEL_MATTE_CS_RGB; }) .usage_inference( - [](const socket_usage_inference::InputSocketUsageParams ¶ms) -> std::optional { + [](const socket_usage_inference::SocketUsageParams ¶ms) -> std::optional { return params.menu_input_may_be("Limit Method", CMP_NODE_CHANNEL_MATTE_LIMIT_ALGORITHM_SINGLE) && params.menu_input_may_be("Color Space", CMP_NODE_CHANNEL_MATTE_CS_RGB); @@ -203,7 +203,7 @@ static void cmp_node_channel_matte_declare(NodeDeclarationBuilder &b) CMP_NODE_CHANNEL_MATTE_CS_HSV; }) .usage_inference( - [](const socket_usage_inference::InputSocketUsageParams ¶ms) -> std::optional { + [](const socket_usage_inference::SocketUsageParams ¶ms) -> std::optional { return params.menu_input_may_be("Limit Method", CMP_NODE_CHANNEL_MATTE_LIMIT_ALGORITHM_SINGLE) && params.menu_input_may_be("Color Space", CMP_NODE_CHANNEL_MATTE_CS_HSV); @@ -225,7 +225,7 @@ static void cmp_node_channel_matte_declare(NodeDeclarationBuilder &b) CMP_NODE_CHANNEL_MATTE_CS_YUV; }) .usage_inference( - [](const socket_usage_inference::InputSocketUsageParams ¶ms) -> std::optional { + [](const socket_usage_inference::SocketUsageParams ¶ms) -> std::optional { return params.menu_input_may_be("Limit Method", CMP_NODE_CHANNEL_MATTE_LIMIT_ALGORITHM_SINGLE) && params.menu_input_may_be("Color Space", CMP_NODE_CHANNEL_MATTE_CS_YUV); @@ -247,7 +247,7 @@ static void cmp_node_channel_matte_declare(NodeDeclarationBuilder &b) CMP_NODE_CHANNEL_MATTE_CS_YCC; }) .usage_inference( - [](const socket_usage_inference::InputSocketUsageParams ¶ms) -> std::optional { + [](const socket_usage_inference::SocketUsageParams ¶ms) -> std::optional { return params.menu_input_may_be("Limit Method", CMP_NODE_CHANNEL_MATTE_LIMIT_ALGORITHM_SINGLE) && params.menu_input_may_be("Color Space", CMP_NODE_CHANNEL_MATTE_CS_YCC); diff --git a/source/blender/nodes/intern/node_declaration.cc b/source/blender/nodes/intern/node_declaration.cc index 6c0c650d8f2..37b91c9a156 100644 --- a/source/blender/nodes/intern/node_declaration.cc +++ b/source/blender/nodes/intern/node_declaration.cc @@ -769,9 +769,9 @@ BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder::custom_draw(CustomSo } BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder::usage_inference( - InputSocketUsageInferenceFn fn) + SocketUsageInferenceFn fn) { - decl_base_->usage_inference_fn = std::make_unique(std::move(fn)); + decl_base_->usage_inference_fn = std::make_unique(std::move(fn)); return *this; } @@ -810,19 +810,21 @@ BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder::usage_by_single_menu bNodeSocketValueMenu *value = socket.default_value_typed(); value->value = menu_value; }); - this->usage_inference([menu_value](const socket_usage_inference::InputSocketUsageParams ¶ms) + this->usage_inference([menu_value](const socket_usage_inference::SocketUsageParams ¶ms) -> std::optional { const bNodeSocket &socket = find_single_menu_input(params.node); - if (const std::optional any_output_used = params.any_output_is_used()) { - if (!*any_output_used) { - /* If no output is used, none of the inputs is used either. */ - return false; + if (params.socket.is_input()) { + if (const std::optional any_output_used = params.any_output_is_used()) { + if (!*any_output_used) { + /* If no output is used, none of the inputs is used either. */ + return false; + } + } + else { + /* It's not known if any output is used yet. This function will be called again once new + * information about output usages is available. */ + return std::nullopt; } - } - else { - /* It's not known if any output is used yet. This function will be called again once new - * information about output usages is available. */ - return std::nullopt; } return params.menu_input_may_be(socket.identifier, menu_value); }); @@ -850,17 +852,19 @@ BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder::usage_by_menu( }); this->usage_inference( [menu_input_identifier, menu_values]( - const socket_usage_inference::InputSocketUsageParams ¶ms) -> std::optional { - if (const std::optional any_output_used = params.any_output_is_used()) { - if (!*any_output_used) { - /* If no output is used, none of the inputs is used either. */ - return false; + const socket_usage_inference::SocketUsageParams ¶ms) -> std::optional { + if (params.socket.is_input()) { + if (const std::optional any_output_used = params.any_output_is_used()) { + if (!*any_output_used) { + /* If no output is used, none of the inputs is used either. */ + return false; + } + } + else { + /* It's not known if any output is used yet. This function will be called again once + * new information about output usages is available. */ + return std::nullopt; } - } - else { - /* It's not known if any output is used yet. This function will be called again once new - * information about output usages is available. */ - return std::nullopt; } /* Check if the menu might be any of the given values. */ diff --git a/source/blender/nodes/intern/socket_usage_inference.cc b/source/blender/nodes/intern/socket_usage_inference.cc index 70cfd1b2d3c..09dd7b83c93 100644 --- a/source/blender/nodes/intern/socket_usage_inference.cc +++ b/source/blender/nodes/intern/socket_usage_inference.cc @@ -33,7 +33,7 @@ namespace blender::nodes::socket_usage_inference { /** Utility class to simplify passing global state into all the functions during inferencing. */ class SocketUsageInferencerImpl { private: - friend InputSocketUsageParams; + friend SocketUsageParams; bke::ComputeContextCache &compute_context_cache_; @@ -486,7 +486,7 @@ class SocketUsageInferencerImpl { socket, socket->owner_node().output_sockets(), {}, socket.context); return; } - InputSocketUsageParams params{ + SocketUsageParams params{ *owner_, socket.context, socket->owner_tree(), socket->owner_node(), *socket}; const std::optional is_used = (*socket_decl->usage_inference_fn)(params); if (!is_used.has_value()) { @@ -656,6 +656,22 @@ class SocketUsageInferencerImpl { this->disabled_output_task__output__enable_output_node(socket); break; } + + const SocketDeclaration *socket_declaration = socket->runtime->declaration; + if (socket_declaration && socket_declaration->usage_inference_fn) { + SocketUsageParams params{ + *owner_, socket.context, socket->owner_tree(), socket->owner_node(), *socket}; + const std::optional is_used = (*socket_declaration->usage_inference_fn)(params); + if (!is_used.has_value()) { + /* Some value was requested, come back later when that value is available. */ + return; + } + if (!*is_used) { + all_socket_disable_states_.add_new(socket, true); + break; + } + } + /* By default, all output sockets are enabled unless they are explicitly disabled by some * rule above. */ all_socket_disable_states_.add_new(socket, false); @@ -969,11 +985,11 @@ void infer_group_interface_usage(const bNodeTree &group, group, group_input_values, r_input_usages, r_output_usages); } -InputSocketUsageParams::InputSocketUsageParams(SocketUsageInferencer &inferencer, - const ComputeContext *compute_context, - const bNodeTree &tree, - const bNode &node, - const bNodeSocket &socket) +SocketUsageParams::SocketUsageParams(SocketUsageInferencer &inferencer, + const ComputeContext *compute_context, + const bNodeTree &tree, + const bNode &node, + const bNodeSocket &socket) : inferencer_(inferencer), compute_context_(compute_context), tree(tree), @@ -982,13 +998,13 @@ InputSocketUsageParams::InputSocketUsageParams(SocketUsageInferencer &inferencer { } -InferenceValue InputSocketUsageParams::get_input(const StringRef identifier) const +InferenceValue SocketUsageParams::get_input(const StringRef identifier) const { const SocketInContext input_socket{compute_context_, this->node.input_by_identifier(identifier)}; return inferencer_.impl_.get_socket_value(input_socket); } -std::optional InputSocketUsageParams::any_output_is_used() const +std::optional SocketUsageParams::any_output_is_used() const { const bNodeSocket *first_missing = nullptr; for (const bNodeSocket *output_socket : this->node.output_sockets()) { @@ -1010,8 +1026,7 @@ std::optional InputSocketUsageParams::any_output_is_used() const return false; } -bool InputSocketUsageParams::menu_input_may_be(const StringRef identifier, - const int enum_value) const +bool SocketUsageParams::menu_input_may_be(const StringRef identifier, const int enum_value) const { BLI_assert(this->node.input_by_identifier(identifier)->type == SOCK_MENU); const InferenceValue value = this->get_input(identifier);