OpenVDB  11.0.0
NodeManager.h
Go to the documentation of this file.
1 // Copyright Contributors to the OpenVDB Project
2 // SPDX-License-Identifier: MPL-2.0
3 
4 /*!
5  \file NodeManager.h
6 
7  \author Ken Museth
8 
9  \date February 12, 2021
10 
11  \brief This class allows for sequential access to nodes
12  in a NanoVDB tree on both the host and device.
13 
14  \details The ordering of the sequential access to nodes is always breadth-first!
15 */
16 
17 #include <nanovdb/NanoVDB.h>// for NanoGrid etc
18 #include "HostBuffer.h"// for HostBuffer
19 
20 #ifndef NANOVDB_NODEMANAGER_H_HAS_BEEN_INCLUDED
21 #define NANOVDB_NODEMANAGER_H_HAS_BEEN_INCLUDED
22 
23 namespace nanovdb {
24 
25 /// @brief NodeManager allows for sequential access to nodes
26 template <typename BuildT>
28 
29 /// @brief NodeManagerHandle manages the memory of a NodeManager
30 template<typename BufferT = HostBuffer>
32 
33 /// @brief brief Construct a NodeManager and return its handle
34 ///
35 /// @param grid grid whose nodes will be accessed sequentially
36 /// @param buffer buffer from which to allocate the output handle
37 ///
38 /// @note This is the only way to create a NodeManager since it's using
39 /// managed memory pointed to by a NodeManagerHandle.
40 template <typename BuildT, typename BufferT = HostBuffer>
42  const BufferT& buffer = BufferT());
43 
45 {// 48B = 6*8B
46  uint64_t mMagic;// 8B
47  union {int64_t mPadding; uint8_t mLinear;};// 8B of which 1B is used for a binary flag
48  void *mGrid;// 8B pointer to either host or device grid
49  union {int64_t *mPtr[3], mOff[3];};// 24B, use mOff if mLinear!=0
50 };
51 
52 /// @brief This class serves to manage a raw memory buffer of a NanoVDB NodeManager or LeafManager.
53 template<typename BufferT>
55 {
56  GridType mGridType{GridType::Unknown};
57  BufferT mBuffer;
58 
59  template<typename BuildT>
60  const NodeManager<BuildT>* getMgr() const {
61  return mGridType == mapToGridType<BuildT>() ? (const NodeManager<BuildT>*)mBuffer.data() : nullptr;
62  }
63 
64  template<typename BuildT, typename U = BufferT>
66  getDeviceMgr() const {
67  return mGridType == mapToGridType<BuildT>() ? (const NodeManager<BuildT>*)mBuffer.deviceData() : nullptr;
68  }
69 
70  template <typename T>
71  static T* no_const(const T* ptr) { return const_cast<T*>(ptr); }
72 
73 public:
74  /// @brief Move constructor from a buffer
75  NodeManagerHandle(GridType gridType, BufferT&& buffer) : mGridType(gridType) { mBuffer = std::move(buffer); }
76  /// @brief Empty ctor
77  NodeManagerHandle() = default;
78  /// @brief Disallow copy-construction
79  NodeManagerHandle(const NodeManagerHandle&) = delete;
80  /// @brief Disallow copy assignment operation
81  NodeManagerHandle& operator=(const NodeManagerHandle&) = delete;
82  /// @brief Move copy assignment operation
84  mGridType = other.mGridType;
85  mBuffer = std::move(other.mBuffer);
86  other.mGridType = GridType::Unknown;
87  return *this;
88  }
89  /// @brief Move copy-constructor
91  mGridType = other.mGridType;
92  mBuffer = std::move(other.mBuffer);
93  other.mGridType = GridType::Unknown;
94  }
95  /// @brief Default destructor
96  ~NodeManagerHandle() { this->reset(); }
97  /// @brief clear the buffer
98  void reset() { mBuffer.clear(); }
99 
100  /// @brief Return a reference to the buffer
101  BufferT& buffer() { return mBuffer; }
102 
103  /// @brief Return a const reference to the buffer
104  const BufferT& buffer() const { return mBuffer; }
105 
106  /// @brief Returns a non-const pointer to the data.
107  ///
108  /// @warning Note that the return pointer can be NULL if the NodeManagerHandle was not initialized
109  uint8_t* data() { return mBuffer.data(); }
110 
111  /// @brief Returns a const pointer to the data.
112  ///
113  /// @warning Note that the return pointer can be NULL if the NodeManagerHandle was not initialized
114  const uint8_t* data() const { return mBuffer.data(); }
115 
116  /// @brief Returns the size in bytes of the raw memory buffer managed by this NodeManagerHandle's allocator.
117  uint64_t size() const { return mBuffer.size(); }
118 
119  /// @brief Returns a const pointer to the NodeManager encoded in this NodeManagerHandle.
120  ///
121  /// @warning Note that the return pointer can be NULL if the template parameter does not match the specified grid!
122  template<typename BuildT>
123  const NodeManager<BuildT>* mgr() const { return this->template getMgr<BuildT>(); }
124 
125  /// @brief Returns a pointer to the NodeManager encoded in this NodeManagerHandle.
126  ///
127  /// @warning Note that the return pointer can be NULL if the template parameter does not match the specified grid!
128  template<typename BuildT>
129  NodeManager<BuildT>* mgr() { return no_const(this->template getMgr<BuildT>()); }
130 
131  /// @brief Return a const pointer to the NodeManager encoded in this NodeManagerHandle on the device, e.g. GPU
132  ///
133  /// @warning Note that the return pointer can be NULL if the template parameter does not match the specified grid!
134  template<typename BuildT, typename U = BufferT>
136  deviceMgr() const { return this->template getDeviceMgr<BuildT>(); }
137 
138  /// @brief Return a const pointer to the NodeManager encoded in this NodeManagerHandle on the device, e.g. GPU
139  ///
140  /// @warning Note that the return pointer can be NULL if the template parameter does not match the specified grid!
141  template<typename BuildT, typename U = BufferT>
143  deviceMgr() { return no_const(this->template getDeviceMgr<BuildT>()); }
144 
145  /// @brief Upload the NodeManager to the device, e.g. from CPU to GPU
146  ///
147  /// @note This method is only available if the buffer supports devices
148  template<typename U = BufferT>
149  typename enable_if<BufferTraits<U>::hasDeviceDual, void>::type
150  deviceUpload(void* deviceGrid, void* stream = nullptr, bool sync = true)
151  {
152  assert(deviceGrid);
153  auto *data = reinterpret_cast<NodeManagerData*>(mBuffer.data());
154  void *tmp = data->mGrid;
155  data->mGrid = deviceGrid;
156  mBuffer.deviceUpload(stream, sync);
157  data->mGrid = tmp;
158  }
159 
160  /// @brief Download the NodeManager to from the device, e.g. from GPU to CPU
161  ///
162  /// @note This method is only available if the buffer supports devices
163  template<typename U = BufferT>
164  typename enable_if<BufferTraits<U>::hasDeviceDual, void>::type
165  deviceDownload(void* stream = nullptr, bool sync = true)
166  {
167  auto *data = reinterpret_cast<NodeManagerData*>(mBuffer.data());
168  void *tmp = data->mGrid;
169  mBuffer.deviceDownload(stream, sync);
170  data->mGrid = tmp;
171  }
172 };// NodeManagerHandle
173 
174 /// @brief This class allows for sequential access to nodes in a NanoVDB tree
175 ///
176 /// @details Nodes are always arranged breadth first during sequential access of nodes
177 /// at a particular level.
178 template<typename BuildT>
179 class NodeManager : private NodeManagerData
180 {
181  using DataT = NodeManagerData;
182  using GridT = NanoGrid<BuildT>;
183  using TreeT = typename GridTree<GridT>::type;
184  template<int LEVEL>
185  using NodeT = typename NodeTrait<TreeT, LEVEL>::type;
186  using RootT = NodeT<3>;// root node
187  using Node2 = NodeT<2>;// upper internal node
188  using Node1 = NodeT<1>;// lower internal node
189  using Node0 = NodeT<0>;// leaf node
190 
191 public:
192  static constexpr bool FIXED_SIZE = Node0::FIXED_SIZE && Node1::FIXED_SIZE && Node2::FIXED_SIZE;
193 
194  NodeManager(const NodeManager&) = delete;
195  NodeManager(NodeManager&&) = delete;
196  NodeManager& operator=(const NodeManager&) = delete;
197  NodeManager& operator=(NodeManager&&) = delete;
198  ~NodeManager() = delete;
199 
200  /// @brief return true if the nodes have both fixed size and are arranged breadth-first in memory.
201  /// This allows for direct and memory-efficient linear access to nodes.
202  __hostdev__ static bool isLinear(const GridT &grid) {return FIXED_SIZE && grid.isBreadthFirst();}
203 
204  /// @brief return true if the nodes have both fixed size and are arranged breadth-first in memory.
205  /// This allows for direct and memory-efficient linear access to nodes.
206  __hostdev__ bool isLinear() const {return DataT::mLinear!=0u;}
207 
208  /// @brief Return the memory footprint in bytes of the NodeManager derived from the specified grid
209  __hostdev__ static uint64_t memUsage(const GridT &grid) {
210  uint64_t size = sizeof(NodeManagerData);
211  if (!NodeManager::isLinear(grid)) {
212  const uint32_t *p = grid.tree().mNodeCount;
213  size += sizeof(int64_t)*(p[0]+p[1]+p[2]);
214  }
215  return size;
216  }
217 
218  /// @brief Return the memory footprint in bytes of this instance
219  __hostdev__ uint64_t memUsage() const {return NodeManager::memUsage(this->grid());}
220 
221  /// @brief Return a reference to the grid
222  __hostdev__ GridT& grid() { return *reinterpret_cast<GridT*>(DataT::mGrid); }
223  __hostdev__ const GridT& grid() const { return *reinterpret_cast<const GridT*>(DataT::mGrid); }
224 
225  /// @brief Return a reference to the tree
226  __hostdev__ TreeT& tree() { return this->grid().tree(); }
227  __hostdev__ const TreeT& tree() const { return this->grid().tree(); }
228 
229  /// @brief Return a reference to the root
230  __hostdev__ RootT& root() { return this->tree().root(); }
231  __hostdev__ const RootT& root() const { return this->tree().root(); }
232 
233  /// @brief Return the number of tree nodes at the specified level
234  /// @details 0 is leaf, 1 is lower internal, and 2 is upper internal level
235  __hostdev__ uint64_t nodeCount(int level) const { return this->tree().nodeCount(level); }
236 
237  __hostdev__ uint64_t leafCount() const { return this->tree().nodeCount(0); }
238  __hostdev__ uint64_t lowerCount() const { return this->tree().nodeCount(1); }
239  __hostdev__ uint64_t upperCount() const { return this->tree().nodeCount(2); }
240 
241  /// @brief Return the i'th leaf node with respect to breadth-first ordering
242  template <int LEVEL>
243  __hostdev__ const NodeT<LEVEL>& node(uint32_t i) const {
244  NANOVDB_ASSERT(i < this->nodeCount(LEVEL));
245  const NodeT<LEVEL>* ptr = nullptr;
246  if (DataT::mLinear) {
247  ptr = PtrAdd<const NodeT<LEVEL>>(DataT::mGrid, DataT::mOff[LEVEL]) + i;
248  } else {
249  ptr = PtrAdd<const NodeT<LEVEL>>(DataT::mGrid, DataT::mPtr[LEVEL][i]);
250  }
251  NANOVDB_ASSERT(isValid(ptr));
252  return *ptr;
253  }
254 
255  /// @brief Return the i'th node with respect to breadth-first ordering
256  template <int LEVEL>
257  __hostdev__ NodeT<LEVEL>& node(uint32_t i) {
258  NANOVDB_ASSERT(i < this->nodeCount(LEVEL));
259  NodeT<LEVEL>* ptr = nullptr;
260  if (DataT::mLinear) {
261  ptr = PtrAdd<NodeT<LEVEL>>(DataT::mGrid, DataT::mOff[LEVEL]) + i;
262  } else {
263  ptr = PtrAdd<NodeT<LEVEL>>(DataT::mGrid, DataT::mPtr[LEVEL][i]);
264  }
265  NANOVDB_ASSERT(isValid(ptr));
266  return *ptr;
267  }
268 
269  /// @brief Return the i'th leaf node with respect to breadth-first ordering
270  __hostdev__ const Node0& leaf(uint32_t i) const { return this->node<0>(i); }
271  __hostdev__ Node0& leaf(uint32_t i) { return this->node<0>(i); }
272 
273  /// @brief Return the i'th lower internal node with respect to breadth-first ordering
274  __hostdev__ const Node1& lower(uint32_t i) const { return this->node<1>(i); }
275  __hostdev__ Node1& lower(uint32_t i) { return this->node<1>(i); }
276 
277  /// @brief Return the i'th upper internal node with respect to breadth-first ordering
278  __hostdev__ const Node2& upper(uint32_t i) const { return this->node<2>(i); }
279  __hostdev__ Node2& upper(uint32_t i) { return this->node<2>(i); }
280 
281 }; // NodeManager<BuildT> class
282 
283 template <typename BuildT, typename BufferT>
285  const BufferT& buffer)
286 {
287  NodeManagerHandle<BufferT> handle(mapToGridType<BuildT>(), BufferT::create(NodeManager<BuildT>::memUsage(grid), &buffer));
288  auto *data = reinterpret_cast<NodeManagerData*>(handle.data());
289  NANOVDB_ASSERT(isValid(data));
290  NANOVDB_ASSERT(mapToGridType<BuildT>() == grid.gridType());
291 #ifdef NANOVDB_USE_NEW_MAGIC_NUMBERS
292  *data = NodeManagerData{NANOVDB_MAGIC_NODE, 0u, (void*)&grid, {0u,0u,0u}};
293 #else
294  *data = NodeManagerData{NANOVDB_MAGIC_NUMBER, 0u, (void*)&grid, {0u,0u,0u}};
295 #endif
296 
297  if (NodeManager<BuildT>::isLinear(grid)) {
298  data->mLinear = uint8_t(1u);
299  data->mOff[0] = PtrDiff(grid.tree().template getFirstNode<0>(), &grid);
300  data->mOff[1] = PtrDiff(grid.tree().template getFirstNode<1>(), &grid);
301  data->mOff[2] = PtrDiff(grid.tree().template getFirstNode<2>(), &grid);
302  } else {
303  int64_t *ptr0 = data->mPtr[0] = reinterpret_cast<int64_t*>(data + 1);
304  int64_t *ptr1 = data->mPtr[1] = data->mPtr[0] + grid.tree().nodeCount(0);
305  int64_t *ptr2 = data->mPtr[2] = data->mPtr[1] + grid.tree().nodeCount(1);
306  // Performs depth first traversal but breadth first insertion
307  for (auto it2 = grid.tree().root().cbeginChild(); it2; ++it2) {
308  *ptr2++ = PtrDiff(&*it2, &grid);
309  for (auto it1 = it2->cbeginChild(); it1; ++it1) {
310  *ptr1++ = PtrDiff(&*it1, &grid);
311  for (auto it0 = it1->cbeginChild(); it0; ++it0) {
312  *ptr0++ = PtrDiff(&*it0, &grid);
313  }// loop over child nodes of the lower internal node
314  }// loop over child nodes of the upper internal node
315  }// loop over child nodes of the root node
316  }
317 
318  return handle;// // is converted to r-value so return value is move constructed!
319 }
320 
321 } // namespace nanovdb
322 
323 #if defined(__CUDACC__)
324 #include <nanovdb/util/cuda/CudaNodeManager.cuh>
325 #endif// defined(__CUDACC__)
326 
327 #endif // NANOVDB_NODEMANAGER_H_HAS_BEEN_INCLUDED
NodeManagerHandle manages the memory of a NodeManager.
Definition: NodeManager.h:31
uint8_t * data()
Returns a non-const pointer to the data.
Definition: NodeManager.h:109
NodeManager< BuildT > * mgr()
Returns a pointer to the NodeManager encoded in this NodeManagerHandle.
Definition: NodeManager.h:129
Highest level of the data structure. Contains a tree and a world->index transform (that currently onl...
Definition: NanoVDB.h:3698
bool isBreadthFirst() const
Definition: NanoVDB.h:3842
__hostdev__ NodeT< LEVEL > & node(uint32_t i)
Return the i&#39;th node with respect to breadth-first ordering.
Definition: NodeManager.h:257
__hostdev__ uint64_t nodeCount(int level) const
Return the number of tree nodes at the specified level.
Definition: NodeManager.h:235
HostBuffer - a buffer that contains a shared or private bump pool to either externally or internally ...
__hostdev__ const Node1 & lower(uint32_t i) const
Return the i&#39;th lower internal node with respect to breadth-first ordering.
Definition: NodeManager.h:274
__hostdev__ uint64_t lowerCount() const
Definition: NodeManager.h:238
const TreeT & tree() const
Return a const reference to the tree.
Definition: NanoVDB.h:3753
int64_t mOff[3]
Definition: NodeManager.h:49
__hostdev__ const NodeT< LEVEL > & node(uint32_t i) const
Return the i&#39;th leaf node with respect to breadth-first ordering.
Definition: NodeManager.h:243
__hostdev__ bool isLinear() const
return true if the nodes have both fixed size and are arranged breadth-first in memory. This allows for direct and memory-efficient linear access to nodes.
Definition: NodeManager.h:206
const uint8_t * data() const
Returns a const pointer to the data.
Definition: NodeManager.h:114
enable_if< BufferTraits< U >::hasDeviceDual, NodeManager< BuildT > * >::type deviceMgr()
Return a const pointer to the NodeManager encoded in this NodeManagerHandle on the device...
Definition: NodeManager.h:143
uint64_t mMagic
Definition: NodeManager.h:46
int64_t * mPtr[3]
Definition: NodeManager.h:49
static __hostdev__ bool isLinear(const GridT &grid)
return true if the nodes have both fixed size and are arranged breadth-first in memory. This allows for direct and memory-efficient linear access to nodes.
Definition: NodeManager.h:202
__hostdev__ TreeT & tree()
Return a reference to the tree.
Definition: NodeManager.h:226
__hostdev__ uint64_t memUsage() const
Return the memory footprint in bytes of this instance.
Definition: NodeManager.h:219
Implements a light-weight self-contained VDB data-structure in a single file! In other words...
Definition: NanoVDB.h:247
const NodeManager< BuildT > * mgr() const
Returns a const pointer to the NodeManager encoded in this NodeManagerHandle.
Definition: NodeManager.h:123
Struct to derive node type from its level in a given grid, tree or root while preserving constness...
Definition: NanoVDB.h:3404
typename GridT::TreeType type
Definition: NanoVDB.h:3976
const BufferT & buffer() const
Return a const reference to the buffer.
Definition: NodeManager.h:104
NodeManagerHandle & operator=(NodeManagerHandle &&other) noexcept
Move copy assignment operation.
Definition: NodeManager.h:83
Definition: NodeManager.h:44
BufferT & buffer()
Return a reference to the buffer.
Definition: NodeManager.h:101
#define NANOVDB_MAGIC_NODE
Definition: NanoVDB.h:129
#define NANOVDB_MAGIC_NUMBER
Definition: NanoVDB.h:126
int64_t mPadding
Definition: NodeManager.h:47
__hostdev__ const Node0 & leaf(uint32_t i) const
Return the i&#39;th leaf node with respect to breadth-first ordering.
Definition: NodeManager.h:270
__hostdev__ GridT & grid()
Return a reference to the grid.
Definition: NodeManager.h:222
__hostdev__ uint64_t leafCount() const
Definition: NodeManager.h:237
NodeManagerHandle(NodeManagerHandle &&other) noexcept
Move copy-constructor.
Definition: NodeManager.h:90
__hostdev__ const RootT & root() const
Definition: NodeManager.h:231
void reset()
clear the buffer
Definition: NodeManager.h:98
const GridType & gridType() const
Definition: NanoVDB.h:3827
__hostdev__ uint64_t upperCount() const
Definition: NodeManager.h:239
#define NANOVDB_ASSERT(x)
Definition: NanoVDB.h:190
__hostdev__ RootT & root()
Return a reference to the root.
Definition: NodeManager.h:230
GridType
List of types that are currently supported by NanoVDB.
Definition: NanoVDB.h:317
NodeManager allows for sequential access to nodes.
Definition: NodeManager.h:27
__hostdev__ const TreeT & tree() const
Definition: NodeManager.h:227
uint64_t size() const
Returns the size in bytes of the raw memory buffer managed by this NodeManagerHandle&#39;s allocator...
Definition: NodeManager.h:117
static __hostdev__ uint64_t memUsage(const GridT &grid)
Return the memory footprint in bytes of the NodeManager derived from the specified grid...
Definition: NodeManager.h:209
void * mGrid
Definition: NodeManager.h:48
uint8_t mLinear
Definition: NodeManager.h:47
enable_if< BufferTraits< U >::hasDeviceDual, void >::type deviceDownload(void *stream=nullptr, bool sync=true)
Download the NodeManager to from the device, e.g. from GPU to CPU.
Definition: NodeManager.h:165
enable_if< BufferTraits< U >::hasDeviceDual, void >::type deviceUpload(void *deviceGrid, void *stream=nullptr, bool sync=true)
Upload the NodeManager to the device, e.g. from CPU to GPU.
Definition: NodeManager.h:150
enable_if< BufferTraits< U >::hasDeviceDual, const NodeManager< BuildT > * >::type deviceMgr() const
Return a const pointer to the NodeManager encoded in this NodeManagerHandle on the device...
Definition: NodeManager.h:136
NodeManagerHandle< BufferT > createNodeManager(const NanoGrid< BuildT > &grid, const BufferT &buffer=BufferT())
brief Construct a NodeManager and return its handle
Definition: NodeManager.h:284
__hostdev__ Node1 & lower(uint32_t i)
Definition: NodeManager.h:275
static int64_t PtrDiff(const T1 *p, const T2 *q)
Compute the distance, in bytes, between two pointers.
Definition: NanoVDB.h:780
static bool isValid(const void *p)
return true if the specified pointer is aligned and not NULL
Definition: NanoVDB.h:743
__hostdev__ const Node2 & upper(uint32_t i) const
Return the i&#39;th upper internal node with respect to breadth-first ordering.
Definition: NodeManager.h:278
#define __hostdev__
Definition: NanoVDB.h:213
__hostdev__ Node2 & upper(uint32_t i)
Definition: NodeManager.h:279
__hostdev__ const GridT & grid() const
Definition: NodeManager.h:223
C++11 implementation of std::enable_if.
Definition: NanoVDB.h:492
__hostdev__ Node0 & leaf(uint32_t i)
Definition: NodeManager.h:271
NodeManagerHandle(GridType gridType, BufferT &&buffer)
Move constructor from a buffer.
Definition: NodeManager.h:75
~NodeManagerHandle()
Default destructor.
Definition: NodeManager.h:96