diff --git a/source/blender/nodes/NOD_socket_usage_inference.hh b/source/blender/nodes/NOD_socket_usage_inference.hh index bac364048c0..113ffbbacd5 100644 --- a/source/blender/nodes/NOD_socket_usage_inference.hh +++ b/source/blender/nodes/NOD_socket_usage_inference.hh @@ -34,10 +34,9 @@ class SocketUsageInferencer { public: SocketUsageInferencer(const bNodeTree &tree, - std::optional> tree_input_values, ResourceScope &scope, + SocketValueInferencer &value_inferencer, bke::ComputeContextCache &compute_context_cache, - std::optional> top_level_ignored_inputs = std::nullopt, bool ignore_top_level_node_muting = false); bool is_socket_used(const SocketInContext &socket); diff --git a/source/blender/nodes/NOD_socket_value_inference.hh b/source/blender/nodes/NOD_socket_value_inference.hh index 23f6091f7f0..f4f2f47aa5d 100644 --- a/source/blender/nodes/NOD_socket_value_inference.hh +++ b/source/blender/nodes/NOD_socket_value_inference.hh @@ -82,11 +82,12 @@ class SocketValueInferencer { SocketValueInferencerImpl &impl_; public: - SocketValueInferencer(const bNodeTree &tree, - ResourceScope &scope, - bke::ComputeContextCache &compute_context_cache, - const std::optional> tree_input_values, - const std::optional> top_level_ignored_inputs); + SocketValueInferencer( + const bNodeTree &tree, + ResourceScope &scope, + bke::ComputeContextCache &compute_context_cache, + FunctionRef group_input_value_fn = nullptr, + std::optional> top_level_ignored_inputs = std::nullopt); InferenceValue get_socket_value(const SocketInContext &socket); }; diff --git a/source/blender/nodes/intern/geometry_nodes_gizmos.cc b/source/blender/nodes/intern/geometry_nodes_gizmos.cc index 549f2a7c7a2..231e3acc4a6 100644 --- a/source/blender/nodes/intern/geometry_nodes_gizmos.cc +++ b/source/blender/nodes/intern/geometry_nodes_gizmos.cc @@ -385,8 +385,13 @@ static void foreach_active_gizmo_exposed_to_modifier( ResourceScope scope; const Vector input_values = get_geometry_nodes_input_inference_values( *nmd.node_group, nmd.settings.properties, scope); + + SocketValueInferencer value_inferencer{ + *nmd.node_group, scope, compute_context_cache, [&](const int group_input_i) { + return input_values[group_input_i]; + }}; socket_usage_inference::SocketUsageInferencer usage_inferencer( - *nmd.node_group, input_values, scope, compute_context_cache); + *nmd.node_group, scope, value_inferencer, compute_context_cache); const ComputeContext &root_compute_context = compute_context_cache.for_modifier(nullptr, nmd); for (auto &&item : tree.runtime->gizmo_propagation->gizmo_inputs_by_group_inputs.items()) { diff --git a/source/blender/nodes/intern/socket_usage_inference.cc b/source/blender/nodes/intern/socket_usage_inference.cc index 4ce1a760b47..90dae5a8aad 100644 --- a/source/blender/nodes/intern/socket_usage_inference.cc +++ b/source/blender/nodes/intern/socket_usage_inference.cc @@ -35,11 +35,10 @@ class SocketUsageInferencerImpl { private: friend InputSocketUsageParams; - ResourceScope &scope_; bke::ComputeContextCache &compute_context_cache_; /** Inferences the socket values if possible. */ - SocketValueInferencer value_inferencer_; + SocketValueInferencer &value_inferencer_; /** Root node tree. */ const bNodeTree &root_tree_; @@ -77,15 +76,11 @@ class SocketUsageInferencerImpl { SocketUsageInferencer *owner_ = nullptr; SocketUsageInferencerImpl(const bNodeTree &tree, - const std::optional> tree_input_values, - ResourceScope &scope, + SocketValueInferencer &value_inferencer, bke::ComputeContextCache &compute_context_cache, - const std::optional> top_level_ignored_inputs, const bool ignore_top_level_node_muting) - : scope_(scope), - compute_context_cache_(compute_context_cache), - value_inferencer_( - tree, scope_, compute_context_cache_, tree_input_values, top_level_ignored_inputs), + : compute_context_cache_(compute_context_cache), + value_inferencer_(value_inferencer), root_tree_(tree), ignore_top_level_node_muting_(ignore_top_level_node_muting) { @@ -735,19 +730,13 @@ class SocketUsageInferencerImpl { } }; -SocketUsageInferencer::SocketUsageInferencer( - const bNodeTree &tree, - const std::optional> tree_input_values, - ResourceScope &scope, - bke::ComputeContextCache &compute_context_cache, - const std::optional> top_level_ignored_inputs, - const bool ignore_top_level_node_muting) - : impl_(scope.construct(tree, - tree_input_values, - scope, - compute_context_cache, - top_level_ignored_inputs, - ignore_top_level_node_muting)) +SocketUsageInferencer::SocketUsageInferencer(const bNodeTree &tree, + ResourceScope &scope, + SocketValueInferencer &value_inferencer, + bke::ComputeContextCache &compute_context_cache, + const bool ignore_top_level_node_muting) + : impl_(scope.construct( + tree, value_inferencer, compute_context_cache, ignore_top_level_node_muting)) { impl_.owner_ = this; } @@ -776,15 +765,13 @@ Array infer_all_sockets_usage(const bNodeTree &tree) { /* Find actual socket usages. */ - SocketUsageInferencer inferencer{tree, - std::nullopt, - scope, - compute_context_cache, - std::nullopt, - ignore_top_level_node_muting}; - inferencer.mark_top_level_node_outputs_as_used(); + SocketValueInferencer value_inferencer{tree, scope, compute_context_cache}; + SocketUsageInferencer usage_inferencer{ + tree, scope, value_inferencer, compute_context_cache, ignore_top_level_node_muting}; + usage_inferencer.mark_top_level_node_outputs_as_used(); for (const bNodeSocket *socket : all_input_sockets) { - all_usages[socket->index_in_tree()].is_used = inferencer.is_socket_used({nullptr, socket}); + all_usages[socket->index_in_tree()].is_used = usage_inferencer.is_socket_used( + {nullptr, socket}); } } @@ -797,20 +784,22 @@ Array infer_all_sockets_usage(const bNodeTree &tree) only_controllers_used[i] = !input_may_affect_visibility(socket); } }); - SocketUsageInferencer inferencer_all_unknown{tree, - std::nullopt, - scope, - compute_context_cache, - all_ignored_inputs, - ignore_top_level_node_muting}; - SocketUsageInferencer inferencer_only_controllers{tree, - std::nullopt, - scope, - compute_context_cache, - only_controllers_used, - ignore_top_level_node_muting}; - inferencer_all_unknown.mark_top_level_node_outputs_as_used(); - inferencer_only_controllers.mark_top_level_node_outputs_as_used(); + SocketValueInferencer value_inferencer_all_unknown{ + tree, scope, compute_context_cache, nullptr, all_ignored_inputs}; + SocketUsageInferencer usage_inferencer_all_unknown{tree, + scope, + value_inferencer_all_unknown, + compute_context_cache, + ignore_top_level_node_muting}; + SocketValueInferencer value_inferencer_only_controllers{ + tree, scope, compute_context_cache, nullptr, only_controllers_used}; + SocketUsageInferencer usage_inferencer_only_controllers{tree, + scope, + value_inferencer_only_controllers, + compute_context_cache, + ignore_top_level_node_muting}; + usage_inferencer_all_unknown.mark_top_level_node_outputs_as_used(); + usage_inferencer_only_controllers.mark_top_level_node_outputs_as_used(); for (const bNodeSocket *socket : all_input_sockets) { SocketUsage &usage = all_usages[socket->index_in_tree()]; if (usage.is_used) { @@ -818,12 +807,12 @@ Array infer_all_sockets_usage(const bNodeTree &tree) continue; } const SocketInContext socket_ctx{nullptr, socket}; - if (inferencer_only_controllers.is_socket_used(socket_ctx)) { + if (usage_inferencer_only_controllers.is_socket_used(socket_ctx)) { /* The input should be visible if it's used if only visibility-controlling inputs are * considered. */ continue; } - if (!inferencer_all_unknown.is_socket_used(socket_ctx)) { + if (!usage_inferencer_all_unknown.is_socket_used(socket_ctx)) { /* The input should be visible if it's never used, regardless of any inputs. Its usage does * not depend on any visibility-controlling input. */ continue; @@ -836,7 +825,7 @@ Array infer_all_sockets_usage(const bNodeTree &tree) continue; } const SocketInContext socket_ctx{nullptr, socket}; - if (inferencer_only_controllers.is_disabled_output(socket_ctx)) { + if (usage_inferencer_only_controllers.is_disabled_output(socket_ctx)) { SocketUsage &usage = all_usages[socket->index_in_tree()]; usage.is_visible = false; } @@ -863,43 +852,50 @@ void infer_group_interface_usage(const bNodeTree &group, { /* Detect actually used inputs. */ - SocketUsageInferencer inferencer{group, group_input_values, scope, compute_context_cache}; + SocketValueInferencer value_inferencer{ + group, scope, compute_context_cache, [&](const int group_input_i) { + return group_input_values[group_input_i]; + }}; + SocketUsageInferencer usage_inferencer{group, scope, value_inferencer, compute_context_cache}; for (const int i : group.interface_inputs().index_range()) { - r_input_usages[i].is_used |= inferencer.is_group_input_used(i); + r_input_usages[i].is_used |= usage_inferencer.is_group_input_used(i); } } bool visibility_controlling_input_exists = false; - Array inputs_all_unknown(group_input_values.size(), - InferenceValue::Unknown()); - Array inputs_only_controllers = group_input_values; for (const int i : group.interface_inputs().index_range()) { const bNodeTreeInterfaceSocket &io_socket = *group.interface_inputs()[i]; if (input_may_affect_visibility(io_socket)) { visibility_controlling_input_exists = true; } - else { - inputs_only_controllers[i] = InferenceValue::Unknown(); - } } if (!visibility_controlling_input_exists) { /* If there is no visibility controller inputs, all inputs are always visible. */ return; } - SocketUsageInferencer inferencer_all_unknown{ - group, inputs_all_unknown, scope, compute_context_cache}; - SocketUsageInferencer inferencer_only_controllers{ - group, inputs_only_controllers, scope, compute_context_cache}; + SocketValueInferencer value_inferencer_all_unknown{group, scope, compute_context_cache}; + SocketUsageInferencer usage_inferencer_all_unknown{ + group, scope, value_inferencer_all_unknown, compute_context_cache}; + SocketValueInferencer value_inferencer_only_controllers{ + group, scope, compute_context_cache, [&](const int group_input_i) { + const bNodeTreeInterfaceSocket &io_socket = *group.interface_inputs()[group_input_i]; + if (input_may_affect_visibility(io_socket)) { + return group_input_values[group_input_i]; + } + return InferenceValue::Unknown(); + }}; + SocketUsageInferencer usage_inferencer_only_controllers{ + group, scope, value_inferencer_only_controllers, compute_context_cache}; for (const int i : group.interface_inputs().index_range()) { if (r_input_usages[i].is_used) { /* Used inputs are always visible. */ continue; } - if (inferencer_only_controllers.is_group_input_used(i)) { + if (usage_inferencer_only_controllers.is_group_input_used(i)) { /* The input should be visible if it's used if only visibility-controlling inputs are * considered. */ continue; } - if (!inferencer_all_unknown.is_group_input_used(i)) { + if (!usage_inferencer_all_unknown.is_group_input_used(i)) { /* The input should be visible if it's never used, regardless of any inputs. Its usage does * not depend on any visibility-controlling input. */ continue; @@ -908,7 +904,7 @@ void infer_group_interface_usage(const bNodeTree &group, } if (r_output_usages) { for (const int i : group.interface_outputs().index_range()) { - if (inferencer_only_controllers.is_disabled_group_output(i)) { + if (usage_inferencer_only_controllers.is_disabled_group_output(i)) { SocketUsage &usage = (*r_output_usages)[i]; usage.is_used = false; usage.is_visible = false; diff --git a/source/blender/nodes/intern/socket_value_inference.cc b/source/blender/nodes/intern/socket_value_inference.cc index fecb08b2fde..36be6ba6113 100644 --- a/source/blender/nodes/intern/socket_value_inference.cc +++ b/source/blender/nodes/intern/socket_value_inference.cc @@ -40,6 +40,8 @@ class SocketValueInferencerImpl { */ Map all_socket_values_; + FunctionRef group_input_value_fn_; + /** * All sockets that have animation data and thus their value is not fixed statically. This can * contain sockets from multiple different trees. @@ -51,39 +53,21 @@ class SocketValueInferencerImpl { const bNodeTree &root_tree_; public: - SocketValueInferencerImpl(const bNodeTree &tree, - ResourceScope &scope, - bke::ComputeContextCache &compute_context_cache, - const std::optional> tree_input_values, - const std::optional> top_level_ignored_inputs) + SocketValueInferencerImpl( + const bNodeTree &tree, + ResourceScope &scope, + bke::ComputeContextCache &compute_context_cache, + const FunctionRef group_input_value_fn, + const std::optional> top_level_ignored_inputs) : scope_(scope), compute_context_cache_(compute_context_cache), + group_input_value_fn_(group_input_value_fn), top_level_ignored_inputs_(top_level_ignored_inputs), root_tree_(tree) { root_tree_.ensure_topology_cache(); root_tree_.ensure_interface_cache(); this->ensure_animation_data_processed(root_tree_); - - for (const bNode *node : root_tree_.group_input_nodes()) { - for (const int i : root_tree_.interface_inputs().index_range()) { - const bNodeSocket &socket = node->output_socket(i); - if (!socket.is_directly_linked()) { - /* This socket is not linked, hence it's value is never used. Thus we don't have to add - * it to #all_socket_values_. This optimization helps a lot when the node group has a - * very large number of inputs and group input nodes. */ - continue; - } - const SocketInContext socket_in_context{nullptr, &socket}; - InferenceValue input_value = InferenceValue::Unknown(); - if (!this->treat_socket_as_unknown(socket_in_context)) { - if (tree_input_values.has_value()) { - input_value = (*tree_input_values)[i]; - } - } - all_socket_values_.add_new(socket_in_context, input_value); - } - } } InferenceValue get_socket_value(const SocketInContext &socket) @@ -255,8 +239,15 @@ class SocketValueInferencerImpl { void value_task__output__group_input_node(const SocketInContext &socket) { - /* Group inputs for the root context should be initialized already. */ - BLI_assert(socket.context != nullptr); + const bool is_root_context = socket.context == nullptr; + if (is_root_context) { + InferenceValue value = InferenceValue::Unknown(); + if (group_input_value_fn_) { + value = group_input_value_fn_(socket->index()); + } + all_socket_values_.add_new(socket, value); + return; + } const bke::GroupNodeComputeContext &group_context = *static_cast(socket.context); @@ -900,10 +891,10 @@ SocketValueInferencer::SocketValueInferencer( const bNodeTree &tree, ResourceScope &scope, bke::ComputeContextCache &compute_context_cache, - const std::optional> tree_input_values, + const FunctionRef group_input_value_fn, const std::optional> top_level_ignored_inputs) : impl_(scope.construct( - tree, scope, compute_context_cache, tree_input_values, top_level_ignored_inputs)) + tree, scope, compute_context_cache, group_input_value_fn, top_level_ignored_inputs)) { }