diff options
Diffstat (limited to 'source/blender/nodes/intern/geometry_nodes_lazy_function.cc')
-rw-r--r-- | source/blender/nodes/intern/geometry_nodes_lazy_function.cc | 279 |
1 files changed, 197 insertions, 82 deletions
diff --git a/source/blender/nodes/intern/geometry_nodes_lazy_function.cc b/source/blender/nodes/intern/geometry_nodes_lazy_function.cc index e4d476e6374..197f0997160 100644 --- a/source/blender/nodes/intern/geometry_nodes_lazy_function.cc +++ b/source/blender/nodes/intern/geometry_nodes_lazy_function.cc @@ -16,6 +16,7 @@ #include "NOD_multi_function.hh" #include "NOD_node_declaration.hh" +#include "BLI_lazy_threading.hh" #include "BLI_map.hh" #include "DNA_ID.h" @@ -27,6 +28,8 @@ #include "FN_field_cpp_type.hh" #include "FN_lazy_function_graph_executor.hh" +#include "DEG_depsgraph_query.h" + namespace blender::nodes { using fn::ValueOrField; @@ -134,13 +137,14 @@ class LazyFunctionForGeometryNode : public LazyFunction { 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( *user_data->compute_context); - tree_logger.node_execution_times.append({node_.name, start_time, end_time}); + tree_logger.node_execution_times.append( + {tree_logger.allocator->copy_string(node_.name), start_time, end_time}); } } }; /** - * 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. */ class LazyFunctionForMultiInput : public LazyFunction { @@ -154,8 +158,9 @@ class LazyFunctionForMultiInput : public LazyFunction { base_type_ = get_socket_cpp_type(socket); BLI_assert(base_type_ != nullptr); BLI_assert(socket.is_multi_input()); + const bNodeTree &btree = socket.owner_tree(); for (const bNodeLink *link : socket.directly_linked_links()) { - if (!link->is_muted()) { + if (!(link->is_muted() || nodeIsDanglingReroute(&btree, link->fromnode))) { inputs_.append({"Input", *base_type_}); } } @@ -164,14 +169,14 @@ class LazyFunctionForMultiInput : public LazyFunction { outputs_.append({"Output", *vector_type}); } - void execute_impl(lf::Params ¶ms, const lf::Context &UNUSED(context)) const override + void execute_impl(lf::Params ¶ms, const lf::Context & /*context*/) const override { /* Currently we only have multi-inputs for geometry and string sockets. This could be * generalized in the future. */ base_type_->to_static_type_tag<GeometrySet, ValueOrField<std::string>>([&](auto type_tag) { using T = typename decltype(type_tag)::type; if constexpr (std::is_void_v<T>) { - /* This type is not support in this node for now. */ + /* This type is not supported in this node for now. */ BLI_assert_unreachable(); } else { @@ -198,7 +203,7 @@ class LazyFunctionForRerouteNode : public LazyFunction { outputs_.append({"Output", type}); } - void execute_impl(lf::Params ¶ms, const lf::Context &UNUSED(context)) const override + void execute_impl(lf::Params ¶ms, const lf::Context & /*context*/) const override { void *input_value = params.try_get_input_data_ptr(0); void *output_value = params.get_output_data_ptr(0); @@ -211,6 +216,28 @@ class LazyFunctionForRerouteNode : public LazyFunction { }; /** + * Lazy functions for nodes whose type cannot be found. An undefined function just outputs default + * values. It's useful to have so other parts of the conversion don't have to care about undefined + * nodes. + */ +class LazyFunctionForUndefinedNode : public LazyFunction { + public: + LazyFunctionForUndefinedNode(const bNode &node, Vector<const bNodeSocket *> &r_used_outputs) + { + debug_name_ = "Undefined"; + Vector<const bNodeSocket *> dummy_used_inputs; + Vector<lf::Input> dummy_inputs; + lazy_function_interface_from_node( + node, dummy_used_inputs, r_used_outputs, dummy_inputs, outputs_); + } + + void execute_impl(lf::Params ¶ms, const lf::Context & /*context*/) const override + { + params.set_default_remaining_outputs(); + } +}; + +/** * Executes a multi-function. If all inputs are single values, the results will also be single * values. If any input is a field, the outputs will also be fields. */ @@ -325,7 +352,7 @@ class LazyFunctionForMutedNode : public LazyFunction { } } - void execute_impl(lf::Params ¶ms, const lf::Context &UNUSED(context)) const override + void execute_impl(lf::Params ¶ms, const lf::Context & /*context*/) const override { for (const int output_i : outputs_.index_range()) { if (params.output_was_set(output_i)) { @@ -397,7 +424,7 @@ class LazyFunctionForMultiFunctionConversion : public LazyFunction { outputs_.append({"To", to}); } - void execute_impl(lf::Params ¶ms, const lf::Context &UNUSED(context)) const override + void execute_impl(lf::Params ¶ms, const lf::Context & /*context*/) const override { const void *from_value = params.try_get_input_data_ptr(0); void *to_value = params.get_output_data_ptr(0); @@ -416,18 +443,16 @@ class LazyFunctionForMultiFunctionConversion : public LazyFunction { */ class LazyFunctionForMultiFunctionNode : public LazyFunction { private: - const bNode &node_; const NodeMultiFunctions::Item fn_item_; Vector<const ValueOrFieldCPPType *> input_types_; Vector<const ValueOrFieldCPPType *> output_types_; - Vector<const bNodeSocket *> output_sockets_; public: LazyFunctionForMultiFunctionNode(const bNode &node, NodeMultiFunctions::Item fn_item, Vector<const bNodeSocket *> &r_used_inputs, Vector<const bNodeSocket *> &r_used_outputs) - : node_(node), fn_item_(std::move(fn_item)) + : fn_item_(std::move(fn_item)) { BLI_assert(fn_item_.fn != nullptr); debug_name_ = node.name; @@ -438,10 +463,9 @@ class LazyFunctionForMultiFunctionNode : public LazyFunction { for (const lf::Output &fn_output : outputs_) { output_types_.append(dynamic_cast<const ValueOrFieldCPPType *>(fn_output.type)); } - output_sockets_ = r_used_outputs; } - void execute_impl(lf::Params ¶ms, const lf::Context &UNUSED(context)) const override + void execute_impl(lf::Params ¶ms, const lf::Context & /*context*/) const override { Vector<const void *> input_values(inputs_.size()); Vector<void *> output_values(outputs_.size()); @@ -478,7 +502,7 @@ class LazyFunctionForImplicitInput : public LazyFunction { outputs_.append({"Output", type}); } - void execute_impl(lf::Params ¶ms, const lf::Context &UNUSED(context)) const override + void execute_impl(lf::Params ¶ms, const lf::Context & /*context*/) const override { void *value = params.get_output_data_ptr(0); init_fn_(value); @@ -514,21 +538,61 @@ class LazyFunctionForViewerNode : public LazyFunction { { GeoNodesLFUserData *user_data = dynamic_cast<GeoNodesLFUserData *>(context.user_data); BLI_assert(user_data != nullptr); + if (user_data->modifier_data == nullptr) { + return; + } + if (user_data->modifier_data->eval_log == nullptr) { + return; + } GeometrySet geometry = params.extract_input<GeometrySet>(0); + const NodeGeometryViewer *storage = static_cast<NodeGeometryViewer *>(bnode_.storage); - GField field; if (use_field_input_) { const void *value_or_field = params.try_get_input_data_ptr(1); BLI_assert(value_or_field != nullptr); const ValueOrFieldCPPType &value_or_field_type = static_cast<const ValueOrFieldCPPType &>( *inputs_[1].type); - field = value_or_field_type.as_field(value_or_field); + GField field = value_or_field_type.as_field(value_or_field); + const eAttrDomain domain = eAttrDomain(storage->domain); + const StringRefNull viewer_attribute_name = ".viewer"; + if (domain == ATTR_DOMAIN_INSTANCE) { + if (geometry.has_instances()) { + GeometryComponent &component = geometry.get_component_for_write( + GEO_COMPONENT_TYPE_INSTANCES); + bke::try_capture_field_on_geometry( + component, viewer_attribute_name, ATTR_DOMAIN_INSTANCE, field); + } + } + else { + geometry.modify_geometry_sets([&](GeometrySet &geometry) { + for (const GeometryComponentType type : {GEO_COMPONENT_TYPE_MESH, + GEO_COMPONENT_TYPE_POINT_CLOUD, + GEO_COMPONENT_TYPE_CURVE}) { + if (geometry.has(type)) { + GeometryComponent &component = geometry.get_component_for_write(type); + eAttrDomain used_domain = domain; + if (used_domain == ATTR_DOMAIN_AUTO) { + if (const std::optional<eAttrDomain> detected_domain = + bke::try_detect_field_domain(component, field)) { + used_domain = *detected_domain; + } + else { + used_domain = type == GEO_COMPONENT_TYPE_MESH ? ATTR_DOMAIN_CORNER : + ATTR_DOMAIN_POINT; + } + } + bke::try_capture_field_on_geometry( + component, viewer_attribute_name, used_domain, field); + } + } + }); + } } geo_eval_log::GeoTreeLogger &tree_logger = user_data->modifier_data->eval_log->get_local_tree_logger(*user_data->compute_context); - tree_logger.log_viewer_node(bnode_, geometry, field); + tree_logger.log_viewer_node(bnode_, std::move(geometry)); } }; @@ -539,6 +603,8 @@ class LazyFunctionForViewerNode : public LazyFunction { class LazyFunctionForGroupNode : public LazyFunction { private: const bNode &group_node_; + bool has_many_nodes_ = false; + bool use_fallback_outputs_ = false; std::optional<GeometryNodesLazyFunctionLogger> lf_logger_; std::optional<GeometryNodesLazyFunctionSideEffectProvider> lf_side_effect_provider_; std::optional<lf::GraphExecutor> graph_executor_; @@ -557,6 +623,8 @@ class LazyFunctionForGroupNode : public LazyFunction { bNodeTree *group_btree = reinterpret_cast<bNodeTree *>(group_node_.id); BLI_assert(group_btree != nullptr); + has_many_nodes_ = lf_graph_info.num_inline_nodes_approximate > 1000; + Vector<const lf::OutputSocket *> graph_inputs; for (const lf::OutputSocket *socket : lf_graph_info.mapping.group_input_sockets) { if (socket != nullptr) { @@ -573,9 +641,12 @@ class LazyFunctionForGroupNode : public LazyFunction { } } } + else { + use_fallback_outputs_ = true; + } lf_logger_.emplace(lf_graph_info); - lf_side_effect_provider_.emplace(lf_graph_info); + lf_side_effect_provider_.emplace(); graph_executor_.emplace(lf_graph_info.graph, std::move(graph_inputs), std::move(graph_outputs), @@ -588,6 +659,17 @@ class LazyFunctionForGroupNode : public LazyFunction { GeoNodesLFUserData *user_data = dynamic_cast<GeoNodesLFUserData *>(context.user_data); BLI_assert(user_data != nullptr); + if (has_many_nodes_) { + /* 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. */ + 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. */ bke::NodeGroupComputeContext compute_context{user_data->compute_context, group_node_.name}; GeoNodesLFUserData group_user_data = *user_data; @@ -599,12 +681,12 @@ class LazyFunctionForGroupNode : public LazyFunction { graph_executor_->execute(params, group_context); } - void *init_storage(LinearAllocator<> &allocator) const + void *init_storage(LinearAllocator<> &allocator) const override { return graph_executor_->init_storage(allocator); } - void destruct_storage(void *storage) const + void destruct_storage(void *storage) const override { graph_executor_->destruct_storage(storage); } @@ -679,6 +761,7 @@ struct GeometryNodesLazyFunctionGraphBuilder { this->add_default_inputs(); lf_graph_->update_node_indices(); + lf_graph_info_->num_inline_nodes_approximate += lf_graph_->nodes().size(); } private: @@ -776,6 +859,11 @@ struct GeometryNodesLazyFunctionGraphBuilder { *bnode); if (fn_item.fn != nullptr) { this->handle_multi_function_node(*bnode, fn_item); + break; + } + if (node_type == &NodeTypeUndefined) { + this->handle_undefined_node(*bnode); + break; } /* Nodes that don't match any of the criteria above are just ignored. */ break; @@ -890,6 +978,8 @@ struct GeometryNodesLazyFunctionGraphBuilder { mapping_->bsockets_by_lf_socket_map.add(&lf_socket, &bsocket); } mapping_->group_node_map.add(&bnode, &lf_node); + lf_graph_info_->num_inline_nodes_approximate += + group_lf_graph_info->num_inline_nodes_approximate; } void handle_geometry_node(const bNode &bnode) @@ -969,6 +1059,21 @@ struct GeometryNodesLazyFunctionGraphBuilder { mapping_->viewer_node_map.add(&bnode, &lf_node); } + void handle_undefined_node(const bNode &bnode) + { + Vector<const bNodeSocket *> used_outputs; + auto lazy_function = std::make_unique<LazyFunctionForUndefinedNode>(bnode, used_outputs); + lf::FunctionNode &lf_node = lf_graph_->add_function(*lazy_function); + lf_graph_info_->functions.append(std::move(lazy_function)); + + for (const int i : used_outputs.index_range()) { + const bNodeSocket &bsocket = *used_outputs[i]; + lf::OutputSocket &lf_socket = lf_node.output(i); + output_socket_map_.add(&bsocket, &lf_socket); + mapping_->bsockets_by_lf_socket_map.add(&lf_socket, &bsocket); + } + } + void handle_links() { for (const auto item : output_socket_map_.items()) { @@ -978,6 +1083,10 @@ struct GeometryNodesLazyFunctionGraphBuilder { void insert_links_from_socket(const bNodeSocket &from_bsocket, lf::OutputSocket &from_lf_socket) { + if (nodeIsDanglingReroute(&btree_, &from_bsocket.owner_node())) { + return; + } + const Span<const bNodeLink *> links_from_bsocket = from_bsocket.directly_linked_links(); struct TypeWithLinks { @@ -991,10 +1100,10 @@ struct GeometryNodesLazyFunctionGraphBuilder { if (link->is_muted()) { continue; } - const bNodeSocket &to_bsocket = *link->tosock; - if (!to_bsocket.is_available()) { + if (!link->is_available()) { continue; } + const bNodeSocket &to_bsocket = *link->tosock; const CPPType *to_type = get_socket_cpp_type(to_bsocket); if (to_type == nullptr) { continue; @@ -1044,7 +1153,8 @@ struct GeometryNodesLazyFunctionGraphBuilder { if (multi_input_link == link) { break; } - if (!multi_input_link->is_muted()) { + if (!(multi_input_link->is_muted() || + nodeIsDanglingReroute(&btree_, multi_input_link->fromnode))) { link_index++; } } @@ -1135,79 +1245,41 @@ struct GeometryNodesLazyFunctionGraphBuilder { bool try_add_implicit_input(const bNodeSocket &input_bsocket, lf::InputSocket &input_lf_socket) { const bNode &bnode = input_bsocket.owner_node(); - const NodeDeclaration *node_declaration = bnode.declaration(); - if (node_declaration == nullptr) { + const SocketDeclaration *socket_decl = input_bsocket.runtime->declaration; + if (socket_decl == nullptr) { return false; } - const SocketDeclaration &socket_declaration = - *node_declaration->inputs()[input_bsocket.index()]; - if (socket_declaration.input_field_type() != InputSocketFieldType::Implicit) { + if (socket_decl->input_field_type() != InputSocketFieldType::Implicit) { return false; } - const CPPType &type = input_lf_socket.type(); - std::function<void(void *)> init_fn = this->get_implicit_input_init_function(bnode, - input_bsocket); - if (!init_fn) { + const ImplicitInputValueFn *implicit_input_fn = socket_decl->implicit_input_fn(); + if (implicit_input_fn == nullptr) { return false; } - + std::function<void(void *)> init_fn = [&bnode, implicit_input_fn](void *r_value) { + (*implicit_input_fn)(bnode, r_value); + }; + const CPPType &type = input_lf_socket.type(); auto lazy_function = std::make_unique<LazyFunctionForImplicitInput>(type, std::move(init_fn)); lf::Node &lf_node = lf_graph_->add_function(*lazy_function); lf_graph_info_->functions.append(std::move(lazy_function)); lf_graph_->add_link(lf_node.output(0), input_lf_socket); return true; } - - std::function<void(void *)> get_implicit_input_init_function(const bNode &bnode, - const bNodeSocket &bsocket) - { - const bNodeSocketType &socket_type = *bsocket.typeinfo; - if (socket_type.type == SOCK_VECTOR) { - if (bnode.type == GEO_NODE_SET_CURVE_HANDLES) { - StringRef side = ((NodeGeometrySetCurveHandlePositions *)bnode.storage)->mode == - GEO_NODE_CURVE_HANDLE_LEFT ? - "handle_left" : - "handle_right"; - return [side](void *r_value) { - new (r_value) ValueOrField<float3>(bke::AttributeFieldInput::Create<float3>(side)); - }; - } - else if (bnode.type == GEO_NODE_EXTRUDE_MESH) { - return [](void *r_value) { - new (r_value) - ValueOrField<float3>(Field<float3>(std::make_shared<bke::NormalFieldInput>())); - }; - } - else { - return [](void *r_value) { - new (r_value) ValueOrField<float3>(bke::AttributeFieldInput::Create<float3>("position")); - }; - } - } - else if (socket_type.type == SOCK_INT) { - if (ELEM(bnode.type, FN_NODE_RANDOM_VALUE, GEO_NODE_INSTANCE_ON_POINTS)) { - return [](void *r_value) { - new (r_value) - ValueOrField<int>(Field<int>(std::make_shared<bke::IDAttributeFieldInput>())); - }; - } - else { - return [](void *r_value) { - new (r_value) ValueOrField<int>(Field<int>(std::make_shared<fn::IndexFieldInput>())); - }; - } - } - return {}; - } }; const GeometryNodesLazyFunctionGraphInfo *ensure_geometry_nodes_lazy_function_graph( const bNodeTree &btree) { btree.ensure_topology_cache(); - if (btree.has_link_cycle()) { + if (btree.has_available_link_cycle()) { return nullptr; } + if (const ID *id_orig = DEG_get_original_id(const_cast<ID *>(&btree.id))) { + if (id_orig->tag & LIB_TAG_MISSING) { + return nullptr; + } + } std::unique_ptr<GeometryNodesLazyFunctionGraphInfo> &lf_graph_info_ptr = btree.runtime->geometry_nodes_lazy_function_graph_info; @@ -1300,12 +1372,6 @@ void GeometryNodesLazyFunctionLogger::dump_when_input_is_set_twice( user_data->compute_context->print_stack(std::cout, ss.str()); } -GeometryNodesLazyFunctionSideEffectProvider::GeometryNodesLazyFunctionSideEffectProvider( - const GeometryNodesLazyFunctionGraphInfo &lf_graph_info) - : lf_graph_info_(lf_graph_info) -{ -} - Vector<const lf::FunctionNode *> GeometryNodesLazyFunctionSideEffectProvider:: get_nodes_with_side_effects(const lf::Context &context) const { @@ -1324,4 +1390,53 @@ GeometryNodesLazyFunctionGraphInfo::~GeometryNodesLazyFunctionGraphInfo() } } +[[maybe_unused]] static void add_thread_id_debug_message( + const GeometryNodesLazyFunctionGraphInfo &lf_graph_info, + const lf::FunctionNode &node, + const lf::Context &context) +{ + static std::atomic<int> thread_id_source = 0; + static thread_local const int thread_id = thread_id_source.fetch_add(1); + static thread_local const std::string thread_id_str = "Thread: " + std::to_string(thread_id); + + GeoNodesLFUserData *user_data = dynamic_cast<GeoNodesLFUserData *>(context.user_data); + BLI_assert(user_data != nullptr); + if (user_data->modifier_data->eval_log == nullptr) { + return; + } + geo_eval_log::GeoTreeLogger &tree_logger = + user_data->modifier_data->eval_log->get_local_tree_logger(*user_data->compute_context); + + /* Find corresponding node based on the socket mapping. */ + auto check_sockets = [&](const Span<const lf::Socket *> lf_sockets) { + for (const lf::Socket *lf_socket : lf_sockets) { + const Span<const bNodeSocket *> bsockets = + lf_graph_info.mapping.bsockets_by_lf_socket_map.lookup(lf_socket); + if (!bsockets.is_empty()) { + const bNodeSocket &bsocket = *bsockets[0]; + const bNode &bnode = bsocket.owner_node(); + tree_logger.debug_messages.append( + {tree_logger.allocator->copy_string(bnode.name), thread_id_str}); + return true; + } + } + return false; + }; + + if (check_sockets(node.inputs().cast<const lf::Socket *>())) { + return; + } + check_sockets(node.outputs().cast<const lf::Socket *>()); +} + +void GeometryNodesLazyFunctionLogger::log_before_node_execute(const lf::FunctionNode &node, + const lf::Params & /*params*/, + const lf::Context &context) const +{ + /* Enable this to see the threads that invoked a node. */ + if constexpr (false) { + add_thread_id_debug_message(lf_graph_info_, node, context); + } +} + } // namespace blender::nodes |