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
This commit is contained in:
@@ -197,8 +197,8 @@ struct CustomSocketDrawParams {
|
||||
};
|
||||
|
||||
using CustomSocketDrawFn = std::function<void(CustomSocketDrawParams ¶ms)>;
|
||||
using InputSocketUsageInferenceFn = std::function<std::optional<bool>(
|
||||
const socket_usage_inference::InputSocketUsageParams ¶ms)>;
|
||||
using SocketUsageInferenceFn =
|
||||
std::function<std::optional<bool>(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<CustomSocketDrawFn> 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<InputSocketUsageInferenceFn> usage_inference_fn;
|
||||
std::unique_ptr<SocketUsageInferenceFn> 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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
namespace blender::nodes::socket_usage_inference {
|
||||
|
||||
class InputSocketUsageParams;
|
||||
class SocketUsageParams;
|
||||
|
||||
struct SocketUsage {
|
||||
bool is_used = true;
|
||||
|
||||
@@ -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<bool> {
|
||||
[](const socket_usage_inference::SocketUsageParams ¶ms) -> std::optional<bool> {
|
||||
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<bool> {
|
||||
[](const socket_usage_inference::SocketUsageParams ¶ms) -> std::optional<bool> {
|
||||
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<bool> {
|
||||
[](const socket_usage_inference::SocketUsageParams ¶ms) -> std::optional<bool> {
|
||||
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<bool> {
|
||||
[](const socket_usage_inference::SocketUsageParams ¶ms) -> std::optional<bool> {
|
||||
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);
|
||||
|
||||
@@ -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<InputSocketUsageInferenceFn>(std::move(fn));
|
||||
decl_base_->usage_inference_fn = std::make_unique<SocketUsageInferenceFn>(std::move(fn));
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -810,19 +810,21 @@ BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder::usage_by_single_menu
|
||||
bNodeSocketValueMenu *value = socket.default_value_typed<bNodeSocketValueMenu>();
|
||||
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<bool> {
|
||||
const bNodeSocket &socket = find_single_menu_input(params.node);
|
||||
if (const std::optional<bool> 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<bool> 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<bool> {
|
||||
if (const std::optional<bool> 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<bool> {
|
||||
if (params.socket.is_input()) {
|
||||
if (const std::optional<bool> 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. */
|
||||
|
||||
@@ -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<bool> 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<bool> 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<bool> InputSocketUsageParams::any_output_is_used() const
|
||||
std::optional<bool> 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<bool> 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);
|
||||
|
||||
Reference in New Issue
Block a user