Changeset View
Changeset View
Standalone View
Standalone View
source/blender/nodes/intern/geometry_nodes_lazy_function.cc
| Show All 11 Lines | |||||
| */ | */ | ||||
| #include "NOD_geometry_exec.hh" | #include "NOD_geometry_exec.hh" | ||||
| #include "NOD_geometry_nodes_lazy_function.hh" | #include "NOD_geometry_nodes_lazy_function.hh" | ||||
| #include "NOD_multi_function.hh" | #include "NOD_multi_function.hh" | ||||
| #include "NOD_node_declaration.hh" | #include "NOD_node_declaration.hh" | ||||
| #include "BLI_cpp_types.hh" | #include "BLI_cpp_types.hh" | ||||
| #include "BLI_dot_export.hh" | |||||
| #include "BLI_hash.h" | |||||
| #include "BLI_lazy_threading.hh" | #include "BLI_lazy_threading.hh" | ||||
| #include "BLI_map.hh" | #include "BLI_map.hh" | ||||
| #include "DNA_ID.h" | #include "DNA_ID.h" | ||||
| #include "BKE_compute_contexts.hh" | #include "BKE_compute_contexts.hh" | ||||
| #include "BKE_geometry_set.hh" | #include "BKE_geometry_set.hh" | ||||
| #include "BKE_type_conversions.hh" | #include "BKE_type_conversions.hh" | ||||
| ▲ Show 20 Lines • Show All 78 Lines • ▼ Show 20 Lines | |||||
| /** | /** | ||||
| * Used for most normal geometry nodes like Subdivision Surface and Set Position. | * Used for most normal geometry nodes like Subdivision Surface and Set Position. | ||||
| */ | */ | ||||
| class LazyFunctionForGeometryNode : public LazyFunction { | class LazyFunctionForGeometryNode : public LazyFunction { | ||||
| private: | private: | ||||
| const bNode &node_; | const bNode &node_; | ||||
| public: | public: | ||||
| Map<StringRef, int> lf_input_for_output_bsocket_usage_; | |||||
| Map<StringRef, int> lf_input_for_attribute_propagation_to_output_; | |||||
| LazyFunctionForGeometryNode(const bNode &node, | LazyFunctionForGeometryNode(const bNode &node, | ||||
| Vector<const bNodeSocket *> &r_used_inputs, | Vector<const bNodeSocket *> &r_used_inputs, | ||||
| Vector<const bNodeSocket *> &r_used_outputs) | Vector<const bNodeSocket *> &r_used_outputs) | ||||
| : node_(node) | : node_(node) | ||||
| { | { | ||||
| BLI_assert(node.typeinfo->geometry_node_execute != nullptr); | BLI_assert(node.typeinfo->geometry_node_execute != nullptr); | ||||
| debug_name_ = node.name; | debug_name_ = node.name; | ||||
| lazy_function_interface_from_node(node, r_used_inputs, r_used_outputs, inputs_, outputs_); | lazy_function_interface_from_node(node, r_used_inputs, r_used_outputs, inputs_, outputs_); | ||||
| const NodeDeclaration &node_decl = *node.declaration(); | |||||
| if (const aal::RelationsInNode *relations = node_decl.anonymous_attribute_relations()) { | |||||
| { | |||||
| Vector<int> required_output_usages; | |||||
| for (const aal::AvailableRelation &relation : relations->available_relations) { | |||||
| const bNodeSocket &output_bsocket = node.output_socket(relation.field_output); | |||||
| if (output_bsocket.is_available()) { | |||||
| required_output_usages.append_non_duplicates(relation.field_output); | |||||
| } | |||||
| } | |||||
| for (const int output_index : required_output_usages) { | |||||
| const bNodeSocket &output_bsocket = node.output_socket(output_index); | |||||
| const int lf_index = inputs_.append_and_get_index_as("Output Used", | |||||
| CPPType::get<bool>()); | |||||
| lf_input_for_output_bsocket_usage_.add(output_bsocket.identifier, lf_index); | |||||
| } | |||||
| } | |||||
| { | |||||
| Vector<int> required_output_propagations; | |||||
| for (const aal::PropagateRelation &relation : relations->propagate_relations) { | |||||
| if (node.output_socket(relation.to_geometry_output).is_available()) { | |||||
| required_output_propagations.append_non_duplicates(relation.to_geometry_output); | |||||
| } | |||||
| } | |||||
| for (const int output_index : required_output_propagations) { | |||||
| const bNodeSocket &output_geometry_bsocket = node.output_socket(output_index); | |||||
| const int lf_index = inputs_.append_and_get_index_as( | |||||
| "Propagate to Output", CPPType::get<bke::AnonymousAttributeSet>()); | |||||
| lf_input_for_attribute_propagation_to_output_.add(output_geometry_bsocket.identifier, | |||||
| lf_index); | |||||
| } | |||||
| } | |||||
| } | |||||
| } | } | ||||
| void execute_impl(lf::Params ¶ms, const lf::Context &context) const override | void execute_impl(lf::Params ¶ms, const lf::Context &context) const override | ||||
| { | { | ||||
| GeoNodesLFUserData *user_data = dynamic_cast<GeoNodesLFUserData *>(context.user_data); | GeoNodesLFUserData *user_data = dynamic_cast<GeoNodesLFUserData *>(context.user_data); | ||||
| BLI_assert(user_data != nullptr); | BLI_assert(user_data != nullptr); | ||||
| GeoNodeExecParams geo_params{node_, params, context}; | GeoNodeExecParams geo_params{node_, | ||||
| params, | |||||
| context, | |||||
| lf_input_for_output_bsocket_usage_, | |||||
| lf_input_for_attribute_propagation_to_output_}; | |||||
| geo_eval_log::TimePoint start_time = geo_eval_log::Clock::now(); | geo_eval_log::TimePoint start_time = geo_eval_log::Clock::now(); | ||||
| node_.typeinfo->geometry_node_execute(geo_params); | node_.typeinfo->geometry_node_execute(geo_params); | ||||
| geo_eval_log::TimePoint end_time = geo_eval_log::Clock::now(); | geo_eval_log::TimePoint end_time = geo_eval_log::Clock::now(); | ||||
| if (geo_eval_log::GeoModifierLog *modifier_log = user_data->modifier_data->eval_log) { | if (geo_eval_log::GeoModifierLog *modifier_log = user_data->modifier_data->eval_log) { | ||||
| geo_eval_log::GeoTreeLogger &tree_logger = modifier_log->get_local_tree_logger( | geo_eval_log::GeoTreeLogger &tree_logger = modifier_log->get_local_tree_logger( | ||||
| *user_data->compute_context); | *user_data->compute_context); | ||||
| tree_logger.node_execution_times.append({node_.identifier, start_time, end_time}); | tree_logger.node_execution_times.append({node_.identifier, start_time, end_time}); | ||||
| } | } | ||||
| } | } | ||||
| std::string input_name(const int index) const override | |||||
| { | |||||
| for (const auto [identifier, lf_index] : lf_input_for_output_bsocket_usage_.items()) { | |||||
| if (index == lf_index) { | |||||
| return "Use Output '" + identifier + "'"; | |||||
| } | |||||
| } | |||||
| for (const auto [identifier, lf_index] : | |||||
| lf_input_for_attribute_propagation_to_output_.items()) { | |||||
| if (index == lf_index) { | |||||
| return "Propagate to '" + identifier + "'"; | |||||
| } | |||||
| } | |||||
| return inputs_[index].debug_name; | |||||
| } | |||||
| std::string output_name(const int index) const override | |||||
| { | |||||
| return outputs_[index].debug_name; | |||||
| } | |||||
| }; | }; | ||||
| /** | /** | ||||
| * Used to gather all inputs of a multi-input socket. A separate node is necessary because | * Used to gather all inputs of a multi-input socket. A separate node is necessary because | ||||
| * multi-inputs are not supported in lazy-function graphs. | * multi-inputs are not supported in lazy-function graphs. | ||||
| */ | */ | ||||
| class LazyFunctionForMultiInput : public LazyFunction { | class LazyFunctionForMultiInput : public LazyFunction { | ||||
| private: | private: | ||||
| ▲ Show 20 Lines • Show All 434 Lines • ▼ Show 20 Lines | void execute_impl(lf::Params ¶ms, const lf::Context &context) const override | ||||
| } | } | ||||
| geo_eval_log::GeoTreeLogger &tree_logger = | geo_eval_log::GeoTreeLogger &tree_logger = | ||||
| user_data->modifier_data->eval_log->get_local_tree_logger(*user_data->compute_context); | user_data->modifier_data->eval_log->get_local_tree_logger(*user_data->compute_context); | ||||
| tree_logger.log_viewer_node(bnode_, std::move(geometry)); | tree_logger.log_viewer_node(bnode_, std::move(geometry)); | ||||
| } | } | ||||
| }; | }; | ||||
| class LazyFunctionForViewerInputUsage : public LazyFunction { | |||||
| private: | |||||
| const lf::FunctionNode &lf_viewer_node_; | |||||
| public: | |||||
| LazyFunctionForViewerInputUsage(const lf::FunctionNode &lf_viewer_node) | |||||
| : lf_viewer_node_(lf_viewer_node) | |||||
| { | |||||
| debug_name_ = "Viewer Input Usage"; | |||||
| outputs_.append_as("Viewer is Used", CPPType::get<bool>()); | |||||
| } | |||||
| void execute_impl(lf::Params ¶ms, const lf::Context &context) const override | |||||
| { | |||||
| GeoNodesLFUserData *user_data = dynamic_cast<GeoNodesLFUserData *>(context.user_data); | |||||
| BLI_assert(user_data != nullptr); | |||||
| const ComputeContextHash &context_hash = user_data->compute_context->hash(); | |||||
| const GeoNodesModifierData &modifier_data = *user_data->modifier_data; | |||||
| const Span<const lf::FunctionNode *> nodes_with_side_effects = | |||||
| modifier_data.side_effect_nodes->lookup(context_hash); | |||||
| const bool viewer_is_used = nodes_with_side_effects.contains(&lf_viewer_node_); | |||||
| params.set_output(0, viewer_is_used); | |||||
| } | |||||
| }; | |||||
| /** | /** | ||||
| * This lazy-function wraps a group node. Internally it just executes the lazy-function graph of | * This lazy-function wraps a group node. Internally it just executes the lazy-function graph of | ||||
| * the referenced group. | * the referenced group. | ||||
| */ | */ | ||||
| class LazyFunctionForGroupNode : public LazyFunction { | class LazyFunctionForGroupNode : public LazyFunction { | ||||
| private: | private: | ||||
| const bNode &group_node_; | const bNode &group_node_; | ||||
| bool has_many_nodes_ = false; | bool has_many_nodes_ = false; | ||||
| bool use_fallback_outputs_ = false; | |||||
| std::optional<GeometryNodesLazyFunctionLogger> lf_logger_; | std::optional<GeometryNodesLazyFunctionLogger> lf_logger_; | ||||
| std::optional<GeometryNodesLazyFunctionSideEffectProvider> lf_side_effect_provider_; | std::optional<GeometryNodesLazyFunctionSideEffectProvider> lf_side_effect_provider_; | ||||
| std::optional<lf::GraphExecutor> graph_executor_; | std::optional<lf::GraphExecutor> graph_executor_; | ||||
| public: | public: | ||||
| Map<int, int> lf_output_for_input_bsocket_usage_; | |||||
| Map<int, int> lf_input_for_output_bsocket_usage_; | |||||
| Map<int, int> lf_input_for_attribute_propagation_to_output_; | |||||
HooglyBoogly: Can you add a brief description for what these mean? | |||||
| LazyFunctionForGroupNode(const bNode &group_node, | LazyFunctionForGroupNode(const bNode &group_node, | ||||
| const GeometryNodesLazyFunctionGraphInfo &lf_graph_info, | const GeometryNodesLazyFunctionGraphInfo &lf_graph_info) | ||||
| Vector<const bNodeSocket *> &r_used_inputs, | |||||
| Vector<const bNodeSocket *> &r_used_outputs) | |||||
| : group_node_(group_node) | : group_node_(group_node) | ||||
| { | { | ||||
| debug_name_ = group_node.name; | debug_name_ = group_node.name; | ||||
| lazy_function_interface_from_node( | allow_missing_requested_inputs_ = true; | ||||
| group_node, r_used_inputs, r_used_outputs, inputs_, outputs_); | |||||
| bNodeTree *group_btree = reinterpret_cast<bNodeTree *>(group_node_.id); | Vector<const bNodeSocket *> tmp_inputs; | ||||
| BLI_assert(group_btree != nullptr); | Vector<const bNodeSocket *> tmp_outputs; | ||||
| lazy_function_interface_from_node(group_node, tmp_inputs, tmp_outputs, inputs_, outputs_); | |||||
| has_many_nodes_ = lf_graph_info.num_inline_nodes_approximate > 1000; | has_many_nodes_ = lf_graph_info.num_inline_nodes_approximate > 1000; | ||||
| Vector<const lf::OutputSocket *> graph_inputs; | Vector<const lf::OutputSocket *> graph_inputs; | ||||
| for (const lf::OutputSocket *socket : lf_graph_info.mapping.group_input_sockets) { | graph_inputs.extend(lf_graph_info.mapping.group_input_sockets); | ||||
| if (socket != nullptr) { | for (const int i : group_node.output_sockets().index_range()) { | ||||
| graph_inputs.append(socket); | lf_input_for_output_bsocket_usage_.add_new( | ||||
| } | i, | ||||
| graph_inputs.append_and_get_index(lf_graph_info.mapping.group_output_used_sockets[i])); | |||||
| inputs_.append_as("Output is Used", CPPType::get<bool>(), lf::ValueUsage::Maybe); | |||||
| } | } | ||||
| graph_inputs.extend(lf_graph_info.mapping.group_output_used_sockets); | |||||
| Vector<const lf::InputSocket *> graph_outputs; | Vector<const lf::InputSocket *> graph_outputs; | ||||
| if (const bNode *group_output_bnode = group_btree->group_output_node()) { | graph_outputs.extend(lf_graph_info.mapping.standard_group_output_sockets); | ||||
| for (const bNodeSocket *bsocket : group_output_bnode->input_sockets().drop_back(1)) { | for (const int i : group_node.input_sockets().index_range()) { | ||||
| const lf::Socket *socket = lf_graph_info.mapping.dummy_socket_map.lookup_default(bsocket, | const InputUsageHint &input_usage_hint = lf_graph_info.mapping.group_input_usage_hints[i]; | ||||
| nullptr); | if (input_usage_hint.type == InputUsageHintType::DynamicSocket) { | ||||
| if (socket != nullptr) { | const lf::InputSocket *lf_socket = lf_graph_info.mapping.group_input_usage_sockets[i]; | ||||
| graph_outputs.append(&socket->as_input()); | lf_output_for_input_bsocket_usage_.add_new(i, | ||||
| graph_outputs.append_and_get_index(lf_socket)); | |||||
| outputs_.append_as("Input is Used", CPPType::get<bool>()); | |||||
| } | } | ||||
| } | } | ||||
| } | |||||
| else { | |||||
| use_fallback_outputs_ = true; | |||||
| } | |||||
| for (auto [output_index, lf_socket] : | |||||
| lf_graph_info.mapping.attribute_set_by_geometry_output.items()) { | |||||
| const int lf_index = inputs_.append_and_get_index_as( | |||||
| "Attribute Set", CPPType::get<bke::AnonymousAttributeSet>(), lf::ValueUsage::Maybe); | |||||
| graph_inputs.append(lf_socket); | |||||
| lf_input_for_attribute_propagation_to_output_.add(output_index, lf_index); | |||||
| } | |||||
| lf_logger_.emplace(lf_graph_info); | lf_logger_.emplace(lf_graph_info); | ||||
| lf_side_effect_provider_.emplace(); | lf_side_effect_provider_.emplace(); | ||||
| graph_executor_.emplace(lf_graph_info.graph, | graph_executor_.emplace(lf_graph_info.graph, | ||||
| std::move(graph_inputs), | std::move(graph_inputs), | ||||
| std::move(graph_outputs), | std::move(graph_outputs), | ||||
| &*lf_logger_, | &*lf_logger_, | ||||
| &*lf_side_effect_provider_); | &*lf_side_effect_provider_); | ||||
| } | } | ||||
| void execute_impl(lf::Params ¶ms, const lf::Context &context) const override | void execute_impl(lf::Params ¶ms, const lf::Context &context) const override | ||||
| { | { | ||||
| GeoNodesLFUserData *user_data = dynamic_cast<GeoNodesLFUserData *>(context.user_data); | GeoNodesLFUserData *user_data = dynamic_cast<GeoNodesLFUserData *>(context.user_data); | ||||
| BLI_assert(user_data != nullptr); | BLI_assert(user_data != nullptr); | ||||
| if (has_many_nodes_) { | if (has_many_nodes_) { | ||||
| /* If the called node group has many nodes, it's likely that executing it takes a while even | /* If the called node group has many nodes, it's likely that executing it takes a while even | ||||
| * if every individual node is very small. */ | * if every individual node is very small. */ | ||||
| lazy_threading::send_hint(); | lazy_threading::send_hint(); | ||||
| } | } | ||||
| if (use_fallback_outputs_) { | |||||
| /* The node group itself does not have an output node, so use default values as outputs. | |||||
| * The group should still be executed in case it has side effects. */ | |||||
| params.set_default_remaining_outputs(); | |||||
| } | |||||
| /* The compute context changes when entering a node group. */ | /* The compute context changes when entering a node group. */ | ||||
| bke::NodeGroupComputeContext compute_context{user_data->compute_context, | bke::NodeGroupComputeContext compute_context{user_data->compute_context, | ||||
| group_node_.identifier}; | group_node_.identifier}; | ||||
| GeoNodesLFUserData group_user_data = *user_data; | GeoNodesLFUserData group_user_data = *user_data; | ||||
| group_user_data.compute_context = &compute_context; | group_user_data.compute_context = &compute_context; | ||||
| lf::Context group_context = context; | lf::Context group_context = context; | ||||
| group_context.user_data = &group_user_data; | group_context.user_data = &group_user_data; | ||||
| graph_executor_->execute(params, group_context); | graph_executor_->execute(params, group_context); | ||||
| } | } | ||||
| void *init_storage(LinearAllocator<> &allocator) const override | void *init_storage(LinearAllocator<> &allocator) const override | ||||
| { | { | ||||
| return graph_executor_->init_storage(allocator); | return graph_executor_->init_storage(allocator); | ||||
| } | } | ||||
| void destruct_storage(void *storage) const override | void destruct_storage(void *storage) const override | ||||
| { | { | ||||
| graph_executor_->destruct_storage(storage); | graph_executor_->destruct_storage(storage); | ||||
| } | } | ||||
| std::string name() const override | |||||
| { | |||||
| std::stringstream ss; | |||||
| ss << "Group '" << (group_node_.id->name + 2) << "'"; | |||||
| return ss.str(); | |||||
| } | |||||
| std::string input_name(const int i) const override | |||||
| { | |||||
| if (i < group_node_.input_sockets().size()) { | |||||
| return group_node_.input_socket(i).name; | |||||
| } | |||||
| for (const auto [bsocket_index, lf_socket_index] : | |||||
| lf_input_for_output_bsocket_usage_.items()) { | |||||
| if (i == lf_socket_index) { | |||||
| std::stringstream ss; | |||||
| ss << "'" << group_node_.output_socket(bsocket_index).name << "' output is used"; | |||||
| return ss.str(); | |||||
| } | |||||
| } | |||||
| for (const auto [bsocket_index, lf_index] : | |||||
| lf_input_for_attribute_propagation_to_output_.items()) { | |||||
| if (i == lf_index) { | |||||
| std::stringstream ss; | |||||
| ss << "Propagate to '" << group_node_.output_socket(bsocket_index).name << "'"; | |||||
| return ss.str(); | |||||
| } | |||||
| } | |||||
| return inputs_[i].debug_name; | |||||
| } | |||||
| std::string output_name(const int i) const override | |||||
| { | |||||
| if (i < group_node_.output_sockets().size()) { | |||||
| return group_node_.output_socket(i).name; | |||||
| } | |||||
| for (const auto [bsocket_index, lf_socket_index] : | |||||
| lf_output_for_input_bsocket_usage_.items()) { | |||||
| if (i == lf_socket_index) { | |||||
| std::stringstream ss; | |||||
| ss << "'" << group_node_.input_socket(bsocket_index).name << "' input is used"; | |||||
| return ss.str(); | |||||
| } | |||||
| } | |||||
| return outputs_[i].debug_name; | |||||
| } | |||||
| }; | }; | ||||
| static GMutablePointer get_socket_default_value(LinearAllocator<> &allocator, | static GMutablePointer get_socket_default_value(LinearAllocator<> &allocator, | ||||
| const bNodeSocket &bsocket) | const bNodeSocket &bsocket) | ||||
| { | { | ||||
| const bNodeSocketType &typeinfo = *bsocket.typeinfo; | const bNodeSocketType &typeinfo = *bsocket.typeinfo; | ||||
| const CPPType *type = get_socket_cpp_type(typeinfo); | const CPPType *type = get_socket_cpp_type(typeinfo); | ||||
| if (type == nullptr) { | if (type == nullptr) { | ||||
| Show All 29 Lines | public: | ||||
| } | } | ||||
| std::string input_name(const int i) const override | std::string input_name(const int i) const override | ||||
| { | { | ||||
| return this->socket_names[i]; | return this->socket_names[i]; | ||||
| } | } | ||||
| }; | }; | ||||
| class LazyFunctionForLogicalOr : public lf::LazyFunction { | |||||
| public: | |||||
| LazyFunctionForLogicalOr(const int inputs_num) | |||||
| { | |||||
| debug_name_ = "Logical Or"; | |||||
| for ([[maybe_unused]] const int i : IndexRange(inputs_num)) { | |||||
| inputs_.append_as("Input", CPPType::get<bool>(), lf::ValueUsage::Maybe); | |||||
| } | |||||
| outputs_.append_as("Output", CPPType::get<bool>()); | |||||
| } | |||||
| void execute_impl(lf::Params ¶ms, const lf::Context & /*context*/) const override | |||||
| { | |||||
| int first_unavailable_input = -1; | |||||
| for (const int i : inputs_.index_range()) { | |||||
| if (const bool *value = params.try_get_input_data_ptr<bool>(i)) { | |||||
| if (*value) { | |||||
| params.set_output(0, true); | |||||
| return; | |||||
| } | |||||
| } | |||||
| else { | |||||
| first_unavailable_input = i; | |||||
| } | |||||
| } | |||||
| if (first_unavailable_input == -1) { | |||||
| params.set_output(0, false); | |||||
| return; | |||||
| } | |||||
| params.try_get_input_data_ptr_or_request(first_unavailable_input); | |||||
| } | |||||
| }; | |||||
| class LazyFunctionForSwitchSocketUsage : public lf::LazyFunction { | |||||
| public: | |||||
| LazyFunctionForSwitchSocketUsage() | |||||
| { | |||||
| debug_name_ = "Switch Socket Usage"; | |||||
| inputs_.append_as("Condition", CPPType::get<ValueOrField<bool>>()); | |||||
| outputs_.append_as("False", CPPType::get<bool>()); | |||||
| outputs_.append_as("True", CPPType::get<bool>()); | |||||
| } | |||||
| void execute_impl(lf::Params ¶ms, const lf::Context & /*context*/) const override | |||||
| { | |||||
| const ValueOrField<bool> &condition = params.get_input<ValueOrField<bool>>(0); | |||||
| if (condition.is_field()) { | |||||
| params.set_output(0, true); | |||||
| params.set_output(1, true); | |||||
| } | |||||
| else { | |||||
| const bool value = condition.as_value(); | |||||
| params.set_output(0, !value); | |||||
| params.set_output(1, value); | |||||
| } | |||||
| } | |||||
| }; | |||||
| class LazyFunctionForExtractingAnonymousAttributeSet : public lf::LazyFunction { | |||||
| private: | |||||
| const ValueOrFieldCPPType &type_; | |||||
| public: | |||||
| LazyFunctionForExtractingAnonymousAttributeSet(const ValueOrFieldCPPType &type) : type_(type) | |||||
| { | |||||
| debug_name_ = "Extract Attribute Set"; | |||||
| inputs_.append_as("Field", type.self); | |||||
| outputs_.append_as("Attributes", CPPType::get<bke::AnonymousAttributeSet>()); | |||||
| } | |||||
| void execute_impl(lf::Params ¶ms, const lf::Context & /*context*/) const override | |||||
| { | |||||
| const void *value_or_field = params.try_get_input_data_ptr(0); | |||||
| bke::AnonymousAttributeSet attributes; | |||||
| if (type_.is_field(value_or_field)) { | |||||
| const GField &field = *type_.get_field_ptr(value_or_field); | |||||
| field.node().for_each_expected_anonymous_attribute([&](const StringRef name) { | |||||
| if (!attributes.names) { | |||||
| attributes.names = std::make_shared<Set<std::string>>(); | |||||
| } | |||||
| attributes.names->add_as(name); | |||||
| }); | |||||
| } | |||||
| params.set_output(0, std::move(attributes)); | |||||
| } | |||||
| }; | |||||
| class LazyFunctionForJoiningAnonymousAttributeSets : public lf::LazyFunction { | |||||
| const int amount_; | |||||
| public: | |||||
| LazyFunctionForJoiningAnonymousAttributeSets(const int amount) : amount_(amount) | |||||
| { | |||||
| debug_name_ = "Join Attribute Sets"; | |||||
| for ([[maybe_unused]] const int i : IndexRange(amount)) { | |||||
| inputs_.append_as("Use", CPPType::get<bool>()); | |||||
| inputs_.append_as( | |||||
| "Attribute Set", CPPType::get<bke::AnonymousAttributeSet>(), lf::ValueUsage::Maybe); | |||||
| } | |||||
| outputs_.append_as("Attribute Set", CPPType::get<bke::AnonymousAttributeSet>()); | |||||
| } | |||||
| void execute_impl(lf::Params ¶ms, const lf::Context & /*context*/) const override | |||||
| { | |||||
| Vector<bke::AnonymousAttributeSet *> sets; | |||||
| bool set_is_missing = false; | |||||
Done Inline ActionsSuggestion: Similar for LazyFunctionForExtractingAnonymousAttributeSet above HooglyBoogly: Suggestion:
`LazyFunctionForJoiningAnonymousAttributeSets` ->… | |||||
| for (const int i : IndexRange(amount_)) { | |||||
| if (params.get_input<bool>(this->get_use_input(i))) { | |||||
| if (bke::AnonymousAttributeSet *set = | |||||
| params.try_get_input_data_ptr_or_request<bke::AnonymousAttributeSet>( | |||||
| this->get_attribute_set_input(i))) { | |||||
| sets.append(set); | |||||
| } | |||||
| else { | |||||
| set_is_missing = true; | |||||
| } | |||||
| } | |||||
| } | |||||
| if (set_is_missing) { | |||||
| return; | |||||
| } | |||||
| bke::AnonymousAttributeSet joined_set; | |||||
| if (sets.is_empty()) { | |||||
| /* Nothing to do. */ | |||||
| } | |||||
| else if (sets.size() == 1) { | |||||
| joined_set.names = std::move(sets[0]->names); | |||||
| } | |||||
| else { | |||||
| joined_set.names = std::make_shared<Set<std::string>>(); | |||||
| for (const bke::AnonymousAttributeSet *set : sets) { | |||||
| if (set->names) { | |||||
| for (const std::string &name : *set->names) { | |||||
| joined_set.names->add(name); | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| params.set_output(0, std::move(joined_set)); | |||||
| } | |||||
| int get_use_input(const int i) const | |||||
| { | |||||
| return 2 * i; | |||||
| } | |||||
| int get_attribute_set_input(const int i) const | |||||
| { | |||||
| return 2 * i + 1; | |||||
| } | |||||
| }; | |||||
| enum class AttributeReferenceKeyType { | |||||
| /** Attribute referenced by a field passed into the group. */ | |||||
| InputField, | |||||
| /** Attributes referenced on the output geometry outside of the current group. */ | |||||
| OutputGeometry, | |||||
| /** Attribute referenced by a field created within the current group. */ | |||||
| Socket, | |||||
| }; | |||||
| struct AttributeReferenceKey { | |||||
| AttributeReferenceKeyType type; | |||||
| /* Used when type is InputField or OutputGeometry. */ | |||||
| int index = 0; | |||||
| /* Used when type is Socket. */ | |||||
| const bNodeSocket *bsocket = nullptr; | |||||
| uint64_t hash() const | |||||
| { | |||||
| return get_default_hash_3(this->type, this->bsocket, this->index); | |||||
| } | |||||
| friend bool operator==(const AttributeReferenceKey &a, const AttributeReferenceKey &b) | |||||
| { | |||||
| return a.type == b.type && a.bsocket == b.bsocket && a.index == b.index; | |||||
| } | |||||
| friend std::ostream &operator<<(std::ostream &stream, const AttributeReferenceKey &value) | |||||
| { | |||||
| if (value.type == AttributeReferenceKeyType::InputField) { | |||||
| stream << "Input Field: " << value.index; | |||||
| } | |||||
| else if (value.type == AttributeReferenceKeyType::OutputGeometry) { | |||||
| stream << "Output Geometry: " << value.index; | |||||
| } | |||||
| else { | |||||
| stream << "Socket: " << value.bsocket->owner_node().name << " -> " << value.bsocket->name; | |||||
| } | |||||
| return stream; | |||||
| } | |||||
| }; | |||||
| struct AttributeReferenceInfo { | |||||
| lf::OutputSocket *lf_field_socket = nullptr; | |||||
| lf::OutputSocket *lf_attribute_set_socket = nullptr; | |||||
| Vector<const bNodeSocket *> initial_geometry_sockets; | |||||
| }; | |||||
| /** | /** | ||||
| * Utility class to build a lazy-function graph based on a geometry nodes tree. | * Utility class to build a lazy-function graph based on a geometry nodes tree. | ||||
| * This is mainly a separate class because it makes it easier to have variables that can be | * This is mainly a separate class because it makes it easier to have variables that can be | ||||
| * accessed by many functions. | * accessed by many functions. | ||||
| */ | */ | ||||
| struct GeometryNodesLazyFunctionGraphBuilder { | struct GeometryNodesLazyFunctionGraphBuilder { | ||||
| private: | private: | ||||
| const bNodeTree &btree_; | const bNodeTree &btree_; | ||||
| GeometryNodesLazyFunctionGraphInfo *lf_graph_info_; | GeometryNodesLazyFunctionGraphInfo *lf_graph_info_; | ||||
| lf::Graph *lf_graph_; | lf::Graph *lf_graph_; | ||||
| GeometryNodeLazyFunctionGraphMapping *mapping_; | GeometryNodeLazyFunctionGraphMapping *mapping_; | ||||
| MultiValueMap<const bNodeSocket *, lf::InputSocket *> input_socket_map_; | MultiValueMap<const bNodeSocket *, lf::InputSocket *> input_socket_map_; | ||||
| Map<const bNodeSocket *, lf::OutputSocket *> output_socket_map_; | Map<const bNodeSocket *, lf::OutputSocket *> output_socket_map_; | ||||
| Map<const bNodeSocket *, lf::Node *> multi_input_socket_nodes_; | Map<const bNodeSocket *, lf::Node *> multi_input_socket_nodes_; | ||||
| const bke::DataTypeConversions *conversions_; | const bke::DataTypeConversions *conversions_; | ||||
| /** | |||||
| * Maps bsockets to boolean sockets in the graph whereby each boolean socket indicates whether | |||||
| * the bsocket is used. Sockets not contained in this map are not used. | |||||
| * This is indexed by `bNodeSocket::index_in_tree()`. | |||||
| */ | |||||
| Array<lf::OutputSocket *> socket_is_used_map_; | |||||
| /** | |||||
| * Some built-in nodes get additional boolean inputs that indicate whether certain outputs are | |||||
| * used (field output sockets that contain new anonymous attribute references). | |||||
| */ | |||||
| Vector<std::pair<const bNodeSocket *, lf::InputSocket *>> output_used_sockets_for_builtin_nodes_; | |||||
| /** | |||||
| * Maps from output geometry sockets to corresponding attribute set inputs. | |||||
| */ | |||||
| Map<const bNodeSocket *, lf::InputSocket *> attribute_set_propagation_map_; | |||||
| /** | |||||
| * Boolean inputs that tell a node that a specific output is used. If this socket is in a | |||||
| * link-cycle, its input can become a constant true. | |||||
| */ | |||||
| Set<const lf::InputSocket *> output_usage_inputs_; | |||||
| /** | /** | ||||
| * All group input nodes are combined into one dummy node in the lazy-function graph. | * All group input nodes are combined into one dummy node in the lazy-function graph. | ||||
| */ | */ | ||||
| lf::DummyNode *group_input_lf_node_; | lf::DummyNode *group_input_lf_node_; | ||||
| friend class UsedSocketVisualizeOptions; | |||||
| public: | public: | ||||
| GeometryNodesLazyFunctionGraphBuilder(const bNodeTree &btree, | GeometryNodesLazyFunctionGraphBuilder(const bNodeTree &btree, | ||||
| GeometryNodesLazyFunctionGraphInfo &lf_graph_info) | GeometryNodesLazyFunctionGraphInfo &lf_graph_info) | ||||
| : btree_(btree), lf_graph_info_(&lf_graph_info) | : btree_(btree), lf_graph_info_(&lf_graph_info) | ||||
| { | { | ||||
| } | } | ||||
| void build() | void build() | ||||
| { | { | ||||
| btree_.ensure_topology_cache(); | btree_.ensure_topology_cache(); | ||||
| lf_graph_ = &lf_graph_info_->graph; | lf_graph_ = &lf_graph_info_->graph; | ||||
| mapping_ = &lf_graph_info_->mapping; | mapping_ = &lf_graph_info_->mapping; | ||||
| conversions_ = &bke::get_implicit_type_conversions(); | conversions_ = &bke::get_implicit_type_conversions(); | ||||
| socket_is_used_map_.reinitialize(btree_.all_sockets().size()); | |||||
| socket_is_used_map_.fill(nullptr); | |||||
| this->prepare_node_multi_functions(); | this->prepare_node_multi_functions(); | ||||
| this->build_group_input_node(); | this->build_group_input_node(); | ||||
| if (btree_.group_output_node() == nullptr) { | |||||
| this->build_fallback_output_node(); | |||||
| } | |||||
| this->handle_nodes(); | this->handle_nodes(); | ||||
| this->handle_links(); | this->handle_links(); | ||||
| this->add_default_inputs(); | this->add_default_inputs(); | ||||
| this->build_attribute_propagation_input_node(); | |||||
| this->build_output_usage_input_node(); | |||||
| this->build_input_usage_output_node(); | |||||
| this->build_socket_usages(); | |||||
| this->build_attribute_propagation_sets(); | |||||
| this->fix_link_cycles(); | |||||
| // this->print_graph(); | |||||
| lf_graph_->update_node_indices(); | lf_graph_->update_node_indices(); | ||||
| lf_graph_info_->num_inline_nodes_approximate += lf_graph_->nodes().size(); | lf_graph_info_->num_inline_nodes_approximate += lf_graph_->nodes().size(); | ||||
| } | } | ||||
| private: | private: | ||||
| void prepare_node_multi_functions() | void prepare_node_multi_functions() | ||||
| { | { | ||||
| lf_graph_info_->node_multi_functions = std::make_unique<NodeMultiFunctions>(btree_); | lf_graph_info_->node_multi_functions = std::make_unique<NodeMultiFunctions>(btree_); | ||||
| Show All 13 Lines | void build_group_input_node() | ||||
| for (const int i : interface_inputs.index_range()) { | for (const int i : interface_inputs.index_range()) { | ||||
| mapping_->group_input_sockets.append(&group_input_lf_node_->output(i)); | mapping_->group_input_sockets.append(&group_input_lf_node_->output(i)); | ||||
| debug_info->socket_names.append(interface_inputs[i]->name); | debug_info->socket_names.append(interface_inputs[i]->name); | ||||
| } | } | ||||
| lf_graph_info_->dummy_debug_infos_.append(std::move(debug_info)); | lf_graph_info_->dummy_debug_infos_.append(std::move(debug_info)); | ||||
| } | } | ||||
| void build_fallback_output_node() | |||||
| { | |||||
| Vector<const CPPType *, 16> output_cpp_types; | |||||
| auto debug_info = std::make_unique<GroupOutputDebugInfo>(); | |||||
| for (const bNodeSocket *interface_output : btree_.interface_outputs()) { | |||||
| output_cpp_types.append(interface_output->typeinfo->geometry_nodes_cpp_type); | |||||
| debug_info->socket_names.append(interface_output->name); | |||||
| } | |||||
| lf::Node &lf_node = lf_graph_->add_dummy(output_cpp_types, {}, debug_info.get()); | |||||
| for (lf::InputSocket *lf_socket : lf_node.inputs()) { | |||||
| const CPPType &type = lf_socket->type(); | |||||
| lf_socket->set_default_value(type.default_value()); | |||||
| } | |||||
| mapping_->standard_group_output_sockets = lf_node.inputs(); | |||||
| lf_graph_info_->dummy_debug_infos_.append(std::move(debug_info)); | |||||
| } | |||||
| void handle_nodes() | void handle_nodes() | ||||
| { | { | ||||
| /* Insert all nodes into the lazy function graph. */ | /* Insert all nodes into the lazy function graph. */ | ||||
| for (const bNode *bnode : btree_.all_nodes()) { | for (const bNode *bnode : btree_.all_nodes()) { | ||||
| const bNodeType *node_type = bnode->typeinfo; | const bNodeType *node_type = bnode->typeinfo; | ||||
| if (node_type == nullptr) { | if (node_type == nullptr) { | ||||
| continue; | continue; | ||||
| } | } | ||||
| ▲ Show 20 Lines • Show All 101 Lines • ▼ Show 20 Lines | for (const int i : btree_.interface_inputs().index_range()) { | ||||
| mapping_->dummy_socket_map.add_new(&bsocket, &lf_socket); | mapping_->dummy_socket_map.add_new(&bsocket, &lf_socket); | ||||
| mapping_->bsockets_by_lf_socket_map.add(&lf_socket, &bsocket); | mapping_->bsockets_by_lf_socket_map.add(&lf_socket, &bsocket); | ||||
| } | } | ||||
| } | } | ||||
| void handle_group_output_node(const bNode &bnode) | void handle_group_output_node(const bNode &bnode) | ||||
| { | { | ||||
| Vector<const CPPType *, 16> output_cpp_types; | Vector<const CPPType *, 16> output_cpp_types; | ||||
| const Span<const bNodeSocket *> interface_outputs = btree_.interface_outputs(); | auto debug_info = std::make_unique<GroupOutputDebugInfo>(); | ||||
| for (const bNodeSocket *interface_input : interface_outputs) { | for (const bNodeSocket *interface_input : btree_.interface_outputs()) { | ||||
| output_cpp_types.append(interface_input->typeinfo->geometry_nodes_cpp_type); | output_cpp_types.append(interface_input->typeinfo->geometry_nodes_cpp_type); | ||||
| debug_info->socket_names.append(interface_input->name); | |||||
| } | } | ||||
| auto debug_info = std::make_unique<GroupOutputDebugInfo>(); | |||||
| lf::DummyNode &group_output_lf_node = lf_graph_->add_dummy( | lf::DummyNode &group_output_lf_node = lf_graph_->add_dummy( | ||||
| output_cpp_types, {}, debug_info.get()); | output_cpp_types, {}, debug_info.get()); | ||||
| for (const int i : interface_outputs.index_range()) { | for (const int i : group_output_lf_node.inputs().index_range()) { | ||||
| const bNodeSocket &bsocket = bnode.input_socket(i); | const bNodeSocket &bsocket = bnode.input_socket(i); | ||||
| lf::InputSocket &lf_socket = group_output_lf_node.input(i); | lf::InputSocket &lf_socket = group_output_lf_node.input(i); | ||||
| input_socket_map_.add(&bsocket, &lf_socket); | input_socket_map_.add(&bsocket, &lf_socket); | ||||
| mapping_->dummy_socket_map.add(&bsocket, &lf_socket); | mapping_->dummy_socket_map.add(&bsocket, &lf_socket); | ||||
| mapping_->bsockets_by_lf_socket_map.add(&lf_socket, &bsocket); | mapping_->bsockets_by_lf_socket_map.add(&lf_socket, &bsocket); | ||||
| debug_info->socket_names.append(interface_outputs[i]->name); | } | ||||
| if (&bnode == btree_.group_output_node()) { | |||||
| mapping_->standard_group_output_sockets = group_output_lf_node.inputs(); | |||||
| } | } | ||||
| lf_graph_info_->dummy_debug_infos_.append(std::move(debug_info)); | lf_graph_info_->dummy_debug_infos_.append(std::move(debug_info)); | ||||
| } | } | ||||
| void handle_group_node(const bNode &bnode) | void handle_group_node(const bNode &bnode) | ||||
| { | { | ||||
| const bNodeTree *group_btree = reinterpret_cast<bNodeTree *>(bnode.id); | const bNodeTree *group_btree = reinterpret_cast<bNodeTree *>(bnode.id); | ||||
| if (group_btree == nullptr) { | if (group_btree == nullptr) { | ||||
| return; | return; | ||||
| } | } | ||||
| const GeometryNodesLazyFunctionGraphInfo *group_lf_graph_info = | const GeometryNodesLazyFunctionGraphInfo *group_lf_graph_info = | ||||
| ensure_geometry_nodes_lazy_function_graph(*group_btree); | ensure_geometry_nodes_lazy_function_graph(*group_btree); | ||||
| if (group_lf_graph_info == nullptr) { | if (group_lf_graph_info == nullptr) { | ||||
| return; | return; | ||||
| } | } | ||||
| Vector<const bNodeSocket *> used_inputs; | auto lazy_function = std::make_unique<LazyFunctionForGroupNode>(bnode, *group_lf_graph_info); | ||||
| Vector<const bNodeSocket *> used_outputs; | |||||
| auto lazy_function = std::make_unique<LazyFunctionForGroupNode>( | |||||
| bnode, *group_lf_graph_info, used_inputs, used_outputs); | |||||
| lf::FunctionNode &lf_node = lf_graph_->add_function(*lazy_function); | lf::FunctionNode &lf_node = lf_graph_->add_function(*lazy_function); | ||||
| lf_graph_info_->functions.append(std::move(lazy_function)); | |||||
| for (const int i : used_inputs.index_range()) { | for (const int i : bnode.input_sockets().index_range()) { | ||||
| const bNodeSocket &bsocket = *used_inputs[i]; | const bNodeSocket &bsocket = bnode.input_socket(i); | ||||
| BLI_assert(!bsocket.is_multi_input()); | BLI_assert(!bsocket.is_multi_input()); | ||||
| lf::InputSocket &lf_socket = lf_node.input(i); | lf::InputSocket &lf_socket = lf_node.input(i); | ||||
| input_socket_map_.add(&bsocket, &lf_socket); | input_socket_map_.add(&bsocket, &lf_socket); | ||||
| mapping_->bsockets_by_lf_socket_map.add(&lf_socket, &bsocket); | mapping_->bsockets_by_lf_socket_map.add(&lf_socket, &bsocket); | ||||
| } | } | ||||
| for (const int i : used_outputs.index_range()) { | for (const int i : bnode.output_sockets().index_range()) { | ||||
| const bNodeSocket &bsocket = *used_outputs[i]; | const bNodeSocket &bsocket = bnode.output_socket(i); | ||||
| lf::OutputSocket &lf_socket = lf_node.output(i); | lf::OutputSocket &lf_socket = lf_node.output(i); | ||||
| output_socket_map_.add_new(&bsocket, &lf_socket); | output_socket_map_.add_new(&bsocket, &lf_socket); | ||||
| mapping_->bsockets_by_lf_socket_map.add(&lf_socket, &bsocket); | mapping_->bsockets_by_lf_socket_map.add(&lf_socket, &bsocket); | ||||
| } | } | ||||
| mapping_->group_node_map.add(&bnode, &lf_node); | mapping_->group_node_map.add(&bnode, &lf_node); | ||||
| lf_graph_info_->num_inline_nodes_approximate += | lf_graph_info_->num_inline_nodes_approximate += | ||||
| group_lf_graph_info->num_inline_nodes_approximate; | group_lf_graph_info->num_inline_nodes_approximate; | ||||
| static const bool static_false = false; | |||||
| for (const int i : lazy_function->lf_input_for_output_bsocket_usage_.values()) { | |||||
| lf_node.input(i).set_default_value(&static_false); | |||||
| output_usage_inputs_.add(&lf_node.input(i)); | |||||
| } | |||||
| for (const auto [output_index, lf_input_index] : | |||||
| lazy_function->lf_input_for_attribute_propagation_to_output_.items()) { | |||||
| attribute_set_propagation_map_.add(&bnode.output_socket(output_index), | |||||
| &lf_node.input(lf_input_index)); | |||||
| } | |||||
| lf_graph_info_->functions.append(std::move(lazy_function)); | |||||
| } | } | ||||
| void handle_geometry_node(const bNode &bnode) | void handle_geometry_node(const bNode &bnode) | ||||
| { | { | ||||
| Vector<const bNodeSocket *> used_inputs; | Vector<const bNodeSocket *> used_inputs; | ||||
| Vector<const bNodeSocket *> used_outputs; | Vector<const bNodeSocket *> used_outputs; | ||||
| auto lazy_function = std::make_unique<LazyFunctionForGeometryNode>( | auto lazy_function = std::make_unique<LazyFunctionForGeometryNode>( | ||||
| bnode, used_inputs, used_outputs); | bnode, used_inputs, used_outputs); | ||||
| lf::Node &lf_node = lf_graph_->add_function(*lazy_function); | lf::Node &lf_node = lf_graph_->add_function(*lazy_function); | ||||
| lf_graph_info_->functions.append(std::move(lazy_function)); | |||||
| for (const int i : used_inputs.index_range()) { | for (const int i : used_inputs.index_range()) { | ||||
| const bNodeSocket &bsocket = *used_inputs[i]; | const bNodeSocket &bsocket = *used_inputs[i]; | ||||
| lf::InputSocket &lf_socket = lf_node.input(i); | lf::InputSocket &lf_socket = lf_node.input(i); | ||||
| if (bsocket.is_multi_input()) { | if (bsocket.is_multi_input()) { | ||||
| auto multi_input_lazy_function = std::make_unique<LazyFunctionForMultiInput>(bsocket); | auto multi_input_lazy_function = std::make_unique<LazyFunctionForMultiInput>(bsocket); | ||||
| lf::Node &lf_multi_input_node = lf_graph_->add_function(*multi_input_lazy_function); | lf::Node &lf_multi_input_node = lf_graph_->add_function(*multi_input_lazy_function); | ||||
| Show All 10 Lines | for (const int i : used_inputs.index_range()) { | ||||
| } | } | ||||
| } | } | ||||
| for (const int i : used_outputs.index_range()) { | for (const int i : used_outputs.index_range()) { | ||||
| const bNodeSocket &bsocket = *used_outputs[i]; | const bNodeSocket &bsocket = *used_outputs[i]; | ||||
| lf::OutputSocket &lf_socket = lf_node.output(i); | lf::OutputSocket &lf_socket = lf_node.output(i); | ||||
| output_socket_map_.add_new(&bsocket, &lf_socket); | output_socket_map_.add_new(&bsocket, &lf_socket); | ||||
| mapping_->bsockets_by_lf_socket_map.add(&lf_socket, &bsocket); | mapping_->bsockets_by_lf_socket_map.add(&lf_socket, &bsocket); | ||||
| } | } | ||||
| for (const auto [identifier, lf_input_index] : | |||||
Done Inline ActionsSome short comment used as a label for this code section would be helpful for the reader IMO HooglyBoogly: Some short comment used as a label for this code section would be helpful for the reader IMO | |||||
| lazy_function->lf_input_for_output_bsocket_usage_.items()) { | |||||
| output_used_sockets_for_builtin_nodes_.append_as(&bnode.output_by_identifier(identifier), | |||||
| &lf_node.input(lf_input_index)); | |||||
| output_usage_inputs_.add_new(&lf_node.input(lf_input_index)); | |||||
| } | |||||
| for (const auto [identifier, lf_input_index] : | |||||
| lazy_function->lf_input_for_attribute_propagation_to_output_.items()) { | |||||
| attribute_set_propagation_map_.add(&bnode.output_by_identifier(identifier), | |||||
| &lf_node.input(lf_input_index)); | |||||
| } | |||||
| lf_graph_info_->functions.append(std::move(lazy_function)); | |||||
| } | } | ||||
| void handle_multi_function_node(const bNode &bnode, const NodeMultiFunctions::Item &fn_item) | void handle_multi_function_node(const bNode &bnode, const NodeMultiFunctions::Item &fn_item) | ||||
| { | { | ||||
| Vector<const bNodeSocket *> used_inputs; | Vector<const bNodeSocket *> used_inputs; | ||||
| Vector<const bNodeSocket *> used_outputs; | Vector<const bNodeSocket *> used_outputs; | ||||
| auto lazy_function = std::make_unique<LazyFunctionForMultiFunctionNode>( | auto lazy_function = std::make_unique<LazyFunctionForMultiFunctionNode>( | ||||
| bnode, fn_item, used_inputs, used_outputs); | bnode, fn_item, used_inputs, used_outputs); | ||||
| ▲ Show 20 Lines • Show All 227 Lines • ▼ Show 20 Lines | bool try_add_implicit_input(const bNodeSocket &input_bsocket, lf::InputSocket &input_lf_socket) | ||||
| }; | }; | ||||
| const CPPType &type = input_lf_socket.type(); | const CPPType &type = input_lf_socket.type(); | ||||
| auto lazy_function = std::make_unique<LazyFunctionForImplicitInput>(type, std::move(init_fn)); | auto lazy_function = std::make_unique<LazyFunctionForImplicitInput>(type, std::move(init_fn)); | ||||
| lf::Node &lf_node = lf_graph_->add_function(*lazy_function); | lf::Node &lf_node = lf_graph_->add_function(*lazy_function); | ||||
| lf_graph_info_->functions.append(std::move(lazy_function)); | lf_graph_info_->functions.append(std::move(lazy_function)); | ||||
| lf_graph_->add_link(lf_node.output(0), input_lf_socket); | lf_graph_->add_link(lf_node.output(0), input_lf_socket); | ||||
| return true; | return true; | ||||
| } | } | ||||
| void build_attribute_propagation_input_node() | |||||
| { | |||||
| const aal::RelationsInNode &tree_relations = *btree_.runtime->anonymous_attribute_relations; | |||||
| Vector<int> output_indices; | |||||
| for (const aal::PropagateRelation &relation : tree_relations.propagate_relations) { | |||||
| output_indices.append_non_duplicates(relation.to_geometry_output); | |||||
| } | |||||
| Vector<const CPPType *> cpp_types; | |||||
| auto debug_info = std::make_unique<lf::SimpleDummyDebugInfo>(); | |||||
| debug_info->name = "Attributes to Propagate to Output"; | |||||
| cpp_types.append_n_times(&CPPType::get<bke::AnonymousAttributeSet>(), output_indices.size()); | |||||
| lf::Node &lf_node = lf_graph_->add_dummy({}, cpp_types, debug_info.get()); | |||||
| for (const int i : output_indices.index_range()) { | |||||
| const int output_index = output_indices[i]; | |||||
| mapping_->attribute_set_by_geometry_output.add(output_index, &lf_node.output(i)); | |||||
| debug_info->output_names.append(btree_.interface_outputs()[output_index]->name); | |||||
| } | |||||
| lf_graph_info_->dummy_debug_infos_.append(std::move(debug_info)); | |||||
| } | |||||
| void build_output_usage_input_node() | |||||
| { | |||||
| const Span<const bNodeSocket *> interface_outputs = btree_.interface_outputs(); | |||||
| Vector<const CPPType *> cpp_types; | |||||
| cpp_types.append_n_times(&CPPType::get<bool>(), interface_outputs.size()); | |||||
| auto debug_info = std::make_unique<lf::SimpleDummyDebugInfo>(); | |||||
| debug_info->name = "Output Socket Usage"; | |||||
| lf::Node &lf_node = lf_graph_->add_dummy({}, cpp_types, debug_info.get()); | |||||
| for (const int i : interface_outputs.index_range()) { | |||||
| mapping_->group_output_used_sockets.append(&lf_node.output(i)); | |||||
| debug_info->output_names.append(interface_outputs[i]->name); | |||||
| } | |||||
| lf_graph_info_->dummy_debug_infos_.append(std::move(debug_info)); | |||||
| } | |||||
| void build_input_usage_output_node() | |||||
| { | |||||
| const Span<const bNodeSocket *> interface_inputs = btree_.interface_inputs(); | |||||
| Vector<const CPPType *> cpp_types; | |||||
| cpp_types.append_n_times(&CPPType::get<bool>(), interface_inputs.size()); | |||||
| auto debug_info = std::make_unique<lf::SimpleDummyDebugInfo>(); | |||||
| debug_info->name = "Input Socket Usage"; | |||||
| lf::Node &lf_node = lf_graph_->add_dummy(cpp_types, {}, debug_info.get()); | |||||
| for (const int i : interface_inputs.index_range()) { | |||||
| mapping_->group_input_usage_sockets.append(&lf_node.input(i)); | |||||
| debug_info->input_names.append(interface_inputs[i]->name); | |||||
| } | |||||
| lf_graph_info_->dummy_debug_infos_.append(std::move(debug_info)); | |||||
| } | |||||
| void build_socket_usages() | |||||
| { | |||||
| OrSocketUsagesCache or_socket_usages_cache; | |||||
| if (const bNode *group_output_bnode = btree_.group_output_node()) { | |||||
| /* Whether a group output is used is determined by a group input that has been created | |||||
| * exactly for this purpose. */ | |||||
| for (const bNodeSocket *bsocket : group_output_bnode->input_sockets().drop_back(1)) { | |||||
| const int index = bsocket->index(); | |||||
| socket_is_used_map_[bsocket->index_in_tree()] = const_cast<lf::OutputSocket *>( | |||||
| mapping_->group_output_used_sockets[index]); | |||||
| } | |||||
| } | |||||
| /* Iterate over all nodes from right to left to determine when which sockets are used. */ | |||||
| for (const bNode *bnode : btree_.toposort_right_to_left()) { | |||||
| const bNodeType *node_type = bnode->typeinfo; | |||||
| if (node_type == nullptr) { | |||||
| /* Ignore. */ | |||||
| continue; | |||||
| } | |||||
| this->build_output_socket_usages(*bnode, or_socket_usages_cache); | |||||
| switch (node_type->type) { | |||||
| case NODE_GROUP_OUTPUT: { | |||||
| /* Handled before this loop already. */ | |||||
| break; | |||||
| } | |||||
| case NODE_GROUP_INPUT: { | |||||
| /* Handled after this loop. */ | |||||
| break; | |||||
| } | |||||
| case NODE_FRAME: { | |||||
| /* Ignored. */ | |||||
| break; | |||||
| } | |||||
| case NODE_REROUTE: { | |||||
| /* The input is used exactly when the output is used. */ | |||||
| socket_is_used_map_[bnode->input_socket(0).index_in_tree()] = | |||||
| socket_is_used_map_[bnode->output_socket(0).index_in_tree()]; | |||||
| break; | |||||
| } | |||||
| case GEO_NODE_SWITCH: { | |||||
| this->build_switch_node_socket_usage(*bnode); | |||||
| break; | |||||
| } | |||||
| case GEO_NODE_VIEWER: { | |||||
| this->build_viewer_node_socket_usage(*bnode); | |||||
| break; | |||||
| } | |||||
| case NODE_GROUP: | |||||
| case NODE_CUSTOM_GROUP: { | |||||
| this->build_group_node_socket_usage(*bnode, or_socket_usages_cache); | |||||
| break; | |||||
| } | |||||
| default: { | |||||
| this->build_standard_node_socket_usage(*bnode, or_socket_usages_cache); | |||||
| break; | |||||
| } | |||||
| } | |||||
| } | |||||
| this->build_group_input_usages(or_socket_usages_cache); | |||||
| this->link_output_used_sockets_for_builtin_nodes(); | |||||
| } | |||||
| using OrSocketUsagesCache = Map<Vector<lf::OutputSocket *>, lf::OutputSocket *>; | |||||
| lf::OutputSocket *or_socket_usages(MutableSpan<lf::OutputSocket *> usages, | |||||
| OrSocketUsagesCache &cache) | |||||
| { | |||||
| if (usages.is_empty()) { | |||||
| return nullptr; | |||||
| } | |||||
| if (usages.size() == 1) { | |||||
| return usages[0]; | |||||
| } | |||||
| std::sort(usages.begin(), usages.end()); | |||||
| return cache.lookup_or_add_cb_as(usages, [&]() { | |||||
| auto logical_or_fn = std::make_unique<LazyFunctionForLogicalOr>(usages.size()); | |||||
| lf::Node &logical_or_node = lf_graph_->add_function(*logical_or_fn); | |||||
| lf_graph_info_->functions.append(std::move(logical_or_fn)); | |||||
| for (const int i : usages.index_range()) { | |||||
| lf_graph_->add_link(*usages[i], logical_or_node.input(i)); | |||||
| } | |||||
| return &logical_or_node.output(0); | |||||
| }); | |||||
| } | |||||
| void build_output_socket_usages(const bNode &bnode, OrSocketUsagesCache &or_socket_usages_cache) | |||||
| { | |||||
| /* Output sockets are used when any of their linked inputs are used. */ | |||||
| for (const bNodeSocket *socket : bnode.output_sockets()) { | |||||
| if (!socket->is_available()) { | |||||
| continue; | |||||
| } | |||||
| /* Determine when linked target sockets are used. */ | |||||
| Vector<lf::OutputSocket *> target_usages; | |||||
| for (const bNodeLink *link : socket->directly_linked_links()) { | |||||
| if (!link->is_used()) { | |||||
| continue; | |||||
| } | |||||
| const bNodeSocket &target_socket = *link->tosock; | |||||
| if (lf::OutputSocket *is_used_socket = | |||||
| socket_is_used_map_[target_socket.index_in_tree()]) { | |||||
| target_usages.append_non_duplicates(is_used_socket); | |||||
| } | |||||
| } | |||||
| /* Combine target socket usages into the usage of the current socket. */ | |||||
| socket_is_used_map_[socket->index_in_tree()] = this->or_socket_usages( | |||||
| target_usages, or_socket_usages_cache); | |||||
| } | |||||
| } | |||||
| void build_switch_node_socket_usage(const bNode &bnode) | |||||
| { | |||||
| const bNodeSocket *switch_input_bsocket = nullptr; | |||||
| const bNodeSocket *false_input_bsocket = nullptr; | |||||
| const bNodeSocket *true_input_bsocket = nullptr; | |||||
| const bNodeSocket *output_bsocket = nullptr; | |||||
| for (const bNodeSocket *socket : bnode.input_sockets()) { | |||||
| if (!socket->is_available()) { | |||||
| continue; | |||||
| } | |||||
| if (socket->name == StringRef("Switch")) { | |||||
| switch_input_bsocket = socket; | |||||
| } | |||||
| else if (socket->name == StringRef("False")) { | |||||
| false_input_bsocket = socket; | |||||
| } | |||||
| else if (socket->name == StringRef("True")) { | |||||
| true_input_bsocket = socket; | |||||
| } | |||||
| } | |||||
| for (const bNodeSocket *socket : bnode.output_sockets()) { | |||||
| if (socket->is_available()) { | |||||
| output_bsocket = socket; | |||||
| break; | |||||
| } | |||||
| } | |||||
| if (lf::OutputSocket *output_is_used_socket = | |||||
| socket_is_used_map_[output_bsocket->index_in_tree()]) { | |||||
| socket_is_used_map_[switch_input_bsocket->index_in_tree()] = output_is_used_socket; | |||||
| lf::InputSocket *lf_switch_input = input_socket_map_.lookup(switch_input_bsocket)[0]; | |||||
| if (lf::OutputSocket *lf_switch_origin = lf_switch_input->origin()) { | |||||
| static const LazyFunctionForSwitchSocketUsage switch_socket_usage_fn; | |||||
| lf::Node &lf_node = lf_graph_->add_function(switch_socket_usage_fn); | |||||
| lf_graph_->add_link(*lf_switch_origin, lf_node.input(0)); | |||||
| socket_is_used_map_[false_input_bsocket->index_in_tree()] = &lf_node.output(0); | |||||
| socket_is_used_map_[true_input_bsocket->index_in_tree()] = &lf_node.output(1); | |||||
| } | |||||
| else { | |||||
| if (switch_input_bsocket->default_value_typed<bNodeSocketValueBoolean>()->value) { | |||||
| socket_is_used_map_[true_input_bsocket->index_in_tree()] = output_is_used_socket; | |||||
| } | |||||
| else { | |||||
| socket_is_used_map_[false_input_bsocket->index_in_tree()] = output_is_used_socket; | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| void build_viewer_node_socket_usage(const bNode &bnode) | |||||
| { | |||||
| const lf::FunctionNode &lf_viewer_node = *mapping_->viewer_node_map.lookup(&bnode); | |||||
| auto lazy_function = std::make_unique<LazyFunctionForViewerInputUsage>(lf_viewer_node); | |||||
| lf::Node &lf_node = lf_graph_->add_function(*lazy_function); | |||||
| lf_graph_info_->functions.append(std::move(lazy_function)); | |||||
| for (const bNodeSocket *bsocket : bnode.input_sockets()) { | |||||
| if (bsocket->is_available()) { | |||||
| socket_is_used_map_[bsocket->index_in_tree()] = &lf_node.output(0); | |||||
| } | |||||
| } | |||||
| } | |||||
| void build_group_node_socket_usage(const bNode &bnode, | |||||
| OrSocketUsagesCache &or_socket_usages_cache) | |||||
| { | |||||
| const bNodeTree *bgroup = reinterpret_cast<const bNodeTree *>(bnode.id); | |||||
| if (bgroup == nullptr) { | |||||
| return; | |||||
| } | |||||
| const GeometryNodesLazyFunctionGraphInfo *group_lf_graph_info = | |||||
| ensure_geometry_nodes_lazy_function_graph(*bgroup); | |||||
| if (group_lf_graph_info == nullptr) { | |||||
| return; | |||||
| } | |||||
| lf::FunctionNode &lf_group_node = const_cast<lf::FunctionNode &>( | |||||
| *mapping_->group_node_map.lookup(&bnode)); | |||||
| const auto &fn = static_cast<const LazyFunctionForGroupNode &>(lf_group_node.function()); | |||||
| for (const bNodeSocket *input_bsocket : bnode.input_sockets()) { | |||||
| const int input_index = input_bsocket->index(); | |||||
| const InputUsageHint &input_usage_hint = | |||||
| group_lf_graph_info->mapping.group_input_usage_hints[input_index]; | |||||
| switch (input_usage_hint.type) { | |||||
| case InputUsageHintType::Never: { | |||||
| /* Nothing to do. */ | |||||
| break; | |||||
| } | |||||
| case InputUsageHintType::DependsOnOutput: { | |||||
| Vector<lf::OutputSocket *> output_usages; | |||||
| for (const int i : input_usage_hint.output_dependencies) { | |||||
| if (lf::OutputSocket *lf_socket = | |||||
| socket_is_used_map_[bnode.output_socket(i).index_in_tree()]) { | |||||
| output_usages.append(lf_socket); | |||||
| } | |||||
| } | |||||
| socket_is_used_map_[input_bsocket->index_in_tree()] = this->or_socket_usages( | |||||
| output_usages, or_socket_usages_cache); | |||||
| break; | |||||
| } | |||||
| case InputUsageHintType::DynamicSocket: { | |||||
| socket_is_used_map_[input_bsocket->index_in_tree()] = &const_cast<lf::OutputSocket &>( | |||||
| lf_group_node.output(fn.lf_output_for_input_bsocket_usage_.lookup(input_index))); | |||||
| break; | |||||
| } | |||||
| } | |||||
| } | |||||
| for (const bNodeSocket *output_bsocket : bnode.output_sockets()) { | |||||
| const int output_index = output_bsocket->index(); | |||||
| const int lf_input_index = fn.lf_input_for_output_bsocket_usage_.lookup(output_index); | |||||
| lf::InputSocket &lf_socket = lf_group_node.input(lf_input_index); | |||||
| if (lf::OutputSocket *lf_output_is_used = | |||||
| socket_is_used_map_[output_bsocket->index_in_tree()]) { | |||||
| lf_graph_->add_link(*lf_output_is_used, lf_socket); | |||||
| } | |||||
| else { | |||||
| static const bool static_false = false; | |||||
| lf_socket.set_default_value(&static_false); | |||||
| } | |||||
| } | |||||
| } | |||||
| void build_standard_node_socket_usage(const bNode &bnode, | |||||
| OrSocketUsagesCache &or_socket_usages_cache) | |||||
| { | |||||
| if (bnode.input_sockets().is_empty()) { | |||||
| return; | |||||
| } | |||||
| Vector<lf::OutputSocket *> output_usages; | |||||
| for (const bNodeSocket *output_socket : bnode.output_sockets()) { | |||||
| if (!output_socket->is_available()) { | |||||
| continue; | |||||
| } | |||||
| if (lf::OutputSocket *is_used_socket = socket_is_used_map_[output_socket->index_in_tree()]) { | |||||
| output_usages.append_non_duplicates(is_used_socket); | |||||
| } | |||||
| } | |||||
| /* Assume every input is used when any output is used. */ | |||||
| lf::OutputSocket *lf_usage = this->or_socket_usages(output_usages, or_socket_usages_cache); | |||||
| if (lf_usage == nullptr) { | |||||
| return; | |||||
| } | |||||
| for (const bNodeSocket *input_socket : bnode.input_sockets()) { | |||||
| if (input_socket->is_available()) { | |||||
| socket_is_used_map_[input_socket->index_in_tree()] = lf_usage; | |||||
| } | |||||
| } | |||||
| } | |||||
| void build_group_input_usages(OrSocketUsagesCache &or_socket_usages_cache) | |||||
| { | |||||
| const Span<const bNode *> group_input_nodes = btree_.group_input_nodes(); | |||||
| for (const int i : btree_.interface_inputs().index_range()) { | |||||
| Vector<lf::OutputSocket *> target_usages; | |||||
| for (const bNode *group_input_node : group_input_nodes) { | |||||
| if (lf::OutputSocket *lf_socket = | |||||
| socket_is_used_map_[group_input_node->output_socket(i).index_in_tree()]) { | |||||
| target_usages.append_non_duplicates(lf_socket); | |||||
| } | |||||
| } | |||||
| lf::OutputSocket *lf_socket = this->or_socket_usages(target_usages, or_socket_usages_cache); | |||||
| lf::InputSocket *lf_group_output = const_cast<lf::InputSocket *>( | |||||
| mapping_->group_input_usage_sockets[i]); | |||||
| InputUsageHint input_usage_hint; | |||||
| if (lf_socket == nullptr) { | |||||
| static const bool static_false = false; | |||||
| lf_group_output->set_default_value(&static_false); | |||||
| input_usage_hint.type = InputUsageHintType::Never; | |||||
| } | |||||
| else { | |||||
| lf_graph_->add_link(*lf_socket, *lf_group_output); | |||||
| if (lf_socket->node().is_dummy()) { | |||||
| /* Can support slightly more complex cases where it depends on more than one output in | |||||
| * the future. */ | |||||
| input_usage_hint.type = InputUsageHintType::DependsOnOutput; | |||||
| input_usage_hint.output_dependencies = { | |||||
| mapping_->group_output_used_sockets.first_index_of(lf_socket)}; | |||||
| } | |||||
| else { | |||||
| input_usage_hint.type = InputUsageHintType::DynamicSocket; | |||||
| } | |||||
| } | |||||
| lf_graph_info_->mapping.group_input_usage_hints.append(std::move(input_usage_hint)); | |||||
| } | |||||
| } | |||||
| void link_output_used_sockets_for_builtin_nodes() | |||||
| { | |||||
| for (const auto &[output_bsocket, lf_input] : output_used_sockets_for_builtin_nodes_) { | |||||
| if (lf::OutputSocket *lf_is_used = socket_is_used_map_[output_bsocket->index_in_tree()]) { | |||||
| lf_graph_->add_link(*lf_is_used, *lf_input); | |||||
| } | |||||
| else { | |||||
| static const bool static_false = false; | |||||
| lf_input->set_default_value(&static_false); | |||||
| } | |||||
| } | |||||
| } | |||||
| void build_attribute_propagation_sets() | |||||
| { | |||||
| ResourceScope scope; | |||||
| const Array<const aal::RelationsInNode *> relations_by_node = | |||||
| bke::anonymous_attribute_inferencing::get_relations_by_node(btree_, scope); | |||||
| VectorSet<AttributeReferenceKey> attribute_reference_keys; | |||||
| /* Indexed by reference key index. */ | |||||
| Vector<AttributeReferenceInfo> attribute_reference_infos; | |||||
| this->build_attribute_references( | |||||
| relations_by_node, attribute_reference_keys, attribute_reference_infos); | |||||
| MultiValueMap<const bNodeSocket *, int> referenced_by_field_socket; | |||||
| MultiValueMap<const bNodeSocket *, int> propagated_to_geometry_socket; | |||||
| this->gather_referenced_and_potentially_propagated_data(relations_by_node, | |||||
| attribute_reference_keys, | |||||
| attribute_reference_infos, | |||||
| referenced_by_field_socket, | |||||
| propagated_to_geometry_socket); | |||||
| MultiValueMap<const bNodeSocket *, int> required_by_geometry_socket; | |||||
| MultiValueMap<const bNodeSocket *, int> required_propagated_to_geometry_socket; | |||||
| this->gather_required_propagated_data(relations_by_node, | |||||
| attribute_reference_keys, | |||||
| referenced_by_field_socket, | |||||
| propagated_to_geometry_socket, | |||||
| required_by_geometry_socket, | |||||
| required_propagated_to_geometry_socket); | |||||
| this->build_attribute_sets_to_propagate(attribute_reference_keys, | |||||
| attribute_reference_infos, | |||||
| required_propagated_to_geometry_socket); | |||||
| } | |||||
| void build_attribute_references(const Span<const aal::RelationsInNode *> relations_by_node, | |||||
| VectorSet<AttributeReferenceKey> &r_attribute_reference_keys, | |||||
| Vector<AttributeReferenceInfo> &r_attribute_reference_infos) | |||||
| { | |||||
| auto add_get_attributes_node = [&](lf::OutputSocket &lf_field_socket) -> lf::OutputSocket & { | |||||
| const ValueOrFieldCPPType &type = *ValueOrFieldCPPType::get_from_self( | |||||
| lf_field_socket.type()); | |||||
| auto lazy_function = std::make_unique<LazyFunctionForExtractingAnonymousAttributeSet>(type); | |||||
| lf::Node &lf_node = lf_graph_->add_function(*lazy_function); | |||||
| lf_graph_->add_link(lf_field_socket, lf_node.input(0)); | |||||
| lf_graph_info_->functions.append(std::move(lazy_function)); | |||||
| return lf_node.output(0); | |||||
| }; | }; | ||||
| for (const bNode *node : btree_.all_nodes()) { | |||||
| const aal::RelationsInNode &relations = *relations_by_node[node->index()]; | |||||
| for (const aal::AvailableRelation &relation : relations.available_relations) { | |||||
| const bNodeSocket &geometry_bsocket = node->output_socket(relation.geometry_output); | |||||
| const bNodeSocket &field_bsocket = node->output_socket(relation.field_output); | |||||
| if (field_bsocket.is_available()) { | |||||
| AttributeReferenceKey key; | |||||
| key.type = AttributeReferenceKeyType::Socket; | |||||
| key.bsocket = &field_bsocket; | |||||
| const int key_index = r_attribute_reference_keys.index_of_or_add(key); | |||||
| if (key_index >= r_attribute_reference_infos.size()) { | |||||
| AttributeReferenceInfo info; | |||||
| info.lf_field_socket = output_socket_map_.lookup(&field_bsocket); | |||||
| info.lf_attribute_set_socket = &add_get_attributes_node(*info.lf_field_socket); | |||||
| r_attribute_reference_infos.append(info); | |||||
| } | |||||
| AttributeReferenceInfo &info = r_attribute_reference_infos[key_index]; | |||||
| if (geometry_bsocket.is_available()) { | |||||
| info.initial_geometry_sockets.append(&geometry_bsocket); | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| const aal::RelationsInNode &tree_relations = *btree_.runtime->anonymous_attribute_relations; | |||||
| for (const aal::EvalRelation &relation : tree_relations.eval_relations) { | |||||
| AttributeReferenceKey key; | |||||
| key.type = AttributeReferenceKeyType::InputField; | |||||
| key.index = relation.field_input; | |||||
| r_attribute_reference_keys.add_new(key); | |||||
| AttributeReferenceInfo info; | |||||
| info.lf_field_socket = const_cast<lf::OutputSocket *>( | |||||
| mapping_->group_input_sockets[relation.field_input]); | |||||
| info.lf_attribute_set_socket = &add_get_attributes_node(*info.lf_field_socket); | |||||
| for (const bNode *bnode : btree_.group_input_nodes()) { | |||||
| info.initial_geometry_sockets.append(&bnode->output_socket(relation.geometry_input)); | |||||
| } | |||||
| r_attribute_reference_infos.append(std::move(info)); | |||||
| } | |||||
| for (const aal::PropagateRelation &relation : tree_relations.propagate_relations) { | |||||
| AttributeReferenceKey key; | |||||
| key.type = AttributeReferenceKeyType::OutputGeometry; | |||||
| key.index = relation.to_geometry_output; | |||||
| const int key_index = r_attribute_reference_keys.index_of_or_add(key); | |||||
| if (key_index >= r_attribute_reference_infos.size()) { | |||||
| AttributeReferenceInfo info; | |||||
| info.lf_attribute_set_socket = const_cast<lf::OutputSocket *>( | |||||
| mapping_->attribute_set_by_geometry_output.lookup(relation.to_geometry_output)); | |||||
| r_attribute_reference_infos.append(info); | |||||
| } | |||||
| AttributeReferenceInfo &info = r_attribute_reference_infos[key_index]; | |||||
| for (const bNode *bnode : btree_.group_input_nodes()) { | |||||
| info.initial_geometry_sockets.append(&bnode->output_socket(relation.from_geometry_input)); | |||||
| } | |||||
| } | |||||
| } | |||||
| void gather_referenced_and_potentially_propagated_data( | |||||
| const Span<const aal::RelationsInNode *> relations_by_node, | |||||
| const Span<AttributeReferenceKey> attribute_reference_keys, | |||||
| const Span<AttributeReferenceInfo> attribute_reference_infos, | |||||
| MultiValueMap<const bNodeSocket *, int> &r_referenced_by_field_socket, | |||||
| MultiValueMap<const bNodeSocket *, int> &r_propagated_to_geometry_socket) | |||||
| { | |||||
| for (const int key_index : attribute_reference_keys.index_range()) { | |||||
| const AttributeReferenceKey &key = attribute_reference_keys[key_index]; | |||||
| const AttributeReferenceInfo &info = attribute_reference_infos[key_index]; | |||||
| switch (key.type) { | |||||
| case AttributeReferenceKeyType::InputField: { | |||||
| for (const bNode *bnode : btree_.group_input_nodes()) { | |||||
| const bNodeSocket &bsocket = bnode->output_socket(key.index); | |||||
| r_referenced_by_field_socket.add(&bsocket, key_index); | |||||
| } | |||||
| break; | |||||
| } | |||||
| case AttributeReferenceKeyType::OutputGeometry: { | |||||
| break; | |||||
| } | |||||
| case AttributeReferenceKeyType::Socket: { | |||||
| r_referenced_by_field_socket.add(key.bsocket, key_index); | |||||
| break; | |||||
| } | |||||
| } | |||||
| for (const bNodeSocket *geometry_bsocket : info.initial_geometry_sockets) { | |||||
| r_propagated_to_geometry_socket.add(geometry_bsocket, key_index); | |||||
| } | |||||
| } | |||||
| for (const bNode *bnode : btree_.toposort_left_to_right()) { | |||||
| for (const bNodeSocket *bsocket : bnode->input_sockets()) { | |||||
| if (bsocket->is_available()) { | |||||
| Vector<int> referenced_keys; | |||||
| Vector<int> propagated_keys; | |||||
| for (const bNodeLink *blink : bsocket->directly_linked_links()) { | |||||
| if (blink->is_used()) { | |||||
| referenced_keys.extend_non_duplicates( | |||||
| r_referenced_by_field_socket.lookup(blink->fromsock)); | |||||
| propagated_keys.extend_non_duplicates( | |||||
| r_propagated_to_geometry_socket.lookup(blink->fromsock)); | |||||
| } | |||||
| } | |||||
| if (!referenced_keys.is_empty()) { | |||||
| r_referenced_by_field_socket.add_multiple(bsocket, referenced_keys); | |||||
| } | |||||
| if (!propagated_keys.is_empty()) { | |||||
| r_propagated_to_geometry_socket.add_multiple(bsocket, propagated_keys); | |||||
| } | |||||
| } | |||||
| } | |||||
| const aal::RelationsInNode &relations = *relations_by_node[bnode->index()]; | |||||
| for (const aal::ReferenceRelation &relation : relations.reference_relations) { | |||||
| const bNodeSocket &input_bsocket = bnode->input_socket(relation.from_field_input); | |||||
| const bNodeSocket &output_bsocket = bnode->output_socket(relation.to_field_output); | |||||
| if (!input_bsocket.is_available() || !output_bsocket.is_available()) { | |||||
| continue; | |||||
| } | |||||
| r_referenced_by_field_socket.add_multiple( | |||||
| &output_bsocket, Vector<int>(r_referenced_by_field_socket.lookup(&input_bsocket))); | |||||
| } | |||||
| for (const aal::PropagateRelation &relation : relations.propagate_relations) { | |||||
| const bNodeSocket &input_bsocket = bnode->input_socket(relation.from_geometry_input); | |||||
| const bNodeSocket &output_bsocket = bnode->output_socket(relation.to_geometry_output); | |||||
| if (!input_bsocket.is_available() || !output_bsocket.is_available()) { | |||||
| continue; | |||||
| } | |||||
| r_propagated_to_geometry_socket.add_multiple( | |||||
| &output_bsocket, Vector<int>(r_propagated_to_geometry_socket.lookup(&input_bsocket))); | |||||
| } | |||||
| } | |||||
| } | |||||
| void gather_required_propagated_data( | |||||
| const Span<const aal::RelationsInNode *> relations_by_node, | |||||
| const VectorSet<AttributeReferenceKey> &attribute_reference_keys, | |||||
| const MultiValueMap<const bNodeSocket *, int> &referenced_by_field_socket, | |||||
| const MultiValueMap<const bNodeSocket *, int> &propagated_to_geometry_socket, | |||||
| MultiValueMap<const bNodeSocket *, int> &r_required_by_geometry_socket, | |||||
| MultiValueMap<const bNodeSocket *, int> &r_required_propagated_to_geometry_socket) | |||||
| { | |||||
| const aal::RelationsInNode &tree_relations = *btree_.runtime->anonymous_attribute_relations; | |||||
| if (const bNode *group_output_bnode = btree_.group_output_node()) { | |||||
| for (const aal::PropagateRelation &relation : tree_relations.propagate_relations) { | |||||
| AttributeReferenceKey key; | |||||
| key.type = AttributeReferenceKeyType::OutputGeometry; | |||||
| key.index = relation.to_geometry_output; | |||||
| const int key_index = attribute_reference_keys.index_of(key); | |||||
| r_required_by_geometry_socket.add( | |||||
| &group_output_bnode->input_socket(relation.to_geometry_output), key_index); | |||||
| } | |||||
| for (const aal::AvailableRelation &relation : tree_relations.available_relations) { | |||||
| const bNodeSocket &geometry_bsocket = group_output_bnode->input_socket( | |||||
| relation.geometry_output); | |||||
| const bNodeSocket &field_bsocket = group_output_bnode->input_socket(relation.field_output); | |||||
| r_required_by_geometry_socket.add_multiple( | |||||
| &geometry_bsocket, referenced_by_field_socket.lookup(&field_bsocket)); | |||||
| } | |||||
| } | |||||
| for (const bNode *bnode : btree_.toposort_right_to_left()) { | |||||
| const aal::RelationsInNode &relations = *relations_by_node[bnode->index()]; | |||||
| for (const bNodeSocket *bsocket : bnode->output_sockets()) { | |||||
| if (!bsocket->is_available()) { | |||||
| continue; | |||||
| } | |||||
| Vector<int> required_attributes; | |||||
| for (const bNodeLink *blink : bsocket->directly_linked_links()) { | |||||
| if (blink->is_used()) { | |||||
| const bNodeSocket &to_socket = *blink->tosock; | |||||
| required_attributes.extend_non_duplicates( | |||||
| r_required_by_geometry_socket.lookup(&to_socket)); | |||||
| } | |||||
| } | |||||
| const Span<int> available_attributes = propagated_to_geometry_socket.lookup(bsocket); | |||||
| for (const int key_index : required_attributes) { | |||||
| if (available_attributes.contains(key_index)) { | |||||
| r_required_by_geometry_socket.add(bsocket, key_index); | |||||
| const AttributeReferenceKey &key = attribute_reference_keys[key_index]; | |||||
| if (key.type != AttributeReferenceKeyType::Socket || | |||||
| &key.bsocket->owner_node() != bnode) { | |||||
| r_required_propagated_to_geometry_socket.add(bsocket, key_index); | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| for (const bNodeSocket *bsocket : bnode->input_sockets()) { | |||||
| if (!bsocket->is_available()) { | |||||
| continue; | |||||
| } | |||||
| Vector<int> required_attributes; | |||||
| for (const aal::PropagateRelation &relation : relations.propagate_relations) { | |||||
| if (relation.from_geometry_input == bsocket->index()) { | |||||
| const bNodeSocket &output_bsocket = bnode->output_socket(relation.to_geometry_output); | |||||
| required_attributes.extend_non_duplicates( | |||||
| r_required_by_geometry_socket.lookup(&output_bsocket)); | |||||
| } | |||||
| } | |||||
| for (const aal::EvalRelation &relation : relations.eval_relations) { | |||||
| if (relation.geometry_input == bsocket->index()) { | |||||
| const bNodeSocket &field_bsocket = bnode->input_socket(relation.field_input); | |||||
| if (field_bsocket.is_available()) { | |||||
| required_attributes.extend_non_duplicates( | |||||
| referenced_by_field_socket.lookup(&field_bsocket)); | |||||
| } | |||||
| } | |||||
| } | |||||
| const Span<int> available_attributes = propagated_to_geometry_socket.lookup(bsocket); | |||||
| for (const int key_index : required_attributes) { | |||||
| if (available_attributes.contains(key_index)) { | |||||
| r_required_by_geometry_socket.add(bsocket, key_index); | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| void build_attribute_sets_to_propagate( | |||||
| const Span<AttributeReferenceKey> attribute_reference_keys, | |||||
| const Span<AttributeReferenceInfo> attribute_reference_infos, | |||||
| const MultiValueMap<const bNodeSocket *, int> &required_propagated_to_geometry_socket) | |||||
| { | |||||
| JoinAttibuteSetsCache join_attribute_sets_cache; | |||||
| for (const auto [geometry_output_bsocket, lf_attribute_set_input] : | |||||
| attribute_set_propagation_map_.items()) { | |||||
| const Span<int> required = required_propagated_to_geometry_socket.lookup( | |||||
| geometry_output_bsocket); | |||||
| Vector<lf::OutputSocket *> attribute_set_sockets; | |||||
| Vector<lf::OutputSocket *> used_sockets; | |||||
| for (const int i : required.index_range()) { | |||||
| const int key_index = required[i]; | |||||
| const AttributeReferenceKey &key = attribute_reference_keys[key_index]; | |||||
| const AttributeReferenceInfo &info = attribute_reference_infos[key_index]; | |||||
| attribute_set_sockets.append(info.lf_attribute_set_socket); | |||||
| switch (key.type) { | |||||
| case AttributeReferenceKeyType::InputField: { | |||||
| used_sockets.append(nullptr); | |||||
| break; | |||||
| } | |||||
| case AttributeReferenceKeyType::OutputGeometry: { | |||||
| used_sockets.append(nullptr); | |||||
| break; | |||||
| } | |||||
| case AttributeReferenceKeyType::Socket: { | |||||
| used_sockets.append(socket_is_used_map_[key.bsocket->index_in_tree()]); | |||||
| break; | |||||
| } | |||||
| } | |||||
| } | |||||
| if (lf::OutputSocket *joined_attribute_set = this->join_attribute_sets( | |||||
| attribute_set_sockets, used_sockets, join_attribute_sets_cache)) { | |||||
| lf_graph_->add_link(*joined_attribute_set, *lf_attribute_set_input); | |||||
| } | |||||
| else { | |||||
| static const bke::AnonymousAttributeSet empty_set; | |||||
| lf_attribute_set_input->set_default_value(&empty_set); | |||||
| } | |||||
| } | |||||
| } | |||||
| using JoinAttibuteSetsCache = Map<Vector<lf::OutputSocket *>, lf::OutputSocket *>; | |||||
| lf::OutputSocket *join_attribute_sets(const Span<lf::OutputSocket *> attribute_set_sockets, | |||||
| const Span<lf::OutputSocket *> used_sockets, | |||||
| JoinAttibuteSetsCache &cache) | |||||
| { | |||||
| BLI_assert(attribute_set_sockets.size() == used_sockets.size()); | |||||
| if (attribute_set_sockets.is_empty()) { | |||||
| return nullptr; | |||||
| } | |||||
| if (attribute_set_sockets.size() == 1 && used_sockets[0] == nullptr) { | |||||
| return attribute_set_sockets[0]; | |||||
| } | |||||
| Vector<lf::OutputSocket *, 16> key; | |||||
| key.extend(attribute_set_sockets); | |||||
| key.extend(used_sockets); | |||||
| std::sort(key.begin(), key.end()); | |||||
| return cache.lookup_or_add_cb(key, [&]() { | |||||
| auto lazy_function = std::make_unique<LazyFunctionForJoiningAnonymousAttributeSets>( | |||||
| attribute_set_sockets.size()); | |||||
| lf::Node &lf_node = lf_graph_->add_function(*lazy_function); | |||||
| for (const int i : attribute_set_sockets.index_range()) { | |||||
| lf::InputSocket &lf_use_input = lf_node.input(lazy_function->get_use_input(i)); | |||||
| lf::InputSocket &lf_attributes_input = lf_node.input( | |||||
| lazy_function->get_attribute_set_input(i)); | |||||
| if (used_sockets[i] == nullptr) { | |||||
| static const bool static_true = true; | |||||
| lf_use_input.set_default_value(&static_true); | |||||
| } | |||||
| else { | |||||
| lf_graph_->add_link(*used_sockets[i], lf_use_input); | |||||
| } | |||||
| lf_graph_->add_link(*attribute_set_sockets[i], lf_attributes_input); | |||||
| } | |||||
| lf_graph_info_->functions.append(std::move(lazy_function)); | |||||
| return &lf_node.output(0); | |||||
| }); | |||||
| } | |||||
| void fix_link_cycles() | |||||
| { | |||||
| lf_graph_->update_node_indices(); | |||||
| const int sockets_num = lf_graph_->socket_index_in_graph_size(); | |||||
| struct SocketState { | |||||
| bool done = false; | |||||
| bool in_stack = false; | |||||
| }; | |||||
| Array<SocketState> socket_states(sockets_num); | |||||
| Stack<lf::Socket *> lf_sockets_to_check; | |||||
| for (lf::Node *lf_node : lf_graph_->nodes()) { | |||||
| if (lf_node->is_function()) { | |||||
| for (lf::OutputSocket *lf_socket : lf_node->outputs()) { | |||||
| if (lf_socket->targets().is_empty()) { | |||||
| lf_sockets_to_check.push(lf_socket); | |||||
| } | |||||
| } | |||||
| } | |||||
| if (lf_node->outputs().is_empty()) { | |||||
| for (lf::InputSocket *lf_socket : lf_node->inputs()) { | |||||
| lf_sockets_to_check.push(lf_socket); | |||||
| } | |||||
| } | |||||
| } | |||||
| Vector<lf::Socket *> lf_socket_stack; | |||||
| while (!lf_sockets_to_check.is_empty()) { | |||||
| lf::Socket *lf_inout_socket = lf_sockets_to_check.peek(); | |||||
| lf::Node &lf_node = lf_inout_socket->node(); | |||||
| SocketState &state = socket_states[lf_inout_socket->index_in_graph()]; | |||||
| lf_socket_stack.append(lf_inout_socket); | |||||
| state.in_stack = true; | |||||
| Vector<lf::Socket *, 16> lf_origin_sockets; | |||||
| if (lf_inout_socket->is_input()) { | |||||
| lf::InputSocket &lf_input_socket = lf_inout_socket->as_input(); | |||||
| if (lf::OutputSocket *lf_origin_socket = lf_input_socket.origin()) { | |||||
| lf_origin_sockets.append(lf_origin_socket); | |||||
| } | |||||
| } | |||||
| else { | |||||
| lf::OutputSocket &lf_output_socket = lf_inout_socket->as_output(); | |||||
| if (lf_node.is_function()) { | |||||
| lf::FunctionNode &lf_function_node = static_cast<lf::FunctionNode &>(lf_node); | |||||
| const lf::LazyFunction &fn = lf_function_node.function(); | |||||
| fn.possible_output_dependencies( | |||||
| lf_output_socket.index(), [&](const Span<int> input_indices) { | |||||
| for (const int input_index : input_indices) { | |||||
| lf_origin_sockets.append(&lf_node.input(input_index)); | |||||
| } | |||||
| }); | |||||
| } | |||||
| } | |||||
| bool pushed_socket = false; | |||||
| for (lf::Socket *lf_origin_socket : lf_origin_sockets) { | |||||
| if (socket_states[lf_origin_socket->index_in_graph()].in_stack) { | |||||
| const Span<lf::Socket *> cycle = lf_socket_stack.as_span().drop_front( | |||||
| lf_socket_stack.first_index_of(lf_origin_socket)); | |||||
| for (lf::Socket *lf_cycle_socket : cycle) { | |||||
| if (lf_cycle_socket->is_input() && | |||||
| output_usage_inputs_.contains(&lf_cycle_socket->as_input())) { | |||||
| lf::InputSocket &lf_cycle_input_socket = lf_cycle_socket->as_input(); | |||||
| lf_graph_->clear_origin(lf_cycle_input_socket); | |||||
| static const bool static_true = true; | |||||
| lf_cycle_input_socket.set_default_value(&static_true); | |||||
| } | |||||
| } | |||||
| } | |||||
| else if (!socket_states[lf_origin_socket->index_in_graph()].done) { | |||||
| lf_sockets_to_check.push(lf_origin_socket); | |||||
| pushed_socket = true; | |||||
| } | |||||
| } | |||||
| if (pushed_socket) { | |||||
| continue; | |||||
| } | |||||
| state.done = true; | |||||
| state.in_stack = false; | |||||
| lf_sockets_to_check.pop(); | |||||
| lf_socket_stack.pop_last(); | |||||
| } | |||||
| } | |||||
| void print_graph(); | |||||
| }; | |||||
| class UsedSocketVisualizeOptions : public lf::Graph::ToDotOptions { | |||||
| private: | |||||
| const GeometryNodesLazyFunctionGraphBuilder &builder_; | |||||
| Map<const lf::Socket *, std::string> socket_font_colors_; | |||||
| Map<const lf::Socket *, std::string> socket_name_suffixes_; | |||||
| public: | |||||
| UsedSocketVisualizeOptions(const GeometryNodesLazyFunctionGraphBuilder &builder) | |||||
| : builder_(builder) | |||||
| { | |||||
| VectorSet<lf::OutputSocket *> found; | |||||
| for (const int bsocket_index : builder_.socket_is_used_map_.index_range()) { | |||||
| const bNodeSocket *bsocket = builder_.btree_.all_sockets()[bsocket_index]; | |||||
| lf::OutputSocket *lf_used_socket = builder_.socket_is_used_map_[bsocket_index]; | |||||
| if (lf_used_socket == nullptr) { | |||||
| continue; | |||||
| } | |||||
| const float hue = BLI_hash_int_01(uintptr_t(lf_used_socket)); | |||||
| std::stringstream ss; | |||||
| ss.precision(3); | |||||
| ss << hue << " 0.9 0.5"; | |||||
| const std::string color_str = ss.str(); | |||||
| const std::string suffix = " (" + std::to_string(found.index_of_or_add(lf_used_socket)) + | |||||
| ")"; | |||||
| socket_font_colors_.add(lf_used_socket, color_str); | |||||
| socket_name_suffixes_.add(lf_used_socket, suffix); | |||||
| if (bsocket->is_input()) { | |||||
| for (const lf::InputSocket *lf_socket : builder_.input_socket_map_.lookup(bsocket)) { | |||||
| socket_font_colors_.add(lf_socket, color_str); | |||||
| socket_name_suffixes_.add(lf_socket, suffix); | |||||
| } | |||||
| } | |||||
| else if (lf::OutputSocket *lf_socket = builder_.output_socket_map_.lookup(bsocket)) { | |||||
| socket_font_colors_.add(lf_socket, color_str); | |||||
| socket_name_suffixes_.add(lf_socket, suffix); | |||||
| } | |||||
| } | |||||
| } | |||||
| std::optional<std::string> socket_font_color(const lf::Socket &socket) const override | |||||
| { | |||||
| if (const std::string *color = socket_font_colors_.lookup_ptr(&socket)) { | |||||
| return *color; | |||||
| } | |||||
| return std::nullopt; | |||||
| } | |||||
| std::string socket_name(const lf::Socket &socket) const override | |||||
| { | |||||
| return socket.name() + socket_name_suffixes_.lookup_default(&socket, ""); | |||||
| } | |||||
| void add_edge_attributes(const lf::OutputSocket & /*from*/, | |||||
| const lf::InputSocket &to, | |||||
| dot::DirectedEdge &dot_edge) const | |||||
| { | |||||
| if (builder_.output_usage_inputs_.contains_as(&to)) { | |||||
| // dot_edge.attributes.set("constraint", "false"); | |||||
| dot_edge.attributes.set("color", "#00000055"); | |||||
| } | |||||
| } | |||||
| }; | |||||
| void GeometryNodesLazyFunctionGraphBuilder::print_graph() | |||||
| { | |||||
| UsedSocketVisualizeOptions options{*this}; | |||||
| std::cout << "\n\n" << lf_graph_->to_dot(options) << "\n\n"; | |||||
| } | |||||
| const GeometryNodesLazyFunctionGraphInfo *ensure_geometry_nodes_lazy_function_graph( | const GeometryNodesLazyFunctionGraphInfo *ensure_geometry_nodes_lazy_function_graph( | ||||
| const bNodeTree &btree) | const bNodeTree &btree) | ||||
| { | { | ||||
| btree.ensure_topology_cache(); | btree.ensure_topology_cache(); | ||||
| if (btree.has_available_link_cycle()) { | if (btree.has_available_link_cycle()) { | ||||
| return nullptr; | return nullptr; | ||||
| } | } | ||||
| if (const ID *id_orig = DEG_get_original_id(const_cast<ID *>(&btree.id))) { | if (const ID *id_orig = DEG_get_original_id(const_cast<ID *>(&btree.id))) { | ||||
| ▲ Show 20 Lines • Show All 173 Lines • Show Last 20 Lines | |||||
Can you add a brief description for what these mean?