Blender  V3.3
geometry_nodes_eval_log.cc
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 
4 
5 #include "BKE_curves.hh"
7 
8 #include "DNA_modifier_types.h"
9 #include "DNA_space_types.h"
10 
11 #include "FN_field_cpp_type.hh"
12 
13 #include "BLT_translation.h"
14 
15 #include <chrono>
16 
18 
19 using fn::FieldCPPType;
20 using fn::FieldInput;
21 using fn::GField;
22 using fn::ValueOrFieldCPPType;
23 
25  : input_geometry_log_(std::move(logger.input_geometry_log_)),
26  output_geometry_log_(std::move(logger.output_geometry_log_))
27 {
28  root_tree_logs_ = allocator_.construct<TreeLog>();
29 
30  LogByTreeContext log_by_tree_context;
31 
32  /* Combine all the local loggers that have been used by separate threads. */
33  for (LocalGeoLogger &local_logger : logger) {
34  /* Take ownership of the allocator. */
35  logger_allocators_.append(std::move(local_logger.allocator_));
36 
37  for (ValueOfSockets &value_of_sockets : local_logger.values_) {
38  ValueLog *value_log = value_of_sockets.value.get();
39 
40  /* Take centralized ownership of the logged value. It might be referenced by multiple
41  * sockets. */
42  logged_values_.append(std::move(value_of_sockets.value));
43 
44  for (const DSocket &socket : value_of_sockets.sockets) {
45  SocketLog &socket_log = this->lookup_or_add_socket_log(log_by_tree_context, socket);
46  socket_log.value_ = value_log;
47  }
48  }
49 
50  for (NodeWithWarning &node_with_warning : local_logger.node_warnings_) {
51  NodeLog &node_log = this->lookup_or_add_node_log(log_by_tree_context,
52  node_with_warning.node);
53  node_log.warnings_.append(node_with_warning.warning);
54  }
55 
56  for (NodeWithExecutionTime &node_with_exec_time : local_logger.node_exec_times_) {
57  NodeLog &node_log = this->lookup_or_add_node_log(log_by_tree_context,
58  node_with_exec_time.node);
59  node_log.exec_time_ = node_with_exec_time.exec_time;
60  }
61 
62  for (NodeWithDebugMessage &debug_message : local_logger.node_debug_messages_) {
63  NodeLog &node_log = this->lookup_or_add_node_log(log_by_tree_context, debug_message.node);
64  node_log.debug_messages_.append(debug_message.message);
65  }
66 
67  for (NodeWithUsedNamedAttribute &node_with_attribute_name :
68  local_logger.used_named_attributes_) {
69  NodeLog &node_log = this->lookup_or_add_node_log(log_by_tree_context,
70  node_with_attribute_name.node);
71  node_log.used_named_attributes_.append(std::move(node_with_attribute_name.attribute));
72  }
73  }
74 }
75 
76 TreeLog &ModifierLog::lookup_or_add_tree_log(LogByTreeContext &log_by_tree_context,
77  const DTreeContext &tree_context)
78 {
79  TreeLog *tree_log = log_by_tree_context.lookup_default(&tree_context, nullptr);
80  if (tree_log != nullptr) {
81  return *tree_log;
82  }
83 
84  const DTreeContext *parent_context = tree_context.parent_context();
85  if (parent_context == nullptr) {
86  return *root_tree_logs_.get();
87  }
88  TreeLog &parent_log = this->lookup_or_add_tree_log(log_by_tree_context, *parent_context);
89  destruct_ptr<TreeLog> owned_tree_log = allocator_.construct<TreeLog>();
90  tree_log = owned_tree_log.get();
91  log_by_tree_context.add_new(&tree_context, tree_log);
92  parent_log.child_logs_.add_new(tree_context.parent_node()->name(), std::move(owned_tree_log));
93  return *tree_log;
94 }
95 
96 NodeLog &ModifierLog::lookup_or_add_node_log(LogByTreeContext &log_by_tree_context, DNode node)
97 {
98  TreeLog &tree_log = this->lookup_or_add_tree_log(log_by_tree_context, *node.context());
99  NodeLog &node_log = *tree_log.node_logs_.lookup_or_add_cb(node->name(), [&]() {
100  destruct_ptr<NodeLog> node_log = allocator_.construct<NodeLog>();
101  node_log->input_logs_.resize(node->inputs().size());
102  node_log->output_logs_.resize(node->outputs().size());
103  return node_log;
104  });
105  return node_log;
106 }
107 
108 SocketLog &ModifierLog::lookup_or_add_socket_log(LogByTreeContext &log_by_tree_context,
109  DSocket socket)
110 {
111  NodeLog &node_log = this->lookup_or_add_node_log(log_by_tree_context, socket.node());
112  MutableSpan<SocketLog> socket_logs = socket->is_input() ? node_log.input_logs_ :
113  node_log.output_logs_;
114  SocketLog &socket_log = socket_logs[socket->index()];
115  return socket_log;
116 }
117 
118 void ModifierLog::foreach_node_log(FunctionRef<void(const NodeLog &)> fn) const
119 {
120  if (root_tree_logs_) {
121  root_tree_logs_->foreach_node_log(fn);
122  }
123 }
124 
126 {
127  return input_geometry_log_.get();
128 }
130 {
131  return output_geometry_log_.get();
132 }
133 
135 {
136  const destruct_ptr<NodeLog> *node_log = node_logs_.lookup_ptr_as(node_name);
137  if (node_log == nullptr) {
138  return nullptr;
139  }
140  return node_log->get();
141 }
142 
144 {
145  return this->lookup_node_log(node.name);
146 }
147 
149 {
150  const destruct_ptr<TreeLog> *tree_log = child_logs_.lookup_ptr_as(node_name);
151  if (tree_log == nullptr) {
152  return nullptr;
153  }
154  return tree_log->get();
155 }
156 
157 void TreeLog::foreach_node_log(FunctionRef<void(const NodeLog &)> fn) const
158 {
159  for (auto node_log : node_logs_.items()) {
160  fn(*node_log.value);
161  }
162 
163  for (auto child : child_logs_.items()) {
164  child.value->foreach_node_log(fn);
165  }
166 }
167 
169 {
170  BLI_assert(index >= 0);
171  Span<SocketLog> socket_logs = (in_out == SOCK_IN) ? input_logs_ : output_logs_;
172  if (index >= socket_logs.size()) {
173  return nullptr;
174  }
175  return &socket_logs[index];
176 }
177 
178 const SocketLog *NodeLog::lookup_socket_log(const bNode &node, const bNodeSocket &socket) const
179 {
180  ListBase sockets = socket.in_out == SOCK_IN ? node.inputs : node.outputs;
181  int index = BLI_findindex(&sockets, &socket);
182  return this->lookup_socket_log((eNodeSocketInOut)socket.in_out, index);
183 }
184 
185 GFieldValueLog::GFieldValueLog(fn::GField field, bool log_full_field) : type_(field.cpp_type())
186 {
187  const std::shared_ptr<const fn::FieldInputs> &field_input_nodes = field.node().field_inputs();
188 
189  /* Put the deduplicated field inputs into a vector so that they can be sorted below. */
191  if (field_input_nodes) {
192  field_inputs.extend(field_input_nodes->deduplicated_nodes.begin(),
193  field_input_nodes->deduplicated_nodes.end());
194  }
195 
196  std::sort(
197  field_inputs.begin(), field_inputs.end(), [](const FieldInput &a, const FieldInput &b) {
198  const int index_a = (int)a.category();
199  const int index_b = (int)b.category();
200  if (index_a == index_b) {
201  return a.socket_inspection_name().size() < b.socket_inspection_name().size();
202  }
203  return index_a < index_b;
204  });
205 
206  for (const FieldInput &field_input : field_inputs) {
207  input_tooltips_.append(field_input.socket_inspection_name());
208  }
209 
210  if (log_full_field) {
211  field_ = std::move(field);
212  }
213 }
214 
215 GeometryValueLog::GeometryValueLog(const GeometrySet &geometry_set, bool log_full_geometry)
216 {
217  static std::array all_component_types = {GEO_COMPONENT_TYPE_CURVE,
222 
223  /* Keep track handled attribute names to make sure that we do not return the same name twice.
224  * Currently #GeometrySet::attribute_foreach does not do that. Note that this will merge
225  * attributes with the same name but different domains or data types on separate components. */
227 
228  geometry_set.attribute_foreach(
229  all_component_types,
230  true,
231  [&](const bke::AttributeIDRef &attribute_id,
232  const bke::AttributeMetaData &meta_data,
234  if (attribute_id.is_named() && names.add(attribute_id.name())) {
235  this->attributes_.append({attribute_id.name(), meta_data.domain, meta_data.data_type});
236  }
237  });
238 
239  for (const GeometryComponent *component : geometry_set.get_components_for_read()) {
240  component_types_.append(component->type());
241  switch (component->type()) {
243  const MeshComponent &mesh_component = *(const MeshComponent *)component;
244  MeshInfo &info = this->mesh_info.emplace();
245  info.verts_num = mesh_component.attribute_domain_size(ATTR_DOMAIN_POINT);
246  info.edges_num = mesh_component.attribute_domain_size(ATTR_DOMAIN_EDGE);
247  info.faces_num = mesh_component.attribute_domain_size(ATTR_DOMAIN_FACE);
248  break;
249  }
251  const CurveComponent &curve_component = *(const CurveComponent *)component;
252  CurveInfo &info = this->curve_info.emplace();
253  info.splines_num = curve_component.attribute_domain_size(ATTR_DOMAIN_CURVE);
254  break;
255  }
257  const PointCloudComponent &pointcloud_component = *(const PointCloudComponent *)component;
258  PointCloudInfo &info = this->pointcloud_info.emplace();
259  info.points_num = pointcloud_component.attribute_domain_size(ATTR_DOMAIN_POINT);
260  break;
261  }
263  const InstancesComponent &instances_component = *(const InstancesComponent *)component;
264  InstancesInfo &info = this->instances_info.emplace();
265  info.instances_num = instances_component.instances_num();
266  break;
267  }
269  const GeometryComponentEditData &edit_component = *(
271  if (const bke::CurvesEditHints *curve_edit_hints =
272  edit_component.curves_edit_hints_.get()) {
273  EditDataInfo &info = this->edit_data_info.emplace();
274  info.has_deform_matrices = curve_edit_hints->deform_mats.has_value();
275  info.has_deformed_positions = curve_edit_hints->positions.has_value();
276  }
277  break;
278  }
280  break;
281  }
282  }
283  }
284  if (log_full_geometry) {
285  full_geometry_ = std::make_unique<GeometrySet>(geometry_set);
286  full_geometry_->ensure_owns_direct_data();
287  }
288 }
289 
290 Vector<const GeometryAttributeInfo *> NodeLog::lookup_available_attributes() const
291 {
294  for (const SocketLog &socket_log : input_logs_) {
295  const ValueLog *value_log = socket_log.value();
296  if (const GeometryValueLog *geo_value_log = dynamic_cast<const GeometryValueLog *>(
297  value_log)) {
298  for (const GeometryAttributeInfo &attribute : geo_value_log->attributes()) {
299  if (names.add(attribute.name)) {
300  attributes.append(&attribute);
301  }
302  }
303  }
304  }
305  return attributes;
306 }
307 
308 const ModifierLog *ModifierLog::find_root_by_node_editor_context(const SpaceNode &snode)
309 {
310  if (snode.id == nullptr) {
311  return nullptr;
312  }
313  if (GS(snode.id->name) != ID_OB) {
314  return nullptr;
315  }
316  Object *object = (Object *)snode.id;
317  LISTBASE_FOREACH (ModifierData *, md, &object->modifiers) {
318  if (md->type == eModifierType_Nodes) {
320  if (nmd->node_group == snode.nodetree) {
321  return (ModifierLog *)nmd->runtime_eval_log;
322  }
323  }
324  }
325  return nullptr;
326 }
327 
328 const TreeLog *ModifierLog::find_tree_by_node_editor_context(const SpaceNode &snode)
329 {
330  const ModifierLog *eval_log = ModifierLog::find_root_by_node_editor_context(snode);
331  if (eval_log == nullptr) {
332  return nullptr;
333  }
334  Vector<bNodeTreePath *> tree_path_vec = snode.treepath;
335  if (tree_path_vec.is_empty()) {
336  return nullptr;
337  }
338  TreeLog *current = eval_log->root_tree_logs_.get();
339  for (bNodeTreePath *path : tree_path_vec.as_span().drop_front(1)) {
340  destruct_ptr<TreeLog> *tree_log = current->child_logs_.lookup_ptr_as(path->node_name);
341  if (tree_log == nullptr) {
342  return nullptr;
343  }
344  current = tree_log->get();
345  }
346  return current;
347 }
348 
349 const NodeLog *ModifierLog::find_node_by_node_editor_context(const SpaceNode &snode,
350  const bNode &node)
351 {
352  const TreeLog *tree_log = ModifierLog::find_tree_by_node_editor_context(snode);
353  if (tree_log == nullptr) {
354  return nullptr;
355  }
356  return tree_log->lookup_node_log(node);
357 }
358 
359 const NodeLog *ModifierLog::find_node_by_node_editor_context(const SpaceNode &snode,
360  const StringRef node_name)
361 {
362  const TreeLog *tree_log = ModifierLog::find_tree_by_node_editor_context(snode);
363  if (tree_log == nullptr) {
364  return nullptr;
365  }
366  return tree_log->lookup_node_log(node_name);
367 }
368 
369 const SocketLog *ModifierLog::find_socket_by_node_editor_context(const SpaceNode &snode,
370  const bNode &node,
371  const bNodeSocket &socket)
372 {
373  const NodeLog *node_log = ModifierLog::find_node_by_node_editor_context(snode, node);
374  if (node_log == nullptr) {
375  return nullptr;
376  }
377  return node_log->lookup_socket_log(node, socket);
378 }
379 
380 const NodeLog *ModifierLog::find_node_by_spreadsheet_editor_context(
381  const SpaceSpreadsheet &sspreadsheet)
382 {
383  Vector<SpreadsheetContext *> context_path = sspreadsheet.context_path;
384  if (context_path.size() <= 2) {
385  return nullptr;
386  }
387  if (context_path[0]->type != SPREADSHEET_CONTEXT_OBJECT) {
388  return nullptr;
389  }
390  if (context_path[1]->type != SPREADSHEET_CONTEXT_MODIFIER) {
391  return nullptr;
392  }
393  for (SpreadsheetContext *context : context_path.as_span().drop_front(2)) {
394  if (context->type != SPREADSHEET_CONTEXT_NODE) {
395  return nullptr;
396  }
397  }
398  Span<SpreadsheetContextNode *> node_contexts =
399  context_path.as_span().drop_front(2).cast<SpreadsheetContextNode *>();
400 
401  Object *object = ((SpreadsheetContextObject *)context_path[0])->object;
402  StringRefNull modifier_name = ((SpreadsheetContextModifier *)context_path[1])->modifier_name;
403  if (object == nullptr) {
404  return nullptr;
405  }
406 
407  const ModifierLog *eval_log = nullptr;
408  LISTBASE_FOREACH (ModifierData *, md, &object->modifiers) {
409  if (md->type == eModifierType_Nodes) {
410  if (md->name == modifier_name) {
412  eval_log = (const ModifierLog *)nmd->runtime_eval_log;
413  break;
414  }
415  }
416  }
417  if (eval_log == nullptr) {
418  return nullptr;
419  }
420 
421  const TreeLog *tree_log = &eval_log->root_tree();
422  for (SpreadsheetContextNode *context : node_contexts.drop_back(1)) {
423  tree_log = tree_log->lookup_child_log(context->node_name);
424  if (tree_log == nullptr) {
425  return nullptr;
426  }
427  }
428  const NodeLog *node_log = tree_log->lookup_node_log(node_contexts.last()->node_name);
429  return node_log;
430 }
431 
432 void LocalGeoLogger::log_value_for_sockets(Span<DSocket> sockets, GPointer value)
433 {
434  const CPPType &type = *value.type();
435  Span<DSocket> copied_sockets = allocator_->construct_array_copy(sockets);
436  if (type.is<GeometrySet>()) {
437  bool log_full_geometry = false;
438  for (const DSocket &socket : sockets) {
439  if (main_logger_->log_full_sockets_.contains(socket)) {
440  log_full_geometry = true;
441  break;
442  }
443  }
444 
445  const GeometrySet &geometry_set = *value.get<GeometrySet>();
446  destruct_ptr<GeometryValueLog> value_log = allocator_->construct<GeometryValueLog>(
447  geometry_set, log_full_geometry);
448  values_.append({copied_sockets, std::move(value_log)});
449  }
450  else if (const ValueOrFieldCPPType *value_or_field_type =
451  dynamic_cast<const ValueOrFieldCPPType *>(&type)) {
452  const void *value_or_field = value.get();
453  if (value_or_field_type->is_field(value_or_field)) {
454  GField field = *value_or_field_type->get_field_ptr(value_or_field);
455  bool log_full_field = false;
456  if (!field.node().depends_on_input()) {
457  /* Always log constant fields so that their value can be shown in socket inspection.
458  * In the future we can also evaluate the field here and only store the value. */
459  log_full_field = true;
460  }
461  if (!log_full_field) {
462  for (const DSocket &socket : sockets) {
463  if (main_logger_->log_full_sockets_.contains(socket)) {
464  log_full_field = true;
465  break;
466  }
467  }
468  }
469  destruct_ptr<GFieldValueLog> value_log = allocator_->construct<GFieldValueLog>(
470  std::move(field), log_full_field);
471  values_.append({copied_sockets, std::move(value_log)});
472  }
473  else {
474  const CPPType &base_type = value_or_field_type->base_type();
475  const void *value = value_or_field_type->get_value_ptr(value_or_field);
476  void *buffer = allocator_->allocate(base_type.size(), base_type.alignment());
477  base_type.copy_construct(value, buffer);
478  destruct_ptr<GenericValueLog> value_log = allocator_->construct<GenericValueLog>(
479  GMutablePointer{base_type, buffer});
480  values_.append({copied_sockets, std::move(value_log)});
481  }
482  }
483  else {
484  void *buffer = allocator_->allocate(type.size(), type.alignment());
485  type.copy_construct(value.get(), buffer);
486  destruct_ptr<GenericValueLog> value_log = allocator_->construct<GenericValueLog>(
488  values_.append({copied_sockets, std::move(value_log)});
489  }
490 }
491 
492 void LocalGeoLogger::log_multi_value_socket(DSocket socket, Span<GPointer> values)
493 {
494  /* Doesn't have to be logged currently. */
495  UNUSED_VARS(socket, values);
496 }
497 
498 void LocalGeoLogger::log_node_warning(DNode node, NodeWarningType type, std::string message)
499 {
500  node_warnings_.append({node, {type, std::move(message)}});
501 }
502 
503 void LocalGeoLogger::log_execution_time(DNode node, std::chrono::microseconds exec_time)
504 {
505  node_exec_times_.append({node, exec_time});
506 }
507 
508 void LocalGeoLogger::log_used_named_attribute(DNode node,
509  std::string attribute_name,
510  eNamedAttrUsage usage)
511 {
512  used_named_attributes_.append({node, {std::move(attribute_name), usage}});
513 }
514 
515 void LocalGeoLogger::log_debug_message(DNode node, std::string message)
516 {
517  node_debug_messages_.append({node, std::move(message)});
518 }
519 
520 } // namespace blender::nodes::geometry_nodes_eval_log
@ ATTR_DOMAIN_CURVE
Definition: BKE_attribute.h:31
@ ATTR_DOMAIN_POINT
Definition: BKE_attribute.h:27
@ ATTR_DOMAIN_FACE
Definition: BKE_attribute.h:29
@ ATTR_DOMAIN_EDGE
Definition: BKE_attribute.h:28
Low-level operations for curves.
@ GEO_COMPONENT_TYPE_MESH
@ GEO_COMPONENT_TYPE_POINT_CLOUD
@ GEO_COMPONENT_TYPE_INSTANCES
@ GEO_COMPONENT_TYPE_EDIT
@ GEO_COMPONENT_TYPE_CURVE
@ GEO_COMPONENT_TYPE_VOLUME
#define BLI_assert(a)
Definition: BLI_assert.h:46
#define LISTBASE_FOREACH(type, var, list)
Definition: BLI_listbase.h:336
int BLI_findindex(const struct ListBase *listbase, const void *vlink) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
#define UNUSED_VARS(...)
#define UNUSED(x)
static uint8 component(Color32 c, uint i)
Definition: ColorBlock.cpp:108
@ ID_OB
Definition: DNA_ID_enums.h:47
@ eModifierType_Nodes
eNodeSocketInOut
@ SOCK_IN
@ SPREADSHEET_CONTEXT_OBJECT
@ SPREADSHEET_CONTEXT_MODIFIER
@ SPREADSHEET_CONTEXT_NODE
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum type
in reality light always falls off quadratically Particle Retrieve the data of the particle that spawned the object for example to give variation to multiple instances of an object Point Retrieve information about points in a point cloud Retrieve the edges of an object as it appears to Cycles topology will always appear triangulated Convert a blackbody temperature to an RGB value Normal Generate a perturbed normal from an RGB normal map image Typically used for faking highly detailed surfaces Generate an OSL shader from a file or text data block Image Sample an image file as a texture Sky Generate a procedural sky texture Noise Generate fractal Perlin noise Wave Generate procedural bands or rings with noise Voronoi Generate Worley noise based on the distance to random points Typically used to generate textures such as or biological cells Brick Generate a procedural texture producing bricks Texture Retrieve multiple types of texture coordinates nTypically used as inputs for texture nodes Vector Convert a or normal between and object coordinate space Combine Create a color from its and value channels Color Retrieve a color attribute
void sort(btMatrix3x3 &U, btVector3 &sigma, btMatrix3x3 &V, int t)
Helper function of 3X3 SVD for sorting singular values.
std::unique_ptr< blender::bke::CurvesEditHints > curves_edit_hints_
int attribute_domain_size(eAttrDomain domain) const
Definition: geometry_set.cc:63
void copy_construct(const void *src, void *dst) const
int64_t size() const
int64_t alignment() const
const void * get() const
const CPPType * type() const
destruct_ptr< T > construct(Args &&...args)
constexpr Span drop_back(int64_t n) const
Definition: BLI_span.hh:170
constexpr const T & last(const int64_t n=0) const
Definition: BLI_span.hh:313
constexpr int64_t size() const
Definition: BLI_span.hh:240
int64_t size() const
Definition: BLI_vector.hh:694
void append(const T &value)
Definition: BLI_vector.hh:433
Span< T > as_span() const
Definition: BLI_vector.hh:325
bool is_empty() const
Definition: BLI_vector.hh:706
void extend(Span< T > array)
Definition: BLI_vector.hh:530
bool depends_on_input() const
Definition: FN_field.hh:585
const std::shared_ptr< const FieldInputs > & field_inputs() const
Definition: FN_field.hh:590
const FieldNode & node() const
Definition: FN_field.hh:127
const DTreeContext * parent_context() const
const NodeRef * parent_node() const
StringRefNull name() const
void foreach_node_log(FunctionRef< void(const NodeLog &)> fn) const
const SocketLog * lookup_socket_log(eNodeSocketInOut in_out, int index) const
const TreeLog * lookup_child_log(StringRef node_name) const
void foreach_node_log(FunctionRef< void(const NodeLog &)> fn) const
const NodeLog * lookup_node_log(StringRef node_name) const
OperationNode * node
#define GS(x)
Definition: iris.c:225
ccl_global float * buffer
static const char * modifier_name[LS_MODIFIER_NUM]
Definition: linestyle.c:763
static char ** names
Definition: makesdna.c:65
static unsigned a[3]
Definition: RandGen.cpp:78
std::unique_ptr< T, DestructValueAtAddress< T > > destruct_ptr
static const pxr::TfToken b("b", pxr::TfToken::Immortal)
void attribute_foreach(blender::Span< GeometryComponentType > component_types, bool include_instances, AttributeForeachCallback callback) const
char name[66]
Definition: DNA_ID.h:378
struct bNodeTree * node_group
ListBase modifiers
ListBase treepath
struct ID * id
struct bNodeTree * nodetree
char name[64]