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_attribute_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> output_indices; | |||||
| for (const aal::AvailableRelation &relation : relations->available_relations) { | |||||
| const bNodeSocket &output_bsocket = node.output_socket(relation.field_output); | |||||
| if (output_bsocket.is_available()) { | |||||
| output_indices.append_non_duplicates(relation.field_output); | |||||
| } | |||||
| } | |||||
| for (const int output_index : output_indices) { | |||||
| const bNodeSocket &output_bsocket = node.output_socket(output_index); | |||||
| const int lf_index = inputs_.append_and_get_index_as("Output Reference Required", | |||||
| CPPType::get<bool>()); | |||||
| lf_input_for_output_attribute_usage_.add(output_bsocket.identifier, lf_index); | |||||
| } | |||||
| } | |||||
| { | |||||
| Vector<int> output_indices; | |||||
| for (const aal::PropagateRelation &relation : relations->propagate_relations) { | |||||
| output_indices.append_non_duplicates(relation.to_geometry_output); | |||||
| } | |||||
| for (const int output_index : output_indices) { | |||||
| 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_attribute_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_attribute_usage_.items()) { | |||||
| if (index == lf_index) { | |||||
| return "Add '" + 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_by_bsocket_input_; | |||||
| Map<int, int> lf_input_by_bsocket_output_; | |||||
HooglyBoogly: Can you add a brief description for what these mean? | |||||
| Map<int, int> lf_input_for_attribute_propagation_to_output_; | |||||
| 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_by_bsocket_output_.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 InputUsage &input_usage = lf_graph_info.mapping.group_input_used_sockets[i]; | ||||
| nullptr); | if (input_usage.type == InputUsageType::DynamicSocket) { | ||||
| if (socket != nullptr) { | lf_output_by_bsocket_input_.add_new( | ||||
| graph_outputs.append(&socket->as_input()); | i, graph_outputs.append_and_get_index(input_usage.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_by_bsocket_output_.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_by_bsocket_input_.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 OutputIsUsedDebugInfo : public lf::DummyDebugInfo { | |||||
| public: | |||||
| std::string name; | |||||
| std::string node_name() const override | |||||
| { | |||||
| return "Output Is Used"; | |||||
| } | |||||
| std::string output_name(const int /*i*/) const override | |||||
| { | |||||
| return this->name; | |||||
| } | |||||
| }; | |||||
| class InputIsUsedDebugInfo : public lf::DummyDebugInfo { | |||||
| public: | |||||
| std::string name; | |||||
| std::string node_name() const override | |||||
| { | |||||
| return "Input Is Used"; | |||||
| } | |||||
| std::string input_name(const int /*i*/) const override | |||||
| { | |||||
| return this->name; | |||||
| } | |||||
| }; | |||||
| class AttributeSetInputDebugInfo : public lf::DummyDebugInfo { | |||||
| public: | |||||
| std::string name; | |||||
| std::string node_name() const override | |||||
| { | |||||
| return "Attribute Set"; | |||||
| } | |||||
| std::string output_name(const int /*i*/) const override | |||||
| { | |||||
| return name; | |||||
| } | |||||
| }; | |||||
| class AttributeSetOutputDebugInfo : public lf::DummyDebugInfo { | |||||
| public: | |||||
| std::string name; | |||||
| std::string node_name() const override | |||||
| { | |||||
| return "Attribute Set"; | |||||
| } | |||||
| std::string input_name(const int /*i*/) const override | |||||
| { | |||||
| return name; | |||||
| } | |||||
| }; | |||||
| 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 LazyFunctionForLogicalAnd : public lf::LazyFunction { | |||||
| public: | |||||
| LazyFunctionForLogicalAnd(const int inputs_num) | |||||
| { | |||||
| debug_name_ = "Logical And"; | |||||
| 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 | |||||
| { | |||||
Done Inline ActionsSuggestion: Similar for LazyFunctionForExtractingAnonymousAttributeSet above HooglyBoogly: Suggestion:
`LazyFunctionForJoiningAnonymousAttributeSets` ->… | |||||
| 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, false); | |||||
| return; | |||||
| } | |||||
| } | |||||
| else { | |||||
| first_unavailable_input = i; | |||||
| } | |||||
| } | |||||
| if (first_unavailable_input == -1) { | |||||
| params.set_output(0, true); | |||||
| return; | |||||
| } | |||||
| params.try_get_input_data_ptr_or_request(first_unavailable_input); | |||||
| } | |||||
| }; | |||||
| class LazyFunctionForLogicalNot : public lf::LazyFunction { | |||||
| public: | |||||
| LazyFunctionForLogicalNot() | |||||
| { | |||||
| debug_name_ = "Logical Not"; | |||||
| inputs_.append_as("Input", CPPType::get<bool>()); | |||||
| outputs_.append_as("Output", CPPType::get<bool>()); | |||||
| } | |||||
| void execute_impl(lf::Params ¶ms, const lf::Context & /*context*/) const override | |||||
| { | |||||
| const bool value = params.get_input<bool>(0); | |||||
| params.set_output(0, !value); | |||||
| } | |||||
| }; | |||||
| 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) { 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; | |||||
| 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; | |||||
| for (const bke::AnonymousAttributeSet *set : sets) { | |||||
| 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 { | |||||
| Input, | |||||
| Socket, | |||||
| }; | |||||
| struct AttributeReferenceKey { | |||||
| AttributeReferenceKeyType type; | |||||
| /* Used when type is Input. */ | |||||
| int input_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->input_index); | |||||
| } | |||||
| friend bool operator==(const AttributeReferenceKey &a, const AttributeReferenceKey &b) | |||||
| { | |||||
| return a.type == b.type && a.bsocket == b.bsocket && a.input_index == b.input_index; | |||||
| } | |||||
| friend std::ostream &operator<<(std::ostream &stream, const AttributeReferenceKey &value) | |||||
| { | |||||
| if (value.type == AttributeReferenceKeyType::Input) { | |||||
| stream << "Input: " << value.input_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_; | ||||
| Map<const bNodeSocket *, lf::OutputSocket *> socket_is_used_map_; | |||||
| Map<const bNodeSocket *, lf::InputSocket *> use_anonymous_attributes_map_; | |||||
| Map<const bNodeSocket *, lf::InputSocket *> attribute_set_propagation_map_; | |||||
| Set<const lf::InputSocket *> linked_anonymous_attribute_used_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(); | ||||
| 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(); | ||||
| /* Create attribute set inputs and outputs for group. */ | |||||
| { | |||||
| const aal::RelationsInNode &relations = *btree_.runtime->anonymous_attribute_relations; | |||||
| Vector<int> output_indices; | |||||
| for (const aal::PropagateRelation &relation : relations.propagate_relations) { | |||||
| output_indices.append_non_duplicates(relation.to_geometry_output); | |||||
| } | |||||
| for (const int output_index : output_indices) { | |||||
| auto debug_info = std::make_unique<AttributeSetInputDebugInfo>(); | |||||
| debug_info->name = btree_.interface_outputs()[output_index]->name; | |||||
| lf::Node &lf_node = lf_graph_->add_dummy( | |||||
| {}, {&CPPType::get<bke::AnonymousAttributeSet>()}, debug_info.get()); | |||||
| lf_graph_info_->mapping.attribute_set_by_geometry_output.add(output_index, | |||||
| &lf_node.output(0)); | |||||
| lf_graph_info_->dummy_debug_infos_.append(std::move(debug_info)); | |||||
| } | |||||
| } | |||||
| /* Create inputs used relations. */ | |||||
| Map<Vector<lf::OutputSocket *>, lf::OutputSocket *> or_map; | |||||
| MultiValueMap<int, lf::OutputSocket *> inputs_used_map; | |||||
| auto or_socket_usages = [&](MutableSpan<lf::OutputSocket *> usages) -> lf::OutputSocket * { | |||||
| if (usages.is_empty()) { | |||||
| return nullptr; | |||||
| } | |||||
| if (usages.size() == 1) { | |||||
| return usages[0]; | |||||
| } | |||||
| std::sort(usages.begin(), usages.end()); | |||||
| return or_map.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); | |||||
| }); | |||||
| }; | |||||
| for (const int i : btree_.interface_outputs().index_range()) { | |||||
| const bNodeSocket &interface_bsocket = *btree_.interface_outputs()[i]; | |||||
| auto debug_info = std::make_unique<OutputIsUsedDebugInfo>(); | |||||
| debug_info->name = interface_bsocket.name; | |||||
| lf::DummyNode &node = lf_graph_->add_dummy({}, {&CPPType::get<bool>()}, debug_info.get()); | |||||
| lf_graph_info_->dummy_debug_infos_.append(std::move(debug_info)); | |||||
| mapping_->group_output_used_sockets.append(&node.output(0)); | |||||
| } | |||||
| for (const bNode *bnode : btree_.toposort_right_to_left()) { | |||||
| const bNodeType *node_type = bnode->typeinfo; | |||||
| if (node_type == nullptr) { | |||||
| continue; | |||||
| } | |||||
| for (const bNodeSocket *socket : bnode->output_sockets()) { | |||||
| if (!socket->is_available()) { | |||||
| continue; | |||||
| } | |||||
| Vector<lf::OutputSocket *> target_usages; | |||||
| for (const bNodeLink *link : socket->directly_linked_links()) { | |||||
| if (link->is_muted()) { | |||||
| continue; | |||||
| } | |||||
| const bNodeSocket *target_socket = link->tosock; | |||||
| if (!target_socket->is_available()) { | |||||
| continue; | |||||
| } | |||||
| if (lf::OutputSocket *is_used_socket = socket_is_used_map_.lookup_default(target_socket, | |||||
| nullptr)) { | |||||
| target_usages.append_non_duplicates(is_used_socket); | |||||
| } | |||||
| } | |||||
| if (lf::OutputSocket *usage = or_socket_usages(target_usages)) { | |||||
| socket_is_used_map_.add_new(socket, usage); | |||||
| } | |||||
| } | |||||
| switch (node_type->type) { | |||||
| case NODE_FRAME: { | |||||
| /* Ignored. */ | |||||
| break; | |||||
| } | |||||
| case NODE_REROUTE: { | |||||
| if (lf::OutputSocket *is_used_socket = socket_is_used_map_.lookup_default( | |||||
| &bnode->output_socket(0), nullptr)) { | |||||
| socket_is_used_map_.add_new(&bnode->input_socket(0), is_used_socket); | |||||
| } | |||||
| break; | |||||
| } | |||||
| case NODE_GROUP_OUTPUT: { | |||||
| for (const bNodeSocket *bsocket : bnode->input_sockets().drop_back(1)) { | |||||
| const int index = bsocket->index(); | |||||
| socket_is_used_map_.add_new( | |||||
| bsocket, | |||||
| const_cast<lf::OutputSocket *>(mapping_->group_output_used_sockets[index])); | |||||
| } | |||||
| break; | |||||
| } | |||||
| case NODE_GROUP_INPUT: { | |||||
| for (const bNodeSocket *bsocket : bnode->output_sockets().drop_back(1)) { | |||||
| if (lf::OutputSocket *lf_socket = socket_is_used_map_.lookup_default(bsocket, | |||||
| nullptr)) { | |||||
| const Span<lf::OutputSocket *> previous_lf_sockets = inputs_used_map.lookup( | |||||
| bsocket->index()); | |||||
| if (!previous_lf_sockets.contains(lf_socket)) { | |||||
| inputs_used_map.add(bsocket->index(), lf_socket); | |||||
| } | |||||
| } | |||||
| } | |||||
| break; | |||||
| } | |||||
| case GEO_NODE_SWITCH: { | |||||
| const bNodeSocket *switch_input_bsocket; | |||||
| const bNodeSocket *false_input_bsocket; | |||||
| const bNodeSocket *true_input_bsocket; | |||||
| const bNodeSocket *output_bsocket; | |||||
| 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_.lookup_default( | |||||
| output_bsocket, nullptr)) { | |||||
| socket_is_used_map_.add_new(switch_input_bsocket, 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_.add_new(false_input_bsocket, &lf_node.output(0)); | |||||
| socket_is_used_map_.add_new(true_input_bsocket, &lf_node.output(1)); | |||||
| } | |||||
| else { | |||||
| if (switch_input_bsocket->default_value_typed<bNodeSocketValueBoolean>()->value) { | |||||
| socket_is_used_map_.add_new(true_input_bsocket, output_is_used_socket); | |||||
| } | |||||
| else { | |||||
| socket_is_used_map_.add(false_input_bsocket, output_is_used_socket); | |||||
| } | |||||
| } | |||||
| } | |||||
| break; | |||||
| } | |||||
| case GEO_NODE_VIEWER: { | |||||
| 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_.add_new(bsocket, &lf_node.output(0)); | |||||
| } | |||||
| } | |||||
| break; | |||||
| } | |||||
| case NODE_GROUP: | |||||
| case NODE_CUSTOM_GROUP: { | |||||
| const bNodeTree *bgroup = reinterpret_cast<const bNodeTree *>(bnode->id); | |||||
| if (bgroup == nullptr) { | |||||
| break; | |||||
| } | |||||
| const GeometryNodesLazyFunctionGraphInfo *group_lf_graph_info = | |||||
| ensure_geometry_nodes_lazy_function_graph(*bgroup); | |||||
| if (group_lf_graph_info == nullptr) { | |||||
| break; | |||||
| } | |||||
| 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 InputUsage &input_usage = | |||||
| group_lf_graph_info->mapping.group_input_used_sockets[input_index]; | |||||
| switch (input_usage.type) { | |||||
| case InputUsageType::Never: { | |||||
| /* Nothing to do. */ | |||||
| break; | |||||
| } | |||||
| case InputUsageType::DependsOnOutput: { | |||||
| Vector<lf::OutputSocket *> output_usages; | |||||
| for (const int i : input_usage.output_dependencies) { | |||||
| if (lf::OutputSocket *lf_socket = socket_is_used_map_.lookup_default( | |||||
| &bnode->output_socket(i), nullptr)) { | |||||
| output_usages.append(lf_socket); | |||||
| } | |||||
| } | |||||
| if (lf::OutputSocket *lf_socket = or_socket_usages(output_usages)) { | |||||
| socket_is_used_map_.add_new(input_bsocket, lf_socket); | |||||
| } | |||||
| break; | |||||
| } | |||||
| case InputUsageType::DynamicSocket: { | |||||
| lf::OutputSocket &lf_input_is_used_socket = const_cast<lf::OutputSocket &>( | |||||
| lf_group_node.output(fn.lf_output_by_bsocket_input_.lookup(input_index))); | |||||
| socket_is_used_map_.add_new(input_bsocket, &lf_input_is_used_socket); | |||||
| break; | |||||
| } | |||||
| } | |||||
| } | |||||
| for (const bNodeSocket *output_bsocket : bnode->output_sockets()) { | |||||
| const int output_index = output_bsocket->index(); | |||||
| const int lf_input_index = fn.lf_input_by_bsocket_output_.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_.lookup_default( | |||||
| output_bsocket, nullptr)) { | |||||
| lf_graph_->add_link(*lf_output_is_used, lf_socket); | |||||
| } | |||||
| else { | |||||
| static const bool static_false = false; | |||||
| lf_socket.set_default_value(&static_false); | |||||
| } | |||||
| } | |||||
| break; | |||||
| } | |||||
| default: { | |||||
| for (const bNodeSocket *input_socket : bnode->input_sockets()) { | |||||
| if (!input_socket->is_available()) { | |||||
| continue; | |||||
| } | |||||
| 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_.lookup_default( | |||||
| output_socket, nullptr)) { | |||||
| output_usages.append_non_duplicates(is_used_socket); | |||||
| } | |||||
| } | |||||
| if (lf::OutputSocket *usage = or_socket_usages(output_usages)) { | |||||
| socket_is_used_map_.add_new(input_socket, usage); | |||||
| } | |||||
| } | |||||
| break; | |||||
| } | |||||
| } | |||||
| } | |||||
| for (const auto [output_bsocket, lf_input] : use_anonymous_attributes_map_.items()) { | |||||
| if (lf::OutputSocket *lf_is_used = socket_is_used_map_.lookup_default(output_bsocket, | |||||
| nullptr)) { | |||||
| lf_graph_->add_link(*lf_is_used, *lf_input); | |||||
| linked_anonymous_attribute_used_inputs_.add(lf_input); | |||||
| } | |||||
| else { | |||||
| static const bool static_false = false; | |||||
| lf_input->set_default_value(&static_false); | |||||
| } | |||||
| } | |||||
| for (const auto [output_bsocket, lf_input] : attribute_set_propagation_map_.items()) { | |||||
| static const bke::AnonymousAttributeSet empty_set; | |||||
| lf_input->set_default_value(&empty_set); | |||||
| } | |||||
| /* Create input used group outputs. */ | |||||
| for (const int i : btree_.interface_inputs().index_range()) { | |||||
| const bNodeSocket &interface_bsocket = *btree_.interface_inputs()[i]; | |||||
| lf::OutputSocket *lf_socket = or_socket_usages(inputs_used_map.lookup(i)); | |||||
| auto debug_info = std::make_unique<InputIsUsedDebugInfo>(); | |||||
| debug_info->name = interface_bsocket.name; | |||||
| lf::DummyNode &node = lf_graph_->add_dummy({&CPPType::get<bool>()}, {}, debug_info.get()); | |||||
| lf_graph_info_->dummy_debug_infos_.append(std::move(debug_info)); | |||||
| InputUsage input_usage; | |||||
| if (lf_socket == nullptr) { | |||||
| static const bool static_false = false; | |||||
| node.input(0).set_default_value(&static_false); | |||||
| input_usage.type = InputUsageType::Never; | |||||
| } | |||||
| else { | |||||
| lf_graph_->add_link(*lf_socket, node.input(0)); | |||||
| if (lf_socket->node().is_dummy()) { | |||||
| /* TODO: Support slightly more complex cases where it depends on more than one output. */ | |||||
| input_usage.type = InputUsageType::DependsOnOutput; | |||||
| input_usage.output_dependencies = { | |||||
| mapping_->group_output_used_sockets.first_index_of(lf_socket)}; | |||||
| } | |||||
| else { | |||||
| input_usage.type = InputUsageType::DynamicSocket; | |||||
| input_usage.socket = &node.input(0); | |||||
| } | |||||
| } | |||||
| lf_graph_info_->mapping.group_input_used_sockets.append(std::move(input_usage)); | |||||
| } | |||||
| /* Link attribute sets. */ | |||||
| { | |||||
| ResourceScope scope; | |||||
| const Array<const aal::RelationsInNode *> relations_by_node = | |||||
| bke::anonymous_attribute_inferencing::get_relations_by_node(btree_, scope); | |||||
| 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); | |||||
| }; | |||||
| VectorSet<AttributeReferenceKey> attribute_reference_keys; | |||||
| /* Indexed by reference key index. */ | |||||
| Vector<AttributeReferenceInfo> attribute_reference_infos; | |||||
| 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 = attribute_reference_keys.index_of_or_add(key); | |||||
| if (key_index >= 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); | |||||
| attribute_reference_infos.append(info); | |||||
| } | |||||
| AttributeReferenceInfo &info = 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; | |||||
| Vector<int> attribute_reference_inputs; | |||||
| for (const aal::EvalRelation &relation : tree_relations.eval_relations) { | |||||
| AttributeReferenceKey key; | |||||
| key.type = AttributeReferenceKeyType::Input; | |||||
| key.input_index = relation.field_input; | |||||
| 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)); | |||||
| } | |||||
| attribute_reference_infos.append(std::move(info)); | |||||
| } | |||||
| std::cout << "Attribute Key: " << attribute_reference_keys.size() << "\n"; | |||||
| for (const AttributeReferenceKey &key : attribute_reference_keys) { | |||||
| std::cout << " " << key << "\n"; | |||||
| } | |||||
| MultiValueMap<const bNodeSocket *, int> referenced_by_field_socket; | |||||
| MultiValueMap<const bNodeSocket *, int> 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]; | |||||
| if (key.type == AttributeReferenceKeyType::Input) { | |||||
| for (const bNode *bnode : btree_.group_input_nodes()) { | |||||
| const bNodeSocket &bsocket = bnode->output_socket(key.input_index); | |||||
| referenced_by_field_socket.add(&bsocket, key_index); | |||||
| } | |||||
| } | |||||
| else { | |||||
| referenced_by_field_socket.add(key.bsocket, key_index); | |||||
| } | |||||
| for (const bNodeSocket *geometry_bsocket : info.initial_geometry_sockets) { | |||||
| 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( | |||||
| referenced_by_field_socket.lookup(blink->fromsock)); | |||||
| propagated_keys.extend_non_duplicates( | |||||
| propagated_to_geometry_socket.lookup(blink->fromsock)); | |||||
| } | |||||
| } | |||||
| if (!referenced_keys.is_empty()) { | |||||
| referenced_by_field_socket.add_multiple(bsocket, referenced_keys); | |||||
| } | |||||
| if (!propagated_keys.is_empty()) { | |||||
| 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; | |||||
| } | |||||
| referenced_by_field_socket.add_multiple( | |||||
| &output_bsocket, Vector<int>(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; | |||||
| } | |||||
| propagated_to_geometry_socket.add_multiple( | |||||
| &output_bsocket, | |||||
| Vector<int>(propagated_to_geometry_socket.lookup(&input_bsocket))); | |||||
| } | |||||
| } | |||||
| MultiValueMap<const bNodeSocket *, int> required_by_geometry_socket; | |||||
| MultiValueMap<const bNodeSocket *, int> linked_geometry_group_outputs; | |||||
| MultiValueMap<const bNodeSocket *, int> required_propagated_to_geometry_socket; | |||||
| if (const bNode *group_output_bnode = btree_.group_output_node()) { | |||||
| for (const aal::PropagateRelation &relation : tree_relations.propagate_relations) { | |||||
| linked_geometry_group_outputs.add( | |||||
| &group_output_bnode->input_socket(relation.to_geometry_output), | |||||
| relation.to_geometry_output); | |||||
| } | |||||
| 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); | |||||
| 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; | |||||
| Vector<int> required_group_outputs; | |||||
| for (const bNodeLink *blink : bsocket->directly_linked_links()) { | |||||
| if (blink->is_used()) { | |||||
| const bNodeSocket &to_socket = *blink->tosock; | |||||
| required_attributes.extend_non_duplicates( | |||||
| required_by_geometry_socket.lookup(&to_socket)); | |||||
| required_group_outputs.extend_non_duplicates( | |||||
| linked_geometry_group_outputs.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)) { | |||||
| required_by_geometry_socket.add(bsocket, key_index); | |||||
| const AttributeReferenceKey &key = attribute_reference_keys[key_index]; | |||||
| if (key.type == AttributeReferenceKeyType::Input || | |||||
| &key.bsocket->owner_node() != bnode) { | |||||
| required_propagated_to_geometry_socket.add(bsocket, key_index); | |||||
| } | |||||
| } | |||||
| } | |||||
| if (!required_group_outputs.is_empty()) { | |||||
| /* TODO: Filter available. */ | |||||
| linked_geometry_group_outputs.add_multiple(bsocket, required_group_outputs); | |||||
| } | |||||
| } | |||||
| for (const bNodeSocket *bsocket : bnode->input_sockets()) { | |||||
| if (!bsocket->is_available()) { | |||||
| continue; | |||||
| } | |||||
| Vector<int> required_attributes; | |||||
| Vector<int> required_group_outputs; | |||||
| 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( | |||||
| required_by_geometry_socket.lookup(&output_bsocket)); | |||||
| required_group_outputs.extend_non_duplicates( | |||||
| linked_geometry_group_outputs.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)) { | |||||
| required_by_geometry_socket.add(bsocket, key_index); | |||||
| } | |||||
| } | |||||
| if (!required_group_outputs.is_empty()) { | |||||
| /* TODO: Filter available. */ | |||||
| linked_geometry_group_outputs.add_multiple(bsocket, required_group_outputs); | |||||
| } | |||||
| } | |||||
| } | |||||
| Map<Vector<lf::OutputSocket *>, lf::OutputSocket *> joined_attribute_sets_map; | |||||
| auto get_joined_attribute_set = | |||||
| [&](const Span<lf::OutputSocket *> attribute_set_sockets, | |||||
| const Span<lf::OutputSocket *> used_sockets) -> lf::OutputSocket * { | |||||
| 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 joined_attribute_sets_map.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); | |||||
| }); | |||||
| }; | |||||
| 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); | |||||
| const Span<int> linked_outputs = linked_geometry_group_outputs.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); | |||||
| used_sockets.append(socket_is_used_map_.lookup_default(key.bsocket, nullptr)); | |||||
| } | |||||
| for (const int i : linked_outputs.index_range()) { | |||||
| const int output_index = linked_outputs[i]; | |||||
| lf::OutputSocket &attribute_set_source = *const_cast<lf::OutputSocket *>( | |||||
| mapping_->attribute_set_by_geometry_output.lookup(output_index)); | |||||
| attribute_set_sockets.append(&attribute_set_source); | |||||
| used_sockets.append(socket_is_used_map_.lookup_default( | |||||
| &btree_.group_output_node()->input_socket(output_index), nullptr)); | |||||
| } | |||||
| if (lf::OutputSocket *joined_attribute_set = get_joined_attribute_set( | |||||
| attribute_set_sockets, used_sockets)) { | |||||
| lf_graph_->add_link(*joined_attribute_set, *lf_attribute_set_input); | |||||
| } | |||||
| } | |||||
| std::cout << "Referenced:\n"; | |||||
| for (const auto [bsocket, indices] : referenced_by_field_socket.items()) { | |||||
| std::cout << " " << bsocket->owner_node().name << " -> " << bsocket->name << " : "; | |||||
| for (const int i : indices) { | |||||
| std::cout << i << ", "; | |||||
| } | |||||
| std::cout << "\n"; | |||||
| } | |||||
| std::cout << "Propagated:\n"; | |||||
| for (const auto [bsocket, indices] : propagated_to_geometry_socket.items()) { | |||||
| std::cout << " " << bsocket->owner_node().name << " -> " << bsocket->name << " : "; | |||||
| for (const int i : indices) { | |||||
| std::cout << i << ", "; | |||||
| } | |||||
| std::cout << "\n"; | |||||
| } | |||||
| std::cout << "Required:\n"; | |||||
| for (const auto [bsocket, indices] : required_by_geometry_socket.items()) { | |||||
| std::cout << " " << bsocket->owner_node().name << " -> " << bsocket->name << " : "; | |||||
| for (const int i : indices) { | |||||
| std::cout << i << ", "; | |||||
| } | |||||
| std::cout << "\n"; | |||||
| } | |||||
| std::cout << "Required Outputs:\n"; | |||||
| for (const auto [bsocket, indices] : linked_geometry_group_outputs.items()) { | |||||
| std::cout << " " << bsocket->owner_node().name << " -> " << bsocket->name << " : "; | |||||
| for (const int i : indices) { | |||||
| std::cout << i << ", "; | |||||
| } | |||||
| std::cout << "\n"; | |||||
| } | |||||
| } | |||||
| } | |||||
| /* Fix link cycles. */ | |||||
| { | |||||
| Set<lf::Socket *> lf_done_sockets; | |||||
| 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 *> cleared_origins; | |||||
| Vector<Vector<lf::Socket *>> lf_cycles; | |||||
| VectorSet<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(); | |||||
| lf_socket_stack.add(lf_inout_socket); | |||||
| Vector<lf::Socket *> 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 (lf_socket_stack.contains(lf_origin_socket)) { | |||||
| const Span<lf::Socket *> cycle = lf_socket_stack.as_span().drop_front( | |||||
| lf_socket_stack.index_of(lf_origin_socket)); | |||||
| lf_cycles.append(cycle); | |||||
| for (lf::Socket *lf_cycle_socket : cycle) { | |||||
| if (lf_cycle_socket->is_input() && | |||||
| this->is_output_is_used_socket(lf_cycle_socket->as_input())) { | |||||
| lf::InputSocket &lf_cycle_input_socket = lf_cycle_socket->as_input(); | |||||
| lf_graph_->clear_origin(lf_cycle_input_socket); | |||||
| cleared_origins.append(&lf_cycle_input_socket); | |||||
| static const bool static_true = true; | |||||
| lf_cycle_input_socket.set_default_value(&static_true); | |||||
| } | |||||
| } | |||||
| } | |||||
| else if (!lf_done_sockets.contains(lf_origin_socket)) { | |||||
| lf_sockets_to_check.push(lf_origin_socket); | |||||
| pushed_socket = true; | |||||
| } | |||||
| } | |||||
| if (pushed_socket) { | |||||
| continue; | |||||
| } | |||||
| lf_done_sockets.add(lf_inout_socket); | |||||
| lf_sockets_to_check.pop(); | |||||
| lf_socket_stack.pop(); | |||||
| } | |||||
| // std::cout << "Cycles: " << lf_cycles.size() << "\n"; | |||||
| // for (const Span<lf::Socket *> lf_cycle : lf_cycles) { | |||||
| // std::cout << " "; | |||||
| // for (lf::Socket *lf_socket : lf_cycle) { | |||||
| // std::cout << lf_socket->node().name() << ":" << lf_socket->name() << " -> "; | |||||
| // } | |||||
| // std::cout << "\n"; | |||||
| // } | |||||
| // std::cout << "Cleared origins: " << cleared_origins.size() << "\n"; | |||||
| // for (const lf::Socket *lf_socket : cleared_origins) { | |||||
| // std::cout << " " << lf_socket->node().name() << ":" << lf_socket->name() << "\n"; | |||||
| // } | |||||
| } | |||||
| 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(); | ||||
| } | } | ||||
| bool is_output_is_used_socket(const lf::InputSocket &lf_socket) const | |||||
| { | |||||
| return lf_socket.name().find("output is used") != std::string::npos || | |||||
| lf_socket.name().find("Add '") != std::string::npos; | |||||
| } | |||||
| 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_); | ||||
| } | } | ||||
| void build_group_input_node() | void build_group_input_node() | ||||
| { | { | ||||
| Show All 9 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_by_bsocket_output_.values()) { | |||||
| lf_node.input(i).set_default_value(&static_false); | |||||
| } | |||||
| 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_attribute_usage_.items()) { | |||||
| use_anonymous_attributes_map_.add_new(&bnode.output_by_identifier(identifier), | |||||
| &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 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 auto [bsocket, lf_used_socket] : builder_.socket_is_used_map_.items()) { | |||||
| 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_.linked_anonymous_attribute_used_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?