diff --git a/source/blender/blenlib/BLI_generic_key_string.hh b/source/blender/blenlib/BLI_generic_key_string.hh new file mode 100644 index 00000000000..b7c7de0bbbf --- /dev/null +++ b/source/blender/blenlib/BLI_generic_key_string.hh @@ -0,0 +1,48 @@ +/* SPDX-FileCopyrightText: 2025 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include "BLI_generic_key.hh" +#include "BLI_string_ref.hh" +#include "BLI_struct_equality_utils.hh" +#include "BLI_utility_mixins.hh" + +namespace blender { + +/** Utility class that to easy create a #GenericKey from a string. */ +class GenericStringKey : public GenericKey, NonMovable { + private: + std::string value_; + /** This may reference the string stored in value_. */ + StringRef value_ref_; + + public: + GenericStringKey(StringRef value) : value_ref_(value) {} + + uint64_t hash() const override + { + return get_default_hash(value_ref_); + } + + BLI_STRUCT_EQUALITY_OPERATORS_1(GenericStringKey, value_ref_) + + bool equal_to(const GenericKey &other) const override + { + if (const auto *other_typed = dynamic_cast(&other)) { + return value_ref_ == other_typed->value_ref_; + } + return false; + } + + std::unique_ptr to_storable() const override + { + auto storable_key = std::make_unique(""); + storable_key->value_ = value_ref_; + storable_key->value_ref_ = storable_key->value_; + return storable_key; + } +}; + +} // namespace blender diff --git a/source/blender/blenlib/BLI_memory_cache_file_load.hh b/source/blender/blenlib/BLI_memory_cache_file_load.hh new file mode 100644 index 00000000000..ac7c28210bc --- /dev/null +++ b/source/blender/blenlib/BLI_memory_cache_file_load.hh @@ -0,0 +1,34 @@ +/* SPDX-FileCopyrightText: 2025 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include "BLI_memory_cache.hh" +#include "BLI_string_ref.hh" + +namespace blender::memory_cache { + +/** + * Call the given loader function if its result has not been cached yet. The cache key is a + * combination of loader_key and file_paths. load_fn is responsible for still producing a valid + * cache value even if a file is not found. + */ +template +std::shared_ptr get_loaded(const GenericKey &loader_key, + Span file_paths, + FunctionRef()> load_fn); + +std::shared_ptr get_loaded_base(const GenericKey &loader_key, + Span file_paths, + FunctionRef()> load_fn); + +template +inline std::shared_ptr get_loaded(const GenericKey &loader_key, + Span file_paths, + FunctionRef()> load_fn) +{ + return std::dynamic_pointer_cast(get_loaded_base(loader_key, file_paths, load_fn)); +} + +} // namespace blender::memory_cache diff --git a/source/blender/blenlib/CMakeLists.txt b/source/blender/blenlib/CMakeLists.txt index 57dcb473f51..a7d0b5057b4 100644 --- a/source/blender/blenlib/CMakeLists.txt +++ b/source/blender/blenlib/CMakeLists.txt @@ -116,6 +116,7 @@ set(SRC intern/math_vector.cc intern/math_vector_inline.cc intern/memory_cache.cc + intern/memory_cache_file_load.cc intern/memory_counter.cc intern/memory_utils.cc intern/mesh_boolean.cc @@ -239,6 +240,7 @@ set(SRC BLI_function_ref.hh BLI_generic_array.hh BLI_generic_key.hh + BLI_generic_key_string.hh BLI_generic_pointer.hh BLI_generic_span.hh BLI_generic_value_map.hh @@ -325,6 +327,7 @@ set(SRC BLI_memblock.h BLI_memiter.h BLI_memory_cache.hh + BLI_memory_cache_file_load.hh BLI_memory_counter.hh BLI_memory_counter_fwd.hh BLI_memory_utils.h diff --git a/source/blender/blenlib/intern/memory_cache_file_load.cc b/source/blender/blenlib/intern/memory_cache_file_load.cc new file mode 100644 index 00000000000..4c651b7e368 --- /dev/null +++ b/source/blender/blenlib/intern/memory_cache_file_load.cc @@ -0,0 +1,143 @@ +/* SPDX-FileCopyrightText: 2025 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#include +#include + +#include "BLI_fileops.hh" +#include "BLI_hash.hh" +#include "BLI_map.hh" +#include "BLI_memory_cache_file_load.hh" +#include "BLI_task.hh" +#include "BLI_vector.hh" +#include "BLI_vector_set.hh" + +namespace blender::memory_cache { + +/** + * A key used to identify data loaded from one or more files. + */ +class LoadFileKey : public GenericKey { + private: + /** The files to load from. */ + Vector file_paths_; + /** + * The key used to identify the loader. The same files might be loaded with different loaders + * which can result in different data that needs to be cached separately. + */ + std::shared_ptr loader_key_; + + public: + LoadFileKey(Vector file_paths, std::shared_ptr loader_key) + : file_paths_(std::move(file_paths)), loader_key_(std::move(loader_key)) + { + } + + Span file_paths() const + { + return file_paths_; + } + + uint64_t hash() const override + { + return get_default_hash(file_paths_, *loader_key_); + } + + friend bool operator==(const LoadFileKey &a, const LoadFileKey &b) + { + return a.file_paths_ == b.file_paths_ && *a.loader_key_ == *b.loader_key_; + } + + bool equal_to(const GenericKey &other) const override + { + if (const auto *other_typed = dynamic_cast(&other)) { + return *this == *other_typed; + } + return false; + } + + std::unique_ptr to_storable() const override + { + /* Currently #LoadFileKey is always storable, i.e. it owns all the data it references. A + * potential future optimization could be to support just referencing the paths and loader key, + * but that causes some boilerplate now that is not worth it. */ + return std::make_unique(*this); + } +}; + +static std::optional get_file_modification_time(const StringRefNull path) +{ + BLI_stat_t stat; + if (BLI_stat(path.c_str(), &stat) == -1) { + return std::nullopt; + } + return stat.st_mtime; +} + +struct FileStatMap { + std::mutex mutex; + Map> map; +}; + +static FileStatMap &get_file_stat_map() +{ + static FileStatMap file_stat_map; + return file_stat_map; +} + +static void invalidate_outdated_caches_if_necessary(const Span file_paths) +{ + FileStatMap &file_stat_map = get_file_stat_map(); + + /* Retrieve the file modification times before the lock because there is no need for the lock + * yet. While not guaranteed, retrieving the modification time is often optimized by the OS so + * that no actual access to the hard drive is necessary. */ + Array> new_times(file_paths.size()); + for (const int i : file_paths.index_range()) { + new_times[i] = get_file_modification_time(file_paths[i]); + } + + std::lock_guard lock{file_stat_map.mutex}; + + /* Find all paths that have changed on disk. */ + VectorSet outdated_paths; + for (const int i : file_paths.index_range()) { + const StringRefNull path = file_paths[i]; + const std::optional new_time = new_times[i]; + std::optional &old_time = file_stat_map.map.lookup_or_add_as(path, new_time); + if (old_time != new_time) { + outdated_paths.add(path); + old_time = new_time; + } + } + /* If any referenced file was changed, invalidate the caches that use it. */ + if (!outdated_paths.is_empty()) { + /* Isolate because a mutex is locked. */ + threading::isolate_task([&]() { + /* Invalidation is done while the mutex is locked so that other threads won't see the old + * cached value anymore after we've detected that it's oudated. */ + memory_cache::remove_if([&](const GenericKey &other_key) { + if (const auto *other_key_typed = dynamic_cast(&other_key)) { + const Span other_key_paths = other_key_typed->file_paths(); + return std::any_of( + other_key_paths.begin(), other_key_paths.end(), [&](const StringRefNull path) { + return outdated_paths.contains(path); + }); + } + return false; + }); + }); + } +} + +std::shared_ptr get_loaded_base(const GenericKey &loader_key, + Span file_paths, + FunctionRef()> load_fn) +{ + invalidate_outdated_caches_if_necessary(file_paths); + const LoadFileKey key{file_paths, loader_key.to_storable()}; + return memory_cache::get_base(key, load_fn); +} + +} // namespace blender::memory_cache diff --git a/source/blender/nodes/NOD_geometry_nodes_log.hh b/source/blender/nodes/NOD_geometry_nodes_log.hh index 43ad494fcfc..404069b8d0a 100644 --- a/source/blender/nodes/NOD_geometry_nodes_log.hh +++ b/source/blender/nodes/NOD_geometry_nodes_log.hh @@ -50,6 +50,7 @@ struct SpaceNode; struct NodesModifierData; +struct Report; namespace blender::nodes::geo_eval_log { @@ -69,6 +70,9 @@ struct NodeWarning { NodeWarningType type; std::string message; + NodeWarning(NodeWarningType type, StringRef message) : type(type), message(message) {} + NodeWarning(const Report &report); + uint64_t hash() const { return get_default_hash(this->type, this->message); diff --git a/source/blender/nodes/geometry/nodes/node_geo_import_csv.cc b/source/blender/nodes/geometry/nodes/node_geo_import_csv.cc index 7b394eb8b8c..f4228bd5b02 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_import_csv.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_import_csv.cc @@ -2,7 +2,11 @@ * * SPDX-License-Identifier: GPL-2.0-or-later */ +#include + +#include "BLI_generic_key_string.hh" #include "BLI_listbase.h" +#include "BLI_memory_cache_file_load.hh" #include "BLI_string.h" #include "BKE_report.hh" @@ -25,6 +29,17 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_output("Point Cloud"); } +class LoadCsvCache : public memory_cache::CachedValue { + public: + GeometrySet geometry; + Vector warnings; + + void count_memory(MemoryCounter &counter) const override + { + this->geometry.count_memory(counter); + } +}; + static void node_geo_exec(GeoNodeExecParams params) { #ifdef WITH_IO_CSV @@ -47,31 +62,35 @@ static void node_geo_exec(GeoNodeExecParams params) return; } - blender::io::csv::CSVImportParams import_params{}; - import_params.delimiter = delimiter[0]; - STRNCPY(import_params.filepath, path->c_str()); + /* Encode delimiter in key because it affects the result. */ + const std::string loader_key = fmt::format("import_csv_node_{}", delimiter[0]); + std::shared_ptr cached_value = memory_cache::get_loaded( + GenericStringKey{loader_key}, {StringRefNull(*path)}, [&]() { + blender::io::csv::CSVImportParams import_params{}; + import_params.delimiter = delimiter[0]; + STRNCPY(import_params.filepath, path->c_str()); - ReportList reports; - BKE_reports_init(&reports, RPT_STORE); - BLI_SCOPED_DEFER([&]() { BKE_reports_free(&reports); }) - import_params.reports = &reports; + ReportList reports; + BKE_reports_init(&reports, RPT_STORE); + BLI_SCOPED_DEFER([&]() { BKE_reports_free(&reports); }); + import_params.reports = &reports; - PointCloud *pointcloud = blender::io::csv::import_csv_as_pointcloud(import_params); + PointCloud *pointcloud = blender::io::csv::import_csv_as_pointcloud(import_params); - LISTBASE_FOREACH (Report *, report, &(import_params.reports)->list) { - NodeWarningType type; - switch (report->type) { - case RPT_ERROR: - type = NodeWarningType::Error; - break; - default: - type = NodeWarningType::Info; - break; - } - params.error_message_add(type, TIP_(report->message)); + auto cached_value = std::make_unique(); + cached_value->geometry = GeometrySet::from_pointcloud(pointcloud); + + LISTBASE_FOREACH (Report *, report, &(import_params.reports)->list) { + cached_value->warnings.append_as(*report); + } + return cached_value; + }); + + for (const geo_eval_log::NodeWarning &warning : cached_value->warnings) { + params.error_message_add(warning.type, warning.message); } - params.set_output("Point Cloud", GeometrySet::from_pointcloud(pointcloud)); + params.set_output("Point Cloud", cached_value->geometry); #else params.error_message_add(NodeWarningType::Error, TIP_("Disabled, Blender was compiled without CSV I/O")); diff --git a/source/blender/nodes/geometry/nodes/node_geo_import_obj.cc b/source/blender/nodes/geometry/nodes/node_geo_import_obj.cc index 87d3d3c07ae..bb3b4575b8a 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_import_obj.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_import_obj.cc @@ -4,7 +4,9 @@ #include "node_geometry_util.hh" +#include "BLI_generic_key_string.hh" #include "BLI_listbase.h" +#include "BLI_memory_cache_file_load.hh" #include "BLI_string.h" #include "BKE_instances.hh" @@ -25,6 +27,17 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_output("Instances"); } +class LoadObjCache : public memory_cache::CachedValue { + public: + GeometrySet geometry; + Vector warnings; + + void count_memory(MemoryCounter &counter) const override + { + this->geometry.count_memory(counter); + } +}; + static void node_geo_exec(GeoNodeExecParams params) { #ifdef WITH_IO_WAVEFRONT_OBJ @@ -35,42 +48,40 @@ static void node_geo_exec(GeoNodeExecParams params) return; } - OBJImportParams import_params; - STRNCPY(import_params.filepath, path->c_str()); + std::shared_ptr cached_value = memory_cache::get_loaded( + GenericStringKey{"import_obj_node"}, {StringRefNull(*path)}, [&]() { + OBJImportParams import_params; + STRNCPY(import_params.filepath, path->c_str()); - ReportList reports; - BKE_reports_init(&reports, RPT_STORE); - BLI_SCOPED_DEFER([&]() { BKE_reports_free(&reports); }); - import_params.reports = &reports; + ReportList reports; + BKE_reports_init(&reports, RPT_STORE); + BLI_SCOPED_DEFER([&]() { BKE_reports_free(&reports); }); + import_params.reports = &reports; - Vector geometries; - OBJ_import_geometries(&import_params, geometries); + Vector geometries; + OBJ_import_geometries(&import_params, geometries); - LISTBASE_FOREACH (Report *, report, &(import_params.reports)->list) { - NodeWarningType type; - switch (report->type) { - case RPT_ERROR: - type = NodeWarningType::Error; - break; - default: - type = NodeWarningType::Info; - break; - } - params.error_message_add(type, TIP_(report->message)); + bke::Instances *instances = new bke::Instances(); + for (GeometrySet geometry : geometries) { + const int handle = instances->add_reference(bke::InstanceReference{std::move(geometry)}); + instances->add_instance(handle, float4x4::identity()); + } + + auto cached_value = std::make_unique(); + cached_value->geometry = GeometrySet::from_instances(instances); + + LISTBASE_FOREACH (Report *, report, &(import_params.reports)->list) { + cached_value->warnings.append_as(*report); + } + + return cached_value; + }); + + for (const geo_eval_log::NodeWarning &warning : cached_value->warnings) { + params.error_message_add(warning.type, warning.message); } - if (geometries.is_empty()) { - params.set_default_remaining_outputs(); - return; - } - - bke::Instances *instances = new bke::Instances(); - for (GeometrySet geometry : geometries) { - const int handle = instances->add_reference(bke::InstanceReference{std::move(geometry)}); - instances->add_instance(handle, float4x4::identity()); - } - - params.set_output("Instances", GeometrySet::from_instances(instances)); + params.set_output("Instances", cached_value->geometry); #else params.error_message_add(NodeWarningType::Error, TIP_("Disabled, Blender was compiled without OBJ I/O")); diff --git a/source/blender/nodes/geometry/nodes/node_geo_import_ply.cc b/source/blender/nodes/geometry/nodes/node_geo_import_ply.cc index dc3673836de..8c3e5dcd320 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_import_ply.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_import_ply.cc @@ -4,7 +4,9 @@ #include "node_geometry_util.hh" +#include "BLI_generic_key_string.hh" #include "BLI_listbase.h" +#include "BLI_memory_cache_file_load.hh" #include "BLI_string.h" #include "BKE_report.hh" @@ -24,6 +26,17 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_output("Mesh"); } +class LoadPlyCache : public memory_cache::CachedValue { + public: + GeometrySet geometry; + Vector warnings; + + void count_memory(MemoryCounter &counter) const override + { + this->geometry.count_memory(counter); + } +}; + static void node_geo_exec(GeoNodeExecParams params) { #ifdef WITH_IO_PLY @@ -34,31 +47,33 @@ static void node_geo_exec(GeoNodeExecParams params) return; } - PLYImportParams import_params; - STRNCPY(import_params.filepath, path->c_str()); - import_params.import_attributes = true; + std::shared_ptr cached_value = memory_cache::get_loaded( + GenericStringKey{"import_ply_node"}, {StringRefNull(*path)}, [&]() { + PLYImportParams import_params; + STRNCPY(import_params.filepath, path->c_str()); + import_params.import_attributes = true; - ReportList reports; - BKE_reports_init(&reports, RPT_STORE); - BLI_SCOPED_DEFER([&]() { BKE_reports_free(&reports); }) - import_params.reports = &reports; + ReportList reports; + BKE_reports_init(&reports, RPT_STORE); + BLI_SCOPED_DEFER([&]() { BKE_reports_free(&reports); }); + import_params.reports = &reports; - Mesh *mesh = PLY_import_mesh(import_params); + Mesh *mesh = PLY_import_mesh(import_params); - LISTBASE_FOREACH (Report *, report, &(import_params.reports)->list) { - NodeWarningType type; - switch (report->type) { - case RPT_ERROR: - type = NodeWarningType::Error; - break; - default: - type = NodeWarningType::Info; - break; - } - params.error_message_add(type, TIP_(report->message)); + auto cached_value = std::make_unique(); + cached_value->geometry = GeometrySet::from_mesh(mesh); + + LISTBASE_FOREACH (Report *, report, &(import_params.reports)->list) { + cached_value->warnings.append_as(*report); + } + return cached_value; + }); + + for (const geo_eval_log::NodeWarning &warning : cached_value->warnings) { + params.error_message_add(warning.type, warning.message); } - params.set_output("Mesh", GeometrySet::from_mesh(mesh)); + params.set_output("Mesh", cached_value->geometry); #else params.error_message_add(NodeWarningType::Error, diff --git a/source/blender/nodes/geometry/nodes/node_geo_import_stl.cc b/source/blender/nodes/geometry/nodes/node_geo_import_stl.cc index 687bd65866a..e1c229ec8fc 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_import_stl.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_import_stl.cc @@ -4,9 +4,13 @@ #include "node_geometry_util.hh" +#include "BLI_generic_key_string.hh" #include "BLI_listbase.h" +#include "BLI_memory_cache_file_load.hh" #include "BLI_string.h" +#include "DNA_mesh_types.h" + #include "BKE_report.hh" #include "IO_stl.hh" @@ -24,6 +28,17 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_output("Mesh"); } +class LoadStlCache : public memory_cache::CachedValue { + public: + GeometrySet geometry; + Vector warnings; + + void count_memory(MemoryCounter &counter) const override + { + this->geometry.count_memory(counter); + } +}; + static void node_geo_exec(GeoNodeExecParams params) { #ifdef WITH_IO_STL @@ -34,33 +49,36 @@ static void node_geo_exec(GeoNodeExecParams params) return; } - STLImportParams import_params; - STRNCPY(import_params.filepath, path->c_str()); + std::shared_ptr cached_value = memory_cache::get_loaded( + GenericStringKey{"import_stl_node"}, {StringRefNull(*path)}, [&]() { + STLImportParams import_params; + STRNCPY(import_params.filepath, path->c_str()); - import_params.forward_axis = IO_AXIS_NEGATIVE_Z; - import_params.up_axis = IO_AXIS_Y; + import_params.forward_axis = IO_AXIS_NEGATIVE_Z; + import_params.up_axis = IO_AXIS_Y; - ReportList reports; - BKE_reports_init(&reports, RPT_STORE); - BLI_SCOPED_DEFER([&]() { BKE_reports_free(&reports); }) - import_params.reports = &reports; + ReportList reports; + BKE_reports_init(&reports, RPT_STORE); + BLI_SCOPED_DEFER([&]() { BKE_reports_free(&reports); }) + import_params.reports = &reports; - Mesh *mesh = STL_import_mesh(&import_params); + Mesh *mesh = STL_import_mesh(&import_params); - LISTBASE_FOREACH (Report *, report, &(import_params.reports)->list) { - NodeWarningType type; - switch (report->type) { - case RPT_ERROR: - type = NodeWarningType::Error; - break; - default: - type = NodeWarningType::Info; - break; - } - params.error_message_add(type, TIP_(report->message)); + auto cached_value = std::make_unique(); + cached_value->geometry = GeometrySet::from_mesh(mesh); + + LISTBASE_FOREACH (Report *, report, &(import_params.reports)->list) { + cached_value->warnings.append_as(*report); + } + + return cached_value; + }); + + for (const geo_eval_log::NodeWarning &warning : cached_value->warnings) { + params.error_message_add(warning.type, warning.message); } - params.set_output("Mesh", GeometrySet::from_mesh(mesh)); + params.set_output("Mesh", cached_value->geometry); #else params.error_message_add(NodeWarningType::Error, diff --git a/source/blender/nodes/geometry/nodes/node_geo_import_text.cc b/source/blender/nodes/geometry/nodes/node_geo_import_text.cc index b98ac6ce98e..04aa60adffa 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_import_text.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_import_text.cc @@ -3,6 +3,9 @@ * SPDX-License-Identifier: GPL-2.0-or-later */ #include "BLI_fileops.h" +#include "BLI_generic_key_string.hh" +#include "BLI_memory_cache_file_load.hh" +#include "BLI_memory_counter.hh" #include "BLI_string_utf8.h" #include "node_geometry_util.hh" @@ -22,6 +25,17 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_output("String"); } +class LoadTextCache : public memory_cache::CachedValue { + public: + std::string text; + Vector warnings; + + void count_memory(MemoryCounter &counter) const override + { + counter.add(this->text.size()); + } +}; + static void node_geo_exec(GeoNodeExecParams params) { const std::optional path = params.ensure_absolute_path( @@ -31,23 +45,33 @@ static void node_geo_exec(GeoNodeExecParams params) return; } - size_t buffer_len; - void *buffer = BLI_file_read_text_as_mem(path->c_str(), 0, &buffer_len); - if (!buffer) { - const std::string message = fmt::format(fmt::runtime(TIP_("Cannot open file: {}")), *path); - params.error_message_add(NodeWarningType::Error, message); - params.set_default_remaining_outputs(); - return; - } - BLI_SCOPED_DEFER([&]() { MEM_freeN(buffer); }); - if (BLI_str_utf8_invalid_byte(static_cast(buffer), buffer_len) != -1) { - params.error_message_add(NodeWarningType::Error, - TIP_("File contains invalid UTF-8 characters")); - params.set_default_remaining_outputs(); - return; + std::shared_ptr cached_value = memory_cache::get_loaded( + GenericStringKey{"import_text_node"}, {StringRefNull(*path)}, [&]() { + auto cached_value = std::make_unique(); + + size_t buffer_len; + void *buffer = BLI_file_read_text_as_mem(path->c_str(), 0, &buffer_len); + if (!buffer) { + const std::string message = fmt::format(fmt::runtime(TIP_("Cannot open file: {}")), + *path); + cached_value->warnings.append({NodeWarningType::Error, message}); + return cached_value; + } + BLI_SCOPED_DEFER([&]() { MEM_freeN(buffer); }); + if (BLI_str_utf8_invalid_byte(static_cast(buffer), buffer_len) != -1) { + cached_value->warnings.append( + {NodeWarningType::Error, TIP_("File contains invalid UTF-8 characters")}); + return cached_value; + } + cached_value->text = std::string(static_cast(buffer), buffer_len); + return cached_value; + }); + + for (const geo_eval_log::NodeWarning &warning : cached_value->warnings) { + params.error_message_add(warning.type, warning.message); } - params.set_output("String", std::string(static_cast(buffer), buffer_len)); + params.set_output("String", cached_value->text); } static void node_register() diff --git a/source/blender/nodes/intern/geometry_nodes_log.cc b/source/blender/nodes/intern/geometry_nodes_log.cc index 26288cd4d50..3b073597552 100644 --- a/source/blender/nodes/intern/geometry_nodes_log.cc +++ b/source/blender/nodes/intern/geometry_nodes_log.cc @@ -2,6 +2,7 @@ * * SPDX-License-Identifier: GPL-2.0-or-later */ +#include "DNA_windowmanager_types.h" #include "NOD_geometry_nodes_bundle.hh" #include "NOD_geometry_nodes_closure.hh" #include "NOD_geometry_nodes_log.hh" @@ -246,6 +247,19 @@ ClosureValueLog::ClosureValueLog(Vector inputs, } } +NodeWarning::NodeWarning(const Report &report) +{ + switch (report.type) { + case RPT_ERROR: + this->type = NodeWarningType::Error; + break; + default: + this->type = NodeWarningType::Info; + break; + } + this->message = report.message; +} + /* Avoid generating these in every translation unit. */ GeoModifierLog::GeoModifierLog() = default; GeoModifierLog::~GeoModifierLog() = default;