Refactor: Nodes: move socket usage inferencer class to header

The pattern here is very similar to `SocketValueInferencer`. The goal is to make
it easier to reuse this functionality without calling a higher level wrapper
like `infer_group_interface_usage`. This is especially useful when determining
the usage of just a subset of group inputs. I intend to use that to speedup the
detection of what gizmos should be displayed in
`foreach_active_gizmo_exposed_to_modifier`.

Pull Request: https://projects.blender.org/blender/blender/pulls/147234
This commit is contained in:
Jacques Lucke
2025-10-03 09:17:59 +02:00
parent 83097400f0
commit 6637be1c1c
2 changed files with 85 additions and 12 deletions

View File

@@ -21,7 +21,36 @@ struct IDProperty;
namespace blender::nodes::socket_usage_inference {
struct SocketUsageInferencer;
class SocketUsageInferencerImpl;
/**
* Can detect which sockets are used or disabled.
*/
class SocketUsageInferencer {
private:
SocketUsageInferencerImpl &impl_;
friend class InputSocketUsageParams;
public:
SocketUsageInferencer(const bNodeTree &tree,
std::optional<Span<InferenceValue>> tree_input_values,
ResourceScope &scope,
bke::ComputeContextCache &compute_context_cache,
std::optional<Span<bool>> top_level_ignored_inputs = std::nullopt,
bool ignore_top_level_node_muting = false);
bool is_socket_used(const SocketInContext &socket);
bool is_group_input_used(int input_i);
bool is_disabled_output(const SocketInContext &socket);
bool is_disabled_group_output(int output_i);
/** This can be used when detecting the usage of all input sockets in a node tree, instead of
* just the inputs of the group as a whole.
*/
void mark_top_level_node_outputs_as_used();
};
class InputSocketUsageParams {
private:

View File

@@ -31,7 +31,7 @@
namespace blender::nodes::socket_usage_inference {
/** Utility class to simplify passing global state into all the functions during inferencing. */
struct SocketUsageInferencer {
class SocketUsageInferencerImpl {
private:
friend InputSocketUsageParams;
@@ -74,12 +74,14 @@ struct SocketUsageInferencer {
bool ignore_top_level_node_muting_ = false;
public:
SocketUsageInferencer(const bNodeTree &tree,
const std::optional<Span<InferenceValue>> tree_input_values,
ResourceScope &scope,
bke::ComputeContextCache &compute_context_cache,
const std::optional<Span<bool>> top_level_ignored_inputs = std::nullopt,
const bool ignore_top_level_node_muting = false)
SocketUsageInferencer *owner_ = nullptr;
SocketUsageInferencerImpl(const bNodeTree &tree,
const std::optional<Span<InferenceValue>> tree_input_values,
ResourceScope &scope,
bke::ComputeContextCache &compute_context_cache,
const std::optional<Span<bool>> top_level_ignored_inputs,
const bool ignore_top_level_node_muting)
: scope_(scope),
compute_context_cache_(compute_context_cache),
value_inferencer_(
@@ -486,7 +488,7 @@ struct SocketUsageInferencer {
return;
}
InputSocketUsageParams params{
*this, socket.context, socket->owner_tree(), socket->owner_node(), *socket};
*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()) {
/* Some value was requested, come back later when that value is available. */
@@ -733,6 +735,23 @@ struct SocketUsageInferencer {
}
};
SocketUsageInferencer::SocketUsageInferencer(
const bNodeTree &tree,
const std::optional<Span<InferenceValue>> tree_input_values,
ResourceScope &scope,
bke::ComputeContextCache &compute_context_cache,
const std::optional<Span<bool>> top_level_ignored_inputs,
const bool ignore_top_level_node_muting)
: impl_(scope.construct<SocketUsageInferencerImpl>(tree,
tree_input_values,
scope,
compute_context_cache,
top_level_ignored_inputs,
ignore_top_level_node_muting))
{
impl_.owner_ = this;
}
static bool input_may_affect_visibility(const bNodeTreeInterfaceSocket &socket)
{
return socket.socket_type == StringRef("NodeSocketMenu");
@@ -956,14 +975,14 @@ InputSocketUsageParams::InputSocketUsageParams(SocketUsageInferencer &inferencer
InferenceValue InputSocketUsageParams::get_input(const StringRef identifier) const
{
const SocketInContext input_socket{compute_context_, this->node.input_by_identifier(identifier)};
return inferencer_.get_socket_value(input_socket);
return inferencer_.impl_.get_socket_value(input_socket);
}
std::optional<bool> InputSocketUsageParams::any_output_is_used() const
{
const bNodeSocket *first_missing = nullptr;
for (const bNodeSocket *output_socket : this->node.output_sockets()) {
if (const std::optional<bool> is_used = inferencer_.all_socket_usages_.lookup_try(
if (const std::optional<bool> is_used = inferencer_.impl_.all_socket_usages_.lookup_try(
{compute_context_, output_socket}))
{
if (*is_used) {
@@ -975,7 +994,7 @@ std::optional<bool> InputSocketUsageParams::any_output_is_used() const
}
}
if (first_missing) {
inferencer_.push_usage_task({compute_context_, first_missing});
inferencer_.impl_.push_usage_task({compute_context_, first_missing});
return std::nullopt;
}
return false;
@@ -993,4 +1012,29 @@ bool InputSocketUsageParams::menu_input_may_be(const StringRef identifier,
return value.get_primitive<MenuValue>().value == enum_value;
}
void SocketUsageInferencer::mark_top_level_node_outputs_as_used()
{
impl_.mark_top_level_node_outputs_as_used();
}
bool SocketUsageInferencer::is_group_input_used(const int input_i)
{
return impl_.is_group_input_used(input_i);
}
bool SocketUsageInferencer::is_socket_used(const SocketInContext &socket)
{
return impl_.is_socket_used(socket);
}
bool SocketUsageInferencer::is_disabled_group_output(const int output_i)
{
return impl_.is_disabled_group_output(output_i);
}
bool SocketUsageInferencer::is_disabled_output(const SocketInContext &socket)
{
return impl_.is_disabled_output(socket);
}
} // namespace blender::nodes::socket_usage_inference