Blender V4.5
geometry_nodes_log.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
9
10#include "BLI_listbase.h"
11#include "BLI_stack.hh"
12#include "BLI_string_ref.hh"
13#include "BLI_string_utf8.h"
14
18#include "BKE_curves.hh"
20#include "BKE_grease_pencil.hh"
21#include "BKE_lib_query.hh"
23#include "BKE_node_runtime.hh"
26#include "BKE_volume.hh"
27#include "BKE_volume_grid.hh"
28#include "BKE_volume_openvdb.hh"
29
31#include "DNA_modifier_types.h"
32#include "DNA_space_types.h"
33
34#include "ED_geometry.hh"
35#include "ED_node.hh"
36#include "ED_viewer_path.hh"
37
38#include "MOD_nodes.hh"
39
40#include "UI_resources.hh"
41
43
45
46using bke::bNodeTreeZone;
47using bke::bNodeTreeZones;
48using fn::FieldInput;
49using fn::FieldInputs;
50
52{
53 this->value.destruct();
54}
55
57{
58 /* Avoid logging the entirety of long strings, to avoid unnecessary memory usage. */
59 if (string.size() <= 100) {
60 this->truncated = false;
61 this->value = allocator.copy_string(string);
62 return;
63 }
64 this->truncated = true;
65 const char *end = BLI_str_find_prev_char_utf8(string.data() + 100, string.data());
66 this->value = allocator.copy_string(StringRef(string.data(), end));
67}
68
69FieldInfoLog::FieldInfoLog(const GField &field) : type(field.cpp_type())
70{
71 const std::shared_ptr<const fn::FieldInputs> &field_input_nodes = field.node().field_inputs();
72
73 /* Put the deduplicated field inputs into a vector so that they can be sorted below. */
75 if (field_input_nodes) {
76 field_inputs.extend(field_input_nodes->deduplicated_nodes.begin(),
77 field_input_nodes->deduplicated_nodes.end());
78 }
79
80 std::sort(
81 field_inputs.begin(), field_inputs.end(), [](const FieldInput &a, const FieldInput &b) {
82 const int index_a = int(a.category());
83 const int index_b = int(b.category());
84 if (index_a == index_b) {
85 return a.socket_inspection_name().size() < b.socket_inspection_name().size();
86 }
87 return index_a < index_b;
88 });
89
90 for (const FieldInput &field_input : field_inputs) {
91 this->input_tooltips.append(field_input.socket_inspection_name());
92 }
93}
94
96{
97 this->name = geometry_set.name;
98
99 static std::array all_component_types = {bke::GeometryComponent::Type::Curve,
105
106 /* Keep track handled attribute names to make sure that we do not return the same name twice.
107 * Currently #GeometrySet::attribute_foreach does not do that. Note that this will merge
108 * attributes with the same name but different domains or data types on separate components. */
109 Set<StringRef> names;
110
111 geometry_set.attribute_foreach(
112 all_component_types,
113 true,
114 [&](const StringRef attribute_id,
115 const bke::AttributeMetaData &meta_data,
116 const bke::GeometryComponent & /*component*/) {
117 if (!bke::attribute_name_is_anonymous(attribute_id) && names.add(attribute_id)) {
118 this->attributes.append({attribute_id, meta_data.domain, meta_data.data_type});
119 }
120 });
121
122 for (const bke::GeometryComponent *component : geometry_set.get_components()) {
123 this->component_types.append(component->type());
124 switch (component->type()) {
126 const auto &mesh_component = *static_cast<const bke::MeshComponent *>(component);
127 MeshInfo &info = this->mesh_info.emplace();
128 info.verts_num = mesh_component.attribute_domain_size(bke::AttrDomain::Point);
129 info.edges_num = mesh_component.attribute_domain_size(bke::AttrDomain::Edge);
130 info.faces_num = mesh_component.attribute_domain_size(bke::AttrDomain::Face);
131 break;
132 }
134 const auto &curve_component = *static_cast<const bke::CurveComponent *>(component);
135 CurveInfo &info = this->curve_info.emplace();
136 info.points_num = curve_component.attribute_domain_size(bke::AttrDomain::Point);
137 info.splines_num = curve_component.attribute_domain_size(bke::AttrDomain::Curve);
138 break;
139 }
141 const auto &pointcloud_component = *static_cast<const bke::PointCloudComponent *>(
142 component);
143 PointCloudInfo &info = this->pointcloud_info.emplace();
144 info.points_num = pointcloud_component.attribute_domain_size(bke::AttrDomain::Point);
145 break;
146 }
148 const auto &instances_component = *static_cast<const bke::InstancesComponent *>(component);
149 InstancesInfo &info = this->instances_info.emplace();
150 info.instances_num = instances_component.attribute_domain_size(bke::AttrDomain::Instance);
151 break;
152 }
154 const auto &edit_component = *static_cast<const bke::GeometryComponentEditData *>(
155 component);
156 if (!this->edit_data_info) {
157 this->edit_data_info.emplace(EditDataInfo());
158 }
159 EditDataInfo &info = *this->edit_data_info;
160 if (const bke::CurvesEditHints *curve_edit_hints = edit_component.curves_edit_hints_.get())
161 {
162 info.has_deform_matrices = curve_edit_hints->deform_mats.has_value();
163 info.has_deformed_positions = curve_edit_hints->positions().has_value();
164 }
165 if (const bke::GizmoEditHints *gizmo_edit_hints = edit_component.gizmo_edit_hints_.get()) {
166 info.gizmo_transforms_num = gizmo_edit_hints->gizmo_transforms.size();
167 }
168 break;
169 }
171 const auto &volume_component = *static_cast<const bke::VolumeComponent *>(component);
172 if (const Volume *volume = volume_component.get()) {
173 VolumeInfo &info = this->volume_info.emplace();
174 info.grids_num = BKE_volume_num_grids(volume);
175 }
176 break;
177 }
179 const auto &grease_pencil_component = *static_cast<const bke::GreasePencilComponent *>(
180 component);
181 if (const GreasePencil *grease_pencil = grease_pencil_component.get()) {
183 info.layers_num = grease_pencil->layers().size();
184 Set<StringRef> unique_layer_names;
185 for (const bke::greasepencil::Layer *layer : grease_pencil->layers()) {
186 const StringRefNull layer_name = layer->name();
187 if (unique_layer_names.add(layer_name)) {
188 info.layer_names.append(layer_name);
189 }
190 }
191 }
192 break;
193 }
194 }
195 }
196}
197
198#ifdef WITH_OPENVDB
199struct GridIsEmptyOp {
200 const openvdb::GridBase &base_grid;
201 bool result = false;
202
203 template<typename GridType> bool operator()()
204 {
205 result = static_cast<const GridType &>(base_grid).empty();
206 return true;
207 }
208};
209#endif /* WITH_OPENVDB */
210
211GeometryInfoLog::GeometryInfoLog(const bke::GVolumeGrid &grid)
212{
213 GridInfo &info = this->grid_info.emplace();
214#ifdef WITH_OPENVDB
215 bke::VolumeTreeAccessToken token;
216 const openvdb::GridBase &vdb_grid = grid->grid(token);
217 const VolumeGridType grid_type = bke::volume_grid::get_type(vdb_grid);
218
219 GridIsEmptyOp is_empty_op{vdb_grid};
220 if (BKE_volume_grid_type_operation(grid_type, is_empty_op)) {
221 info.is_empty = is_empty_op.result;
222 }
223 else {
224 info.is_empty = true;
225 }
226#else
227 UNUSED_VARS(grid);
228 info.is_empty = true;
229#endif
230}
231
233
236 const std::optional<ClosureSourceLocation> &source_location,
237 std::shared_ptr<ClosureEvalLog> eval_log)
238 : inputs(std::move(inputs)), outputs(std::move(outputs)), eval_log(std::move(eval_log))
239{
240 if (source_location) {
241 const bNodeTree *tree_eval = source_location->tree;
242 const bNodeTree *tree_orig = reinterpret_cast<const bNodeTree *>(
243 DEG_get_original_id(&tree_eval->id));
244 this->source = Source{tree_orig->id.session_uid,
245 source_location->closure_output_node_id,
246 source_location->compute_context_hash};
247 }
248}
249
251{
252 switch (report.type) {
253 case RPT_ERROR:
255 break;
256 default:
258 break;
259 }
260 this->message = report.message;
261}
262
263/* Avoid generating these in every translation unit. */
264GeoNodesLog::GeoNodesLog() = default;
265GeoNodesLog::~GeoNodesLog() = default;
266
269
270GeoNodeLog::GeoNodeLog() = default;
271GeoNodeLog::~GeoNodeLog() = default;
272
274 : root_log_(root_log), tree_loggers_(std::move(tree_loggers))
275{
276 for (GeoTreeLogger *tree_logger : tree_loggers_) {
277 for (const ComputeContextHash &hash : tree_logger->children_hashes) {
278 children_hashes_.add(hash);
279 }
280 }
281}
282
283GeoTreeLog::~GeoTreeLog() = default;
284
285void GeoTreeLogger::log_value(const bNode &node, const bNodeSocket &socket, const GPointer value)
286{
287 const CPPType &type = *value.type();
288
289 auto store_logged_value = [&](destruct_ptr<ValueLog> value_log) {
290 auto &socket_values = socket.in_out == SOCK_IN ? this->input_socket_values :
292 socket_values.append(*this->allocator,
293 {node.identifier, socket.index(), std::move(value_log)});
294 };
295
296 auto log_generic_value = [&](const CPPType &type, const void *value) {
297 void *buffer = this->allocator->allocate(type);
298 type.copy_construct(value, buffer);
299 store_logged_value(this->allocator->construct<GenericValueLog>(GMutablePointer{type, buffer}));
300 };
301
302 if (type.is<bke::GeometrySet>()) {
304 store_logged_value(this->allocator->construct<GeometryInfoLog>(geometry));
305 }
306 else if (type.is<bke::SocketValueVariant>()) {
307 bke::SocketValueVariant value_variant = *value.get<bke::SocketValueVariant>();
308 if (value_variant.is_context_dependent_field()) {
309 const GField field = value_variant.extract<GField>();
310 store_logged_value(this->allocator->construct<FieldInfoLog>(field));
311 }
312#ifdef WITH_OPENVDB
313 else if (value_variant.is_volume_grid()) {
314 const bke::GVolumeGrid grid = value_variant.extract<bke::GVolumeGrid>();
315 store_logged_value(this->allocator->construct<GeometryInfoLog>(grid));
316 }
317#endif
318 else if (value_variant.valid_for_socket(SOCK_BUNDLE)) {
320 if (const BundlePtr bundle = value_variant.extract<BundlePtr>()) {
321 for (const Bundle::StoredItem &item : bundle->items()) {
322 items.append({item.key, item.type});
323 }
324 }
325 store_logged_value(this->allocator->construct<BundleValueLog>(std::move(items)));
326 }
327 else if (value_variant.valid_for_socket(SOCK_CLOSURE)) {
330 std::optional<ClosureSourceLocation> source_location;
331 std::shared_ptr<ClosureEvalLog> eval_log;
332 if (const ClosurePtr closure = value_variant.extract<ClosurePtr>()) {
333 const ClosureSignature &signature = closure->signature();
334 for (const ClosureSignature::Item &item : signature.inputs) {
335 inputs.append({item.key, item.type});
336 }
337 for (const ClosureSignature::Item &item : signature.outputs) {
338 outputs.append({item.key, item.type});
339 }
340 source_location = closure->source_location();
341 eval_log = closure->eval_log_ptr();
342 }
343 store_logged_value(this->allocator->construct<ClosureValueLog>(
344 std::move(inputs), std::move(outputs), source_location, eval_log));
345 }
346 else {
347 value_variant.convert_to_single();
348 const GPointer value = value_variant.get_single_ptr();
349 if (value.type()->is<std::string>()) {
350 const std::string &string = *value.get<std::string>();
351 store_logged_value(this->allocator->construct<StringLog>(string, *this->allocator));
352 }
353 else {
354 log_generic_value(*value.type(), value.get());
355 }
356 }
357 }
358 else {
359 log_generic_value(type, value.get());
360 }
361}
362
364{
366 log->geometry = std::move(geometry);
367 log->geometry.ensure_owns_direct_data();
368 this->viewer_node_logs.append(*this->allocator, {viewer_node.identifier, std::move(log)});
369}
370
371static bool warning_is_propagated(const NodeWarningPropagation propagation,
372 const NodeWarningType warning_type)
373{
374 switch (propagation) {
376 return true;
378 return false;
380 return warning_type == NodeWarningType::Error;
383 }
385 return true;
386}
387
389{
390 if (reduced_node_warnings_) {
391 return;
392 }
393 if (!nmd.node_group) {
394 reduced_node_warnings_ = true;
395 return;
396 }
399 nullptr,
400 &nmd.node_group->id,
401 [&](LibraryIDLinkCallbackData *cb_data) {
402 if (ID *id = *cb_data->id_pointer) {
403 if (GS(id->name) == ID_NT) {
404 const bNodeTree *tree = reinterpret_cast<const bNodeTree *>(id);
405 map.add(id->session_uid, tree);
406 }
407 }
408 return IDWALK_RET_NOP;
409 },
410 nullptr,
412 this->ensure_node_warnings(map);
413}
414
416{
417 if (reduced_node_warnings_) {
418 return;
419 }
421 FOREACH_NODETREE_BEGIN (const_cast<Main *>(&bmain), tree, id) {
422 map.add_new(tree->id.session_uid, tree);
423 }
425 this->ensure_node_warnings(map);
426}
427
429 const Map<uint32_t, const bNodeTree *> &orig_tree_by_session_uid)
430{
431 if (reduced_node_warnings_) {
432 return;
433 }
434 if (tree_loggers_.is_empty()) {
435 return;
436 }
437 const std::optional<uint32_t> tree_uid = tree_loggers_[0]->tree_orig_session_uid;
438 const bNodeTree *tree = tree_uid ? orig_tree_by_session_uid.lookup_default(*tree_uid, nullptr) :
439 nullptr;
440
441 for (GeoTreeLogger *tree_logger : tree_loggers_) {
442 for (const GeoTreeLogger::WarningWithNode &warning : tree_logger->node_warnings) {
444 if (tree) {
445 if (const bNode *node = tree->node_by_id(warning.node_id)) {
446 propagation = NodeWarningPropagation(node->warning_propagation);
447 }
448 }
449 this->nodes.lookup_or_add_default(warning.node_id).warnings.add(warning.warning);
450 if (warning_is_propagated(propagation, warning.warning.type)) {
451 this->all_warnings.add(warning.warning);
452 }
453 }
454 }
455 for (const ComputeContextHash &child_hash : children_hashes_) {
456 GeoTreeLog &child_log = root_log_->get_tree_log(child_hash);
457 if (child_log.tree_loggers_.is_empty()) {
458 continue;
459 }
460 const GeoTreeLogger &first_child_logger = *child_log.tree_loggers_[0];
462 const std::optional<int32_t> &caller_node_id = first_child_logger.parent_node_id;
463 if (tree && caller_node_id) {
464 if (const bNode *caller_node = tree->node_by_id(*caller_node_id)) {
465 propagation = NodeWarningPropagation(caller_node->warning_propagation);
466 }
467 }
468 child_log.ensure_node_warnings(orig_tree_by_session_uid);
469 if (caller_node_id.has_value()) {
470 this->nodes.lookup_or_add_default(*caller_node_id)
471 .warnings.add_multiple(child_log.all_warnings);
472 }
473 for (const NodeWarning &warning : child_log.all_warnings) {
474 if (warning_is_propagated(propagation, warning.type)) {
475 this->all_warnings.add(warning);
476 continue;
477 }
478 }
479 }
480 reduced_node_warnings_ = true;
481}
482
484{
485 if (reduced_execution_times_) {
486 return;
487 }
488 for (GeoTreeLogger *tree_logger : tree_loggers_) {
489 for (const GeoTreeLogger::NodeExecutionTime &timings : tree_logger->node_execution_times) {
490 const std::chrono::nanoseconds duration = timings.end - timings.start;
491 this->nodes.lookup_or_add_default_as(timings.node_id).execution_time += duration;
492 }
493 this->execution_time += tree_logger->execution_time;
494 }
495 reduced_execution_times_ = true;
496}
497
499{
500 if (reduced_socket_values_) {
501 return;
502 }
503 for (GeoTreeLogger *tree_logger : tree_loggers_) {
504 for (const GeoTreeLogger::SocketValueLog &value_log_data : tree_logger->input_socket_values) {
505 this->nodes.lookup_or_add_as(value_log_data.node_id)
506 .input_values_.add(value_log_data.socket_index, value_log_data.value.get());
507 }
508 for (const GeoTreeLogger::SocketValueLog &value_log_data : tree_logger->output_socket_values) {
509 this->nodes.lookup_or_add_as(value_log_data.node_id)
510 .output_values_.add(value_log_data.socket_index, value_log_data.value.get());
511 }
512 }
513 reduced_socket_values_ = true;
514}
515
517{
518 if (reduced_viewer_node_logs_) {
519 return;
520 }
521 for (GeoTreeLogger *tree_logger : tree_loggers_) {
522 for (const GeoTreeLogger::ViewerNodeLogWithNode &viewer_log : tree_logger->viewer_node_logs) {
523 this->viewer_node_logs.add(viewer_log.node_id, viewer_log.viewer_log.get());
524 }
525 }
526 reduced_viewer_node_logs_ = true;
527}
528
530{
531 if (reduced_existing_attributes_) {
532 return;
533 }
534 this->ensure_socket_values();
535
536 auto handle_value_log = [&](const ValueLog &value_log) {
537 const GeometryInfoLog *geo_log = dynamic_cast<const GeometryInfoLog *>(&value_log);
538 if (geo_log == nullptr) {
539 return;
540 }
541 for (const GeometryAttributeInfo &attribute : geo_log->attributes) {
542 this->existing_attributes.append(&attribute);
543 }
544 };
545
546 for (const GeoNodeLog &node_log : this->nodes.values()) {
547 for (const ValueLog *value_log : node_log.input_values_.values()) {
548 handle_value_log(*value_log);
549 }
550 for (const ValueLog *value_log : node_log.output_values_.values()) {
551 handle_value_log(*value_log);
552 }
553 }
554 reduced_existing_attributes_ = true;
555}
556
558{
559 if (reduced_used_named_attributes_) {
560 return;
561 }
562
563 auto add_attribute = [&](const int32_t node_id,
564 const StringRefNull attribute_name,
565 const NamedAttributeUsage &usage) {
566 this->nodes.lookup_or_add_default(node_id).used_named_attributes.lookup_or_add(attribute_name,
567 usage) |= usage;
568 this->used_named_attributes.lookup_or_add_as(attribute_name, usage) |= usage;
569 };
570
571 for (GeoTreeLogger *tree_logger : tree_loggers_) {
572 for (const GeoTreeLogger::AttributeUsageWithNode &item : tree_logger->used_named_attributes) {
573 add_attribute(item.node_id, item.attribute_name, item.usage);
574 }
575 }
576 for (const ComputeContextHash &child_hash : children_hashes_) {
577 GeoTreeLog &child_log = root_log_->get_tree_log(child_hash);
578 if (child_log.tree_loggers_.is_empty()) {
579 continue;
580 }
582 if (const std::optional<int32_t> &parent_node_id = child_log.tree_loggers_[0]->parent_node_id)
583 {
584 for (const auto item : child_log.used_named_attributes.items()) {
585 add_attribute(*parent_node_id, item.key, item.value);
586 }
587 }
588 }
589 reduced_used_named_attributes_ = true;
590}
591
593{
594 if (reduced_debug_messages_) {
595 return;
596 }
597 for (GeoTreeLogger *tree_logger : tree_loggers_) {
598 for (const GeoTreeLogger::DebugMessage &debug_message : tree_logger->debug_messages) {
599 this->nodes.lookup_or_add_as(debug_message.node_id)
600 .debug_messages.append(debug_message.message);
601 }
602 }
603 reduced_debug_messages_ = true;
604}
605
607{
608 if (reduced_evaluated_gizmo_nodes_) {
609 return;
610 }
611 for (const GeoTreeLogger *tree_logger : tree_loggers_) {
612 for (const GeoTreeLogger::EvaluatedGizmoNode &evaluated_gizmo :
613 tree_logger->evaluated_gizmo_nodes)
614 {
615 this->evaluated_gizmo_nodes.add(evaluated_gizmo.node_id);
616 }
617 }
618
619 reduced_evaluated_gizmo_nodes_ = true;
620}
621
623{
624 if (reduced_layer_names_) {
625 return;
626 }
627
628 this->ensure_socket_values();
629
630 auto handle_value_log = [&](const ValueLog &value_log) {
631 const GeometryInfoLog *geo_log = dynamic_cast<const GeometryInfoLog *>(&value_log);
632 if (geo_log == nullptr || !geo_log->grease_pencil_info.has_value()) {
633 return;
634 }
635 for (const std::string &name : geo_log->grease_pencil_info->layer_names) {
636 this->all_layer_names.append(name);
637 }
638 };
639
640 for (const GeoNodeLog &node_log : this->nodes.values()) {
641 for (const ValueLog *value_log : node_log.input_values_.values()) {
642 handle_value_log(*value_log);
643 }
644 for (const ValueLog *value_log : node_log.output_values_.values()) {
645 handle_value_log(*value_log);
646 }
647 }
648
649 reduced_layer_names_ = true;
650}
651
653{
659
660 BLI_assert(reduced_socket_values_);
661 if (query_socket.is_multi_input()) {
662 /* Not supported currently. */
663 return nullptr;
664 }
665
666 Set<const bNodeSocket *> added_sockets;
667 Stack<const bNodeSocket *> sockets_to_check;
668 sockets_to_check.push(&query_socket);
669 added_sockets.add(&query_socket);
670
671 while (!sockets_to_check.is_empty()) {
672 const bNodeSocket &socket = *sockets_to_check.pop();
673 const bNode &node = socket.owner_node();
674 if (GeoNodeLog *node_log = this->nodes.lookup_ptr(node.identifier)) {
675 ValueLog *value_log = socket.is_input() ?
676 node_log->input_values_.lookup_default(socket.index(), nullptr) :
677 node_log->output_values_.lookup_default(socket.index(), nullptr);
678 if (value_log != nullptr) {
679 return value_log;
680 }
681 }
682
683 if (socket.is_input()) {
684 const Span<const bNodeLink *> links = socket.directly_linked_links();
685 for (const bNodeLink *link : links) {
686 const bNodeSocket &from_socket = *link->fromsock;
687 if (added_sockets.add(&from_socket)) {
688 sockets_to_check.push(&from_socket);
689 }
690 }
691 }
692 else {
693 if (node.is_reroute()) {
694 const bNodeSocket &input_socket = node.input_socket(0);
695 if (added_sockets.add(&input_socket)) {
696 sockets_to_check.push(&input_socket);
697 }
698 const Span<const bNodeLink *> links = input_socket.directly_linked_links();
699 for (const bNodeLink *link : links) {
700 const bNodeSocket &from_socket = *link->fromsock;
701 if (added_sockets.add(&from_socket)) {
702 sockets_to_check.push(&from_socket);
703 }
704 }
705 }
706 else if (node.is_muted()) {
707 if (const bNodeSocket *input_socket = socket.internal_link_input()) {
708 if (added_sockets.add(input_socket)) {
709 sockets_to_check.push(input_socket);
710 }
711 const Span<const bNodeLink *> links = input_socket->directly_linked_links();
712 for (const bNodeLink *link : links) {
713 const bNodeSocket &from_socket = *link->fromsock;
714 if (added_sockets.add(&from_socket)) {
715 sockets_to_check.push(&from_socket);
716 }
717 }
718 }
719 }
720 }
721 }
722
723 return nullptr;
724}
725
727 const CPPType &dst_type,
728 void *dst)
729{
730 const void *src_value = value_log.value.get();
731 if (!src_value) {
732 return false;
733 }
735 const CPPType &src_type = *value_log.value.type();
736 if (!conversions.is_convertible(src_type, dst_type) && src_type != dst_type) {
737 return false;
738 }
739 dst_type.destruct(dst);
740 conversions.convert_to_uninitialized(src_type, dst_type, src_value, dst);
741 return true;
742}
743
744static std::optional<uint32_t> get_original_session_uid(const ID *id)
745{
746 if (!id) {
747 return {};
748 }
749 if (DEG_is_original(id)) {
750 return id->session_uid;
751 }
752 if (const ID *id_orig = DEG_get_original(id)) {
753 return id_orig->session_uid;
754 }
755 return {};
756}
757
759{
760 LocalData &local_data = data_per_thread_.local();
762 local_data.tree_logger_by_context;
763 destruct_ptr<GeoTreeLogger> &tree_logger_ptr = local_tree_loggers.lookup_or_add_default(
764 compute_context.hash());
765 if (tree_logger_ptr) {
766 return *tree_logger_ptr;
767 }
768 tree_logger_ptr = local_data.allocator.construct<GeoTreeLogger>();
769 GeoTreeLogger &tree_logger = *tree_logger_ptr;
770 tree_logger.allocator = &local_data.allocator;
771 const ComputeContext *parent_compute_context = compute_context.parent();
772 std::optional<uint32_t> parent_tree_session_uid;
773 if (parent_compute_context != nullptr) {
774 tree_logger.parent_hash = parent_compute_context->hash();
775 GeoTreeLogger &parent_logger = this->get_local_tree_logger(*parent_compute_context);
776 parent_logger.children_hashes.append(compute_context.hash());
777 parent_tree_session_uid = parent_logger.tree_orig_session_uid;
778 }
779 if (const auto *context = dynamic_cast<const bke::GroupNodeComputeContext *>(&compute_context)) {
780 tree_logger.parent_node_id.emplace(context->node_id());
781 if (const bNode *caller_node = context->node()) {
782 tree_logger.tree_orig_session_uid = get_original_session_uid(caller_node->id);
783 }
784 }
785 else if (const auto *context = dynamic_cast<const bke::RepeatZoneComputeContext *>(
786 &compute_context))
787 {
788 tree_logger.parent_node_id.emplace(context->output_node_id());
789 tree_logger.tree_orig_session_uid = parent_tree_session_uid;
790 }
791 else if (const auto *context =
793 &compute_context))
794 {
795 tree_logger.parent_node_id.emplace(context->output_node_id());
796 tree_logger.tree_orig_session_uid = parent_tree_session_uid;
797 }
798 else if (const auto *context = dynamic_cast<const bke::SimulationZoneComputeContext *>(
799 &compute_context))
800 {
801 tree_logger.parent_node_id.emplace(context->output_node_id());
802 tree_logger.tree_orig_session_uid = parent_tree_session_uid;
803 }
804 else if (const auto *context = dynamic_cast<const bke::EvaluateClosureComputeContext *>(
805 &compute_context))
806 {
807 tree_logger.parent_node_id.emplace(context->node_id());
808 const std::optional<nodes::ClosureSourceLocation> &location =
809 context->closure_source_location();
810 if (location.has_value()) {
811 BLI_assert(DEG_is_evaluated(location->tree));
812 tree_logger.tree_orig_session_uid = DEG_get_original_id(&location->tree->id)->session_uid;
813 }
814 }
815 else if (const auto *context = dynamic_cast<const bke::ModifierComputeContext *>(
816 &compute_context))
817 {
818 if (const NodesModifierData *nmd = context->nmd()) {
819 tree_logger.tree_orig_session_uid = get_original_session_uid(
820 reinterpret_cast<const ID *>(nmd->node_group));
821 }
822 }
823 else if (const auto *context = dynamic_cast<const bke::OperatorComputeContext *>(
824 &compute_context))
825 {
826 if (const bNodeTree *tree = context->tree()) {
827 tree_logger.tree_orig_session_uid = tree->id.session_uid;
828 }
829 }
830 return tree_logger;
831}
832
834{
835 GeoTreeLog &reduced_tree_log = *tree_logs_.lookup_or_add_cb(compute_context_hash, [&]() {
836 Vector<GeoTreeLogger *> tree_logs;
837 for (LocalData &local_data : data_per_thread_) {
838 destruct_ptr<GeoTreeLogger> *tree_log = local_data.tree_logger_by_context.lookup_ptr(
839 compute_context_hash);
840 if (tree_log != nullptr) {
841 tree_logs.append(tree_log->get());
842 }
843 }
844 return std::make_unique<GeoTreeLog>(this, std::move(tree_logs));
845 });
846 return reduced_tree_log;
847}
848
850 const bNodeTreeZone &zone,
851 bke::ComputeContextCache &compute_context_cache,
852 const ComputeContext *current,
854{
855 current = ed::space_node::compute_context_for_zone(zone, compute_context_cache, current);
856 if (!current) {
857 return;
858 }
859 r_hash_by_zone.add_new(&zone, current->hash());
860 for (const bNodeTreeZone *child_zone : zone.child_zones) {
861 find_tree_zone_hash_recursive(*child_zone, compute_context_cache, current, r_hash_by_zone);
862 }
863}
864
867 bke::ComputeContextCache &compute_context_cache)
868{
870 snode, compute_context_cache);
871 if (!current) {
872 return {};
873 }
874
875 const bNodeTreeZones *tree_zones = snode.edittree->zones();
876 if (tree_zones == nullptr) {
877 return {};
878 }
880 hash_by_zone.add_new(nullptr, current->hash());
881 for (const bNodeTreeZone *zone : tree_zones->root_zones) {
882 find_tree_zone_hash_recursive(*zone, compute_context_cache, current, hash_by_zone);
883 }
884 return hash_by_zone;
885}
886
887static GeoNodesLog *get_root_log(const SpaceNode &snode)
888{
891 std::optional<ed::space_node::ObjectAndModifier> object_and_modifier =
893 if (!object_and_modifier) {
894 return {};
895 }
896 return object_and_modifier->nmd->runtime->eval_log.get();
897 }
898 case SNODE_GEOMETRY_TOOL: {
901 if (snode.geometry_nodes_tool_tree->id.name + 2 != log.node_group_name) {
902 return {};
903 }
904 return log.log.get();
905 }
906 }
907 return nullptr;
908}
909
911{
912 GeoNodesLog *log = get_root_log(snode);
913 if (!log) {
914 return {};
915 }
916 bke::ComputeContextCache compute_context_cache;
918 GeoNodesLog::get_context_hash_by_zone_for_node_editor(snode, compute_context_cache);
920 for (const auto item : hash_by_zone.items()) {
921 GeoTreeLog &tree_log = log->get_tree_log(item.value);
922 tree_logs_by_zone.add(item.key, &tree_log);
923 }
924 return {tree_logs_by_zone};
925}
926
928{
929 const std::optional<ed::viewer_path::ViewerPathForGeometryNodesViewer> parsed_path =
931 if (!parsed_path.has_value()) {
932 return nullptr;
933 }
934 const Object *object = parsed_path->object;
935 NodesModifierData *nmd = nullptr;
936 LISTBASE_FOREACH (ModifierData *, md, &object->modifiers) {
937 if (md->persistent_uid == parsed_path->modifier_uid) {
938 if (md->type == eModifierType_Nodes) {
939 nmd = reinterpret_cast<NodesModifierData *>(md);
940 }
941 }
942 }
943 if (nmd == nullptr) {
944 return nullptr;
945 }
946 if (!nmd->runtime->eval_log) {
947 return nullptr;
948 }
949 nodes::geo_eval_log::GeoNodesLog *root_log = nmd->runtime->eval_log.get();
950
951 bke::ComputeContextCache compute_context_cache;
952 const ComputeContext *compute_context = &compute_context_cache.for_modifier(nullptr, *nmd);
953 for (const ViewerPathElem *elem : parsed_path->node_path) {
955 *elem, compute_context_cache, compute_context);
956 if (!compute_context) {
957 return nullptr;
958 }
959 }
960 const ComputeContextHash context_hash = compute_context->hash();
961 nodes::geo_eval_log::GeoTreeLog &tree_log = root_log->get_tree_log(context_hash);
962 tree_log.ensure_viewer_node_logs();
963
964 const ViewerNodeLog *viewer_log = tree_log.viewer_node_logs.lookup_default(
965 parsed_path->viewer_node_id, nullptr);
966 return viewer_log;
967}
968
971 : tree_logs_by_zone_(std::move(tree_logs_by_zone))
972{
973}
974
976{
977 return tree_logs_by_zone_.lookup_default(zone, nullptr);
978}
979
981{
982 const bNodeTree &tree = node.owner_tree();
983 const bke::bNodeTreeZones *zones = tree.zones();
984 if (!zones) {
985 return nullptr;
986 }
987 const bke::bNodeTreeZone *zone = zones->get_zone_by_node(node.identifier);
988 return this->get_main_tree_log(zone);
989}
990
992{
993 const bNodeTree &tree = socket.owner_tree();
994 const bke::bNodeTreeZones *zones = tree.zones();
995 if (!zones) {
996 return nullptr;
997 }
998 const bke::bNodeTreeZone *zone = zones->get_zone_by_socket(socket);
999 return this->get_main_tree_log(zone);
1000}
1001
1003{
1004 for (GeoTreeLog *tree_log : tree_logs_by_zone_.values()) {
1005 if (tree_log) {
1006 callback(*tree_log);
1007 }
1008 }
1009}
1010
1011} // namespace blender::nodes::geo_eval_log
Low-level operations for curves.
Low-level operations for grease pencil.
void BKE_library_foreach_ID_link(Main *bmain, ID *id, blender::FunctionRef< LibraryIDLinkCallback > callback, void *user_data, LibraryForeachIDFlag flag)
Definition lib_query.cc:431
@ IDWALK_RET_NOP
@ IDWALK_RECURSE
@ IDWALK_READONLY
#define FOREACH_NODETREE_END
Definition BKE_node.hh:866
#define FOREACH_NODETREE_BEGIN(bmain, _nodetree, _id)
Definition BKE_node.hh:856
Volume data-block.
int BKE_volume_num_grids(const Volume *volume)
VolumeGridType
#define BLI_assert_unreachable()
Definition BLI_assert.h:93
#define BLI_assert(a)
Definition BLI_assert.h:46
#define LISTBASE_FOREACH(type, var, list)
const char * BLI_str_find_prev_char_utf8(const char *p, const char *str_start) ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL ATTR_NONNULL(1
#define UNUSED_VARS(...)
#define ELEM(...)
bool DEG_is_original(const T *id)
bool DEG_is_evaluated(const T *id)
ID * DEG_get_original_id(ID *id)
T * DEG_get_original(T *id)
@ eModifierType_Nodes
@ SOCK_IN
@ SOCK_CLOSURE
@ SOCK_BUNDLE
NodeWarningPropagation
@ NODE_WARNING_PROPAGATION_NONE
@ NODE_WARNING_PROPAGATION_ONLY_ERRORS_AND_WARNINGS
@ NODE_WARNING_PROPAGATION_ONLY_ERRORS
@ NODE_WARNING_PROPAGATION_ALL
SpaceNodeGeometryNodesType
@ SNODE_GEOMETRY_MODIFIER
@ SNODE_GEOMETRY_TOOL
BMesh const char void * data
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
SIMD_FORCE_INLINE btVector3 operator()(const btVector3 &x) const
Return the transform of the vector.
Definition btTransform.h:90
GeometryInfoLog(const bke::GeometrySet &geometry_set)
void append(const T &value)
bool is() const
void copy_construct(const void *src, void *dst) const
void destruct(void *ptr) const
const ComputeContext * parent() const
const ComputeContextHash & hash() const
const CPPType * type() const
const CPPType * type() const
const void * get() const
destruct_ptr< T > construct(Args &&...args)
StringRefNull copy_string(StringRef str)
Value & lookup_or_add_default(const Key &key)
Definition BLI_map.hh:639
bool add(const Key &key, const Value &value)
Definition BLI_map.hh:295
Value lookup_default(const Key &key, const Value &default_value) const
Definition BLI_map.hh:570
void add_new(const Key &key, const Value &value)
Definition BLI_map.hh:265
ItemIterator items() const &
Definition BLI_map.hh:902
bool add(const Key &key)
Definition BLI_set.hh:248
bool is_empty() const
Definition BLI_stack.hh:308
void push(const T &value)
Definition BLI_stack.hh:213
void append(const T &value)
void extend(Span< T > array)
const ModifierComputeContext & for_modifier(const ComputeContext *parent, const NodesModifierData &nmd)
void convert_to_uninitialized(const CPPType &from_type, const CPPType &to_type, const void *from_value, void *to_value) const
bool is_convertible(const CPPType &from_type, const CPPType &to_type) const
bool valid_for_socket(eNodeSocketDatatype socket_type) const
Vector< bNodeTreeZone * > child_zones
Vector< bNodeTreeZone * > root_zones
const bNodeTreeZone * get_zone_by_node(const int32_t node_id) const
const bNodeTreeZone * get_zone_by_socket(const bNodeSocket &socket) const
const FieldNode & node() const
Definition FN_field.hh:135
ClosureValueLog(Vector< Item > inputs, Vector< Item > outputs, const std::optional< ClosureSourceLocation > &source_location, std::shared_ptr< ClosureEvalLog > eval_log)
GeoTreeLog * get_main_tree_log(const bke::bNodeTreeZone *zone) const
ContextualGeoTreeLogs(Map< const bke::bNodeTreeZone *, GeoTreeLog * > tree_logs_by_zone={})
void foreach_tree_log(FunctionRef< void(GeoTreeLog &)> callback) const
static ContextualGeoTreeLogs get_contextual_tree_logs(const SpaceNode &snode)
static Map< const bke::bNodeTreeZone *, ComputeContextHash > get_context_hash_by_zone_for_node_editor(const SpaceNode &snode, bke::ComputeContextCache &compute_context_cache)
static const ViewerNodeLog * find_viewer_node_log_for_path(const ViewerPath &viewer_path)
GeoTreeLogger & get_local_tree_logger(const ComputeContext &compute_context)
GeoTreeLog & get_tree_log(const ComputeContextHash &compute_context_hash)
Vector< const GeometryAttributeInfo * > existing_attributes
void ensure_node_warnings(const NodesModifierData &nmd)
bool try_convert_primitive_socket_value(const GenericValueLog &value_log, const CPPType &dst_type, void *dst)
Map< int32_t, ViewerNodeLog *, 0 > viewer_node_logs
Map< StringRefNull, NamedAttributeUsage > used_named_attributes
GeoTreeLog(GeoNodesLog *root_log, Vector< GeoTreeLogger * > tree_loggers)
ValueLog * find_socket_value_log(const bNodeSocket &query_socket)
linear_allocator::ChunkedList< ViewerNodeLogWithNode > viewer_node_logs
void log_viewer_node(const bNode &viewer_node, bke::GeometrySet geometry)
linear_allocator::ChunkedList< SocketValueLog, 16 > input_socket_values
void log_value(const bNode &node, const bNodeSocket &socket, GPointer value)
linear_allocator::ChunkedList< SocketValueLog, 16 > output_socket_values
std::optional< GreasePencilInfo > grease_pencil_info
Vector< bke::GeometryComponent::Type > component_types
KDTree_3d * tree
#define log
#define this
VolumeGridType get_type(const VolumeGridData &grid)
bool attribute_name_is_anonymous(const StringRef name)
const DataTypeConversions & get_implicit_type_conversions()
const GeoOperatorLog & node_group_operator_static_eval_log()
const ComputeContext * compute_context_for_edittree(const SpaceNode &snode, bke::ComputeContextCache &compute_context_cache)
const ComputeContext * compute_context_for_zone(const bke::bNodeTreeZone &zone, bke::ComputeContextCache &compute_context_cache, const ComputeContext *parent_compute_context)
std::optional< ObjectAndModifier > get_modifier_for_node_editor(const SpaceNode &snode)
std::optional< ViewerPathForGeometryNodesViewer > parse_geometry_nodes_viewer(const ViewerPath &viewer_path)
const ComputeContext * compute_context_for_viewer_path_elem(const ViewerPathElem &elem, bke::ComputeContextCache &compute_context_cache, const ComputeContext *parent_compute_context)
static void find_tree_zone_hash_recursive(const bNodeTreeZone &zone, bke::ComputeContextCache &compute_context_cache, const ComputeContext *current, Map< const bNodeTreeZone *, ComputeContextHash > &r_hash_by_zone)
static std::optional< uint32_t > get_original_session_uid(const ID *id)
static GeoNodesLog * get_root_log(const SpaceNode &snode)
static bool warning_is_propagated(const NodeWarningPropagation propagation, const NodeWarningType warning_type)
ImplicitSharingPtr< Bundle > BundlePtr
ImplicitSharingPtr< Closure > ClosurePtr
std::unique_ptr< T, DestructValueAtAddress< T > > destruct_ptr
static blender::bke::bNodeSocketTemplate outputs[]
static blender::bke::bNodeSocketTemplate inputs[]
#define hash
Definition noise_c.cc:154
Definition DNA_ID.h:404
char name[66]
Definition DNA_ID.h:415
unsigned int session_uid
Definition DNA_ID.h:444
struct bNodeTree * node_group
NodesModifierRuntimeHandle * runtime
ListBase modifiers
const char * message
struct bNodeTree * edittree
struct bNodeTree * geometry_nodes_tool_tree
char geometry_nodes_type
int32_t identifier
Vector< const GeometryComponent * > get_components() const
void attribute_foreach(Span< GeometryComponent::Type > component_types, bool include_instances, AttributeForeachCallback callback) const
NodeWarning(NodeWarningType type, StringRef message)
StringLog(StringRef string, LinearAllocator<> &allocator)