OpenVDB  10.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.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>
27 class NodeManager;
28 
29 /// @brief NodeManagerHandle manages the memory of a NodeManager
30 template<typename BufferT = HostBuffer>
31 class NodeManagerHandle;
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>
41 NodeManagerHandle<BufferT> createNodeManager(const NanoGrid<BuildT> &grid,
42  const BufferT& buffer = BufferT());
43 
45 {// 48B = 6*8B
46  uint64_t mMagic;// 8B
47  void *mGrid;// 8B pointer to either host or device grid
48  union {int64_t *mPtr[3], mOff[3];};// 24B, use mOff if mLinear!=0
49  uint8_t mLinear, mPadding[7];// 7B padding to 8B boundary
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  BufferT mBuffer;
57 
58  template<typename BuildT>
59  const NodeManager<BuildT>* getMgr() const;
60 
61  template<typename BuildT, typename U = BufferT>
62  typename std::enable_if<BufferTraits<U>::hasDeviceDual, const NodeManager<BuildT>*>::type
63  getDeviceMgr() const;
64 
65  template <typename T>
66  static T* no_const(const T* ptr) { return const_cast<T*>(ptr); }
67 
68 public:
69  /// @brief Move constructor from a buffer
70  NodeManagerHandle(BufferT&& buffer) { mBuffer = std::move(buffer); }
71  /// @brief Empty ctor
72  NodeManagerHandle() = default;
73  /// @brief Disallow copy-construction
75  /// @brief Disallow copy assignment operation
77  /// @brief Move copy assignment operation
79  {
80  mBuffer = std::move(other.mBuffer);
81  return *this;
82  }
83  /// @brief Move copy-constructor
84  NodeManagerHandle(NodeManagerHandle&& other) noexcept { mBuffer = std::move(other.mBuffer); }
85  /// @brief Default destructor
86  ~NodeManagerHandle() { this->reset(); }
87  /// @brief clear the buffer
88  void reset() { mBuffer.clear(); }
89 
90  /// @brief Return a reference to the buffer
91  BufferT& buffer() { return mBuffer; }
92 
93  /// @brief Return a const reference to the buffer
94  const BufferT& buffer() const { return mBuffer; }
95 
96  /// @brief Returns a non-const pointer to the data.
97  ///
98  /// @warning Note that the return pointer can be NULL if the NodeManagerHandle was not initialized
99  uint8_t* data() { return mBuffer.data(); }
100 
101  /// @brief Returns a const pointer to the data.
102  ///
103  /// @warning Note that the return pointer can be NULL if the NodeManagerHandle was not initialized
104  const uint8_t* data() const { return mBuffer.data(); }
105 
106  /// @brief Returns the size in bytes of the raw memory buffer managed by this NodeManagerHandle's allocator.
107  uint64_t size() const { return mBuffer.size(); }
108 
109  /// @brief Returns a const pointer to the NodeManager encoded in this NodeManagerHandle.
110  ///
111  /// @warning Note that the return pointer can be NULL if the template parameter does not match the specified grid!
112  template<typename BuildT>
113  const NodeManager<BuildT>* mgr() const { return this->template getMgr<BuildT>(); }
114 
115  /// @brief Returns a pointer to the NodeManager encoded in this NodeManagerHandle.
116  ///
117  /// @warning Note that the return pointer can be NULL if the template parameter does not match the specified grid!
118  template<typename BuildT>
119  NodeManager<BuildT>* mgr() { return no_const(this->template getMgr<BuildT>()); }
120 
121  /// @brief Return a const pointer to the NodeManager encoded in this NodeManagerHandle on the device, e.g. GPU
122  ///
123  /// @warning Note that the return pointer can be NULL if the template parameter does not match the specified grid!
124  template<typename BuildT, typename U = BufferT>
125  typename std::enable_if<BufferTraits<U>::hasDeviceDual, const NodeManager<BuildT>*>::type
126  deviceMgr() const { return this->template getDeviceMgr<BuildT>(); }
127 
128  /// @brief Return a const pointer to the NodeManager encoded in this NodeManagerHandle on the device, e.g. GPU
129  ///
130  /// @warning Note that the return pointer can be NULL if the template parameter does not match the specified grid!
131  template<typename BuildT, typename U = BufferT>
132  typename std::enable_if<BufferTraits<U>::hasDeviceDual, NodeManager<BuildT>*>::type
133  deviceMgr() { return no_const(this->template getDeviceMgr<BuildT>()); }
134 
135  /// @brief Upload the NodeManager to the device, e.g. from CPU to GPU
136  ///
137  /// @note This method is only available if the buffer supports devices
138  template<typename U = BufferT>
139  typename std::enable_if<BufferTraits<U>::hasDeviceDual, void>::type
140  deviceUpload(void* deviceGrid, void* stream = nullptr, bool sync = true)
141  {
142  assert(deviceGrid);
143  auto *data = reinterpret_cast<NodeManagerData*>(mBuffer.data());
144  void *tmp = data->mGrid;
145  data->mGrid = deviceGrid;
146  mBuffer.deviceUpload(stream, sync);
147  data->mGrid = tmp;
148  }
149 
150  /// @brief Download the NodeManager to from the device, e.g. from GPU to CPU
151  ///
152  /// @note This method is only available if the buffer supports devices
153  template<typename U = BufferT>
154  typename std::enable_if<BufferTraits<U>::hasDeviceDual, void>::type
155  deviceDownload(void* stream = nullptr, bool sync = true)
156  {
157  auto *data = reinterpret_cast<NodeManagerData*>(mBuffer.data());
158  void *tmp = data->mGrid;
159  mBuffer.deviceDownload(stream, sync);
160  data->mGrid = tmp;
161  }
162 };// NodeManagerHandle
163 
164 template<typename BufferT>
165 template<typename BuildT>
166 inline const NodeManager<BuildT>* NodeManagerHandle<BufferT>::getMgr() const
167 {
168  using T = const NodeManager<BuildT>*;
169  T mgr = reinterpret_cast<T>(mBuffer.data());// host
170  return mgr && mgr->grid().gridType() == mapToGridType<BuildT>() ? mgr : nullptr;
171 }
172 
173 template<typename BufferT>
174 template<typename BuildT, typename U>
175 inline typename std::enable_if<BufferTraits<U>::hasDeviceDual, const NodeManager<BuildT>*>::type
176 NodeManagerHandle<BufferT>::getDeviceMgr() const
177 {
178  using T = const NodeManager<BuildT>*;
179  T mgr = reinterpret_cast<T>(mBuffer.data());// host
180  return mgr && mgr->grid().gridType() == mapToGridType<BuildT>() ? reinterpret_cast<T>(mBuffer.deviceData()) : nullptr;
181 }
182 
183 /// @brief This class allows for sequential access to nodes in a NanoVDB tree
184 ///
185 /// @details Nodes are always arranged breadth first during sequential access of nodes
186 /// at a particular level.
187 template<typename BuildT>
189 {
190  using DataT = NodeManagerData;
191  using GridT = NanoGrid<BuildT>;
192  using TreeT = typename GridTree<GridT>::type;
193  template<int LEVEL>
194  using NodeT = typename NodeTrait<TreeT, LEVEL>::type;
195  using RootT = NodeT<3>;// root node
196  using Node2 = NodeT<2>;// upper internal node
197  using Node1 = NodeT<1>;// lower internal node
198  using Node0 = NodeT<0>;// leaf node
199  static constexpr bool FIXED_SIZE = Node0::FIXED_SIZE && Node1::FIXED_SIZE && Node2::FIXED_SIZE;
200 
201 public:
202 
203  NodeManager(const NodeManager&) = delete;
205  NodeManager& operator=(const NodeManager&) = delete;
207  ~NodeManager() = delete;
208 
209  /// @brief return true if the nodes have both fixed size and are arranged breadth-first in memory.
210  /// This allows for direct and memory-efficient linear access to nodes.
211  __hostdev__ static bool isLinear(const GridT &grid) {return FIXED_SIZE && grid.isBreadthFirst();}
212 
213  /// @brief return true if the nodes have both fixed size and are arranged breadth-first in memory.
214  /// This allows for direct and memory-efficient linear access to nodes.
215  __hostdev__ bool isLinear() const {return DataT::mLinear!=0u;}
216 
217  /// @brief Return the memory footprint in bytes of the NodeManager derived from the specified grid
218  __hostdev__ static uint64_t memUsage(const GridT &grid) {
219  uint64_t size = sizeof(NodeManagerData);
220  if (!NodeManager::isLinear(grid)) {
221  const uint32_t *p = grid.tree().data()->mNodeCount;
222  size += sizeof(int64_t)*(p[0]+p[1]+p[2]);
223  }
224  return size;
225  }
226 
227  /// @brief Return the memory footprint in bytes of this instance
228  __hostdev__ uint64_t memUsage() const {return NodeManager::memUsage(this->grid());}
229 
230  /// @brief Return a reference to the grid
231  __hostdev__ GridT& grid() { return *reinterpret_cast<GridT*>(DataT::mGrid); }
232  __hostdev__ const GridT& grid() const { return *reinterpret_cast<const GridT*>(DataT::mGrid); }
233 
234  /// @brief Return a reference to the tree
235  __hostdev__ TreeT& tree() { return this->grid().tree(); }
236  __hostdev__ const TreeT& tree() const { return this->grid().tree(); }
237 
238  /// @brief Return a reference to the root
239  __hostdev__ RootT& root() { return this->tree().root(); }
240  __hostdev__ const RootT& root() const { return this->tree().root(); }
241 
242  /// @brief Return the number of tree nodes at the specified level
243  /// @details 0 is leaf, 1 is lower internal, and 2 is upper internal level
244  __hostdev__ uint64_t nodeCount(int level) const { return this->tree().nodeCount(level); }
245 
246  /// @brief Return the i'th leaf node with respect to breadth-first ordering
247  template <int LEVEL>
248  __hostdev__ const NodeT<LEVEL>& node(uint32_t i) const {
249  NANOVDB_ASSERT(i < this->nodeCount(LEVEL));
250  const NodeT<LEVEL>* ptr = nullptr;
251  if (DataT::mLinear) {
252  ptr = PtrAdd<const NodeT<LEVEL>>(DataT::mGrid, DataT::mOff[LEVEL]) + i;
253  } else {
254  ptr = PtrAdd<const NodeT<LEVEL>>(DataT::mGrid, DataT::mPtr[LEVEL][i]);
255  }
256  NANOVDB_ASSERT(isValid(ptr));
257  return *ptr;
258  }
259 
260  /// @brief Return the i'th node with respect to breadth-first ordering
261  template <int LEVEL>
262  __hostdev__ NodeT<LEVEL>& node(uint32_t i) {
263  NANOVDB_ASSERT(i < this->nodeCount(LEVEL));
264  NodeT<LEVEL>* ptr = nullptr;
265  if (DataT::mLinear) {
266  ptr = PtrAdd<NodeT<LEVEL>>(DataT::mGrid, DataT::mOff[LEVEL]) + i;
267  } else {
268  ptr = PtrAdd<NodeT<LEVEL>>(DataT::mGrid, DataT::mPtr[LEVEL][i]);
269  }
270  NANOVDB_ASSERT(isValid(ptr));
271  return *ptr;
272  }
273 
274  /// @brief Return the i'th leaf node with respect to breadth-first ordering
275  __hostdev__ const Node0& leaf(uint32_t i) const { return this->node<0>(i); }
276  __hostdev__ Node0& leaf(uint32_t i) { return this->node<0>(i); }
277 
278  /// @brief Return the i'th lower internal node with respect to breadth-first ordering
279  __hostdev__ const Node1& lower(uint32_t i) const { return this->node<1>(i); }
280  __hostdev__ Node1& lower(uint32_t i) { return this->node<1>(i); }
281 
282  /// @brief Return the i'th upper internal node with respect to breadth-first ordering
283  __hostdev__ const Node2& upper(uint32_t i) const { return this->node<2>(i); }
284  __hostdev__ Node2& upper(uint32_t i) { return this->node<2>(i); }
285 
286 }; // NodeManager<BuildT> class
287 
288 template <typename BuildT, typename BufferT>
290  const BufferT& buffer)
291 {
292  NodeManagerHandle<BufferT> handle(BufferT::create(NodeManager<BuildT>::memUsage(grid), &buffer));
293  auto *data = reinterpret_cast<NodeManagerData*>(handle.data());
294  NANOVDB_ASSERT(isValid(data));
295  data->mMagic = NANOVDB_MAGIC_NUMBER;
296  data->mGrid = const_cast<NanoGrid<BuildT>*>(&grid);
297 
298  if ((data->mLinear = NodeManager<BuildT>::isLinear(grid)?1u:0u)) {
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().beginChild(); it2; ++it2) {
308  *ptr2++ = PtrDiff(&*it2, &grid);
309  for (auto it1 = it2->beginChild(); it1; ++it1) {
310  *ptr1++ = PtrDiff(&*it1, &grid);
311  for (auto it0 = it1->beginChild(); 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 #endif // NANOVDB_NODEMANAGER_H_HAS_BEEN_INCLUDED
HostBuffer - a buffer that contains a shared or private bump pool to either externally or internally ...
#define __hostdev__
Definition: NanoVDB.h:192
#define NANOVDB_ASSERT(x)
Definition: NanoVDB.h:173
#define NANOVDB_MAGIC_NUMBER
Definition: NanoVDB.h:121
Highest level of the data structure. Contains a tree and a world->index transform (that currently onl...
Definition: NanoVDB.h:2556
bool isBreadthFirst() const
Definition: NanoVDB.h:2687
const TreeT & tree() const
Return a const reference to the tree.
Definition: NanoVDB.h:2598
NodeManagerHandle manages the memory of a NodeManager.
Definition: NodeManager.h:55
const uint8_t * data() const
Returns a const pointer to the data.
Definition: NodeManager.h:104
BufferT & buffer()
Return a reference to the buffer.
Definition: NodeManager.h:91
NodeManagerHandle(BufferT &&buffer)
Move constructor from a buffer.
Definition: NodeManager.h:70
NodeManagerHandle & operator=(const NodeManagerHandle &)=delete
Disallow copy assignment operation.
std::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:155
uint8_t * data()
Returns a non-const pointer to the data.
Definition: NodeManager.h:99
const BufferT & buffer() const
Return a const reference to the buffer.
Definition: NodeManager.h:94
uint64_t size() const
Returns the size in bytes of the raw memory buffer managed by this NodeManagerHandle's allocator.
Definition: NodeManager.h:107
std::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:140
NodeManagerHandle()=default
Empty ctor.
NodeManagerHandle(const NodeManagerHandle &)=delete
Disallow copy-construction.
std::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:133
NodeManagerHandle & operator=(NodeManagerHandle &&other) noexcept
Move copy assignment operation.
Definition: NodeManager.h:78
const NodeManager< BuildT > * mgr() const
Returns a const pointer to the NodeManager encoded in this NodeManagerHandle.
Definition: NodeManager.h:113
void reset()
clear the buffer
Definition: NodeManager.h:88
NodeManager< BuildT > * mgr()
Returns a pointer to the NodeManager encoded in this NodeManagerHandle.
Definition: NodeManager.h:119
NodeManagerHandle(NodeManagerHandle &&other) noexcept
Move copy-constructor.
Definition: NodeManager.h:84
~NodeManagerHandle()
Default destructor.
Definition: NodeManager.h:86
std::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:126
NodeManager allows for sequential access to nodes.
Definition: NodeManager.h:189
const GridT & grid() const
Definition: NodeManager.h:232
bool isLinear() const
return true if the nodes have both fixed size and are arranged breadth-first in memory....
Definition: NodeManager.h:215
static uint64_t memUsage(const GridT &grid)
Return the memory footprint in bytes of the NodeManager derived from the specified grid.
Definition: NodeManager.h:218
const Node0 & leaf(uint32_t i) const
Return the i'th leaf node with respect to breadth-first ordering.
Definition: NodeManager.h:275
uint64_t nodeCount(int level) const
Return the number of tree nodes at the specified level.
Definition: NodeManager.h:244
const Node1 & lower(uint32_t i) const
Return the i'th lower internal node with respect to breadth-first ordering.
Definition: NodeManager.h:279
NodeManager & operator=(NodeManager &&)=delete
NodeManager(const NodeManager &)=delete
NodeManager(NodeManager &&)=delete
TreeT & tree()
Return a reference to the tree.
Definition: NodeManager.h:235
Node0 & leaf(uint32_t i)
Definition: NodeManager.h:276
GridT & grid()
Return a reference to the grid.
Definition: NodeManager.h:231
static bool isLinear(const GridT &grid)
return true if the nodes have both fixed size and are arranged breadth-first in memory....
Definition: NodeManager.h:211
NodeManager & operator=(const NodeManager &)=delete
uint64_t memUsage() const
Return the memory footprint in bytes of this instance.
Definition: NodeManager.h:228
NodeT< LEVEL > & node(uint32_t i)
Return the i'th node with respect to breadth-first ordering.
Definition: NodeManager.h:262
const NodeT< LEVEL > & node(uint32_t i) const
Return the i'th leaf node with respect to breadth-first ordering.
Definition: NodeManager.h:248
const RootT & root() const
Definition: NodeManager.h:240
Node2 & upper(uint32_t i)
Definition: NodeManager.h:284
RootT & root()
Return a reference to the root.
Definition: NodeManager.h:239
Node1 & lower(uint32_t i)
Definition: NodeManager.h:280
const Node2 & upper(uint32_t i) const
Return the i'th upper internal node with respect to breadth-first ordering.
Definition: NodeManager.h:283
const TreeT & tree() const
Definition: NodeManager.h:236
Definition: NanoVDB.h:208
static bool isValid(const void *p)
return true if the specified pointer is aligned and not NULL
Definition: NanoVDB.h:504
static int64_t PtrDiff(const T1 *p, const T2 *q)
Definition: NanoVDB.h:535
NodeManagerHandle< BufferT > createNodeManager(const NanoGrid< BuildT > &grid, const BufferT &buffer=BufferT())
brief Construct a NodeManager and return its handle
Definition: NodeManager.h:289
typename GridT::TreeType type
Definition: NanoVDB.h:2785
Definition: NodeManager.h:45
int64_t * mPtr[3]
Definition: NodeManager.h:48
uint8_t mPadding[7]
Definition: NodeManager.h:49
int64_t mOff[3]
Definition: NodeManager.h:48
uint64_t mMagic
Definition: NodeManager.h:46
void * mGrid
Definition: NodeManager.h:47
uint8_t mLinear
Definition: NodeManager.h:49
Struct to derive node type from its level in a given grid, tree or root while preserving constness.
Definition: NanoVDB.h:2343