OpenVDB  12.1.0
VolumeExecutable.h
Go to the documentation of this file.
1 // Copyright Contributors to the OpenVDB Project
2 // SPDX-License-Identifier: Apache-2.0
3 
4 /// @file compiler/VolumeExecutable.h
5 ///
6 /// @authors Nick Avramoussis, Francisco Gochez, Richard Jones
7 ///
8 /// @brief The VolumeExecutable, produced by the OpenVDB AX Compiler for
9 /// execution over Numerical OpenVDB Grids.
10 ///
11 
12 #ifndef OPENVDB_AX_COMPILER_VOLUME_EXECUTABLE_HAS_BEEN_INCLUDED
13 #define OPENVDB_AX_COMPILER_VOLUME_EXECUTABLE_HAS_BEEN_INCLUDED
14 
15 #include "CustomData.h"
16 #include "AttributeRegistry.h"
17 #include "AttributeBindings.h"
18 
19 #include <openvdb/version.h>
20 #include <openvdb/Grid.h>
21 
22 #include <llvm/Config/llvm-config.h>
23 
24 #include <unordered_map>
25 
26 struct TestVolumeExecutableAcc;
27 
28 namespace llvm {
29 class ExecutionEngine;
30 class LLVMContext;
31 namespace orc {
32 class LLJIT;
33 }
34 }
35 
36 namespace openvdb {
38 namespace OPENVDB_VERSION_NAME {
39 namespace ax {
40 
41 class Compiler;
42 
43 /// @brief Object that encapsulates compiled AX code which can be executed on a
44 /// collection of VDB volume grids. Executables are created by the compiler
45 /// and hold the final immutable JIT compiled function and context.
46 /// @details The VolumeExecutable is returned from the ax::Compiler when
47 /// compiling AX code for volume execution. The class represents a typical AX
48 /// executable object; immutable except for execution settings and implements
49 /// 'execute' functions which can be called multiple times for arbitrary sets
50 /// of inputs. The intended usage of these executables is to configure their
51 /// runtime arguments and then call VolumeExecutable::execute with your VDBs.
52 /// For example:
53 /// @code
54 /// VolumeExecutable::Ptr exe = compiler.compile<VolumeExecutable>("@a += 1");
55 /// exe->setTreeExecutionLevel(0); // only process leaf nodes
56 /// exe->setValueIterator(VolumeExecutable::IterType::ALL); // process all values
57 /// exe->execute(vdbs); // run on a set of vdbs
58 /// exe->execute(grid); // run on a single vdb
59 /// @endcode
60 ///
61 /// The Volume executable is initialised with specific configurable settings:
62 /// - Iteration Level: min=0, max=RootNode::Level.
63 /// By default, processes the entire VDB tree hierarchy.
64 /// @sa setTreeExecutionLevel
65 /// - Iteration Type: ON
66 /// By default, processes ACTIVE values.
67 /// @sa setValueIterator
68 /// - Active Tile Streaming: ON, OFF or AUTO depending on AX code.
69 /// By default, if AX detects that the AX program may produce unique
70 /// values for leaf level voxels that would otherwise comprise a
71 /// given active tile, this setting is set to ON or AUTO. Otherwise it is
72 /// set to OFF.
73 /// @sa setActiveTileStreaming
74 /// - Grain sizes: 1:32
75 /// The default grain sizes passed to the tbb partitioner for leaf level
76 /// processing and active tile processing.
77 /// @sa setGrainSize
78 /// @sa setActiveTileStreamingGrainSize
79 /// - AttributeBindings: None
80 /// Whether to indriect any AX accesses to different grid names.
81 /// @sa setAttributeBindings
82 ///
83 /// For more in depth information, see the @ref vdbaxcompilerexe documentation.
85 {
86 public:
87  using Ptr = std::shared_ptr<VolumeExecutable>;
89 
90  /// @brief Copy constructor. Shares the LLVM constructs but deep copies the
91  /// settings. Multiple copies of an executor can be used at the same time
92  /// safely.
93  VolumeExecutable(const VolumeExecutable& other);
94 
95  ////////////////////////////////////////////////////////
96 
97  ///@{
98  /// @brief Run this volume executable binary on target volumes.
99  /// @details This method reads from the stored settings on the executable
100  /// to determine certain behaviour and runs the JIT compiled function
101  /// across every valid VDB value. Topology may be changed, deleted or
102  /// created.
103  ///
104  /// This method is thread safe; it can be run concurrently from the same
105  /// VolumeExecutable instance on different inputs.
106  ///
107  /// @param grids The VDB Volumes to process
108  void execute(openvdb::GridPtrVec& grids) const;
109  void execute(openvdb::GridBase& grids) const;
110  ///@}
111 
112  ////////////////////////////////////////////////////////
113 
114  /// @brief Set the behaviour when missing grids are accessed. Default
115  /// behaviour is true, which creates them with default transforms and
116  /// background values
117  /// @param flag Enables or disables the creation of missing attributes
118  void setCreateMissing(const bool flag);
119  /// @return Whether this executable will generate new grids.
120  bool getCreateMissing() const;
121 
122  /// @brief Set the execution level for this executable. This controls what
123  /// nodes are processed when execute is called. Possible values depend on
124  /// the OpenVDB configuration in use, however a value of 0 will always
125  /// correspond to the lowest level (leaf-level). By default, the min
126  /// level is zero (LeafNodeType::LEVEL) and the max level is the root
127  /// node's level (RootNodeType::LEVEL). In other words, the default
128  /// execution level settings process the whole of the tree.
129  /// @note A value larger that the number of levels in the tree (i.e. larger
130  /// than the root node's level) will cause this method to throw a runtime
131  /// error.
132  /// @param min The minimum tree execution level to set
133  /// @param max The maximum tree execution level to set
134  void setTreeExecutionLevel(const Index min, const Index max);
135  /// @param level The tree execution level to set. Calls setTreeExecutionLevel
136  /// with min and max arguments as level.
137  void setTreeExecutionLevel(const Index level);
138  /// @brief Get the tree execution levels.
139  /// @param min The minimum tree execution level
140  /// @param max The maximum tree execution level
141  void getTreeExecutionLevel(Index& min, Index& max) const;
142 
143  /// @brief The streaming type of active tiles during execution.
144  /// @param ON active tiles are temporarily densified (converted to leaf
145  /// level voxels) on an "as needed" basis and the subsequent voxel
146  /// values are processed. The temporarily densified node is added to the
147  /// tree only if a non constant voxel buffer is produced. Otherwise a
148  /// child tile may be created or the original tile's value may simply be
149  /// modified.
150  /// @param OFF tile topologies are left unchanged and their single value is
151  /// processed.
152  /// @param AUTO the volume executable analyzes the compiled kernel and
153  /// attempts to determine if expansion of active tiles would lead to
154  /// different, non-constant values in the respective voxels. This is
155  /// done on a per grid basis; ultimately each execution will be set to
156  /// ON or OFF. This option will always fall back to ON if there is any
157  /// chance the kernel may produce child nodes
158  ///
159  /// @note The volume executable always runs an AUTO check on creation and
160  /// will set itself to ON (if all grids always need child nodes), OFF (if
161  /// grids never need child nodes) or remains as AUTO (if this depends on
162  /// which grid is being processed).
163  ///
164  /// @details When an AX kernel is run over coarser levels of the tree (i.e.
165  /// not leaf voxels), it is often desirable to densify these areas into
166  /// unique voxels such that they can each receive a unique value. For
167  /// example, consider the following AX code which assigns a vector volume
168  /// to the world space position of each voxel:
169  /// @code
170  /// v@v = getvoxelpws();
171  /// @endcode
172  /// Active tiles hold a single value but comprise an area greater than
173  /// that of a single voxel. As the above kernel varies with respect to
174  /// a nodes position, we'd need to replace these tiles with leaf voxels
175  /// to get unique per node values. The stream flag is initialised to ON
176  /// in this case.
177  ///
178  /// This behaviour, however, is not always desirable .i.e:
179  /// @code
180  /// v@v = {1,2,3};
181  /// @endcode
182  /// In this instance, all values within a volume receive the same value
183  /// and are not dependent on any spatially or iteratively varying
184  /// metrics. The stream flag is set to OFF.
185  ///
186  /// The AUTO flag is set in cases where the runtime access pattern of the
187  /// inputs determines streaming:
188  /// @code
189  /// f@density = f@mask;
190  /// f@mask = 0;
191  /// @endcode
192  /// In this instance, the runtime topology and values of \@mask determines
193  /// whether child topology needs to be created in \@density, but \@mask
194  /// itself does not need streaming. Streaming will be set to ON for
195  /// density but OFF for mask.
196  ///
197  /// @note This behaviour is only applied to active tiles. If the value
198  /// iterator is set to OFF, this option is ignored.
199  /// @warning This option can generate large amounts of leaf level voxels.
200  /// It is recommended to use a good concurrent memory allocator (such as
201  /// jemalloc) for the best performance.
202  enum class Streaming { ON, OFF, AUTO };
203  /// @brief Controls the behaviour of expansion of active tiles.
204  /// @param s The behaviour to set
205  void setActiveTileStreaming(const Streaming& s);
206  /// @return The current stream behaviour.
207  Streaming getActiveTileStreaming() const;
208  /// @return The current stream behaviour for a particular grid. This is
209  /// either ON or OFF.
210  /// @param name The name of the grid to query
211  /// @param type The grids type
212  Streaming getActiveTileStreaming(const std::string& name,
213  const ast::tokens::CoreType& type) const;
214 
215  enum class IterType { ON, OFF, ALL };
216  /// @brief Set the value iterator type to use with this executable. Options
217  /// are ON, OFF, ALL. Default is ON.
218  /// @param iter The value iterator type to set
219  void setValueIterator(const IterType& iter);
220  /// @return The current value iterator type
221  IterType getValueIterator() const;
222 
223  ///@{
224  /// @brief Set the threading grain sizes used when iterating over nodes
225  /// in a VDB.
226  /// @details Two grain sizes are provided, the first of which (g1) is used
227  /// to determine the chunk size of nodes which are not being streamed (see
228  /// setActiveTileStream). Leaf node execution always uses this grain size.
229  /// The default value for g1 is 1 which is typically appropriate for most
230  /// AX kernels.
231  /// The second grain size is used when streaming execution over active
232  /// tiles in a VDB. This execution model differs significantly from
233  /// typical leaf node execution due to the potential for substantially
234  /// more memory to be allocated. The default value is 32, which works well
235  /// for the default configuration of OpenVDB. If streaming is disabled,
236  /// this value has no effect.
237  /// @note Setting g1 or g2 to zero has the effect of disabling
238  /// multi-threading for the respective node executions. Setting both to
239  /// zero will disable all multi-threading performed by the execute method.
240  void setGrainSize(const size_t g1);
241  void setActiveTileStreamingGrainSize(const size_t g2);
242  /// @return The current g1 grain size
243  /// @sa setGrainSize
244  size_t getGrainSize() const;
245  /// @return The current g2 grain size
246  /// @sa setActiveTileStreamingGrainSize
247  size_t getActiveTileStreamingGrainSize() const;
248  ///@}
249 
250  /// @brief Set attribute bindings.
251  /// @param bindings A map of attribute bindings to expected names on
252  /// the geometry to be executed over. By default the AX attributes will be
253  /// bound to volumes of the same name. Supplying bindings
254  /// for a subset of the attributes will leave the others unchanged.
255  /// AX attributes can only bind to a single volume and vice versa.
256  /// However, in a single set call these can be swapped e.g. a -> b and b -> a.
257  /// When bindings are overriden through subsequent calls to this function,
258  /// any dangling volumes will be automatically bound by name.
259  /// To reset these bindings call get function and create a target set of bindings
260  /// for each attribute of name -> name.
261  void setAttributeBindings(const AttributeBindings& bindings);
262  /// @return The current attribute bindings map
263  const AttributeBindings& getAttributeBindings() const;
264 
265  ////////////////////////////////////////////////////////
266 
267  // foward declaration of settings for this executable
268  template <bool> struct Settings;
269 
270  /// @brief Command Line Interface handling for the VolumeExecutable.
271  /// @details This class wraps the logic for converting commands specific
272  /// to the VolumeExecutable to the internal Settings. Subsequent
273  /// executables can be initialized from the CLI object that gets created.
275  {
276  ~CLI();
277  CLI(CLI&&);
278  CLI& operator=(CLI&&);
279  static CLI create(size_t argc, const char* argv[], bool* used=nullptr);
280  static void usage(std::ostream& os, const bool verbose);
281  private:
282  friend class VolumeExecutable;
283  CLI();
284  std::unique_ptr<Settings<true>> mSettings;
285  };
286 
287  /// @brief Intialize the Settings of this executables from the CLI object
288  /// @param cli The CLI object
289  /// @{
290  void setSettingsFromCLI(const CLI& cli);
291  /// @}
292 
293  ////////////////////////////////////////////////////////
294 
295  /// @return The tree execution level.
296  OPENVDB_DEPRECATED_MESSAGE("Use getTreeExecutionLevel(Index&, Index&)")
297  Index getTreeExecutionLevel() const;
298 
299 private:
300  friend class Compiler;
301  friend struct ::TestVolumeExecutableAcc;
302 
303  /// @brief Constructor, expected to be invoked by the compiler. Should not
304  /// be invoked directly.
305  /// @param context Shared pointer to an llvm:LLVMContext associated with the
306  /// execution engine
307  /// @param engine Shared pointer to an llvm::ExecutionEngine used to build
308  /// functions. Context should be the associated LLVMContext
309  /// @param accessRegistry Registry of volumes accessed by AX code
310  /// @param customData Custom data which will be shared by this executable.
311  /// It can be used to retrieve external data from within the AX code
312  /// @param functions A map of function names to physical memory addresses
313  /// which were built by llvm using engine
314  /// @param tree The AST linked to this executable. The AST is not stored
315  /// after compilation but can be used during construction of the exe to
316  /// infer some pre/post processing optimisations.
318 #if LLVM_VERSION_MAJOR <= 15
319  const std::shared_ptr<const llvm::LLVMContext>& context,
320  const std::shared_ptr<const llvm::ExecutionEngine>& engine,
321 #else
322  const std::shared_ptr<const llvm::orc::LLJIT>& mExecutionEngine,
323 #endif
324  const AttributeRegistry::ConstPtr& accessRegistry,
325  const CustomData::ConstPtr& customData,
326  const std::unordered_map<std::string, uint64_t>& functions,
327  const ast::Tree& tree);
328 
329 private:
330 #if LLVM_VERSION_MAJOR <= 15
331  // The Context and ExecutionEngine must exist _only_ for object lifetime
332  // management. The ExecutionEngine must be destroyed before the Context
333  const std::shared_ptr<const llvm::LLVMContext> mContext;
334  const std::shared_ptr<const llvm::ExecutionEngine> mExecutionEngine;
335 #else
336  const std::shared_ptr<const llvm::orc::LLJIT> mExecutionEngine;
337 #endif
338  const AttributeRegistry::ConstPtr mAttributeRegistry;
339  const CustomData::ConstPtr mCustomData;
340  const std::unordered_map<std::string, uint64_t> mFunctionAddresses;
341  std::unique_ptr<Settings<false>> mSettings;
342 };
343 
344 } // namespace ax
345 } // namespace OPENVDB_VERSION_NAME
346 } // namespace openvdb
347 
348 #endif // OPENVDB_AX_COMPILER_VOLUME_EXECUTABLE_HAS_BEEN_INCLUDED
349 
Definition: FunctionRegistry.h:23
std::vector< GridBase::Ptr > GridPtrVec
Definition: Grid.h:508
Streaming
The streaming type of active tiles during execution.
Definition: VolumeExecutable.h:202
#define OPENVDB_AX_API
Definition: Platform.h:312
A Tree is the highest concrete (non-abstract) node in the entire AX AST hierarchy. It represents an entire conversion of a valid AX string.
Definition: AST.h:562
Definition: VolumeExecutable.h:268
Index32 Index
Definition: Types.h:54
std::shared_ptr< const CustomData > ConstPtr
Definition: CustomData.h:38
Object that encapsulates compiled AX code which can be executed on a collection of VDB volume grids...
Definition: VolumeExecutable.h:84
Definition: IndexIterator.h:44
std::shared_ptr< const AttributeRegistry > ConstPtr
Definition: AttributeRegistry.h:43
CoreType
Definition: Tokens.h:31
The Attribute Bindings class is used by the compiled Executables to handle the mapping of AX Attribut...
const std::enable_if<!VecTraits< T >::IsVec, T >::type & min(const T &a, const T &b)
Definition: Composite.h:106
Definition: Exceptions.h:13
Abstract base class for typed grids.
Definition: Grid.h:77
The compiler class. This holds a set of compiler options and constructs executable objects (e...
Definition: Compiler.h:50
These classes contain lists of expected attributes and volumes which are populated by compiler during...
Access to the CustomData class which can provide custom user user data to the OpenVDB AX Compiler...
IterType
Definition: VolumeExecutable.h:215
#define OPENVDB_VERSION_NAME
The version namespace name for this library version.
Definition: version.h.in:121
std::shared_ptr< VolumeExecutable > Ptr
Definition: VolumeExecutable.h:87
Command Line Interface handling for the VolumeExecutable.
Definition: VolumeExecutable.h:274
#define OPENVDB_DEPRECATED_MESSAGE(msg)
Definition: Platform.h:171
const std::enable_if<!VecTraits< T >::IsVec, T >::type & max(const T &a, const T &b)
Definition: Composite.h:110
This class wraps an interface for a map of attribute bindings. These map attributes in AX code to con...
Definition: AttributeBindings.h:36
#define OPENVDB_USE_VERSION_NAMESPACE
Definition: version.h.in:218