Welcome to mirror list, hosted at ThFree Co, Russian Federation.

NOD_geometry_nodes_log.hh « nodes « blender « source - git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: dd4868b6ba0d73839dc1370bd15b96fc7b196da0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
/* SPDX-License-Identifier: GPL-2.0-or-later */

#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 system for logging data during evaluation and accessing the data after
 * evaluation. Geometry nodes is executed by a modifier, therefore the "root" of logging is
 * #GeoModifierLog which will contain all data generated in a modifier.
 *
 * The system makes a distinction between "loggers" and the "log":
 * - Logger (#GeoTreeLogger): Is used during geometry nodes evaluation. Each thread logs data
 *   independently to avoid communication between threads. Logging should generally be fast.
 *   Generally, the logged data is just dumped into simple containers. Any processing of the data
 *   happens later if necessary. This is important for performance, because in practice, most of
 *   the logged data is never used again. So any processing of the data is likely to be a waste of
 *   resources.
 * - Log (#GeoTreeLog, #GeoNodeLog): Those are used when accessing logged data in UI code. They
 *   contain and cache preprocessed data produced during logging. The log combines data from all
 *   thread-local loggers to provide simple access. Importantly, the (preprocessed) log is only
 *   created when it is actually used by UI code.
 */

#include <chrono>

#include "BLI_compute_context.hh"
#include "BLI_enumerable_thread_specific.hh"
#include "BLI_generic_pointer.hh"
#include "BLI_multi_value_map.hh"

#include "BKE_attribute.h"
#include "BKE_geometry_set.hh"

#include "FN_field.hh"

#include "DNA_node_types.h"

struct SpaceNode;
struct SpaceSpreadsheet;
struct NodesModifierData;

namespace blender::nodes::geo_eval_log {

using fn::GField;

enum class NodeWarningType {
  Error,
  Warning,
  Info,
};

struct NodeWarning {
  NodeWarningType type;
  std::string message;
};

enum class NamedAttributeUsage {
  None = 0,
  Read = 1 << 0,
  Write = 1 << 1,
  Remove = 1 << 2,
};
ENUM_OPERATORS(NamedAttributeUsage, NamedAttributeUsage::Remove);

/**
 * Values of different types are logged differently. This is necessary because some types are so
 * simple that we can log them entirely (e.g. `int`), while we don't want to log all intermediate
 * geometries in their entirety.
 *
 * #ValueLog is a base class for the different ways we log values.
 */
class ValueLog {
 public:
  virtual ~ValueLog() = default;
};

/**
 * Simplest logger. It just stores a copy of the entire value. This is used for most simple types
 * like `int`.
 */
class GenericValueLog : public ValueLog {
 public:
  /**
   * This is owning the value, but not the memory.
   */
  GMutablePointer value;

  GenericValueLog(const GMutablePointer value) : value(value)
  {
  }

  ~GenericValueLog();
};

/**
 * Fields are not logged entirely, because they might contain arbitrarily large data (e.g.
 * geometries that are sampled). Instead, only the data needed for UI features is logged.
 */
class FieldInfoLog : public ValueLog {
 public:
  const CPPType &type;
  Vector<std::string> input_tooltips;

  FieldInfoLog(const GField &field);
};

struct GeometryAttributeInfo {
  std::string name;
  /** Can be empty when #name does not actually exist on a geometry yet. */
  std::optional<eAttrDomain> domain;
  std::optional<eCustomDataType> data_type;
};

/**
 * Geometries are not logged entirely, because that would result in a lot of time and memory
 * overhead. Instead, only the data needed for UI features is logged.
 */
class GeometryInfoLog : public ValueLog {
 public:
  Vector<GeometryAttributeInfo> attributes;
  Vector<GeometryComponentType> component_types;

  struct MeshInfo {
    int verts_num, edges_num, faces_num;
  };
  struct CurveInfo {
    int splines_num;
  };
  struct PointCloudInfo {
    int points_num;
  };
  struct InstancesInfo {
    int instances_num;
  };
  struct EditDataInfo {
    bool has_deformed_positions;
    bool has_deform_matrices;
  };

  std::optional<MeshInfo> mesh_info;
  std::optional<CurveInfo> curve_info;
  std::optional<PointCloudInfo> pointcloud_info;
  std::optional<InstancesInfo> instances_info;
  std::optional<EditDataInfo> edit_data_info;

  GeometryInfoLog(const GeometrySet &geometry_set);
};

/**
 * Data logged by a viewer node when it is executed. In this case, we do want to log the entire
 * geometry.
 */
class ViewerNodeLog {
 public:
  GeometrySet geometry;
  GField field;
};

using Clock = std::chrono::steady_clock;
using TimePoint = Clock::time_point;

/**
 * Logs all data for a specific geometry node tree in a specific context. When the same node group
 * is used in multiple times each instantiation will have a separate logger.
 */
class GeoTreeLogger {
 public:
  std::optional<ComputeContextHash> parent_hash;
  std::optional<std::string> group_node_name;
  Vector<ComputeContextHash> children_hashes;

  LinearAllocator<> *allocator = nullptr;

  struct WarningWithNode {
    std::string node_name;
    NodeWarning warning;
  };
  struct SocketValueLog {
    std::string node_name;
    std::string socket_identifier;
    destruct_ptr<ValueLog> value;
  };
  struct NodeExecutionTime {
    std::string node_name;
    TimePoint start;
    TimePoint end;
  };
  struct ViewerNodeLogWithNode {
    std::string node_name;
    destruct_ptr<ViewerNodeLog> viewer_log;
  };
  struct AttributeUsageWithNode {
    std::string node_name;
    std::string attribute_name;
    NamedAttributeUsage usage;
  };
  struct DebugMessage {
    std::string node_name;
    std::string message;
  };

  Vector<WarningWithNode> node_warnings;
  Vector<SocketValueLog> input_socket_values;
  Vector<SocketValueLog> output_socket_values;
  Vector<NodeExecutionTime> node_execution_times;
  Vector<ViewerNodeLogWithNode, 0> viewer_node_logs;
  Vector<AttributeUsageWithNode, 0> used_named_attributes;
  Vector<DebugMessage, 0> debug_messages;

  GeoTreeLogger();
  ~GeoTreeLogger();

  void log_value(const bNode &node, const bNodeSocket &socket, GPointer value);
  void log_viewer_node(const bNode &viewer_node, const GeometrySet &geometry, const GField &field);
};

/**
 * Contains data that has been logged for a specific node in a context. So when the node is in a
 * node group that is used multiple times, there will be a different #GeoNodeLog for every
 * instance.
 *
 * By default, not all of the info below is valid. A #GeoTreeLog::ensure_* method has to be called
 * first.
 */
class GeoNodeLog {
 public:
  /** Warnings generated for that node. */
  Vector<NodeWarning> warnings;
  /**
   * Time spend in that node. For node groups this is the sum of the run times of the nodes
   * inside.
   */
  std::chrono::nanoseconds run_time{0};
  /** Maps from socket identifiers to their values. */
  Map<std::string, ValueLog *> input_values_;
  Map<std::string, ValueLog *> output_values_;
  /** Maps from attribute name to their usage flags. */
  Map<std::string, NamedAttributeUsage> used_named_attributes;
  /** Messages that are used for debugging purposes during development. */
  Vector<std::string> debug_messages;

  GeoNodeLog();
  ~GeoNodeLog();
};

class GeoModifierLog;

/**
 * Contains data that has been logged for a specific node group in a context. If the same node
 * group is used multiple times, there will be a different #GeoTreeLog for every instance.
 *
 * This contains lazily evaluated data. Call the corresponding `ensure_*` methods before accessing
 * data.
 */
class GeoTreeLog {
 private:
  GeoModifierLog *modifier_log_;
  Vector<GeoTreeLogger *> tree_loggers_;
  VectorSet<ComputeContextHash> children_hashes_;
  bool reduced_node_warnings_ = false;
  bool reduced_node_run_times_ = false;
  bool reduced_socket_values_ = false;
  bool reduced_viewer_node_logs_ = false;
  bool reduced_existing_attributes_ = false;
  bool reduced_used_named_attributes_ = false;
  bool reduced_debug_messages_ = false;

 public:
  Map<std::string, GeoNodeLog> nodes;
  Map<std::string, ViewerNodeLog *, 0> viewer_node_logs;
  Vector<NodeWarning> all_warnings;
  std::chrono::nanoseconds run_time_sum{0};
  Vector<const GeometryAttributeInfo *> existing_attributes;
  Map<std::string, NamedAttributeUsage> used_named_attributes;

  GeoTreeLog(GeoModifierLog *modifier_log, Vector<GeoTreeLogger *> tree_loggers);
  ~GeoTreeLog();

  void ensure_node_warnings();
  void ensure_node_run_time();
  void ensure_socket_values();
  void ensure_viewer_node_logs();
  void ensure_existing_attributes();
  void ensure_used_named_attributes();
  void ensure_debug_messages();

  ValueLog *find_socket_value_log(const bNodeSocket &query_socket);
};

/**
 * There is one #GeoModifierLog for every modifier that evaluates geometry nodes. It contains all
 * the loggers that are used during evaluation as well as the preprocessed logs that are used by UI
 * code.
 */
class GeoModifierLog {
 private:
  /** Data that is stored for each thread. */
  struct LocalData {
    /** Each thread has its own allocator. */
    LinearAllocator<> allocator;
    /**
     * Store a separate #GeoTreeLogger for each instance of the corresponding node group (e.g.
     * when the same node group is used multiple times).
     */
    Map<ComputeContextHash, destruct_ptr<GeoTreeLogger>> tree_logger_by_context;
  };

  /** Container for all thread-local data. */
  threading::EnumerableThreadSpecific<LocalData> data_per_thread_;
  /**
   * A #GeoTreeLog for every compute context. Those are created lazily when requested by UI code.
   */
  Map<ComputeContextHash, std::unique_ptr<GeoTreeLog>> tree_logs_;

 public:
  GeoModifierLog();
  ~GeoModifierLog();

  /**
   * Get a thread-local logger for the current node tree.
   */
  GeoTreeLogger &get_local_tree_logger(const ComputeContext &compute_context);

  /**
   * Get a log a specific node tree instance.
   */
  GeoTreeLog &get_tree_log(const ComputeContextHash &compute_context_hash);

  /**
   * Utility accessor to logged data.
   */
  static GeoTreeLog *get_tree_log_for_node_editor(const SpaceNode &snode);
  static const ViewerNodeLog *find_viewer_node_log_for_spreadsheet(
      const SpaceSpreadsheet &sspreadsheet);
};

}  // namespace blender::nodes::geo_eval_log