From 0153e99780aef64913dfd4c323984874bf688249 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Wed, 7 Jul 2021 11:20:19 +0200 Subject: Geometry Nodes: refactor logging during geometry nodes evaluation Many ui features for geometry nodes need access to information generated during evaluation: * Node warnings. * Attribute search. * Viewer node. * Socket inspection (not in master yet). The way we logged the required information before had some disadvantages: * Viewer node used a completely separate system from node warnings and attribute search. * Most of the context of logged information is lost when e.g. the same node group is used multiple times. * A global lock was needed every time something is logged. This new implementation solves these problems: * All four mentioned ui features use the same underlying logging system. * All context information for logged values is kept intact. * Every thread has its own local logger. The logged informatiton is combined in the end. Differential Revision: https://developer.blender.org/D11785 --- source/blender/blenkernel/BKE_node_ui_storage.hh | 133 --------- source/blender/blenkernel/BKE_object.h | 4 - source/blender/blenkernel/CMakeLists.txt | 2 - source/blender/blenkernel/intern/node.cc | 9 - .../blender/blenkernel/intern/node_ui_storage.cc | 169 ----------- source/blender/blenkernel/intern/object.c | 22 -- source/blender/editors/space_node/CMakeLists.txt | 17 ++ source/blender/editors/space_node/node_draw.cc | 66 +++-- .../space_node/node_geometry_attribute_search.cc | 85 +++--- .../editors/space_spreadsheet/CMakeLists.txt | 1 + .../spreadsheet_data_source_geometry.cc | 23 +- source/blender/makesdna/DNA_modifier_types.h | 4 + source/blender/makesdna/DNA_node_types.h | 3 - source/blender/makesdna/DNA_object_types.h | 6 - source/blender/modifiers/intern/MOD_nodes.cc | 121 +++----- .../modifiers/intern/MOD_nodes_evaluator.cc | 56 ++-- .../modifiers/intern/MOD_nodes_evaluator.hh | 7 +- source/blender/nodes/CMakeLists.txt | 4 +- source/blender/nodes/NOD_geometry_exec.hh | 8 +- .../blender/nodes/NOD_geometry_nodes_eval_log.hh | 291 ++++++++++++++++++ .../nodes/intern/geometry_nodes_eval_log.cc | 330 +++++++++++++++++++++ source/blender/nodes/intern/node_geometry_exec.cc | 17 +- 22 files changed, 829 insertions(+), 549 deletions(-) delete mode 100644 source/blender/blenkernel/BKE_node_ui_storage.hh delete mode 100644 source/blender/blenkernel/intern/node_ui_storage.cc create mode 100644 source/blender/nodes/NOD_geometry_nodes_eval_log.hh create mode 100644 source/blender/nodes/intern/geometry_nodes_eval_log.cc diff --git a/source/blender/blenkernel/BKE_node_ui_storage.hh b/source/blender/blenkernel/BKE_node_ui_storage.hh deleted file mode 100644 index 4ec165aad8c..00000000000 --- a/source/blender/blenkernel/BKE_node_ui_storage.hh +++ /dev/null @@ -1,133 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#pragma once - -#include - -#include "BLI_hash.hh" -#include "BLI_map.hh" -#include "BLI_session_uuid.h" -#include "BLI_set.hh" - -#include "DNA_ID.h" -#include "DNA_customdata_types.h" -#include "DNA_modifier_types.h" -#include "DNA_session_uuid_types.h" - -#include "BKE_attribute.h" - -struct ModifierData; -struct Object; -struct bNode; -struct bNodeTree; -struct bContext; - -/** - * Contains the context necessary to determine when to display settings for a certain node tree - * that may be used for multiple modifiers and objects. The object name and modifier session UUID - * are used instead of pointers because they are re-allocated between evaluations. - * - * \note This does not yet handle the context of nested node trees. - */ -class NodeTreeEvaluationContext { - private: - std::string object_name_; - SessionUUID modifier_session_uuid_; - - public: - NodeTreeEvaluationContext(const Object &object, const ModifierData &modifier) - { - object_name_ = reinterpret_cast(object).name; - modifier_session_uuid_ = modifier.session_uuid; - } - - uint64_t hash() const - { - return blender::get_default_hash_2(object_name_, modifier_session_uuid_); - } - - friend bool operator==(const NodeTreeEvaluationContext &a, const NodeTreeEvaluationContext &b) - { - return a.object_name_ == b.object_name_ && - BLI_session_uuid_is_equal(&a.modifier_session_uuid_, &b.modifier_session_uuid_); - } -}; - -enum class NodeWarningType { - Error, - Warning, - Info, -}; - -struct NodeWarning { - NodeWarningType type; - std::string message; -}; - -struct AvailableAttributeInfo { - std::string name; - AttributeDomain domain; - CustomDataType data_type; - - uint64_t hash() const - { - return blender::get_default_hash(name); - } - - friend bool operator==(const AvailableAttributeInfo &a, const AvailableAttributeInfo &b) - { - return a.name == b.name; - } -}; - -struct NodeUIStorage { - blender::Vector warnings; - blender::Set attribute_hints; -}; - -struct NodeTreeUIStorage { - std::mutex mutex; - blender::Map> context_map; - - /** - * Attribute search uses this to store the fake info for the string typed into a node, in order - * to pass the info to the execute callback that sets node socket values. This is mutable since - * we can count on only one attribute search being open at a time, and there is no real data - * stored here. - */ - mutable AvailableAttributeInfo dummy_info_for_search; -}; - -const NodeUIStorage *BKE_node_tree_ui_storage_get_from_context(const bContext *C, - const bNodeTree &ntree, - const bNode &node); - -void BKE_nodetree_ui_storage_free_for_context(bNodeTree &ntree, - const NodeTreeEvaluationContext &context); - -void BKE_nodetree_error_message_add(bNodeTree &ntree, - const NodeTreeEvaluationContext &context, - const bNode &node, - const NodeWarningType type, - std::string message); - -void BKE_nodetree_attribute_hint_add(bNodeTree &ntree, - const NodeTreeEvaluationContext &context, - const bNode &node, - const blender::StringRef attribute_name, - const AttributeDomain domain, - const CustomDataType data_type); diff --git a/source/blender/blenkernel/BKE_object.h b/source/blender/blenkernel/BKE_object.h index cd66f026828..4724e6dfab6 100644 --- a/source/blender/blenkernel/BKE_object.h +++ b/source/blender/blenkernel/BKE_object.h @@ -70,10 +70,6 @@ void BKE_object_free_curve_cache(struct Object *ob); void BKE_object_free_derived_caches(struct Object *ob); void BKE_object_free_caches(struct Object *object); -void BKE_object_preview_geometry_set_add(struct Object *ob, - const uint64_t key, - struct GeometrySet *geometry_set); - void BKE_object_modifier_hook_reset(struct Object *ob, struct HookModifierData *hmd); void BKE_object_modifier_gpencil_hook_reset(struct Object *ob, struct HookGpencilModifierData *hmd); diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index 1db23002c1e..f14396f152e 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -215,7 +215,6 @@ set(SRC intern/multires_versioning.c intern/nla.c intern/node.cc - intern/node_ui_storage.cc intern/object.c intern/object_deform.c intern/object_dupli.cc @@ -399,7 +398,6 @@ set(SRC BKE_multires.h BKE_nla.h BKE_node.h - BKE_node_ui_storage.hh BKE_object.h BKE_object_deform.h BKE_object_facemap.h diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index 66ddfcf6127..5bed5b1aaad 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -69,7 +69,6 @@ #include "BKE_lib_query.h" #include "BKE_main.h" #include "BKE_node.h" -#include "BKE_node_ui_storage.hh" #include "BLI_ghash.h" #include "BLI_threads.h" @@ -220,10 +219,6 @@ static void ntree_copy_data(Main *UNUSED(bmain), ID *id_dst, const ID *id_src, c /* node tree will generate its own interface type */ ntree_dst->interface_type = nullptr; - - /* Don't copy error messages in the runtime struct. - * They should be filled during execution anyway. */ - ntree_dst->ui_storage = nullptr; } static void ntree_free_data(ID *id) @@ -277,8 +272,6 @@ static void ntree_free_data(ID *id) if (ntree->id.tag & LIB_TAG_LOCALIZED) { BKE_libblock_free_data(&ntree->id, true); } - - delete ntree->ui_storage; } static void library_foreach_node_socket(LibraryForeachIDData *data, bNodeSocket *sock) @@ -621,7 +614,6 @@ static void ntree_blend_write(BlendWriter *writer, ID *id, const void *id_addres ntree->interface_type = nullptr; ntree->progress = nullptr; ntree->execdata = nullptr; - ntree->ui_storage = nullptr; BLO_write_id_struct(writer, bNodeTree, id_address, &ntree->id); @@ -653,7 +645,6 @@ void ntreeBlendReadData(BlendDataReader *reader, bNodeTree *ntree) ntree->progress = nullptr; ntree->execdata = nullptr; - ntree->ui_storage = nullptr; BLO_read_data_address(reader, &ntree->adt); BKE_animdata_blend_read_data(reader, ntree->adt); diff --git a/source/blender/blenkernel/intern/node_ui_storage.cc b/source/blender/blenkernel/intern/node_ui_storage.cc deleted file mode 100644 index e5e9f00c7c3..00000000000 --- a/source/blender/blenkernel/intern/node_ui_storage.cc +++ /dev/null @@ -1,169 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#include "CLG_log.h" - -#include - -#include "BLI_map.hh" -#include "BLI_string_ref.hh" -#include "BLI_vector.hh" - -#include "DNA_node_types.h" -#include "DNA_object_types.h" - -#include "BKE_context.h" -#include "BKE_node_ui_storage.hh" -#include "BKE_object.h" - -static CLG_LogRef LOG = {"bke.node_ui_storage"}; - -using blender::Map; -using blender::StringRef; -using blender::Vector; - -/* Use a global mutex because otherwise it would have to be stored directly in the - * bNodeTree struct in DNA. This could change if the node tree had a runtime struct. */ -static std::mutex global_ui_storage_mutex; - -static NodeTreeUIStorage &ui_storage_ensure(bNodeTree &ntree) -{ - /* As an optimization, only acquire a lock if the UI storage doesn't exist, - * because it only needs to be allocated once for every node tree. */ - if (ntree.ui_storage == nullptr) { - std::lock_guard lock(global_ui_storage_mutex); - /* Check again-- another thread may have allocated the storage while this one waited. */ - if (ntree.ui_storage == nullptr) { - ntree.ui_storage = new NodeTreeUIStorage(); - } - } - return *ntree.ui_storage; -} - -const NodeUIStorage *BKE_node_tree_ui_storage_get_from_context(const bContext *C, - const bNodeTree &ntree, - const bNode &node) -{ - const NodeTreeUIStorage *ui_storage = ntree.ui_storage; - if (ui_storage == nullptr) { - return nullptr; - } - - const Object *active_object = CTX_data_active_object(C); - if (active_object == nullptr) { - return nullptr; - } - - const ModifierData *active_modifier = BKE_object_active_modifier(active_object); - if (active_modifier == nullptr) { - return nullptr; - } - - const NodeTreeEvaluationContext context(*active_object, *active_modifier); - const Map *storage = ui_storage->context_map.lookup_ptr(context); - if (storage == nullptr) { - return nullptr; - } - - return storage->lookup_ptr_as(StringRef(node.name)); -} - -/** - * Removes only the UI data associated with a particular evaluation context. The same node tree - * can be used for execution in multiple places, but the entire UI storage can't be removed when - * one execution starts, or all of the data associated with the node tree would be lost. - */ -void BKE_nodetree_ui_storage_free_for_context(bNodeTree &ntree, - const NodeTreeEvaluationContext &context) -{ - NodeTreeUIStorage *ui_storage = ntree.ui_storage; - if (ui_storage != nullptr) { - std::lock_guard lock(ui_storage->mutex); - ui_storage->context_map.remove(context); - } -} - -static void node_error_message_log(bNodeTree &ntree, - const bNode &node, - const StringRef message, - const NodeWarningType type) -{ - switch (type) { - case NodeWarningType::Error: - CLOG_ERROR(&LOG, - "Node Tree: \"%s\", Node: \"%s\", %s", - ntree.id.name + 2, - node.name, - message.data()); - break; - case NodeWarningType::Warning: - CLOG_WARN(&LOG, - "Node Tree: \"%s\", Node: \"%s\", %s", - ntree.id.name + 2, - node.name, - message.data()); - break; - case NodeWarningType::Info: - CLOG_INFO(&LOG, - 2, - "Node Tree: \"%s\", Node: \"%s\", %s", - ntree.id.name + 2, - node.name, - message.data()); - break; - } -} - -static NodeUIStorage &node_ui_storage_ensure(NodeTreeUIStorage &locked_ui_storage, - const NodeTreeEvaluationContext &context, - const bNode &node) -{ - Map &node_tree_ui_storage = - locked_ui_storage.context_map.lookup_or_add_default(context); - NodeUIStorage &node_ui_storage = node_tree_ui_storage.lookup_or_add_default_as( - StringRef(node.name)); - return node_ui_storage; -} - -void BKE_nodetree_error_message_add(bNodeTree &ntree, - const NodeTreeEvaluationContext &context, - const bNode &node, - const NodeWarningType type, - std::string message) -{ - NodeTreeUIStorage &ui_storage = ui_storage_ensure(ntree); - std::lock_guard lock{ui_storage.mutex}; - - node_error_message_log(ntree, node, message, type); - - NodeUIStorage &node_ui_storage = node_ui_storage_ensure(ui_storage, context, node); - node_ui_storage.warnings.append({type, std::move(message)}); -} - -void BKE_nodetree_attribute_hint_add(bNodeTree &ntree, - const NodeTreeEvaluationContext &context, - const bNode &node, - const StringRef attribute_name, - const AttributeDomain domain, - const CustomDataType data_type) -{ - NodeTreeUIStorage &ui_storage = ui_storage_ensure(ntree); - std::lock_guard lock{ui_storage.mutex}; - - NodeUIStorage &node_ui_storage = node_ui_storage_ensure(ui_storage, context, node); - node_ui_storage.attribute_hints.add_as( - AvailableAttributeInfo{attribute_name, domain, data_type}); -} diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c index e0b23958ec9..2a37aebd8f0 100644 --- a/source/blender/blenkernel/intern/object.c +++ b/source/blender/blenkernel/intern/object.c @@ -1760,10 +1760,6 @@ void BKE_object_free_derived_caches(Object *ob) BKE_geometry_set_free(ob->runtime.geometry_set_eval); ob->runtime.geometry_set_eval = NULL; } - if (ob->runtime.geometry_set_previews != NULL) { - BLI_ghash_free(ob->runtime.geometry_set_previews, NULL, (GHashValFreeFP)BKE_geometry_set_free); - ob->runtime.geometry_set_previews = NULL; - } } void BKE_object_free_caches(Object *object) @@ -1814,24 +1810,6 @@ void BKE_object_free_caches(Object *object) } } -/* Can be called from multiple threads. */ -void BKE_object_preview_geometry_set_add(Object *ob, - const uint64_t key, - struct GeometrySet *geometry_set) -{ - static ThreadMutex mutex = BLI_MUTEX_INITIALIZER; - BLI_mutex_lock(&mutex); - if (ob->runtime.geometry_set_previews == NULL) { - ob->runtime.geometry_set_previews = BLI_ghash_int_new(__func__); - } - BLI_ghash_reinsert(ob->runtime.geometry_set_previews, - POINTER_FROM_UINT(key), - geometry_set, - NULL, - (GHashValFreeFP)BKE_geometry_set_free); - BLI_mutex_unlock(&mutex); -} - /** * Actual check for internal data, not context or flags. */ diff --git a/source/blender/editors/space_node/CMakeLists.txt b/source/blender/editors/space_node/CMakeLists.txt index 6e234c5b2ce..0c1463e69c1 100644 --- a/source/blender/editors/space_node/CMakeLists.txt +++ b/source/blender/editors/space_node/CMakeLists.txt @@ -24,6 +24,7 @@ set(INC ../../compositor ../../depsgraph ../../draw + ../../functions ../../gpu ../../imbuf ../../makesdna @@ -78,4 +79,20 @@ if(WITH_OPENSUBDIV) add_definitions(-DWITH_OPENSUBDIV) endif() +if(WITH_TBB) + add_definitions(-DWITH_TBB) + if(WIN32) + # TBB includes Windows.h which will define min/max macros + # that will collide with the stl versions. + add_definitions(-DNOMINMAX) + endif() + list(APPEND INC_SYS + ${TBB_INCLUDE_DIRS} + ) + + list(APPEND LIB + ${TBB_LIBRARIES} + ) +endif() + blender_add_lib(bf_editor_space_node "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") diff --git a/source/blender/editors/space_node/node_draw.cc b/source/blender/editors/space_node/node_draw.cc index 8a341da0b5c..d4f178603b8 100644 --- a/source/blender/editors/space_node/node_draw.cc +++ b/source/blender/editors/space_node/node_draw.cc @@ -48,7 +48,6 @@ #include "BKE_lib_id.h" #include "BKE_main.h" #include "BKE_node.h" -#include "BKE_node_ui_storage.hh" #include "BKE_object.h" #include "DEG_depsgraph.h" @@ -76,6 +75,8 @@ #include "RNA_access.h" +#include "NOD_geometry_nodes_eval_log.hh" + #include "node_intern.h" /* own include */ #ifdef WITH_COMPOSITOR @@ -86,6 +87,9 @@ using blender::Map; using blender::Set; using blender::Span; using blender::Vector; +using blender::fn::CPPType; +using blender::fn::GPointer; +namespace geo_log = blender::nodes::geometry_nodes_eval_log; extern "C" { /* XXX interface.h */ @@ -1185,14 +1189,14 @@ void node_draw_sockets(const View2D *v2d, } } -static int node_error_type_to_icon(const NodeWarningType type) +static int node_error_type_to_icon(const geo_log::NodeWarningType type) { switch (type) { - case NodeWarningType::Error: + case geo_log::NodeWarningType::Error: return ICON_ERROR; - case NodeWarningType::Warning: + case geo_log::NodeWarningType::Warning: return ICON_ERROR; - case NodeWarningType::Info: + case geo_log::NodeWarningType::Info: return ICON_INFO; } @@ -1200,14 +1204,14 @@ static int node_error_type_to_icon(const NodeWarningType type) return ICON_ERROR; } -static uint8_t node_error_type_priority(const NodeWarningType type) +static uint8_t node_error_type_priority(const geo_log::NodeWarningType type) { switch (type) { - case NodeWarningType::Error: + case geo_log::NodeWarningType::Error: return 3; - case NodeWarningType::Warning: + case geo_log::NodeWarningType::Warning: return 2; - case NodeWarningType::Info: + case geo_log::NodeWarningType::Info: return 1; } @@ -1215,11 +1219,11 @@ static uint8_t node_error_type_priority(const NodeWarningType type) return 0; } -static NodeWarningType node_error_highest_priority(Span warnings) +static geo_log::NodeWarningType node_error_highest_priority(Span warnings) { uint8_t highest_priority = 0; - NodeWarningType highest_priority_type = NodeWarningType::Info; - for (const NodeWarning &warning : warnings) { + geo_log::NodeWarningType highest_priority_type = geo_log::NodeWarningType::Info; + for (const geo_log::NodeWarning &warning : warnings) { const uint8_t priority = node_error_type_priority(warning.type); if (priority > highest_priority) { highest_priority = priority; @@ -1229,15 +1233,17 @@ static NodeWarningType node_error_highest_priority(Span warnings) return highest_priority_type; } +struct NodeErrorsTooltipData { + Span warnings; +}; + static char *node_errors_tooltip_fn(bContext *UNUSED(C), void *argN, const char *UNUSED(tip)) { - const NodeUIStorage **storage_pointer_alloc = static_cast(argN); - const NodeUIStorage *node_ui_storage = *storage_pointer_alloc; - Span warnings = node_ui_storage->warnings; + NodeErrorsTooltipData &data = *(NodeErrorsTooltipData *)argN; std::string complete_string; - for (const NodeWarning &warning : warnings.drop_back(1)) { + for (const geo_log::NodeWarning &warning : data.warnings.drop_back(1)) { complete_string += warning.message; /* Adding the period is not ideal for multi-line messages, but it is consistent * with other tooltip implementations in Blender, so it is added here. */ @@ -1246,7 +1252,7 @@ static char *node_errors_tooltip_fn(bContext *UNUSED(C), void *argN, const char } /* Let the tooltip system automatically add the last period. */ - complete_string += warnings.last().message; + complete_string += data.warnings.last().message; return BLI_strdupn(complete_string.c_str(), complete_string.size()); } @@ -1254,20 +1260,26 @@ static char *node_errors_tooltip_fn(bContext *UNUSED(C), void *argN, const char #define NODE_HEADER_ICON_SIZE (0.8f * U.widget_unit) static void node_add_error_message_button( - const bContext *C, bNodeTree &ntree, bNode &node, const rctf &rect, float &icon_offset) + const bContext *C, bNodeTree &UNUSED(ntree), bNode &node, const rctf &rect, float &icon_offset) { - const NodeUIStorage *node_ui_storage = BKE_node_tree_ui_storage_get_from_context(C, ntree, node); - if (node_ui_storage == nullptr || node_ui_storage->warnings.is_empty()) { + SpaceNode *snode = CTX_wm_space_node(C); + const geo_log::NodeLog *node_log = geo_log::ModifierLog::find_node_by_node_editor_context(*snode, + node); + if (node_log == nullptr) { + return; + } + + Span warnings = node_log->warnings(); + + if (warnings.is_empty()) { return; } - /* The UI API forces us to allocate memory for each error button, because the - * ownership of #UI_but_func_tooltip_set's argument is transferred to the button. */ - const NodeUIStorage **storage_pointer_alloc = (const NodeUIStorage **)MEM_mallocN( - sizeof(NodeUIStorage *), __func__); - *storage_pointer_alloc = node_ui_storage; + NodeErrorsTooltipData *tooltip_data = (NodeErrorsTooltipData *)MEM_mallocN( + sizeof(NodeErrorsTooltipData), __func__); + tooltip_data->warnings = warnings; - const NodeWarningType display_type = node_error_highest_priority(node_ui_storage->warnings); + const geo_log::NodeWarningType display_type = node_error_highest_priority(warnings); icon_offset -= NODE_HEADER_ICON_SIZE; UI_block_emboss_set(node.block, UI_EMBOSS_NONE); @@ -1285,7 +1297,7 @@ static void node_add_error_message_button( 0, 0, nullptr); - UI_but_func_tooltip_set(but, node_errors_tooltip_fn, storage_pointer_alloc, MEM_freeN); + UI_but_func_tooltip_set(but, node_errors_tooltip_fn, tooltip_data, MEM_freeN); UI_block_emboss_set(node.block, UI_EMBOSS); } diff --git a/source/blender/editors/space_node/node_geometry_attribute_search.cc b/source/blender/editors/space_node/node_geometry_attribute_search.cc index 94080a7b616..ab63531ff49 100644 --- a/source/blender/editors/space_node/node_geometry_attribute_search.cc +++ b/source/blender/editors/space_node/node_geometry_attribute_search.cc @@ -27,7 +27,6 @@ #include "DNA_space_types.h" #include "BKE_context.h" -#include "BKE_node_ui_storage.hh" #include "BKE_object.h" #include "RNA_access.h" @@ -40,17 +39,21 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "NOD_geometry_nodes_eval_log.hh" + #include "node_intern.h" using blender::IndexRange; using blender::Map; using blender::Set; using blender::StringRef; +namespace geo_log = blender::nodes::geometry_nodes_eval_log; +using geo_log::GeometryAttributeInfo; struct AttributeSearchData { - AvailableAttributeInfo &dummy_info_for_search; - const NodeUIStorage &ui_storage; - bNodeSocket &socket; + const bNodeTree *tree; + const bNode *node; + bNodeSocket *socket; }; /* This class must not have a destructor, since it is used by buttons and freed with #MEM_freeN. */ @@ -73,7 +76,7 @@ static StringRef attribute_domain_string(const AttributeDomain domain) /* Unicode arrow. */ #define MENU_SEP "\xe2\x96\xb6" -static bool attribute_search_item_add(uiSearchItems *items, const AvailableAttributeInfo &item) +static bool attribute_search_item_add(uiSearchItems *items, const GeometryAttributeInfo &item) { const StringRef data_type_name = attribute_data_type_string(item.data_type); const StringRef domain_name = attribute_domain_string(item.domain); @@ -84,31 +87,47 @@ static bool attribute_search_item_add(uiSearchItems *items, const AvailableAttri items, search_item_text.c_str(), (void *)&item, ICON_NONE, UI_BUT_HAS_SEP_CHAR, 0); } -static void attribute_search_update_fn(const bContext *UNUSED(C), - void *arg, - const char *str, - uiSearchItems *items, - const bool is_first) +static GeometryAttributeInfo &get_dummy_item_info() +{ + static GeometryAttributeInfo info; + return info; +} + +static void attribute_search_update_fn( + const bContext *C, void *arg, const char *str, uiSearchItems *items, const bool is_first) { AttributeSearchData *data = static_cast(arg); - const Set &attribute_hints = data->ui_storage.attribute_hints; + SpaceNode *snode = CTX_wm_space_node(C); + const geo_log::NodeLog *node_log = geo_log::ModifierLog::find_node_by_node_editor_context( + *snode, *data->node); + if (node_log == nullptr) { + return; + } + blender::Vector infos = node_log->lookup_available_attributes(); + + GeometryAttributeInfo &dummy_info = get_dummy_item_info(); /* Any string may be valid, so add the current search string along with the hints. */ if (str[0] != '\0') { - /* Note that the attribute domain and data type are dummies, since - * #AvailableAttributeInfo equality is only based on the string. */ - if (!attribute_hints.contains(AvailableAttributeInfo{str, ATTR_DOMAIN_AUTO, CD_PROP_BOOL})) { - data->dummy_info_for_search.name = std::string(str); - UI_search_item_add(items, str, &data->dummy_info_for_search, ICON_ADD, 0, 0); + bool contained = false; + for (const GeometryAttributeInfo *attribute_info : infos) { + if (attribute_info->name == str) { + contained = true; + break; + } + } + if (!contained) { + dummy_info.name = str; + UI_search_item_add(items, str, &dummy_info, ICON_ADD, 0, 0); } } if (str[0] == '\0' && !is_first) { /* Allow clearing the text field when the string is empty, but not on the first pass, * or opening an attribute field for the first time would show this search item. */ - data->dummy_info_for_search.name = std::string(str); - UI_search_item_add(items, str, &data->dummy_info_for_search, ICON_X, 0, 0); + dummy_info.name = str; + UI_search_item_add(items, str, &dummy_info, ICON_X, 0, 0); } /* Don't filter when the menu is first opened, but still run the search @@ -116,15 +135,15 @@ static void attribute_search_update_fn(const bContext *UNUSED(C), const char *string = is_first ? "" : str; StringSearch *search = BLI_string_search_new(); - for (const AvailableAttributeInfo &item : attribute_hints) { - BLI_string_search_add(search, item.name.c_str(), (void *)&item); + for (const GeometryAttributeInfo *item : infos) { + BLI_string_search_add(search, item->name.c_str(), (void *)item); } - AvailableAttributeInfo **filtered_items; + GeometryAttributeInfo **filtered_items; const int filtered_amount = BLI_string_search_query(search, string, (void ***)&filtered_items); for (const int i : IndexRange(filtered_amount)) { - const AvailableAttributeInfo *item = filtered_items[i]; + const GeometryAttributeInfo *item = filtered_items[i]; if (!attribute_search_item_add(items, *item)) { break; } @@ -137,31 +156,21 @@ static void attribute_search_update_fn(const bContext *UNUSED(C), static void attribute_search_exec_fn(bContext *C, void *data_v, void *item_v) { AttributeSearchData *data = static_cast(data_v); - AvailableAttributeInfo *item = static_cast(item_v); + GeometryAttributeInfo *item = (GeometryAttributeInfo *)item_v; - bNodeSocket &socket = data->socket; + bNodeSocket &socket = *data->socket; bNodeSocketValueString *value = static_cast(socket.default_value); BLI_strncpy(value->value, item->name.c_str(), MAX_NAME); ED_undo_push(C, "Assign Attribute Name"); } -void node_geometry_add_attribute_search_button(const bContext *C, +void node_geometry_add_attribute_search_button(const bContext *UNUSED(C), const bNodeTree *node_tree, const bNode *node, PointerRNA *socket_ptr, uiLayout *layout) { - const NodeUIStorage *ui_storage = BKE_node_tree_ui_storage_get_from_context( - C, *node_tree, *node); - - if (ui_storage == nullptr) { - uiItemR(layout, socket_ptr, "default_value", 0, "", 0); - return; - } - - const NodeTreeUIStorage *tree_ui_storage = node_tree->ui_storage; - uiBlock *block = uiLayoutGetBlock(layout); uiBut *but = uiDefIconTextButR(block, UI_BTYPE_SEARCH_MENU, @@ -181,10 +190,8 @@ void node_geometry_add_attribute_search_button(const bContext *C, 0.0f, ""); - AttributeSearchData *data = OBJECT_GUARDED_NEW(AttributeSearchData, - {tree_ui_storage->dummy_info_for_search, - *ui_storage, - *static_cast(socket_ptr->data)}); + AttributeSearchData *data = OBJECT_GUARDED_NEW( + AttributeSearchData, {node_tree, node, (bNodeSocket *)socket_ptr->data}); UI_but_func_search_set_results_are_suggestions(but, true); UI_but_func_search_set_sep_string(but, MENU_SEP); diff --git a/source/blender/editors/space_spreadsheet/CMakeLists.txt b/source/blender/editors/space_spreadsheet/CMakeLists.txt index 1ea6593588a..9a06447b631 100644 --- a/source/blender/editors/space_spreadsheet/CMakeLists.txt +++ b/source/blender/editors/space_spreadsheet/CMakeLists.txt @@ -27,6 +27,7 @@ set(INC ../../gpu ../../makesdna ../../makesrna + ../../nodes ../../windowmanager ../../../../intern/glew-mx ../../../../intern/guardedalloc diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc index f730f199ca4..44b17b8c391 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc @@ -31,11 +31,15 @@ #include "ED_spreadsheet.h" +#include "NOD_geometry_nodes_eval_log.hh" + #include "bmesh.h" #include "spreadsheet_data_source_geometry.hh" #include "spreadsheet_intern.hh" +namespace geo_log = blender::nodes::geometry_nodes_eval_log; + namespace blender::ed::spreadsheet { void GeometryDataSource::foreach_default_column_ids( @@ -438,13 +442,18 @@ GeometrySet spreadsheet_get_display_geometry_set(const SpaceSpreadsheet *sspread } } else { - if (object_eval->runtime.geometry_set_previews != nullptr) { - GHash *ghash = (GHash *)object_eval->runtime.geometry_set_previews; - const uint64_t key = ED_spreadsheet_context_path_hash(sspreadsheet); - GeometrySet *geometry_set_preview = (GeometrySet *)BLI_ghash_lookup_default( - ghash, POINTER_FROM_UINT(key), nullptr); - if (geometry_set_preview != nullptr) { - geometry_set = *geometry_set_preview; + const geo_log::NodeLog *node_log = + geo_log::ModifierLog::find_node_by_spreadsheet_editor_context(*sspreadsheet); + if (node_log != nullptr) { + for (const geo_log::SocketLog &input_log : node_log->input_logs()) { + if (const geo_log::GeometryValueLog *geo_value_log = + dynamic_cast(input_log.value())) { + const GeometrySet *full_geometry = geo_value_log->full_geometry(); + if (full_geometry != nullptr) { + geometry_set = *full_geometry; + break; + } + } } } } diff --git a/source/blender/makesdna/DNA_modifier_types.h b/source/blender/makesdna/DNA_modifier_types.h index 08a38f0652c..1c765d19ce2 100644 --- a/source/blender/makesdna/DNA_modifier_types.h +++ b/source/blender/makesdna/DNA_modifier_types.h @@ -2256,6 +2256,10 @@ typedef struct NodesModifierData { ModifierData modifier; struct bNodeTree *node_group; struct NodesModifierSettings settings; + + /* Contains logged information from the last evaluation. This can be used to help the user to + * debug a node tree. */ + void *runtime_eval_log; } NodesModifierData; typedef struct MeshToVolumeModifierData { diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index 199b69d61c5..1a3415bf74e 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -37,7 +37,6 @@ struct Collection; struct ID; struct Image; struct ListBase; -struct NodeTreeUIStorage; struct bGPdata; struct bNodeInstanceHash; struct bNodeLink; @@ -516,8 +515,6 @@ typedef struct bNodeTree { int (*test_break)(void *); void (*update_draw)(void *); void *tbh, *prh, *sdh, *udh; - - struct NodeTreeUIStorage *ui_storage; } bNodeTree; /* ntree->type, index */ diff --git a/source/blender/makesdna/DNA_object_types.h b/source/blender/makesdna/DNA_object_types.h index 262d650c1ef..605cd28c793 100644 --- a/source/blender/makesdna/DNA_object_types.h +++ b/source/blender/makesdna/DNA_object_types.h @@ -171,12 +171,6 @@ typedef struct Object_Runtime { */ struct GeometrySet *geometry_set_eval; - /** - * A GHash that contains geometry sets for intermediate stages of evaluation. The keys are just a - * hash and are not owned by the map. The geometry sets are owned. - */ - void *geometry_set_previews; - /** * Mesh structure created during object evaluation. * It has deformation only modifiers applied on it. diff --git a/source/blender/modifiers/intern/MOD_nodes.cc b/source/blender/modifiers/intern/MOD_nodes.cc index 76eec76acb4..51f9bedc0a9 100644 --- a/source/blender/modifiers/intern/MOD_nodes.cc +++ b/source/blender/modifiers/intern/MOD_nodes.cc @@ -48,6 +48,7 @@ #include "DNA_space_types.h" #include "DNA_windowmanager_types.h" +#include "BKE_attribute_math.hh" #include "BKE_customdata.h" #include "BKE_geometry_set_instances.hh" #include "BKE_global.h" @@ -56,7 +57,6 @@ #include "BKE_main.h" #include "BKE_mesh.h" #include "BKE_modifier.h" -#include "BKE_node_ui_storage.hh" #include "BKE_object.h" #include "BKE_pointcloud.h" #include "BKE_screen.h" @@ -83,8 +83,10 @@ #include "NOD_derived_node_tree.hh" #include "NOD_geometry.h" +#include "NOD_geometry_nodes_eval_log.hh" #include "NOD_node_tree_multi_function.hh" +using blender::destruct_ptr; using blender::float3; using blender::FunctionRef; using blender::IndexRange; @@ -97,6 +99,7 @@ using blender::Vector; using blender::fn::GMutablePointer; using blender::fn::GPointer; using blender::nodes::GeoNodeExecParams; +using blender::threading::EnumerableThreadSpecific; using namespace blender::fn::multi_function_types; using namespace blender::nodes::derived_node_tree_types; @@ -734,19 +737,6 @@ static void initialize_group_input(NodesModifierData &nmd, property_type->init_cpp_value(*property, r_value); } -static void reset_tree_ui_storage(Span trees, - const Object &object, - const ModifierData &modifier) -{ - const NodeTreeEvaluationContext context = {object, modifier}; - - for (const blender::nodes::NodeTreeRef *tree : trees) { - bNodeTree *btree_cow = tree->btree(); - bNodeTree *btree_original = (bNodeTree *)DEG_get_original_id((ID *)btree_cow); - BKE_nodetree_ui_storage_free_for_context(*btree_original, context); - } -} - static Vector find_spreadsheet_editors(Main *bmain) { wmWindowManager *wm = (wmWindowManager *)bmain->wm.first; @@ -766,8 +756,6 @@ static Vector find_spreadsheet_editors(Main *bmain) return spreadsheets; } -using PreviewSocketMap = blender::MultiValueMap; - static DSocket try_get_socket_to_preview_for_spreadsheet(SpaceSpreadsheet *sspreadsheet, NodesModifierData *nmd, const ModifierEvalContext *ctx, @@ -821,19 +809,10 @@ static DSocket try_get_socket_to_preview_for_spreadsheet(SpaceSpreadsheet *sspre } const NodeTreeRef &tree_ref = context->tree(); - for (const NodeRef *node_ref : tree_ref.nodes()) { + for (const NodeRef *node_ref : tree_ref.nodes_by_type("GeometryNodeViewer")) { if (node_ref->name() == last_context->node_name) { const DNode viewer_node{context, node_ref}; - DSocket socket_to_view; - viewer_node.input(0).foreach_origin_socket( - [&](const DSocket socket) { socket_to_view = socket; }); - if (!socket_to_view) { - return {}; - } - bNodeSocket *bsocket = socket_to_view->bsocket(); - if (bsocket->type == SOCK_GEOMETRY && bsocket->flag != SOCK_MULTI_INPUT) { - return socket_to_view; - } + return viewer_node.input(0); } } return {}; @@ -842,7 +821,7 @@ static DSocket try_get_socket_to_preview_for_spreadsheet(SpaceSpreadsheet *sspre static void find_sockets_to_preview(NodesModifierData *nmd, const ModifierEvalContext *ctx, const DerivedNodeTree &tree, - PreviewSocketMap &r_sockets_to_preview) + Set &r_sockets_to_preview) { Main *bmain = DEG_get_bmain(ctx->depsgraph); @@ -852,51 +831,16 @@ static void find_sockets_to_preview(NodesModifierData *nmd, for (SpaceSpreadsheet *sspreadsheet : spreadsheets) { const DSocket socket = try_get_socket_to_preview_for_spreadsheet(sspreadsheet, nmd, ctx, tree); if (socket) { - const uint64_t key = ED_spreadsheet_context_path_hash(sspreadsheet); - r_sockets_to_preview.add_non_duplicates(socket, key); + r_sockets_to_preview.add(socket); } } } -static void log_preview_socket_value(const Span values, - Object *object, - Span keys) +static void clear_runtime_data(NodesModifierData *nmd) { - GeometrySet geometry_set = *(const GeometrySet *)values[0].get(); - geometry_set.ensure_owns_direct_data(); - for (uint64_t key : keys) { - BKE_object_preview_geometry_set_add(object, key, new GeometrySet(geometry_set)); - } -} - -static void log_ui_hints(const DSocket socket, - const Span values, - Object *self_object, - NodesModifierData *nmd) -{ - const DNode node = socket.node(); - if (node->is_reroute_node() || socket->typeinfo()->type != SOCK_GEOMETRY) { - return; - } - bNodeTree *btree_cow = node->btree(); - bNodeTree *btree_original = (bNodeTree *)DEG_get_original_id((ID *)btree_cow); - const NodeTreeEvaluationContext context{*self_object, nmd->modifier}; - for (const GPointer &data : values) { - if (data.type() == &CPPType::get()) { - const GeometrySet &geometry_set = *(const GeometrySet *)data.get(); - blender::bke::geometry_set_instances_attribute_foreach( - geometry_set, - [&](StringRefNull attribute_name, const AttributeMetaData &meta_data) { - BKE_nodetree_attribute_hint_add(*btree_original, - context, - *node->bnode(), - attribute_name, - meta_data.domain, - meta_data.data_type); - return true; - }, - 8); - } + if (nmd->runtime_eval_log != nullptr) { + delete (geo_log::ModifierLog *)nmd->runtime_eval_log; + nmd->runtime_eval_log = nullptr; } } @@ -952,32 +896,32 @@ static GeometrySet compute_geometry(const DerivedNodeTree &tree, Vector group_outputs; group_outputs.append({root_context, &socket_to_compute}); - PreviewSocketMap preview_sockets; - find_sockets_to_preview(nmd, ctx, tree, preview_sockets); - - auto log_socket_value = [&](const DSocket socket, const Span values) { - if (!logging_enabled(ctx)) { - return; - } - Span keys = preview_sockets.lookup(socket); - if (!keys.is_empty()) { - log_preview_socket_value(values, ctx->object, keys); - } - log_ui_hints(socket, values, ctx->object, nmd); - }; + std::optional geo_logger; blender::modifiers::geometry_nodes::GeometryNodesEvaluationParams eval_params; + + if (logging_enabled(ctx)) { + Set preview_sockets; + find_sockets_to_preview(nmd, ctx, tree, preview_sockets); + eval_params.force_compute_sockets.extend(preview_sockets.begin(), preview_sockets.end()); + geo_logger.emplace(std::move(preview_sockets)); + } + eval_params.input_values = group_inputs; eval_params.output_sockets = group_outputs; - eval_params.force_compute_sockets.extend(preview_sockets.keys().begin(), - preview_sockets.keys().end()); eval_params.mf_by_node = &mf_by_node; eval_params.modifier_ = nmd; eval_params.depsgraph = ctx->depsgraph; eval_params.self_object = ctx->object; - eval_params.log_socket_value_fn = log_socket_value; + eval_params.geo_logger = geo_logger.has_value() ? &*geo_logger : nullptr; blender::modifiers::geometry_nodes::evaluate_geometry_nodes(eval_params); + if (geo_logger.has_value()) { + NodesModifierData *nmd_orig = (NodesModifierData *)BKE_modifier_get_original(&nmd->modifier); + clear_runtime_data(nmd_orig); + nmd_orig->runtime_eval_log = new geo_log::ModifierLog(*geo_logger); + } + BLI_assert(eval_params.r_output_values.size() == 1); GMutablePointer result = eval_params.r_output_values[0]; return result.relocate_out(); @@ -1067,10 +1011,6 @@ static void modifyGeometry(ModifierData *md, return; } - if (logging_enabled(ctx)) { - reset_tree_ui_storage(tree.used_node_tree_refs(), *ctx->object, *md); - } - geometry_set = compute_geometry( tree, input_nodes, *group_outputs[0], std::move(geometry_set), nmd, ctx); } @@ -1215,6 +1155,7 @@ static void blendRead(BlendDataReader *reader, ModifierData *md) NodesModifierData *nmd = reinterpret_cast(md); BLO_read_data_address(reader, &nmd->settings.properties); IDP_BlendDataRead(reader, &nmd->settings.properties); + nmd->runtime_eval_log = nullptr; } static void copyData(const ModifierData *md, ModifierData *target, const int flag) @@ -1224,6 +1165,8 @@ static void copyData(const ModifierData *md, ModifierData *target, const int fla BKE_modifier_copydata_generic(md, target, flag); + tnmd->runtime_eval_log = nullptr; + if (nmd->settings.properties != nullptr) { tnmd->settings.properties = IDP_CopyProperty_ex(nmd->settings.properties, flag); } @@ -1236,6 +1179,8 @@ static void freeData(ModifierData *md) IDP_FreeProperty_ex(nmd->settings.properties, false); nmd->settings.properties = nullptr; } + + clear_runtime_data(nmd); } static void requiredDataMask(Object *UNUSED(ob), diff --git a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc index 205bfa34465..18cc1ce6c86 100644 --- a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc +++ b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc @@ -558,11 +558,11 @@ class GeometryNodesEvaluator { for (auto &&item : params_.input_values.items()) { const DOutputSocket socket = item.key; GMutablePointer value = item.value; - this->log_socket_value(socket, value); const DNode node = socket.node(); if (!node_states_.contains_as(node)) { /* The socket is not connected to any output. */ + this->log_socket_value({socket}, value); value.destruct(); continue; } @@ -780,7 +780,6 @@ class GeometryNodesEvaluator { /* Checks if all the linked sockets have been provided already. */ if (multi_value.items.size() == multi_value.expected_size) { input_state.was_ready_for_execution = true; - this->log_socket_value(socket, input_state, multi_value.items); } else if (is_required) { /* The input is required but is not fully provided yet. Therefore the node cannot be @@ -792,7 +791,6 @@ class GeometryNodesEvaluator { SingleInputValue &single_value = *input_state.value.single; if (single_value.value != nullptr) { input_state.was_ready_for_execution = true; - this->log_socket_value(socket, GPointer{input_state.type, single_value.value}); } else if (is_required) { /* The input is required but has not been provided yet. Therefore the node cannot be @@ -1170,6 +1168,9 @@ class GeometryNodesEvaluator { { BLI_assert(value_to_forward.get() != nullptr); + Vector sockets_to_log_to; + sockets_to_log_to.append(from_socket); + Vector to_sockets; auto handle_target_socket_fn = [&, this](const DInputSocket to_socket) { if (this->should_forward_to_socket(to_socket)) { @@ -1177,9 +1178,7 @@ class GeometryNodesEvaluator { } }; auto handle_skipped_socket_fn = [&, this](const DSocket socket) { - /* Log socket value on intermediate sockets to support e.g. attribute search or spreadsheet - * breadcrumbs on group nodes. */ - this->log_socket_value(socket, value_to_forward); + sockets_to_log_to.append(socket); }; from_socket.foreach_target_socket(handle_target_socket_fn, handle_skipped_socket_fn); @@ -1192,11 +1191,18 @@ class GeometryNodesEvaluator { if (from_type == to_type) { /* All target sockets that do not need a conversion will be handled afterwards. */ to_sockets_same_type.append(to_socket); + /* Multi input socket values are logged once all values are available. */ + if (!to_socket->is_multi_input_socket()) { + sockets_to_log_to.append(to_socket); + } continue; } this->forward_to_socket_with_different_type( allocator, value_to_forward, from_socket, to_socket, to_type); } + + this->log_socket_value(sockets_to_log_to, value_to_forward); + this->forward_to_sockets_with_same_type( allocator, to_sockets_same_type, value_to_forward, from_socket); } @@ -1227,6 +1233,7 @@ class GeometryNodesEvaluator { /* Allocate a buffer for the converted value. */ void *buffer = allocator.allocate(to_type.size(), to_type.alignment()); + GMutablePointer value{to_type, buffer}; if (conversions_.is_convertible(from_type, to_type)) { /* Do the conversion if possible. */ @@ -1236,7 +1243,11 @@ class GeometryNodesEvaluator { /* Cannot convert, use default value instead. */ to_type.copy_construct(to_type.default_value(), buffer); } - this->add_value_to_input_socket(to_socket, from_socket, {to_type, buffer}); + /* Multi input socket values are logged once all values are available. */ + if (!to_socket->is_multi_input_socket()) { + this->log_socket_value({to_socket}, value); + } + this->add_value_to_input_socket(to_socket, from_socket, value); } void forward_to_sockets_with_same_type(LinearAllocator<> &allocator, @@ -1284,6 +1295,10 @@ class GeometryNodesEvaluator { /* Add a new value to the multi-input. */ MultiInputValue &multi_value = *input_state.value.multi; multi_value.items.append({origin, value.get()}); + + if (multi_value.expected_size == multi_value.items.size()) { + this->log_socket_value({socket}, input_state, multi_value.items); + } } else { /* Assign the value to the input. */ @@ -1314,10 +1329,14 @@ class GeometryNodesEvaluator { if (input_socket->is_multi_input_socket()) { MultiInputValue &multi_value = *input_state.value.multi; multi_value.items.append({origin_socket, value.get()}); + if (multi_value.expected_size == multi_value.items.size()) { + this->log_socket_value({input_socket}, input_state, multi_value.items); + } } else { SingleInputValue &single_value = *input_state.value.single; single_value.value = value.get(); + this->log_socket_value({input_socket}, value); } } @@ -1370,29 +1389,27 @@ class GeometryNodesEvaluator { return *node_states_.lookup_key_as(node).state; } - void log_socket_value(const DSocket socket, Span values) + void log_socket_value(DSocket socket, InputState &input_state, Span values) { - if (params_.log_socket_value_fn) { - params_.log_socket_value_fn(socket, values); + if (params_.geo_logger == nullptr) { + return; } - } - void log_socket_value(const DSocket socket, - InputState &input_state, - Span values) - { Vector value_pointers; value_pointers.reserve(values.size()); const CPPType &type = *input_state.type; for (const MultiInputValueItem &item : values) { value_pointers.append({type, item.value}); } - this->log_socket_value(socket, value_pointers); + params_.geo_logger->local().log_multi_value_socket(socket, value_pointers); } - void log_socket_value(const DSocket socket, GPointer value) + void log_socket_value(Span sockets, GPointer value) { - this->log_socket_value(socket, Span(&value, 1)); + if (params_.geo_logger == nullptr) { + return; + } + params_.geo_logger->local().log_value_for_sockets(sockets, value); } /* In most cases when `NodeState` is accessed, the node has to be locked first to avoid race @@ -1431,6 +1448,7 @@ NodeParamsProvider::NodeParamsProvider(GeometryNodesEvaluator &evaluator, this->self_object = evaluator.params_.self_object; this->modifier = &evaluator.params_.modifier_->modifier; this->depsgraph = evaluator.params_.depsgraph; + this->logger = evaluator.params_.geo_logger; } bool NodeParamsProvider::can_get_input(StringRef identifier) const @@ -1530,8 +1548,6 @@ void NodeParamsProvider::set_output(StringRef identifier, GMutablePointer value) const DOutputSocket socket = this->dnode.output_by_identifier(identifier); BLI_assert(socket); - evaluator_.log_socket_value(socket, value); - OutputState &output_state = node_state_.outputs[socket->index()]; BLI_assert(!output_state.has_been_computed); evaluator_.forward_output(socket, value); diff --git a/source/blender/modifiers/intern/MOD_nodes_evaluator.hh b/source/blender/modifiers/intern/MOD_nodes_evaluator.hh index 58eb6a4cd0b..f4ee6242dcb 100644 --- a/source/blender/modifiers/intern/MOD_nodes_evaluator.hh +++ b/source/blender/modifiers/intern/MOD_nodes_evaluator.hh @@ -19,20 +19,21 @@ #include "BLI_map.hh" #include "NOD_derived_node_tree.hh" +#include "NOD_geometry_nodes_eval_log.hh" #include "NOD_node_tree_multi_function.hh" #include "FN_generic_pointer.hh" #include "DNA_modifier_types.h" +namespace geo_log = blender::nodes::geometry_nodes_eval_log; + namespace blender::modifiers::geometry_nodes { using namespace nodes::derived_node_tree_types; using fn::GMutablePointer; using fn::GPointer; -using LogSocketValueFn = std::function)>; - struct GeometryNodesEvaluationParams { blender::LinearAllocator<> allocator; @@ -48,7 +49,7 @@ struct GeometryNodesEvaluationParams { const NodesModifierData *modifier_; Depsgraph *depsgraph; Object *self_object; - LogSocketValueFn log_socket_value_fn; + geo_log::GeoLogger *geo_logger; Vector r_output_values; }; diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index a3a52753880..4bfd75c4545 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -165,7 +165,7 @@ set(SRC geometry/nodes/node_geo_common.cc geometry/nodes/node_geo_convex_hull.cc geometry/nodes/node_geo_curve_endpoints.cc - geometry/nodes/node_geo_curve_length.cc + geometry/nodes/node_geo_curve_length.cc geometry/nodes/node_geo_curve_primitive_bezier_segment.cc geometry/nodes/node_geo_curve_primitive_circle.cc geometry/nodes/node_geo_curve_primitive_line.cc @@ -335,6 +335,7 @@ set(SRC texture/node_texture_util.c intern/derived_node_tree.cc + intern/geometry_nodes_eval_log.cc intern/math_functions.cc intern/node_common.c intern/node_exec.cc @@ -358,6 +359,7 @@ set(SRC NOD_function.h NOD_geometry.h NOD_geometry_exec.hh + NOD_geometry_nodes_eval_log.hh NOD_math_functions.hh NOD_node_tree_multi_function.hh NOD_node_tree_ref.hh diff --git a/source/blender/nodes/NOD_geometry_exec.hh b/source/blender/nodes/NOD_geometry_exec.hh index 23474201daa..d6a23051c0b 100644 --- a/source/blender/nodes/NOD_geometry_exec.hh +++ b/source/blender/nodes/NOD_geometry_exec.hh @@ -21,11 +21,11 @@ #include "BKE_attribute_access.hh" #include "BKE_geometry_set.hh" #include "BKE_geometry_set_instances.hh" -#include "BKE_node_ui_storage.hh" #include "DNA_node_types.h" #include "NOD_derived_node_tree.hh" +#include "NOD_geometry_nodes_eval_log.hh" struct Depsgraph; struct ModifierData; @@ -52,10 +52,11 @@ using fn::GVMutableArray; using fn::GVMutableArray_GSpan; using fn::GVMutableArray_Typed; using fn::GVMutableArrayPtr; +using geometry_nodes_eval_log::NodeWarningType; /** - * This class exists to separate the memory management details of the geometry nodes evaluator from - * the node execution functions and related utilities. + * This class exists to separate the memory management details of the geometry nodes evaluator + * from the node execution functions and related utilities. */ class GeoNodeExecParamsProvider { public: @@ -63,6 +64,7 @@ class GeoNodeExecParamsProvider { const Object *self_object = nullptr; const ModifierData *modifier = nullptr; Depsgraph *depsgraph = nullptr; + geometry_nodes_eval_log::GeoLogger *logger = nullptr; /** * Returns true when the node is allowed to get/extract the input value. The identifier is diff --git a/source/blender/nodes/NOD_geometry_nodes_eval_log.hh b/source/blender/nodes/NOD_geometry_nodes_eval_log.hh new file mode 100644 index 00000000000..a52b182fe7e --- /dev/null +++ b/source/blender/nodes/NOD_geometry_nodes_eval_log.hh @@ -0,0 +1,291 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#pragma once + +/** + * Many geometry nodes related ui features need access to data produced during evaluation. Not only + * is the final output required but also the intermediate results. Those features include + * attribute search, node warnings, socket inspection and the viewer node. + * + * This file provides the framework for logging data during evaluation and accessing the data after + * evaluation. + * + * During logging every thread gets its own local logger to avoid too much locking (logging + * generally happens for every socket). After geometry nodes evaluation is done, the threadlocal + * logging information is combined and postprocessed to make it easier for the ui to lookup + * necessary information. + */ + +#include "BLI_enumerable_thread_specific.hh" +#include "BLI_linear_allocator.hh" +#include "BLI_map.hh" + +#include "BKE_geometry_set.hh" + +#include "FN_generic_pointer.hh" + +#include "NOD_derived_node_tree.hh" + +struct SpaceNode; +struct SpaceSpreadsheet; + +namespace blender::nodes::geometry_nodes_eval_log { + +using fn::GMutablePointer; +using fn::GPointer; + +/** Contains information about a value that has been computed during geometry nodes evaluation. */ +class ValueLog { + public: + virtual ~ValueLog() = default; +}; + +/** Contains an owned copy of a value of a generic type. */ +class GenericValueLog : public ValueLog { + private: + GMutablePointer data_; + + public: + GenericValueLog(GMutablePointer data) : data_(data) + { + } + + ~GenericValueLog() + { + data_.destruct(); + } + + GPointer value() const + { + return data_; + } +}; + +struct GeometryAttributeInfo { + std::string name; + AttributeDomain domain; + CustomDataType data_type; +}; + +/** Contains information about a geometry set. In most cases this does not store the entire + * geometry set as this would require too much memory. */ +class GeometryValueLog : public ValueLog { + private: + Vector attributes_; + Vector component_types_; + std::unique_ptr full_geometry_; + + public: + GeometryValueLog(const GeometrySet &geometry_set, bool log_full_geometry); + + Span attributes() const + { + return attributes_; + } + + Span component_types() const + { + return component_types_; + } + + const GeometrySet *full_geometry() const + { + return full_geometry_.get(); + } +}; + +enum class NodeWarningType { + Error, + Warning, + Info, +}; + +struct NodeWarning { + NodeWarningType type; + std::string message; +}; + +struct NodeWithWarning { + DNode node; + NodeWarning warning; +}; + +/** The same value can be referenced by multiple sockets when they are linked. */ +struct ValueOfSockets { + Span sockets; + destruct_ptr value; +}; + +class GeoLogger; +class ModifierLog; + +/** Every thread has its own local logger to avoid having to communicate between threads during + * evaluation. After evaluation the individual logs are combined. */ +class LocalGeoLogger { + private: + /* Back pointer to the owner of this local logger. */ + GeoLogger *main_logger_; + /* Allocator for the many small allocations during logging. This is in a `unique_ptr` so that + * ownership can be transferred later on. */ + std::unique_ptr> allocator_; + Vector values_; + Vector node_warnings_; + + friend ModifierLog; + + public: + LocalGeoLogger(GeoLogger &main_logger) : main_logger_(&main_logger) + { + this->allocator_ = std::make_unique>(); + } + + void log_value_for_sockets(Span sockets, GPointer value); + void log_multi_value_socket(DSocket socket, Span values); + void log_node_warning(DNode node, NodeWarningType type, std::string message); +}; + +/** The root logger class. */ +class GeoLogger { + private: + /** The entire geometry of sockets in this set should be cached, because e.g. the spreadsheet + * displays the data. We don't log the entire geometry at all places, because that would require + * way too much memory. */ + Set log_full_geometry_sockets_; + threading::EnumerableThreadSpecific threadlocals_; + + friend LocalGeoLogger; + + public: + GeoLogger(Set log_full_geometry_sockets) + : log_full_geometry_sockets_(std::move(log_full_geometry_sockets)), + threadlocals_([this]() { return LocalGeoLogger(*this); }) + { + } + + LocalGeoLogger &local() + { + return threadlocals_.local(); + } + + auto begin() + { + return threadlocals_.begin(); + } + + auto end() + { + return threadlocals_.end(); + } +}; + +/** Contains information that has been logged for one specific socket. */ +class SocketLog { + private: + ValueLog *value_ = nullptr; + + friend ModifierLog; + + public: + const ValueLog *value() const + { + return value_; + } +}; + +/** Contains information that has been logged for one specific node. */ +class NodeLog { + private: + Vector input_logs_; + Vector output_logs_; + Vector warnings_; + + friend ModifierLog; + + public: + const SocketLog *lookup_socket_log(eNodeSocketInOut in_out, int index) const; + const SocketLog *lookup_socket_log(const bNode &node, const bNodeSocket &socket) const; + + Span input_logs() const + { + return input_logs_; + } + + Span output_logs() const + { + return output_logs_; + } + + Span warnings() const + { + return warnings_; + } + + Vector lookup_available_attributes() const; +}; + +/** Contains information that has been logged for one specific tree. */ +class TreeLog { + private: + Map> node_logs_; + Map> child_logs_; + + friend ModifierLog; + + public: + const NodeLog *lookup_node_log(StringRef node_name) const; + const NodeLog *lookup_node_log(const bNode &node) const; + const TreeLog *lookup_child_log(StringRef node_name) const; +}; + +/** Contains information about an entire geometry nodes evaluation. */ +class ModifierLog { + private: + LinearAllocator<> allocator_; + /* Allocators of the individual loggers. */ + Vector>> logger_allocators_; + destruct_ptr root_tree_logs_; + Vector> logged_values_; + + public: + ModifierLog(GeoLogger &logger); + + const TreeLog &root_tree() const + { + return *root_tree_logs_; + } + + /* Utilities to find logged informatiton for a specific context. */ + static const ModifierLog *find_root_by_node_editor_context(const SpaceNode &snode); + static const TreeLog *find_tree_by_node_editor_context(const SpaceNode &snode); + static const NodeLog *find_node_by_node_editor_context(const SpaceNode &snode, + const bNode &node); + static const SocketLog *find_socket_by_node_editor_context(const SpaceNode &snode, + const bNode &node, + const bNodeSocket &socket); + static const NodeLog *find_node_by_spreadsheet_editor_context( + const SpaceSpreadsheet &sspreadsheet); + + private: + using LogByTreeContext = Map; + + TreeLog &lookup_or_add_tree_log(LogByTreeContext &log_by_tree_context, + const DTreeContext &tree_context); + NodeLog &lookup_or_add_node_log(LogByTreeContext &log_by_tree_context, DNode node); + SocketLog &lookup_or_add_socket_log(LogByTreeContext &log_by_tree_context, DSocket socket); +}; + +} // namespace blender::nodes::geometry_nodes_eval_log diff --git a/source/blender/nodes/intern/geometry_nodes_eval_log.cc b/source/blender/nodes/intern/geometry_nodes_eval_log.cc new file mode 100644 index 00000000000..85182b67c8a --- /dev/null +++ b/source/blender/nodes/intern/geometry_nodes_eval_log.cc @@ -0,0 +1,330 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "NOD_geometry_nodes_eval_log.hh" + +#include "BKE_geometry_set_instances.hh" + +#include "DNA_modifier_types.h" +#include "DNA_space_types.h" + +namespace blender::nodes::geometry_nodes_eval_log { + +using fn::CPPType; + +ModifierLog::ModifierLog(GeoLogger &logger) +{ + root_tree_logs_ = allocator_.construct(); + + LogByTreeContext log_by_tree_context; + + /* Combine all the local loggers that have been used by separate threads. */ + for (LocalGeoLogger &local_logger : logger) { + /* Take ownership of the allocator. */ + logger_allocators_.append(std::move(local_logger.allocator_)); + + for (ValueOfSockets &value_of_sockets : local_logger.values_) { + ValueLog *value_log = value_of_sockets.value.get(); + + /* Take centralized ownership of the logged value. It might be referenced by multiple + * sockets. */ + logged_values_.append(std::move(value_of_sockets.value)); + + for (const DSocket &socket : value_of_sockets.sockets) { + SocketLog &socket_log = this->lookup_or_add_socket_log(log_by_tree_context, socket); + socket_log.value_ = value_log; + } + } + + for (NodeWithWarning &node_with_warning : local_logger.node_warnings_) { + NodeLog &node_log = this->lookup_or_add_node_log(log_by_tree_context, + node_with_warning.node); + node_log.warnings_.append(node_with_warning.warning); + } + } +} + +TreeLog &ModifierLog::lookup_or_add_tree_log(LogByTreeContext &log_by_tree_context, + const DTreeContext &tree_context) +{ + TreeLog *tree_log = log_by_tree_context.lookup_default(&tree_context, nullptr); + if (tree_log != nullptr) { + return *tree_log; + } + + const DTreeContext *parent_context = tree_context.parent_context(); + if (parent_context == nullptr) { + return *root_tree_logs_.get(); + } + TreeLog &parent_log = this->lookup_or_add_tree_log(log_by_tree_context, *parent_context); + destruct_ptr owned_tree_log = allocator_.construct(); + tree_log = owned_tree_log.get(); + log_by_tree_context.add_new(&tree_context, tree_log); + parent_log.child_logs_.add_new(tree_context.parent_node()->name(), std::move(owned_tree_log)); + return *tree_log; +} + +NodeLog &ModifierLog::lookup_or_add_node_log(LogByTreeContext &log_by_tree_context, DNode node) +{ + TreeLog &tree_log = this->lookup_or_add_tree_log(log_by_tree_context, *node.context()); + NodeLog &node_log = *tree_log.node_logs_.lookup_or_add_cb(node->name(), [&]() { + destruct_ptr node_log = allocator_.construct(); + node_log->input_logs_.resize(node->inputs().size()); + node_log->output_logs_.resize(node->outputs().size()); + return node_log; + }); + return node_log; +} + +SocketLog &ModifierLog::lookup_or_add_socket_log(LogByTreeContext &log_by_tree_context, + DSocket socket) +{ + NodeLog &node_log = this->lookup_or_add_node_log(log_by_tree_context, socket.node()); + MutableSpan socket_logs = socket->is_input() ? node_log.input_logs_ : + node_log.output_logs_; + SocketLog &socket_log = socket_logs[socket->index()]; + return socket_log; +} + +const NodeLog *TreeLog::lookup_node_log(StringRef node_name) const +{ + const destruct_ptr *node_log = node_logs_.lookup_ptr_as(node_name); + if (node_log == nullptr) { + return nullptr; + } + return node_log->get(); +} + +const NodeLog *TreeLog::lookup_node_log(const bNode &node) const +{ + return this->lookup_node_log(node.name); +} + +const TreeLog *TreeLog::lookup_child_log(StringRef node_name) const +{ + const destruct_ptr *tree_log = child_logs_.lookup_ptr_as(node_name); + if (tree_log == nullptr) { + return nullptr; + } + return tree_log->get(); +} + +const SocketLog *NodeLog::lookup_socket_log(eNodeSocketInOut in_out, int index) const +{ + BLI_assert(index >= 0); + Span socket_logs = (in_out == SOCK_IN) ? input_logs_ : output_logs_; + if (index >= socket_logs.size()) { + return nullptr; + } + return &socket_logs[index]; +} + +const SocketLog *NodeLog::lookup_socket_log(const bNode &node, const bNodeSocket &socket) const +{ + ListBase sockets = socket.in_out == SOCK_IN ? node.inputs : node.outputs; + int index = BLI_findindex(&sockets, &socket); + return this->lookup_socket_log((eNodeSocketInOut)socket.in_out, index); +} + +GeometryValueLog::GeometryValueLog(const GeometrySet &geometry_set, bool log_full_geometry) +{ + bke::geometry_set_instances_attribute_foreach( + geometry_set, + [&](StringRefNull attribute_name, const AttributeMetaData &meta_data) { + this->attributes_.append({attribute_name, meta_data.domain, meta_data.data_type}); + return true; + }, + 8); + for (const GeometryComponent *component : geometry_set.get_components_for_read()) { + component_types_.append(component->type()); + } + if (log_full_geometry) { + full_geometry_ = std::make_unique(geometry_set); + full_geometry_->ensure_owns_direct_data(); + } +} + +Vector NodeLog::lookup_available_attributes() const +{ + Vector attributes; + Set names; + for (const SocketLog &socket_log : input_logs_) { + const ValueLog *value_log = socket_log.value(); + if (const GeometryValueLog *geo_value_log = dynamic_cast( + value_log)) { + for (const GeometryAttributeInfo &attribute : geo_value_log->attributes()) { + if (names.add(attribute.name)) { + attributes.append(&attribute); + } + } + } + } + return attributes; +} + +const ModifierLog *ModifierLog::find_root_by_node_editor_context(const SpaceNode &snode) +{ + if (snode.id == nullptr) { + return nullptr; + } + if (GS(snode.id->name) != ID_OB) { + return nullptr; + } + Object *object = (Object *)snode.id; + LISTBASE_FOREACH (ModifierData *, md, &object->modifiers) { + if (md->type == eModifierType_Nodes) { + NodesModifierData *nmd = (NodesModifierData *)md; + if (nmd->node_group == snode.nodetree) { + return (ModifierLog *)nmd->runtime_eval_log; + } + } + } + return nullptr; +} + +const TreeLog *ModifierLog::find_tree_by_node_editor_context(const SpaceNode &snode) +{ + const ModifierLog *eval_log = ModifierLog::find_root_by_node_editor_context(snode); + if (eval_log == nullptr) { + return nullptr; + } + Vector tree_path_vec = snode.treepath; + if (tree_path_vec.is_empty()) { + return nullptr; + } + TreeLog *current = eval_log->root_tree_logs_.get(); + for (bNodeTreePath *path : tree_path_vec.as_span().drop_front(1)) { + destruct_ptr *tree_log = current->child_logs_.lookup_ptr_as(path->node_name); + if (tree_log == nullptr) { + return nullptr; + } + current = tree_log->get(); + } + return current; +} + +const NodeLog *ModifierLog::find_node_by_node_editor_context(const SpaceNode &snode, + const bNode &node) +{ + const TreeLog *tree_log = ModifierLog::find_tree_by_node_editor_context(snode); + if (tree_log == nullptr) { + return nullptr; + } + return tree_log->lookup_node_log(node); +} + +const SocketLog *ModifierLog::find_socket_by_node_editor_context(const SpaceNode &snode, + const bNode &node, + const bNodeSocket &socket) +{ + const NodeLog *node_log = ModifierLog::find_node_by_node_editor_context(snode, node); + if (node_log == nullptr) { + return nullptr; + } + return node_log->lookup_socket_log(node, socket); +} + +const NodeLog *ModifierLog::find_node_by_spreadsheet_editor_context( + const SpaceSpreadsheet &sspreadsheet) +{ + Vector context_path = sspreadsheet.context_path; + if (context_path.size() <= 2) { + return nullptr; + } + if (context_path[0]->type != SPREADSHEET_CONTEXT_OBJECT) { + return nullptr; + } + if (context_path[1]->type != SPREADSHEET_CONTEXT_MODIFIER) { + return nullptr; + } + for (SpreadsheetContext *context : context_path.as_span().drop_front(2)) { + if (context->type != SPREADSHEET_CONTEXT_NODE) { + return nullptr; + } + } + Span node_contexts = + context_path.as_span().drop_front(2).cast(); + + Object *object = ((SpreadsheetContextObject *)context_path[0])->object; + StringRefNull modifier_name = ((SpreadsheetContextModifier *)context_path[1])->modifier_name; + if (object == nullptr) { + return nullptr; + } + + const ModifierLog *eval_log = nullptr; + LISTBASE_FOREACH (ModifierData *, md, &object->modifiers) { + if (md->type == eModifierType_Nodes) { + if (md->name == modifier_name) { + NodesModifierData *nmd = (NodesModifierData *)md; + eval_log = (const ModifierLog *)nmd->runtime_eval_log; + break; + } + } + } + if (eval_log == nullptr) { + return nullptr; + } + + const TreeLog *tree_log = &eval_log->root_tree(); + for (SpreadsheetContextNode *context : node_contexts.drop_back(1)) { + tree_log = tree_log->lookup_child_log(context->node_name); + if (tree_log == nullptr) { + return nullptr; + } + } + const NodeLog *node_log = tree_log->lookup_node_log(node_contexts.last()->node_name); + return node_log; +} + +void LocalGeoLogger::log_value_for_sockets(Span sockets, GPointer value) +{ + const CPPType &type = *value.type(); + Span copied_sockets = allocator_->construct_array_copy(sockets); + if (type.is()) { + bool log_full_geometry = false; + for (const DSocket &socket : sockets) { + if (main_logger_->log_full_geometry_sockets_.contains(socket)) { + log_full_geometry = true; + break; + } + } + + const GeometrySet &geometry_set = *value.get(); + destruct_ptr value_log = allocator_->construct( + geometry_set, log_full_geometry); + values_.append({copied_sockets, std::move(value_log)}); + } + else { + void *buffer = allocator_->allocate(type.size(), type.alignment()); + type.copy_construct(value.get(), buffer); + destruct_ptr value_log = allocator_->construct( + GMutablePointer{type, buffer}); + values_.append({copied_sockets, std::move(value_log)}); + } +} + +void LocalGeoLogger::log_multi_value_socket(DSocket socket, Span values) +{ + /* Doesn't have to be logged currently. */ + UNUSED_VARS(socket, values); +} + +void LocalGeoLogger::log_node_warning(DNode node, NodeWarningType type, std::string message) +{ + node_warnings_.append({node, {type, std::move(message)}}); +} + +} // namespace blender::nodes::geometry_nodes_eval_log diff --git a/source/blender/nodes/intern/node_geometry_exec.cc b/source/blender/nodes/intern/node_geometry_exec.cc index 17a13f2d1b0..5755a14f14d 100644 --- a/source/blender/nodes/intern/node_geometry_exec.cc +++ b/source/blender/nodes/intern/node_geometry_exec.cc @@ -16,8 +16,6 @@ #include "DNA_modifier_types.h" -#include "BKE_node_ui_storage.hh" - #include "DEG_depsgraph_query.h" #include "NOD_geometry_exec.hh" @@ -26,21 +24,14 @@ #include "node_geometry_util.hh" +using blender::nodes::geometry_nodes_eval_log::LocalGeoLogger; + namespace blender::nodes { void GeoNodeExecParams::error_message_add(const NodeWarningType type, std::string message) const { - bNodeTree *btree_cow = provider_->dnode->btree(); - BLI_assert(btree_cow != nullptr); - if (btree_cow == nullptr) { - return; - } - bNodeTree *btree_original = (bNodeTree *)DEG_get_original_id((ID *)btree_cow); - - const NodeTreeEvaluationContext context(*provider_->self_object, *provider_->modifier); - - BKE_nodetree_error_message_add( - *btree_original, context, *provider_->dnode->bnode(), type, std::move(message)); + LocalGeoLogger &local_logger = provider_->logger->local(); + local_logger.log_node_warning(provider_->dnode, type, std::move(message)); } const bNodeSocket *GeoNodeExecParams::find_available_socket(const StringRef name) const -- cgit v1.2.3