From a9250cb1f1f91b7a8308059ef505b7a9d523a521 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Wed, 14 Sep 2022 12:02:27 +0200 Subject: Cleanup: remove unused fields + add override --- .../blender/nodes/intern/geometry_nodes_lazy_function.cc | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) (limited to 'source/blender/nodes/intern/geometry_nodes_lazy_function.cc') diff --git a/source/blender/nodes/intern/geometry_nodes_lazy_function.cc b/source/blender/nodes/intern/geometry_nodes_lazy_function.cc index e4d476e6374..b0b7449b889 100644 --- a/source/blender/nodes/intern/geometry_nodes_lazy_function.cc +++ b/source/blender/nodes/intern/geometry_nodes_lazy_function.cc @@ -416,7 +416,6 @@ class LazyFunctionForMultiFunctionConversion : public LazyFunction { */ class LazyFunctionForMultiFunctionNode : public LazyFunction { private: - const bNode &node_; const NodeMultiFunctions::Item fn_item_; Vector input_types_; Vector output_types_; @@ -427,7 +426,7 @@ class LazyFunctionForMultiFunctionNode : public LazyFunction { NodeMultiFunctions::Item fn_item, Vector &r_used_inputs, Vector &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; @@ -575,7 +574,7 @@ class LazyFunctionForGroupNode : public LazyFunction { } 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), @@ -599,12 +598,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); } @@ -1300,12 +1299,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 GeometryNodesLazyFunctionSideEffectProvider:: get_nodes_with_side_effects(const lf::Context &context) const { -- cgit v1.2.3 From 3d93525069d230c4cd355d48a99a7b8e67378db5 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Fri, 16 Sep 2022 12:53:18 -0500 Subject: Cleanup: Remove unused member variable in lazy function graph --- source/blender/nodes/intern/geometry_nodes_lazy_function.cc | 2 -- 1 file changed, 2 deletions(-) (limited to 'source/blender/nodes/intern/geometry_nodes_lazy_function.cc') diff --git a/source/blender/nodes/intern/geometry_nodes_lazy_function.cc b/source/blender/nodes/intern/geometry_nodes_lazy_function.cc index b0b7449b889..aa684b69772 100644 --- a/source/blender/nodes/intern/geometry_nodes_lazy_function.cc +++ b/source/blender/nodes/intern/geometry_nodes_lazy_function.cc @@ -419,7 +419,6 @@ class LazyFunctionForMultiFunctionNode : public LazyFunction { const NodeMultiFunctions::Item fn_item_; Vector input_types_; Vector output_types_; - Vector output_sockets_; public: LazyFunctionForMultiFunctionNode(const bNode &node, @@ -437,7 +436,6 @@ class LazyFunctionForMultiFunctionNode : public LazyFunction { for (const lf::Output &fn_output : outputs_) { output_types_.append(dynamic_cast(fn_output.type)); } - output_sockets_ = r_used_outputs; } void execute_impl(lf::Params ¶ms, const lf::Context &UNUSED(context)) const override -- cgit v1.2.3 From 4cea4f4c5f845a85dd5bc95967b5ce63296015d1 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Fri, 16 Sep 2022 12:54:46 -0500 Subject: Fix: Geometry nodes crash with undefined node The new evaluator crashes for multi-input sockets coming from undefined nodes. The multi-input socket lazy node tries to retrieve the default value since the undefined node never created output values. But there is no default value stored because the socket is linked. Differential Revision: https://developer.blender.org/D15980 --- .../nodes/intern/geometry_nodes_lazy_function.cc | 42 ++++++++++++++++++++++ 1 file changed, 42 insertions(+) (limited to 'source/blender/nodes/intern/geometry_nodes_lazy_function.cc') diff --git a/source/blender/nodes/intern/geometry_nodes_lazy_function.cc b/source/blender/nodes/intern/geometry_nodes_lazy_function.cc index aa684b69772..137057414d4 100644 --- a/source/blender/nodes/intern/geometry_nodes_lazy_function.cc +++ b/source/blender/nodes/intern/geometry_nodes_lazy_function.cc @@ -210,6 +210,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 &r_used_outputs) + { + debug_name_ = "Undefined"; + Vector dummy_used_inputs; + Vector 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 &UNUSED(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. @@ -773,6 +795,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; @@ -966,6 +993,21 @@ struct GeometryNodesLazyFunctionGraphBuilder { mapping_->viewer_node_map.add(&bnode, &lf_node); } + void handle_undefined_node(const bNode &bnode) + { + Vector used_outputs; + auto lazy_function = std::make_unique(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()) { -- cgit v1.2.3 From 7549e0c5ae36be739f0e0a38e87eb85428808e89 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Sat, 17 Sep 2022 12:08:43 +0200 Subject: Geometry Nodes: use stringref instead of string in logger This reduces logging overhead. The performance difference is only significant when there are many fast nodes. In my test file with many math nodes, the performance improved from 720ms to 630ms. --- source/blender/nodes/intern/geometry_nodes_lazy_function.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'source/blender/nodes/intern/geometry_nodes_lazy_function.cc') diff --git a/source/blender/nodes/intern/geometry_nodes_lazy_function.cc b/source/blender/nodes/intern/geometry_nodes_lazy_function.cc index 137057414d4..996cea26718 100644 --- a/source/blender/nodes/intern/geometry_nodes_lazy_function.cc +++ b/source/blender/nodes/intern/geometry_nodes_lazy_function.cc @@ -134,7 +134,8 @@ 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}); } } }; -- cgit v1.2.3 From 5c81d3bd4691214164a7f071d239ef6c84dba8dc Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Tue, 20 Sep 2022 10:59:12 +0200 Subject: Geometry Nodes: improve evaluator with lazy threading In large node setup the threading overhead was sometimes very significant. That's especially true when most nodes do very little work. This commit improves the scheduling by not using multi-threading in many cases unless it's likely that it will be worth it. For more details see the comments in `BLI_lazy_threading.hh`. Differential Revision: https://developer.blender.org/D15976 --- .../nodes/intern/geometry_nodes_lazy_function.cc | 61 ++++++++++++++++++++++ 1 file changed, 61 insertions(+) (limited to 'source/blender/nodes/intern/geometry_nodes_lazy_function.cc') diff --git a/source/blender/nodes/intern/geometry_nodes_lazy_function.cc b/source/blender/nodes/intern/geometry_nodes_lazy_function.cc index 996cea26718..af6861a59c0 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" @@ -559,6 +560,7 @@ class LazyFunctionForViewerNode : public LazyFunction { class LazyFunctionForGroupNode : public LazyFunction { private: const bNode &group_node_; + bool has_many_nodes_ = false; std::optional lf_logger_; std::optional lf_side_effect_provider_; std::optional graph_executor_; @@ -577,6 +579,8 @@ class LazyFunctionForGroupNode : public LazyFunction { bNodeTree *group_btree = reinterpret_cast(group_node_.id); BLI_assert(group_btree != nullptr); + has_many_nodes_ = lf_graph_info.num_inline_nodes_approximate > 1000; + Vector graph_inputs; for (const lf::OutputSocket *socket : lf_graph_info.mapping.group_input_sockets) { if (socket != nullptr) { @@ -608,6 +612,12 @@ class LazyFunctionForGroupNode : public LazyFunction { GeoNodesLFUserData *user_data = dynamic_cast(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(); + } + /* 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; @@ -699,6 +709,7 @@ struct GeometryNodesLazyFunctionGraphBuilder { this->add_default_inputs(); lf_graph_->update_node_indices(); + lf_graph_info_->num_inline_nodes_approximate += lf_graph_->nodes().size(); } private: @@ -915,6 +926,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) @@ -1358,4 +1371,52 @@ GeometryNodesLazyFunctionGraphInfo::~GeometryNodesLazyFunctionGraphInfo() } } +static void add_thread_id_debug_message(const GeometryNodesLazyFunctionGraphInfo &lf_graph_info, + const lf::FunctionNode &node, + const lf::Context &context) +{ + static std::atomic 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(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 lf_sockets) { + for (const lf::Socket *lf_socket : lf_sockets) { + const Span 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())) { + return; + } + check_sockets(node.outputs().cast()); +} + +void GeometryNodesLazyFunctionLogger::log_before_node_execute(const lf::FunctionNode &node, + const lf::Params &UNUSED(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 -- cgit v1.2.3 From 2b4cb893e7ebb33c24c7413ba0ff10eeb78bddfe Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Tue, 20 Sep 2022 13:21:03 +0200 Subject: Fix T101214: hidden link can cause cycle in node tree Links that are linked to unavailable sockets should be ignored. --- source/blender/nodes/intern/geometry_nodes_lazy_function.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'source/blender/nodes/intern/geometry_nodes_lazy_function.cc') diff --git a/source/blender/nodes/intern/geometry_nodes_lazy_function.cc b/source/blender/nodes/intern/geometry_nodes_lazy_function.cc index af6861a59c0..718aaef5680 100644 --- a/source/blender/nodes/intern/geometry_nodes_lazy_function.cc +++ b/source/blender/nodes/intern/geometry_nodes_lazy_function.cc @@ -1044,10 +1044,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; @@ -1258,7 +1258,7 @@ const GeometryNodesLazyFunctionGraphInfo *ensure_geometry_nodes_lazy_function_gr const bNodeTree &btree) { btree.ensure_topology_cache(); - if (btree.has_link_cycle()) { + if (btree.has_available_link_cycle()) { return nullptr; } -- cgit v1.2.3 From afe91903af49abafb5796c6d13447b119d37c412 Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Tue, 20 Sep 2022 15:39:29 +0200 Subject: Cleanup: Strict compiler warning Resolves -Wunneeded-internal-declaration warning generated by Clang. --- source/blender/nodes/intern/geometry_nodes_lazy_function.cc | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'source/blender/nodes/intern/geometry_nodes_lazy_function.cc') diff --git a/source/blender/nodes/intern/geometry_nodes_lazy_function.cc b/source/blender/nodes/intern/geometry_nodes_lazy_function.cc index 718aaef5680..4ffeb815e34 100644 --- a/source/blender/nodes/intern/geometry_nodes_lazy_function.cc +++ b/source/blender/nodes/intern/geometry_nodes_lazy_function.cc @@ -1371,9 +1371,10 @@ GeometryNodesLazyFunctionGraphInfo::~GeometryNodesLazyFunctionGraphInfo() } } -static void add_thread_id_debug_message(const GeometryNodesLazyFunctionGraphInfo &lf_graph_info, - const lf::FunctionNode &node, - const lf::Context &context) +[[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 thread_id_source = 0; static thread_local const int thread_id = thread_id_source.fetch_add(1); -- cgit v1.2.3 From 35375380d73b93999b879164fd59266ee044472c Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Thu, 22 Sep 2022 23:59:36 -0500 Subject: Cleanup: Grammar fix in lazy function --- source/blender/nodes/intern/geometry_nodes_lazy_function.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'source/blender/nodes/intern/geometry_nodes_lazy_function.cc') diff --git a/source/blender/nodes/intern/geometry_nodes_lazy_function.cc b/source/blender/nodes/intern/geometry_nodes_lazy_function.cc index 4ffeb815e34..cb296cdd93f 100644 --- a/source/blender/nodes/intern/geometry_nodes_lazy_function.cc +++ b/source/blender/nodes/intern/geometry_nodes_lazy_function.cc @@ -142,7 +142,7 @@ class LazyFunctionForGeometryNode : public LazyFunction { }; /** - * 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 { @@ -173,7 +173,7 @@ class LazyFunctionForMultiInput : public LazyFunction { base_type_->to_static_type_tag>([&](auto type_tag) { using T = typename decltype(type_tag)::type; if constexpr (std::is_void_v) { - /* This type is not support in this node for now. */ + /* This type is not supported in this node for now. */ BLI_assert_unreachable(); } else { -- cgit v1.2.3 From c8ee70c96200548699a2d038a93208c5723f91e7 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Sun, 25 Sep 2022 19:16:53 +0200 Subject: Geometry Nodes: decentralize implicit input definitions Previously, all implicit inputs where stored in a centralized place. Now the information which nodes have which implicit inputs is stored in the nodes directly. --- .../nodes/intern/geometry_nodes_lazy_function.cc | 61 ++++------------------ 1 file changed, 9 insertions(+), 52 deletions(-) (limited to 'source/blender/nodes/intern/geometry_nodes_lazy_function.cc') diff --git a/source/blender/nodes/intern/geometry_nodes_lazy_function.cc b/source/blender/nodes/intern/geometry_nodes_lazy_function.cc index cb296cdd93f..55697478989 100644 --- a/source/blender/nodes/intern/geometry_nodes_lazy_function.cc +++ b/source/blender/nodes/intern/geometry_nodes_lazy_function.cc @@ -1188,70 +1188,27 @@ 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 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 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(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 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(bke::AttributeFieldInput::Create(side)); - }; - } - else if (bnode.type == GEO_NODE_EXTRUDE_MESH) { - return [](void *r_value) { - new (r_value) - ValueOrField(Field(std::make_shared())); - }; - } - else { - return [](void *r_value) { - new (r_value) ValueOrField(bke::AttributeFieldInput::Create("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(Field(std::make_shared())); - }; - } - else { - return [](void *r_value) { - new (r_value) ValueOrField(Field(std::make_shared())); - }; - } - } - return {}; - } }; const GeometryNodesLazyFunctionGraphInfo *ensure_geometry_nodes_lazy_function_graph( -- cgit v1.2.3 From e746999aa937f6de638a339d52e1f2e494014919 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Sun, 25 Sep 2022 23:07:21 +0200 Subject: Fix T101309: crash when executing geometry nodes without logger --- source/blender/nodes/intern/geometry_nodes_lazy_function.cc | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'source/blender/nodes/intern/geometry_nodes_lazy_function.cc') diff --git a/source/blender/nodes/intern/geometry_nodes_lazy_function.cc b/source/blender/nodes/intern/geometry_nodes_lazy_function.cc index 55697478989..7a78547b10b 100644 --- a/source/blender/nodes/intern/geometry_nodes_lazy_function.cc +++ b/source/blender/nodes/intern/geometry_nodes_lazy_function.cc @@ -535,6 +535,12 @@ class LazyFunctionForViewerNode : public LazyFunction { { GeoNodesLFUserData *user_data = dynamic_cast(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(0); -- cgit v1.2.3 From c55d38f00b8c0e6ae8bda9cc66614afe28fb3fc9 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Wed, 28 Sep 2022 17:54:59 +0200 Subject: Geometry Nodes: viewport preview This adds support for showing geometry passed to the Viewer in the 3d viewport (instead of just in the spreadsheet). The "viewer geometry" bypasses the group output. So it is not necessary to change the final output of the node group to be able to see the intermediate geometry. **Activation and deactivation of a viewer node** * A viewer node is activated by clicking on it. * Ctrl+shift+click on any node/socket connects it to the viewer and makes it active. * Ctrl+shift+click in empty space deactivates the active viewer. * When the active viewer is not visible anymore (e.g. another object is selected, or the current node group is exit), it is deactivated. * Clicking on the icon in the header of the Viewer node toggles whether its active or not. **Pinning** * The spreadsheet still allows pinning the active viewer as before. When pinned, the spreadsheet still references the viewer node even when it becomes inactive. * The viewport does not support pinning at the moment. It always shows the active viewer. **Attribute** * When a field is linked to the second input of the viewer node it is displayed as an overlay in the viewport. * When possible the correct domain for the attribute is determined automatically. This does not work in all cases. It falls back to the face corner domain on meshes and the point domain on curves. When necessary, the domain can be picked manually. * The spreadsheet now only shows the "Viewer" column for the domain that is selected in the Viewer node. * Instance attributes are visualized as a constant color per instance. **Viewport Options** * The attribute overlay opacity can be controlled with the "Viewer Node" setting in the overlays popover. * A viewport can be configured not to show intermediate viewer-geometry by disabling the "Viewer Node" option in the "View" menu. **Implementation Details** * The "spreadsheet context path" was generalized to a "viewer path" that is used in more places now. * The viewer node itself determines the attribute domain, evaluates the field and stores the result in a `.viewer` attribute. * A new "viewer attribute' overlay displays the data from the `.viewer` attribute. * The ground truth for the active viewer node is stored in the workspace now. Node editors, spreadsheets and viewports retrieve the active viewer from there unless they are pinned. * The depsgraph object iterator has a new "viewer path" setting. When set, the viewed geometry of the corresponding object is part of the iterator instead of the final evaluated geometry. * To support the instance attribute overlay `DupliObject` was extended to contain the information necessary for drawing the overlay. * The ctrl+shift+click operator has been refactored so that it can make existing links to viewers active again. * The auto-domain-detection in the Viewer node works by checking the "preferred domain" for every field input. If there is not exactly one preferred domain, the fallback is used. Known limitations: * Loose edges of meshes don't have the attribute overlay. This could be added separately if necessary. * Some attributes are hard to visualize as a color directly. For example, the values might have to be normalized or some should be drawn as arrays. For now, we encourage users to build node groups that generate appropriate viewer-geometry. We might include some of that functionality in future versions. Support for displaying attribute values as text in the viewport is planned as well. * There seems to be an issue with the attribute overlay for pointclouds on nvidia gpus, to be investigated. Differential Revision: https://developer.blender.org/D15954 --- .../nodes/intern/geometry_nodes_lazy_function.cc | 40 ++++++++++++++++++++-- 1 file changed, 37 insertions(+), 3 deletions(-) (limited to 'source/blender/nodes/intern/geometry_nodes_lazy_function.cc') diff --git a/source/blender/nodes/intern/geometry_nodes_lazy_function.cc b/source/blender/nodes/intern/geometry_nodes_lazy_function.cc index 7a78547b10b..0dec27da0bd 100644 --- a/source/blender/nodes/intern/geometry_nodes_lazy_function.cc +++ b/source/blender/nodes/intern/geometry_nodes_lazy_function.cc @@ -543,19 +543,53 @@ class LazyFunctionForViewerNode : public LazyFunction { } GeometrySet geometry = params.extract_input(0); + const NodeGeometryViewer *storage = static_cast(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( *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 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)); } }; -- cgit v1.2.3 From db0ef8b00d3c92751ff80a3fc061a4e663639d4b Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Thu, 29 Sep 2022 13:03:15 +0200 Subject: Fix T101410: ignore dangling reroute inputs For consistency with other node systems in Blender and older versions of geometry nodes, dangling reroute inputs should not affect the output. When an input socket is linked to dangling reroutes, its own value should be used instead. --- .../nodes/intern/geometry_nodes_lazy_function.cc | 33 ++++++++++++++++++++++ 1 file changed, 33 insertions(+) (limited to 'source/blender/nodes/intern/geometry_nodes_lazy_function.cc') diff --git a/source/blender/nodes/intern/geometry_nodes_lazy_function.cc b/source/blender/nodes/intern/geometry_nodes_lazy_function.cc index 0dec27da0bd..eca0bef3e65 100644 --- a/source/blender/nodes/intern/geometry_nodes_lazy_function.cc +++ b/source/blender/nodes/intern/geometry_nodes_lazy_function.cc @@ -1071,6 +1071,12 @@ struct GeometryNodesLazyFunctionGraphBuilder { void insert_links_from_socket(const bNodeSocket &from_bsocket, lf::OutputSocket &from_lf_socket) { + const bNode &from_bnode = from_bsocket.owner_node(); + if (this->is_dangling_reroute_input(from_bnode)) { + /* Dangling reroutes should not be used as source of values. */ + return; + } + const Span links_from_bsocket = from_bsocket.directly_linked_links(); struct TypeWithLinks { @@ -1166,6 +1172,33 @@ struct GeometryNodesLazyFunctionGraphBuilder { } } + bool is_dangling_reroute_input(const bNode &node) + { + if (!node.is_reroute()) { + return false; + } + const bNode *iter_node = &node; + /* It is guaranteed at a higher level that there are no link cycles. */ + while (true) { + const Span links = iter_node->input_socket(0).directly_linked_links(); + BLI_assert(links.size() <= 1); + if (links.is_empty()) { + return true; + } + const bNodeLink &link = *links[0]; + if (!link.is_available()) { + return false; + } + if (link.is_muted()) { + return false; + } + iter_node = link.fromnode; + if (!iter_node->is_reroute()) { + return false; + } + } + } + lf::OutputSocket *insert_type_conversion_if_necessary( lf::OutputSocket &from_socket, const CPPType &to_type, -- cgit v1.2.3 From 72ceb7dec136ce65261692d57d8d1251a30c5352 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Mon, 3 Oct 2022 19:15:06 +0200 Subject: Fix: crash when linked geometry node group is missing --- source/blender/nodes/intern/geometry_nodes_lazy_function.cc | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'source/blender/nodes/intern/geometry_nodes_lazy_function.cc') diff --git a/source/blender/nodes/intern/geometry_nodes_lazy_function.cc b/source/blender/nodes/intern/geometry_nodes_lazy_function.cc index eca0bef3e65..553b33ee5ea 100644 --- a/source/blender/nodes/intern/geometry_nodes_lazy_function.cc +++ b/source/blender/nodes/intern/geometry_nodes_lazy_function.cc @@ -28,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; @@ -1291,6 +1293,11 @@ const GeometryNodesLazyFunctionGraphInfo *ensure_geometry_nodes_lazy_function_gr if (btree.has_available_link_cycle()) { return nullptr; } + if (const ID *id_orig = DEG_get_original_id(const_cast(&btree.id))) { + if (id_orig->tag & LIB_TAG_MISSING) { + return nullptr; + } + } std::unique_ptr &lf_graph_info_ptr = btree.runtime->geometry_nodes_lazy_function_graph_info; -- cgit v1.2.3 From 97746129d5870beedc40e3c035c7982ce8a6bebc Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Mon, 3 Oct 2022 17:37:25 -0500 Subject: Cleanup: replace UNUSED macro with commented args in C++ code This is the conventional way of dealing with unused arguments in C++, since it works on all compilers. Regex find and replace: `UNUSED\((\w+)\)` -> `/*$1*/` --- .../blender/nodes/intern/geometry_nodes_lazy_function.cc | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'source/blender/nodes/intern/geometry_nodes_lazy_function.cc') diff --git a/source/blender/nodes/intern/geometry_nodes_lazy_function.cc b/source/blender/nodes/intern/geometry_nodes_lazy_function.cc index 553b33ee5ea..6475a16477a 100644 --- a/source/blender/nodes/intern/geometry_nodes_lazy_function.cc +++ b/source/blender/nodes/intern/geometry_nodes_lazy_function.cc @@ -168,7 +168,7 @@ 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. */ @@ -202,7 +202,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); @@ -230,7 +230,7 @@ class LazyFunctionForUndefinedNode : public LazyFunction { node, dummy_used_inputs, r_used_outputs, dummy_inputs, 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 { params.set_default_remaining_outputs(); } @@ -351,7 +351,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)) { @@ -423,7 +423,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); @@ -464,7 +464,7 @@ class LazyFunctionForMultiFunctionNode : 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 { Vector input_values(inputs_.size()); Vector output_values(outputs_.size()); @@ -501,7 +501,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); @@ -1448,7 +1448,7 @@ GeometryNodesLazyFunctionGraphInfo::~GeometryNodesLazyFunctionGraphInfo() } void GeometryNodesLazyFunctionLogger::log_before_node_execute(const lf::FunctionNode &node, - const lf::Params &UNUSED(params), + const lf::Params & /*params*/, const lf::Context &context) const { /* Enable this to see the threads that invoked a node. */ -- cgit v1.2.3 From a6b83617e9de01c3a4719770f88fc0ad2933e55a Mon Sep 17 00:00:00 2001 From: Iliya Katueshenock Date: Mon, 17 Oct 2022 12:00:09 +0200 Subject: Fix T101705: crash when connecting reroute to multi-input socket Differential Revision: https://developer.blender.org/D16203 --- .../nodes/intern/geometry_nodes_lazy_function.cc | 37 +++------------------- 1 file changed, 5 insertions(+), 32 deletions(-) (limited to 'source/blender/nodes/intern/geometry_nodes_lazy_function.cc') diff --git a/source/blender/nodes/intern/geometry_nodes_lazy_function.cc b/source/blender/nodes/intern/geometry_nodes_lazy_function.cc index 6475a16477a..5bf245f1832 100644 --- a/source/blender/nodes/intern/geometry_nodes_lazy_function.cc +++ b/source/blender/nodes/intern/geometry_nodes_lazy_function.cc @@ -158,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_}); } } @@ -1073,9 +1074,7 @@ struct GeometryNodesLazyFunctionGraphBuilder { void insert_links_from_socket(const bNodeSocket &from_bsocket, lf::OutputSocket &from_lf_socket) { - const bNode &from_bnode = from_bsocket.owner_node(); - if (this->is_dangling_reroute_input(from_bnode)) { - /* Dangling reroutes should not be used as source of values. */ + if (nodeIsDanglingReroute(&btree_, &from_bsocket.owner_node())) { return; } @@ -1145,7 +1144,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++; } } @@ -1174,33 +1174,6 @@ struct GeometryNodesLazyFunctionGraphBuilder { } } - bool is_dangling_reroute_input(const bNode &node) - { - if (!node.is_reroute()) { - return false; - } - const bNode *iter_node = &node; - /* It is guaranteed at a higher level that there are no link cycles. */ - while (true) { - const Span links = iter_node->input_socket(0).directly_linked_links(); - BLI_assert(links.size() <= 1); - if (links.is_empty()) { - return true; - } - const bNodeLink &link = *links[0]; - if (!link.is_available()) { - return false; - } - if (link.is_muted()) { - return false; - } - iter_node = link.fromnode; - if (!iter_node->is_reroute()) { - return false; - } - } - } - lf::OutputSocket *insert_type_conversion_if_necessary( lf::OutputSocket &from_socket, const CPPType &to_type, -- cgit v1.2.3 From 7abd79398d712be54935c01f1d2eefb5eaba64a9 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Tue, 18 Oct 2022 16:15:12 +0200 Subject: Fix T101642: crash when deleting group output inside node group --- source/blender/nodes/intern/geometry_nodes_lazy_function.cc | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'source/blender/nodes/intern/geometry_nodes_lazy_function.cc') diff --git a/source/blender/nodes/intern/geometry_nodes_lazy_function.cc b/source/blender/nodes/intern/geometry_nodes_lazy_function.cc index 5bf245f1832..197f0997160 100644 --- a/source/blender/nodes/intern/geometry_nodes_lazy_function.cc +++ b/source/blender/nodes/intern/geometry_nodes_lazy_function.cc @@ -604,6 +604,7 @@ class LazyFunctionForGroupNode : public LazyFunction { private: const bNode &group_node_; bool has_many_nodes_ = false; + bool use_fallback_outputs_ = false; std::optional lf_logger_; std::optional lf_side_effect_provider_; std::optional graph_executor_; @@ -640,6 +641,9 @@ class LazyFunctionForGroupNode : public LazyFunction { } } } + else { + use_fallback_outputs_ = true; + } lf_logger_.emplace(lf_graph_info); lf_side_effect_provider_.emplace(); @@ -660,6 +664,11 @@ class LazyFunctionForGroupNode : public LazyFunction { * 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}; -- cgit v1.2.3