OpenVDB 13.1.0
Loading...
Searching...
No Matches
GridHandle.h
Go to the documentation of this file.
1// Copyright Contributors to the OpenVDB Project
2// SPDX-License-Identifier: Apache-2.0
3
4/*!
5 \file nanovdb/GridHandle.h
6
7 \author Ken Museth
8
9 \date January 8, 2020
10
11 \brief Defines GridHandle, which manages a memory buffer containing one
12 or more NanoVDB grids: host-resident, dual host/device, or a
13 single-space device-only buffer.
14*/
15
16#ifndef NANOVDB_GRID_HANDLE_H_HAS_BEEN_INCLUDED
17#define NANOVDB_GRID_HANDLE_H_HAS_BEEN_INCLUDED
18
19#include <fstream> // for std::ifstream
20#include <iostream> // for std::cerr/cout
21#include <cstring> // for std::memcpy
22#include <stdexcept> // for std::runtime_error
23#include <string> // for std::to_string
24#include <vector>
25#include <initializer_list>
26
27#include <nanovdb/NanoVDB.h>// for toGridType
28#include <nanovdb/HostBuffer.h>
29#include <nanovdb/tools/GridChecksum.h>// for updateGridCount
30
31namespace nanovdb {
32
33// --------------------------> GridHandle <------------------------------------
34
36
37namespace cuda { namespace detail {
38// Defined in nanovdb/cuda/HandleStorage.h: the one gateway to constructing a
39// handle from a buffer plus already-validated metadata (handle-to-handle
40// transfers), so the trust boundary stays visible in a single place.
41struct HandleFactory;
42}}// namespace cuda::detail
43
44namespace detail {
45
46/// @brief Allocates @c bytes of host-readable storage for a GridHandle:
47/// through @c BufferT::create for buffers that provide it, and through
48/// @c pool's resource for a host-accessible single-space buffer (e.g. a
49/// pinned-resource cuda::Buffer). The braced third argument selects the
50/// uninitialized-storage constructor without naming its tag type, so
51/// this header stays CUDA-free.
52template<typename BufferT>
53inline BufferT createHostStorage(uint64_t bytes, const BufferT& pool)
54{
56 return BufferT(pool.stream(), pool.resource(), bytes, {});// stream-ordered resource: allocate on the pool's retained stream
57 } else if constexpr (BufferHasHostSingle<BufferT>::value) {
58 return BufferT(pool.resource(), bytes, {});
59 } else {
60 return BufferT::create(bytes, &pool);
61 }
62}
63
64/// @brief Validates the grid chain in @a bytes of host-readable memory headed
65/// by @a head and fills @a meta with one entry per grid: every header
66/// must be valid, carry its expected index and the chain's total count,
67/// and fit inside the buffer, so a truncated buffer or a forged header
68/// is rejected before its metadata is trusted. This is the host
69/// counterpart of the device-side chain parse, and it is what makes a
70/// handle's metadata safe to adopt without re-validation (cuda::copyTo).
71inline void parseHostGridChain(const GridData* head, uint64_t bytes, std::vector<GridHandleMetaData>& meta)
72{
73 if (bytes < sizeof(GridData))
74 throw std::runtime_error("GridHandle: grid chain exceeds the host buffer (truncated or corrupt grid data)");
75 if (!head->isValid()) throw std::runtime_error("GridHandle was constructed with an invalid host buffer");
76 const uint32_t count = head->mGridCount;
77 if (count == 0) throw std::runtime_error("GridHandle: host buffer contains no grids");
78 if (uint64_t(count) > bytes / sizeof(GridData))// every grid is at least one full header
79 throw std::runtime_error("GridHandle: grid chain exceeds the host buffer (truncated or corrupt grid data)");
80 meta.resize(count);
81 uint64_t offset = 0;
82 for (uint32_t i = 0; i < count; ++i) {
83 auto where = [&] { return " (grid " + std::to_string(i) + " of " + std::to_string(count) + ")"; };
84 if (offset + sizeof(GridData) > bytes)
85 throw std::runtime_error("GridHandle: grid chain exceeds the host buffer (truncated or corrupt grid data)" + where());
86 // Read through a copy: a forged size in the preceding header can place this one at an
87 // unaligned offset, where dereferencing a GridData pointer would be undefined behavior.
88 alignas(GridData) unsigned char raw[sizeof(GridData)];
89 std::memcpy(raw, util::PtrAdd<const void>(head, offset), sizeof(GridData));
90 const GridData* data = reinterpret_cast<const GridData*>(raw);
91 if (!data->isValid())
92 throw std::runtime_error("GridHandle was constructed with an invalid host buffer" + where());
93 if (data->mGridIndex != i || data->mGridCount != count)
94 throw std::runtime_error("GridHandle: inconsistent grid index/count in the host buffer's grid chain" + where());
95 if (data->mGridSize < sizeof(GridData) || data->mGridSize > bytes - offset)
96 throw std::runtime_error("GridHandle: grid size field exceeds the host buffer (truncated or corrupt grid data)" + where());
97 meta[i] = GridHandleMetaData{offset, data->mGridSize, data->mGridType};
98 offset += data->mGridSize;
99 }
100}
101
102}// namespace detail
103
104/// @brief This class serves to manage a buffer containing one or more NanoVDB Grids.
105///
106/// @note It is important to note that this class does NOT depend on OpenVDB.
107template<typename BufferT = HostBuffer>
108class GridHandle
109{
111 "a buffer cannot be both dual-space and single-space");
114 "GridHandle requires byte-addressed single-space storage, e.g. cuda::Buffer<std::byte, R>");
115
116 std::vector<GridHandleMetaData> mMetaData;
117 BufferT mBuffer;
118
119 template <typename T>
120 static T* no_const(const T* ptr) { return const_cast<T*>(ptr); }
121
122 /// @brief Shared lookup behind grid() and deviceGrid(): the @a n'th grid
123 /// within @a base, or nullptr when @a base is null, @a n is out of
124 /// range, or the value type does not match the grid.
125 template<typename ValueT>
126 const NanoGrid<ValueT>* gridAt(const void* base, uint32_t n) const
127 {
128 if (base == nullptr || n >= mMetaData.size() || mMetaData[n].gridType != toGridType<ValueT>()) return nullptr;
129 return util::PtrAdd<NanoGrid<ValueT>>(base, mMetaData[n].offset);
130 }
131
132 /// @brief Adopts a buffer whose metadata is already known, so a deep copy
133 /// does not re-parse and a non-default-constructible buffer (e.g.
134 /// over a ResourceRef) never needs default construction.
135 GridHandle(BufferT&& buffer, std::vector<GridHandleMetaData> meta)
136 : mMetaData(std::move(meta))
137 , mBuffer(std::move(buffer)) {}
138
140
141public:
142 using BufferType = BufferT;
143
144 /// @brief Move constructor from a dual host-device buffer
145 /// @param buffer buffer containing one or more NanoGrids that will be moved into this GridHandle
146 /// @throw Will throw and error with the buffer does not contain a valid NanoGrid!
147 /// @note The implementation of this template specialization is in nanovdb/cuda/GridHandle.cuh since it requires CUDA
148 template<typename T = BufferT, typename util::enable_if<BufferTraits<T>::hasDeviceDual, int>::type = 0>
149 GridHandle(T&& buffer);
150
151 /// @brief Move constructor from a host buffer
152 /// @param buffer buffer containing one or more NanoGrids that will be moved into this GridHandle
153 /// @throw Will throw and error with the buffer does not contain a valid NanoGrid!
154 template<typename T = BufferT, typename util::disable_if<BufferTraits<T>::hasDeviceDual || BufferHasDeviceSingle<T>::value, int>::type = 0>
156
157 /// @brief Move constructor from a single-space device buffer: the grid
158 /// metadata is read through the device on the buffer's stream.
159 /// @param buffer buffer containing one or more NanoGrids that will be moved into this GridHandle
160 /// @throw Will throw an error if the buffer does not contain a valid NanoGrid
161 /// @note The implementation of this template specialization is in nanovdb/cuda/GridHandle.cuh since it requires CUDA
162 template<typename T = BufferT, typename util::enable_if<BufferHasDeviceSingle<T>::value, int>::type = 0, typename = void>
164
165 /// @brief Constructs an empty GridHandle
166 GridHandle() = default;
167
168 /// @brief Disallow copy-construction
169 GridHandle(const GridHandle&) = delete;
170
171 /// @brief Move copy-constructor
172 GridHandle(GridHandle&& other) noexcept
173 : mMetaData(std::move(other.mMetaData))
174 , mBuffer(std::move(other.mBuffer)) {}
175
176 /// @brief clear this GridHandle to an empty handle
177 void reset() {
178 if constexpr (BufferHasDestroy<BufferT>::value) mBuffer.destroy();
179 else mBuffer.clear();
180 mMetaData.clear();
181 }
182
183 /// @brief Disallow copy assignment operation
184 GridHandle& operator=(const GridHandle&) = delete;
185
186 /// @brief Move copy assignment operation
187 GridHandle& operator=(GridHandle&& other) noexcept {
188 mBuffer = std::move(other.mBuffer);
189 mMetaData = std::move(other.mMetaData);
190 return *this;
191 }
192
193 /// @brief Performs a deep copy of the GridHandle, possibly templated on a different buffer type
194 /// @tparam OtherBufferT Buffer type of the deep copy. A single-space device
195 /// handle only copies to its own buffer type (device-to-device,
196 /// ordered on the source's retained stream), so call it as
197 /// copy<BufferT>() -- the default OtherBufferT does not compile.
198 /// @param buffer optional buffer used for allocation; a compile error for
199 /// single-space handles, which allocate through the source buffer's
200 /// resource (use the no-argument overload)
201 /// @return A new handle of the specified buffer type that contains a deep copy of the current handle
202 template <typename OtherBufferT = HostBuffer>
203 GridHandle<OtherBufferT> copy(const OtherBufferT& buffer) const;
204
205 /// @brief Deep copy without a pool argument. The single-space path
206 /// allocates through the source buffer's resource and never
207 /// constructs a pool buffer, so it works for buffers that are not
208 /// default-constructible, e.g. over a ResourceRef. The host path
209 /// default-constructs the pool argument, so it requires a
210 /// default-constructible OtherBufferT.
211 template <typename OtherBufferT = HostBuffer>
213
214 /// @brief Return a reference to the buffer
215 BufferT& buffer() { return mBuffer; }
216
217 /// @brief Return a const reference to the buffer
218 const BufferT& buffer() const { return mBuffer; }
219
220 //@{
221 /// @brief Returns a pointer to the host data; not available for a
222 /// single-space device buffer, which has no host-readable bytes.
223 /// @warning Note that the return pointer can be NULL if the GridHandle was not initialized
224 template<typename U = BufferT, typename util::disable_if<BufferIsDeviceOnly<U>::value, int>::type = 0>
225 void* data() { return mBuffer.data(); }
226 template<typename U = BufferT, typename util::disable_if<BufferIsDeviceOnly<U>::value, int>::type = 0>
227 const void* data() const { return mBuffer.data(); }
228 //@}
229
230 template<typename U = BufferT>
231 typename util::enable_if<BufferTraits<U>::hasDeviceDual, const void*>::type
232 deviceData() const { return mBuffer.deviceData(); }
233 template<typename U = BufferT>
234 typename util::enable_if<BufferTraits<U>::hasDeviceDual, const void*>::type
235 deviceData(int device) const { return mBuffer.deviceData(device); }
236 template<typename U = BufferT>
238 deviceData() { return mBuffer.deviceData(); }
239 template<typename U = BufferT>
241 deviceData(int device) { return mBuffer.deviceData(device); }
242
243 //@{
244 /// @brief For a single-space buffer the device data is the buffer itself.
245 template<typename U = BufferT>
246 typename util::enable_if<BufferHasDeviceSingle<U>::value, const void*>::type
247 deviceData() const { return mBuffer.data(); }
248 template<typename U = BufferT>
250 deviceData() { return mBuffer.data(); }
251 //@}
252
253 //@{
254 /// @brief Returns the size in bytes of the raw memory buffer managed by this GridHandle.
255 [[deprecated("Use GridHandle::bufferSize instead.")]] uint64_t size() const { return mBuffer.size(); }
256 uint64_t bufferSize() const { return mBuffer.size(); }
257 //@}
258
259 //@{
260 /// @brief Return true if this handle is empty, i.e. has no allocated memory
261 bool empty() const { return mBuffer.size() == 0; }
262 bool isEmpty() const { return mBuffer.size() == 0; }
263 //@}
264
265 /// @brief Return true if this handle is not empty, i.e. contains at least one grid
266 operator bool() const { return !this->empty(); }
267
268 /// @brief Returns a const host pointer to the @a n'th NanoVDB grid encoded in this GridHandle.
269 /// @tparam ValueT Value type of the grid point to be returned
270 /// @param n Index of the (host) grid pointer to be returned
271 /// @warning Note that the return pointer can be NULL if the GridHandle no host grid, @a n is invalid
272 /// or if the template parameter does not match the specified grid!
273 template<typename ValueT, typename U = BufferT, typename util::disable_if<BufferIsDeviceOnly<U>::value, int>::type = 0>
274 const NanoGrid<ValueT>* grid(uint32_t n = 0) const;
275
276 /// @brief Returns a host pointer to the @a n'th NanoVDB grid encoded in this GridHandle.
277 /// @tparam ValueT Value type of the grid point to be returned
278 /// @param n Index of the (host) grid pointer to be returned
279 /// @warning Note that the return pointer can be NULL if the GridHandle no host grid, @a n is invalid
280 /// or if the template parameter does not match the specified grid!
281 template<typename ValueT, typename U = BufferT, typename util::disable_if<BufferIsDeviceOnly<U>::value, int>::type = 0>
282 NanoGrid<ValueT>* grid(uint32_t n = 0) {return const_cast<NanoGrid<ValueT>*>(static_cast<const GridHandle*>(this)->template grid<ValueT>(n));}
283
284 /// @brief Return a const pointer to the @a n'th grid encoded in this GridHandle on the device, e.g. GPU
285 /// @tparam ValueT Value type of the grid point to be returned
286 /// @param n Index of the (device) grid pointer to be returned
287 /// @warning Note that the return pointer can be NULL if the GridHandle has no device grid, @a n is invalid,
288 /// or if the template parameter does not match the specified grid.
289 template<typename ValueT, typename U = BufferT>
291 deviceGrid(uint32_t n=0) const;
292
293 /// @brief Return a const pointer to the @a n'th grid encoded in this GridHandle on the device, e.g. GPU
294 /// @tparam ValueT Value type of the grid point to be returned
295 /// @param n Index of the grid pointer to be returned
296 /// @warning Note that the return pointer can be NULL if the GridHandle was not initialized, @a n is invalid,
297 /// or if the template parameter does not match the specified grid.
298 template<typename ValueT, typename U = BufferT>
300 deviceGrid(uint32_t n=0){return const_cast<NanoGrid<ValueT>*>(static_cast<const GridHandle*>(this)->template deviceGrid<ValueT>(n));}
301
302 //@{
303 /// @brief Return a pointer to the @a n'th grid of a single-space device buffer
304 /// @warning Note that the return pointer can be NULL if the GridHandle is empty, @a n is invalid,
305 /// or if the template parameter does not match the specified grid.
306 template<typename ValueT, typename U = BufferT>
308 deviceGrid(uint32_t n=0) const { return this->template gridAt<ValueT>(mBuffer.data(), n); }
309 template<typename ValueT, typename U = BufferT>
311 deviceGrid(uint32_t n=0){return const_cast<NanoGrid<ValueT>*>(static_cast<const GridHandle*>(this)->template deviceGrid<ValueT>(n));}
312 //@}
313
314 /// @brief Upload the grid to the device, e.g. from CPU to GPU
315 /// @note This method is only available if the buffer supports devices
316 template<typename U = BufferT>
318 deviceUpload(void* stream, bool sync = true) { mBuffer.deviceUpload(stream, sync); }
319
320 /// @brief Upload the host buffer to a specific device buffer. It device buffer doesn't exist it's created first
321 /// @param device Device to upload host data to
322 /// @param stream cuda stream
323 /// @param sync if false the memory copy is asynchronous
324 template<typename U = BufferT>
326 deviceUpload(int device = 0, void* stream = nullptr, bool sync = true) { mBuffer.deviceUpload(device, stream, sync); }
327
328 /// @brief Download the grid to from the device, e.g. from GPU to CPU
329 /// @note This method is only available if the buffer supports devices
330 template<typename U = BufferT>
332 deviceDownload(void* stream, bool sync = true) { mBuffer.deviceDownload(stream, sync); }
333
334 template<typename U = BufferT>
336 deviceDownload(int device = 0, void* stream = nullptr, bool sync = true) { mBuffer.deviceDownload(device, stream, sync); }
337
338 /// @brief Check if the buffer is this handle has any padding, i.e. if the buffer is larger than the combined size of all its grids
339 /// @return true is the combined size of all grid is smaller than the buffer size
340 bool isPadded() const {return mMetaData.empty() ? false : mMetaData.back().offset + mMetaData.back().size != mBuffer.size();}
341
342 /// @brief Return the total number of grids contained in this buffer
343 uint32_t gridCount() const {return static_cast<uint32_t>(mMetaData.size());}
344
345 /// @brief Return the grid size of the @a n'th grid in this GridHandle
346 /// @param n index of the grid (assumed to be less than gridCount())
347 /// @return Return the byte size of the specified grid
348 uint64_t gridSize(uint32_t n = 0) const {return mMetaData[n].size; }
349
350 /// @brief compute the total sum of memory footprints of all the grids in this buffer
351 /// @return the number of bytes occupied by all grids associated with this buffer
352 uint64_t totalGridSize() const {
353 uint64_t sum = 0;
354 for (auto &m : mMetaData) sum += m.size;
355 NANOVDB_ASSERT(sum <= mBuffer.size());
356 return sum;
357 }
358
359 /// @brief compute the size of unused storage in this buffer
360 /// @return the number of unused bytes in this buffer.
361 uint64_t freeSize() const {return mBuffer.size() - this->totalGridSize();}
362
363 /// @brief Test if this buffer has any unused storage left, i.e. memory not occupied by grids
364 /// @return true if there is no extra storage left in this buffer, i.e. empty or fully occupied with grids
365 bool isFull() const { return this->totalGridSize() == mBuffer.size(); }
366
367 /// @brief Return the GridType of the @a n'th grid in this GridHandle
368 /// @param n index of the grid (assumed to be less than gridCount())
369 /// @return Return the GridType of the specified grid
370 GridType gridType(uint32_t n = 0) const {return mMetaData[n].gridType; }
371
372 /// @brief Access to the GridData of the n'th grid in the current handle
373 /// @param n zero-based ID of the grid
374 /// @return Const pointer to the n'th GridData in the current handle
375 template<typename U = BufferT, typename util::disable_if<BufferIsDeviceOnly<U>::value, int>::type = 0>
376 const GridData* gridData(uint32_t n = 0) const;
377
378 /// @brief Returns a const point to the @a n'th grid meta data
379 /// @param n zero-based ID of the grid
380 /// @warning Note that the return pointer can be NULL if the GridHandle was not initialized
381 template<typename U = BufferT, typename util::disable_if<BufferIsDeviceOnly<U>::value, int>::type = 0>
382 const GridMetaData* gridMetaData(uint32_t n = 0) const;
383
384 /// @brief Write a specific grid in this buffer to an output stream
385 /// @param os output stream that the buffer will be written to
386 /// @param n zero-based index of the grid to be written to stream
387 void write(std::ostream& os, uint32_t n) const {
388 static_assert(!(BufferIsDeviceOnly<BufferT>::value),
389 "GridHandle::write requires host-accessible grids: cuda::copyTo a host-readable handle first");
390 if (const GridData* data = this->gridData(n)) {
391 os.write((const char*)data, data->mGridSize);
392 } else {
393 throw std::runtime_error("GridHandle does not contain a #" + std::to_string(n) + " grid");
394 }
395 }
396
397 /// @brief Write the entire grid buffer to an output stream
398 /// @param os output stream that the buffer will be written to
399 void write(std::ostream& os) const {
400 static_assert(!(BufferIsDeviceOnly<BufferT>::value),
401 "GridHandle::write requires host-accessible grids: cuda::copyTo a host-readable handle first");
402
403 for (uint32_t n=0; n<this->gridCount(); ++n) this->write(os, n);
404 }
405
406 /// @brief Write this entire grid buffer to a file
407 /// @param fileName string name of the output file
408 void write(const std::string &fileName) const {
409 std::ofstream os(fileName, std::ios::out | std::ios::binary | std::ios::trunc);
410 if (!os.is_open()) throw std::ios_base::failure("Unable to open file named \"" + fileName + "\" for output");
411 this->write(os);
412 }
413
414 /// @brief Write a specific grid to file
415 /// @param fileName string name of the output file
416 /// @param n zero-based index of the grid to be written to file
417 void write(const std::string &fileName, uint32_t n) const {
418 std::ofstream os(fileName, std::ios::out | std::ios::binary | std::ios::trunc);
419 if (!os.is_open()) throw std::ios_base::failure("Unable to open file named \"" + fileName + "\" for output");
420 this->write(os, n);
421 }
422
423 /// @brief Read an entire raw grid buffer from an input stream
424 /// @param is input stream containing a raw grid buffer
425 /// @param pool optional pool from which to allocate the new grid buffer
426 /// @throw Will throw a std::logic_error if the stream does not contain a valid raw grid
427 void read(std::istream& is, const BufferT& pool = BufferT());
428
429 /// @brief Read a specific grid from an input stream containing a raw grid buffer
430 /// @param is input stream containing a raw grid buffer
431 /// @param n zero-based index of the grid to be read
432 /// @param pool optional pool from which to allocate the new grid buffer
433 /// @throw Will throw a std::logic_error if the stream does not contain a valid raw grid
434 void read(std::istream& is, uint32_t n, const BufferT& pool = BufferT());
435
436 /// @brief Read a specific grid from an input stream containing a raw grid buffer
437 /// @param is input stream containing a raw grid buffer
438 /// @param gridName string name of the grid to be read
439 /// @param pool optional pool from which to allocate the new grid buffer
440 /// @throw Will throw a std::logic_error if the stream does not contain a valid raw grid with the specified name
441 void read(std::istream& is, const std::string &gridName, const BufferT& pool = BufferT());
442
443 /// @brief Read a raw grid buffer from a file
444 /// @param fileName string name of the input file containing a raw grid buffer
445 /// @param pool optional pool from which to allocate the new grid buffer
446 void read(const std::string &fileName, const BufferT& pool = BufferT()) {
447 std::ifstream is(fileName, std::ios::in | std::ios::binary);
448 if (!is.is_open()) throw std::ios_base::failure("Unable to open file named \"" + fileName + "\" for input");
449 this->read(is, pool);
450 }
451
452 /// @brief Read a specific grid from a file containing a raw grid buffer
453 /// @param fileName string name of the input file containing a raw grid buffer
454 /// @param n zero-based index of the grid to be read
455 /// @param pool optional pool from which to allocate the new grid buffer
456 /// @throw Will throw a std::ios_base::failure if the file does not exist and a
457 /// std::logic_error if the files does not contain a valid raw grid
458 void read(const std::string &fileName, uint32_t n, const BufferT& pool = BufferT()) {
459 std::ifstream is(fileName, std::ios::in | std::ios::binary);
460 if (!is.is_open()) throw std::ios_base::failure("Unable to open file named \"" + fileName + "\" for input");
461 this->read(is, n, pool);
462 }
463
464 /// @brief Read a specific grid from a file containing a raw grid buffer
465 /// @param fileName string name of the input file containing a raw grid buffer
466 /// @param gridName string name of the grid to be read
467 /// @param pool optional pool from which to allocate the new grid buffer
468 /// @throw Will throw a std::ios_base::failure if the file does not exist and a
469 /// std::logic_error if the files does not contain a valid raw grid withe the specified name
470 void read(const std::string &fileName, const std::string &gridName, const BufferT& pool = BufferT()) {
471 std::ifstream is(fileName, std::ios::in | std::ios::binary);
472 if (!is.is_open()) throw std::ios_base::failure("Unable to open file named \"" + fileName + "\" for input");
473 this->read(is, gridName, pool);
474 }
475}; // GridHandle
476
477// --------------------------> Implementation of private methods in GridHandle <------------------------------------
478
479template<typename BufferT>
480template<typename U, typename util::disable_if<BufferIsDeviceOnly<U>::value, int>::type>
481inline const GridData* GridHandle<BufferT>::gridData(uint32_t n) const
482{
483 const void *data = this->data();
484 if (data == nullptr || n >= mMetaData.size()) return nullptr;
485 return util::PtrAdd<GridData>(data, mMetaData[n].offset);
486}// const GridData* GridHandle<BufferT>::gridData(uint32_t n) const
487
488template<typename BufferT>
489template<typename U, typename util::disable_if<BufferIsDeviceOnly<U>::value, int>::type>
490inline const GridMetaData* GridHandle<BufferT>::gridMetaData(uint32_t n) const
491{
492 const auto *data = this->data();
493 if (data == nullptr || n >= mMetaData.size()) return nullptr;
494 return util::PtrAdd<GridMetaData>(data, mMetaData[n].offset);
495}// const GridMetaData* GridHandle<BufferT>::gridMetaData(uint32_t n) const
496
497// template specialization of move constructor from a host buffer
498template<typename BufferT>
499template<typename T, typename util::disable_if<BufferTraits<T>::hasDeviceDual || BufferHasDeviceSingle<T>::value, int>::type>
501 : mBuffer(std::move(buffer))
502{
503 static_assert(util::is_same<T,BufferT>::value, "Expected U==BufferT");
504 if (auto *data = reinterpret_cast<const GridData*>(mBuffer.data())) {
505 detail::parseHostGridChain(data, mBuffer.size(), mMetaData);
506 }
507}// GridHandle<BufferT>::GridHandle(T&& buffer)
508
509template<typename BufferT>
510template <typename OtherBufferT>
511inline GridHandle<OtherBufferT> GridHandle<BufferT>::copy(const OtherBufferT& other) const
512{
514 "GridHandle::copy(pool) cannot honor a pool argument for a single-space device buffer, "
515 "whose copy allocates through the source buffer's resource: use the no-argument copy() "
516 "for a same-space deep copy, or cuda::copyTo (cuda/HandleStorage.h) to cross address spaces");
517 if (mBuffer.size() == 0) return GridHandle<OtherBufferT>();// return an empty handle
518 auto buffer = detail::createHostStorage<OtherBufferT>(mBuffer.size(), other);
519 std::memcpy(buffer.data(), mBuffer.data(), mBuffer.size());// deep copy of buffer
520 return GridHandle<OtherBufferT>(std::move(buffer));
521}// GridHandle<OtherBufferT> GridHandle<BufferT>::copy(const OtherBufferT& other) const
522
523template<typename BufferT>
524template <typename OtherBufferT>
525inline GridHandle<OtherBufferT> GridHandle<BufferT>::copy() const
526{
529 "GridHandle::copy is same-space only: a single-space device handle copies to its own "
530 "buffer type; use cuda::copyTo (cuda/HandleStorage.h) to move grids across address spaces");
531 // Device-to-device deep copy; for a stream-ordered resource it is
532 // ordered on the source's retained stream, so synchronize that stream
533 // before reading the result. Metadata is host-resident, so the copy
534 // adopts it directly with no device re-parse.
535 return GridHandle(mBuffer.copy(), mMetaData);
536 } else {
538 "GridHandle::copy() without arguments default-constructs the target pool buffer: "
539 "pass a prototype to copy(other) for a buffer over a non-default-constructible resource");
540 return this->copy(OtherBufferT());
541 }
542}// GridHandle<OtherBufferT> GridHandle<BufferT>::copy() const
543
544template<typename BufferT>
545template<typename ValueT, typename U, typename util::disable_if<BufferIsDeviceOnly<U>::value, int>::type>
546inline const NanoGrid<ValueT>* GridHandle<BufferT>::grid(uint32_t n) const
547{
548 return this->template gridAt<ValueT>(mBuffer.data(), n);
549}// const NanoGrid<ValueT>* GridHandle<BufferT>::grid(uint32_t n) const
550
551template<typename BufferT>
552template<typename ValueT, typename U>
555{
556 return this->template gridAt<ValueT>(mBuffer.deviceData(), n);
557}// GridHandle<BufferT>::deviceGrid(uint32_t n) cons
558
559template<typename BufferT>
560void GridHandle<BufferT>::read(std::istream& is, const BufferT& pool)
561{
562 static_assert(!(BufferIsDeviceOnly<BufferT>::value),
563 "GridHandle::read requires a host-accessible buffer: read into a host-readable handle, then cuda::copyTo");
564 const std::streampos start = is.tellg();// remember where the raw buffer begins
566 is.read((char*)&data, sizeof(GridData));
567 if (data.isValid()) {
568 uint64_t size = data.mGridSize, sum = 0u;
569 while(data.mGridIndex + 1u < data.mGridCount) {// loop over remaining raw grids in stream
570 is.seekg(data.mGridSize - sizeof(GridData), std::ios::cur);// skip grid
571 is.read((char*)&data, sizeof(GridData));
572 sum += data.mGridSize;
573 }
574 auto buffer = detail::createHostStorage(size + sum, pool);
575 is.seekg(start);// rewind to the start of the raw buffer
576 is.read((char*)(buffer.data()), buffer.size());
577 *this = GridHandle(std::move(buffer));
578 } else {
579 is.seekg(-sizeof(GridData), std::ios::cur);// rewind
580 throw std::logic_error("This stream does not contain a valid raw grid buffer");
581 }
582}// void GridHandle<BufferT>::read(std::istream& is, const BufferT& pool)
583
584template<typename BufferT>
585void GridHandle<BufferT>::read(std::istream& is, uint32_t n, const BufferT& pool)
586{
587 static_assert(!(BufferIsDeviceOnly<BufferT>::value),
588 "GridHandle::read requires a host-accessible buffer: read into a host-readable handle, then cuda::copyTo");
590 is.read((char*)&data, sizeof(GridData));
591 if (data.isValid()) {
592 if (n>=data.mGridCount) throw std::runtime_error("stream does not contain a #" + std::to_string(n) + " grid");
593 while(data.mGridIndex != n) {
594 is.seekg(data.mGridSize - sizeof(GridData), std::ios::cur);// skip grid
595 is.read((char*)&data, sizeof(GridData));
596 }
597 auto buffer = detail::createHostStorage(data.mGridSize, pool);
598 is.seekg(-sizeof(GridData), std::ios::cur);// rewind
599 is.read((char*)(buffer.data()), data.mGridSize);
600 tools::updateGridCount((GridData*)buffer.data(), 0u, 1u);
601 *this = GridHandle(std::move(buffer));
602 } else {
603 is.seekg(-sizeof(GridData), std::ios::cur);// rewind sizeof(GridData) bytes to undo initial read
604 throw std::logic_error("This file does not contain a valid raw buffer");
605 }
606}// void GridHandle<BufferT>::read(std::istream& is, uint32_t n, const BufferT& pool)
607
608template<typename BufferT>
609void GridHandle<BufferT>::read(std::istream& is, const std::string &gridName, const BufferT& pool)
610{
611 static_assert(!(BufferIsDeviceOnly<BufferT>::value),
612 "GridHandle::read requires a host-accessible buffer: read into a host-readable handle, then cuda::copyTo");
613 static const std::streamsize byteSize = sizeof(GridData);
615 is.read((char*)&data, byteSize);
616 is.seekg(-byteSize, std::ios::cur);// rewind
617 if (data.isValid()) {
618 uint32_t n = 0;
619 while(data.mGridName != gridName && n++ < data.mGridCount) {
620 is.seekg(data.mGridSize, std::ios::cur);// skip grid
621 is.read((char*)&data, byteSize);// read sizeof(GridData) bytes
622 is.seekg(-byteSize, std::ios::cur);// rewind
623 }
624 if (n>data.mGridCount) throw std::runtime_error("No raw grid named \""+gridName+"\"");
625 auto buffer = detail::createHostStorage(data.mGridSize, pool);
626 is.read((char*)(buffer.data()), data.mGridSize);
627 tools::updateGridCount((GridData*)buffer.data(), 0u, 1u);
628 *this = GridHandle(std::move(buffer));
629 } else {
630 throw std::logic_error("This file does not contain a valid raw buffer");
631 }
632}// void GridHandle<BufferT>::read(std::istream& is, const std::string &gridName n, const BufferT& pool)
633
634// --------------------------> free-standing functions <------------------------------------
635
636/// @brief Split all grids in a single GridHandle into a vector of multiple GridHandles each with a single grid
637/// @tparam BufferT Type of the input and output grid buffers
638/// @param handle GridHandle with grids that will be slip into individual GridHandles
639/// @param other optional pool used for allocation of output GridHandle
640/// @return Vector of GridHandles each containing a single grid
641template<typename BufferT, template <class, class...> class VectorT = std::vector>
642inline VectorT<GridHandle<BufferT>>
643splitGrids(const GridHandle<BufferT> &handle, const BufferT* other = nullptr)
644{
645 static_assert(!(BufferIsDeviceOnly<BufferT>::value),
646 "splitGrids requires a buffer type providing create(): cuda::copyTo a HostBuffer handle first");
648 "splitGrids requires a buffer type providing create(): copy the handle to a HostBuffer first");
649 using HandleT = GridHandle<BufferT>;
650 const void *ptr = handle.data();
651 if (ptr == nullptr) return VectorT<HandleT>();
652 VectorT<HandleT> handles(handle.gridCount());
653 for (auto &h : handles) {
654 const GridData *src = reinterpret_cast<const GridData*>(ptr);
655 NANOVDB_ASSERT(src->isValid());
656 auto buffer = BufferT::create(src->mGridSize, other);
657 GridData *dst = reinterpret_cast<GridData*>(buffer.data());
658 std::memcpy(dst, src, src->mGridSize);
659 tools::updateGridCount(dst, 0u, 1u);
660 h = HandleT(std::move(buffer));
661 ptr = util::PtrAdd(ptr, src->mGridSize);
662 }
663 return handles;
664}// splitGrids
665
666/// @brief Combines (or merges) multiple GridHandles into a single GridHandle containing all grids
667/// @tparam BufferT Type of the input and output grid buffers
668/// @param handles Vector of GridHandles to be combined
669/// @param pool optional pool used for allocation of output GridHandle
670/// @return single GridHandle containing all input grids
671template<typename BufferT, template <class, class...> class VectorT>
672inline GridHandle<BufferT>
673mergeGrids(const VectorT<GridHandle<BufferT>> &handles, const BufferT* pool = nullptr)
674{
676 "mergeGrids requires a buffer type providing create(): cuda::copyTo HostBuffer handles first");
678 "mergeGrids requires a buffer type providing create(): copy the handles to HostBuffer first");
679 uint64_t size = 0u;
680 uint32_t counter = 0u, gridCount = 0u;
681 for (auto &h : handles) {
682 gridCount += h.gridCount();
683 for (uint32_t n=0; n<h.gridCount(); ++n) size += h.gridSize(n);
684 }
685 auto buffer = BufferT::create(size, pool);
686 void *dst = buffer.data();
687 for (auto &h : handles) {
688 const void *src = h.data();
689 for (uint32_t n=0; n<h.gridCount(); ++n) {
690 std::memcpy(dst, src, h.gridSize(n));
691 GridData *data = reinterpret_cast<GridData*>(dst);
692 NANOVDB_ASSERT(data->isValid());
693 tools::updateGridCount(data, counter++, gridCount);
694 dst = util::PtrAdd(dst, data->mGridSize);
695 src = util::PtrAdd(src, data->mGridSize);
696 }
697 }
698 return GridHandle<BufferT>(std::move(buffer));
699}// mergeGrids
700
701} // namespace nanovdb
702
703#if defined(__CUDACC__)
704#include <nanovdb/cuda/GridHandle.cuh>
705#endif// defined(__CUDACC__)
706
707#endif // NANOVDB_GRID_HANDLE_H_HAS_BEEN_INCLUDED
HostBuffer - a buffer that contains a shared or private bump pool to either externally or internally ...
Implements a light-weight self-contained VDB data-structure in a single file! In other words,...
This class serves to manage a buffer containing one or more NanoVDB Grids.
Definition GridHandle.h:109
uint64_t freeSize() const
compute the size of unused storage in this buffer
Definition GridHandle.h:361
util::enable_if< BufferHasDeviceSingle< U >::value, constNanoGrid< ValueT > * >::type deviceGrid(uint32_t n=0) const
Return a pointer to the n'th grid of a single-space device buffer.
Definition GridHandle.h:308
GridHandle & operator=(const GridHandle &)=delete
Disallow copy assignment operation.
uint64_t totalGridSize() const
compute the total sum of memory footprints of all the grids in this buffer
Definition GridHandle.h:352
util::enable_if< BufferTraits< U >::hasDeviceDual, void >::type deviceDownload(int device=0, void *stream=nullptr, bool sync=true)
Definition GridHandle.h:336
util::enable_if< BufferTraits< U >::hasDeviceDual, constvoid * >::type deviceData(int device) const
Definition GridHandle.h:235
util::enable_if< BufferTraits< U >::hasDeviceDual, void >::type deviceUpload(void *stream, bool sync=true)
Upload the grid to the device, e.g. from CPU to GPU.
Definition GridHandle.h:318
BufferT & buffer()
Return a reference to the buffer.
Definition GridHandle.h:215
void write(std::ostream &os) const
Write the entire grid buffer to an output stream.
Definition GridHandle.h:399
GridHandle(const GridHandle &)=delete
Disallow copy-construction.
GridHandle & operator=(GridHandle &&other) noexcept
Move copy assignment operation.
Definition GridHandle.h:187
uint64_t bufferSize() const
Definition GridHandle.h:256
void read(const std::string &fileName, const std::string &gridName, const BufferT &pool=BufferT())
Read a specific grid from a file containing a raw grid buffer.
Definition GridHandle.h:470
bool empty() const
Return true if this handle is empty, i.e. has no allocated memory.
Definition GridHandle.h:261
util::enable_if< BufferTraits< U >::hasDeviceDual, constNanoGrid< ValueT > * >::type deviceGrid(uint32_t n=0) const
Return a const pointer to the n'th grid encoded in this GridHandle on the device, e....
Definition GridHandle.h:554
util::enable_if< BufferHasDeviceSingle< U >::value, void * >::type deviceData()
Definition GridHandle.h:250
const void * data() const
Definition GridHandle.h:227
const GridData * gridData(uint32_t n=0) const
Access to the GridData of the n'th grid in the current handle.
Definition GridHandle.h:481
util::enable_if< BufferHasDeviceSingle< U >::value, constvoid * >::type deviceData() const
For a single-space buffer the device data is the buffer itself.
Definition GridHandle.h:247
const NanoGrid< ValueT > * grid(uint32_t n=0) const
Returns a const host pointer to the n'th NanoVDB grid encoded in this GridHandle.
Definition GridHandle.h:546
uint64_t size() const
Returns the size in bytes of the raw memory buffer managed by this GridHandle.
Definition GridHandle.h:255
util::enable_if< BufferTraits< U >::hasDeviceDual, NanoGrid< ValueT > * >::type deviceGrid(uint32_t n=0)
Return a const pointer to the n'th grid encoded in this GridHandle on the device, e....
Definition GridHandle.h:300
const BufferT & buffer() const
Return a const reference to the buffer.
Definition GridHandle.h:218
GridHandle(GridHandle &&other) noexcept
Move copy-constructor.
Definition GridHandle.h:172
util::enable_if< BufferTraits< U >::hasDeviceDual, constvoid * >::type deviceData() const
Definition GridHandle.h:232
uint64_t gridSize(uint32_t n=0) const
Return the grid size of the n'th grid in this GridHandle.
Definition GridHandle.h:348
GridType gridType(uint32_t n=0) const
Return the GridType of the n'th grid in this GridHandle.
Definition GridHandle.h:370
GridHandle(T &&buffer)
Move constructor from a single-space device buffer: the grid metadata is read through the device on t...
util::enable_if< BufferTraits< U >::hasDeviceDual, void * >::type deviceData(int device)
Definition GridHandle.h:241
void read(const std::string &fileName, uint32_t n, const BufferT &pool=BufferT())
Read a specific grid from a file containing a raw grid buffer.
Definition GridHandle.h:458
void write(const std::string &fileName) const
Write this entire grid buffer to a file.
Definition GridHandle.h:408
bool isPadded() const
Check if the buffer is this handle has any padding, i.e. if the buffer is larger than the combined si...
Definition GridHandle.h:340
NanoGrid< ValueT > * grid(uint32_t n=0)
Returns a host pointer to the n'th NanoVDB grid encoded in this GridHandle.
Definition GridHandle.h:282
const GridMetaData * gridMetaData(uint32_t n=0) const
Returns a const point to the n'th grid meta data.
Definition GridHandle.h:490
util::enable_if< BufferHasDeviceSingle< U >::value, NanoGrid< ValueT > * >::type deviceGrid(uint32_t n=0)
Definition GridHandle.h:311
BufferT BufferType
Definition GridHandle.h:142
GridHandle< OtherBufferT > copy() const
Deep copy without a pool argument. The single-space path allocates through the source buffer's resour...
Definition GridHandle.h:525
uint32_t gridCount() const
Return the total number of grids contained in this buffer.
Definition GridHandle.h:343
bool isEmpty() const
Definition GridHandle.h:262
void reset()
clear this GridHandle to an empty handle
Definition GridHandle.h:177
bool isFull() const
Test if this buffer has any unused storage left, i.e. memory not occupied by grids.
Definition GridHandle.h:365
GridHandle(T &&buffer)
Move constructor from a host buffer.
util::enable_if< BufferTraits< U >::hasDeviceDual, void * >::type deviceData()
Definition GridHandle.h:238
void * data()
Returns a pointer to the host data; not available for a single-space device buffer,...
Definition GridHandle.h:225
void read(const std::string &fileName, const BufferT &pool=BufferT())
Read a raw grid buffer from a file.
Definition GridHandle.h:446
util::enable_if< BufferTraits< U >::hasDeviceDual, void >::type deviceUpload(int device=0, void *stream=nullptr, bool sync=true)
Upload the host buffer to a specific device buffer. It device buffer doesn't exist it's created first...
Definition GridHandle.h:326
void write(const std::string &fileName, uint32_t n) const
Write a specific grid to file.
Definition GridHandle.h:417
void read(std::istream &is, const BufferT &pool=BufferT())
Read an entire raw grid buffer from an input stream.
Definition GridHandle.h:560
void write(std::ostream &os, uint32_t n) const
Write a specific grid in this buffer to an output stream.
Definition GridHandle.h:387
GridHandle< OtherBufferT > copy(const OtherBufferT &buffer) const
Performs a deep copy of the GridHandle, possibly templated on a different buffer type.
Definition GridHandle.h:511
GridHandle()=default
Constructs an empty GridHandle.
util::enable_if< BufferTraits< U >::hasDeviceDual, void >::type deviceDownload(void *stream, bool sync=true)
Download the grid to from the device, e.g. from GPU to CPU.
Definition GridHandle.h:332
This is a convenient class that allows for access to grid meta-data that are independent of the value...
Definition NanoVDB.h:5669
Definition VoxToNanoVDB.h:15
Definition GridHandle.h:37
BufferT createHostStorage(uint64_t bytes, const BufferT &pool)
Allocates bytes of host-readable storage for a GridHandle: through BufferT::create for buffers that p...
Definition GridHandle.h:53
void parseHostGridChain(const GridData *head, uint64_t bytes, std::vector< GridHandleMetaData > &meta)
Validates the grid chain in bytes of host-readable memory headed by head and fills meta with one entr...
Definition GridHandle.h:71
void updateGridCount(GridData *data, uint32_t gridIndex, uint32_t gridCount)
Updates the ground index and count, as well as the head checksum if needed.
Definition GridChecksum.h:407
static DstT * PtrAdd(void *p, int64_t offset)
Adds a byte offset to a non-const pointer to produce another non-const pointer.
Definition Util.h:524
Defines a simple memory pool used to call cub functions that use dynamic temporary storage.
Definition GridHandle.h:31
GridHandle< BufferT > mergeGrids(const VectorT< GridHandle< BufferT > > &handles, const BufferT *pool=nullptr)
Combines (or merges) multiple GridHandles into a single GridHandle containing all grids.
Definition GridHandle.h:673
GridType toGridType()
Maps from a templated build type to a GridType enum.
Definition NanoVDB.h:851
Grid< NanoTree< BuildT > > NanoGrid
Definition NanoVDB.h:4742
VectorT< GridHandle< BufferT > > splitGrids(const GridHandle< BufferT > &handle, const BufferT *other=nullptr)
Split all grids in a single GridHandle into a vector of multiple GridHandles each with a single grid.
Definition GridHandle.h:643
GridType
List of types that are currently supported by NanoVDB.
Definition NanoVDB.h:219
Definition Coord.h:590
#define NANOVDB_ASSERT(x)
Definition Util.h:53
static constexpr bool value
Definition HostBuffer.h:147
static constexpr bool value
Definition HostBuffer.h:165
static constexpr bool value
Definition HostBuffer.h:112
static constexpr bool value
Definition HostBuffer.h:121
static constexpr bool value
Definition HostBuffer.h:138
static constexpr bool value
Definition HostBuffer.h:156
static constexpr bool value
Definition HostBuffer.h:132
static constexpr bool hasDeviceDual
Definition HostBuffer.h:102
Struct with all the member data of the Grid (useful during serialization of an openvdb grid)
Definition NanoVDB.h:1977
GridType mGridType
Definition NanoVDB.h:1991
bool isValid() const
return true if the magic number and the version are both valid
Definition NanoVDB.h:2034
uint64_t mGridSize
Definition NanoVDB.h:1985
uint32_t mGridCount
Definition NanoVDB.h:1984
uint32_t mGridIndex
Definition NanoVDB.h:1983
Definition GridHandle.h:35
uint64_t offset
Definition GridHandle.h:35
GridType gridType
Definition GridHandle.h:35
uint64_t size
Definition GridHandle.h:35
The one gateway for constructing a GridHandle from a buffer plus metadata that is already known to be...
Definition HandleStorage.h:146
C++11 implementation of std::enable_if.
Definition Util.h:353
static constexpr bool value
Definition Util.h:328
Computes a pair of uint32_t checksums, of a Grid, by means of 32 bit Cyclic Redundancy Check (CRC32)