Skip to content

Commit e86e55e

Browse files
harrismclaude
andcommitted
NanoVDB: share the buffer trait detectors and deduplicate GridHandle lookups
BufferHasDeviceSingle/BufferHasHostSingle move next to the BufferTraits primary in HostBuffer.h: they are companions to the trait protocol, not GridHandle-specific, so other handle types can consume them without including GridHandle.h. The three identical grid-lookup bodies behind grid() and both deviceGrid() families collapse into one private gridAt() helper, and the supported-combination guard for GridHandle::copy() now lives solely in the no-arg overload, so the two spellings cannot drift apart when cross-space transfers relax it. cuda::Buffer documents the single-space device-buffer concept on its BufferTraits specialization -- naming exactly the interface a consumer of hasDeviceSingle may rely on -- and gains a rebind alias for generic code that constructs sibling buffers. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: Mark Harris <mharris@nvidia.com>
1 parent 5ea0b9e commit e86e55e

3 files changed

Lines changed: 49 additions & 42 deletions

File tree

nanovdb/nanovdb/GridHandle.h

Lines changed: 14 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
#define NANOVDB_GRID_HANDLE_H_HAS_BEEN_INCLUDED
1818

1919
#include <fstream> // for std::ifstream
20-
#include <type_traits> // for std::void_t
2120
#include <iostream> // for std::cerr/cout
2221
#include <vector>
2322
#include <initializer_list>
@@ -32,27 +31,6 @@ namespace nanovdb {
3231

3332
struct GridHandleMetaData {uint64_t offset, size; GridType gridType;};
3433

35-
/// @brief Detects whether @c BufferTraits<BufferT> defines @c hasDeviceSingle,
36-
/// i.e. whether the buffer manages a single device-resident allocation.
37-
/// @details Defaults to false when the trait member is absent, so pre-existing
38-
/// BufferTraits specializations (in or out of tree) that only define
39-
/// hasDeviceDual keep compiling unchanged.
40-
template<typename BufferT, typename = void>
41-
struct BufferHasDeviceSingle { static constexpr bool value = false; };
42-
template<typename BufferT>
43-
struct BufferHasDeviceSingle<BufferT, std::void_t<decltype(BufferTraits<BufferT>::hasDeviceSingle)>>
44-
{ static constexpr bool value = BufferTraits<BufferT>::hasDeviceSingle; };
45-
46-
/// @brief Companion detection for BufferTraits<...>::hasHostSingle: a
47-
/// single-space buffer whose storage is host-accessible (e.g. a
48-
/// pinned-resource cuda::Buffer). GridHandle does not support these
49-
/// yet and rejects them with a named error below.
50-
template<typename BufferT, typename = void>
51-
struct BufferHasHostSingle { static constexpr bool value = false; };
52-
template<typename BufferT>
53-
struct BufferHasHostSingle<BufferT, std::void_t<decltype(BufferTraits<BufferT>::hasHostSingle)>>
54-
{ static constexpr bool value = BufferTraits<BufferT>::hasHostSingle; };
55-
5634
/// @brief This class serves to manage a buffer containing one or more NanoVDB Grids.
5735
///
5836
/// @note It is important to note that this class does NOT depend on OpenVDB.
@@ -71,6 +49,16 @@ class GridHandle
7149
template <typename T>
7250
static T* no_const(const T* ptr) { return const_cast<T*>(ptr); }
7351

52+
/// @brief Shared lookup behind grid() and deviceGrid(): the @a n'th grid
53+
/// within @a base, or nullptr when @a base is null, @a n is out of
54+
/// range, or the value type does not match the grid.
55+
template<typename ValueT>
56+
const NanoGrid<ValueT>* gridAt(const void* base, uint32_t n) const
57+
{
58+
if (base == nullptr || n >= mMetaData.size() || mMetaData[n].gridType != toGridType<ValueT>()) return nullptr;
59+
return util::PtrAdd<NanoGrid<ValueT>>(base, mMetaData[n].offset);
60+
}
61+
7462
/// @brief Adopts a buffer whose metadata is already known, so a deep copy
7563
/// does not re-parse and a non-default-constructible buffer (e.g.
7664
/// over a ResourceRef) never needs default construction.
@@ -241,11 +229,7 @@ class GridHandle
241229
/// or if the template parameter does not match the specified grid.
242230
template<typename ValueT, typename U = BufferT>
243231
typename util::enable_if<BufferHasDeviceSingle<U>::value, const NanoGrid<ValueT>*>::type
244-
deviceGrid(uint32_t n=0) const {
245-
const void *data = mBuffer.data();
246-
if (data == nullptr || n >= mMetaData.size() || mMetaData[n].gridType != toGridType<ValueT>()) return nullptr;
247-
return util::PtrAdd<NanoGrid<ValueT>>(data, mMetaData[n].offset);
248-
}
232+
deviceGrid(uint32_t n=0) const { return this->template gridAt<ValueT>(mBuffer.data(), n); }
249233
template<typename ValueT, typename U = BufferT>
250234
typename util::enable_if<BufferHasDeviceSingle<U>::value, NanoGrid<ValueT>*>::type
251235
deviceGrid(uint32_t n=0){return const_cast<NanoGrid<ValueT>*>(static_cast<const GridHandle*>(this)->template deviceGrid<ValueT>(n));}
@@ -463,11 +447,8 @@ template <typename OtherBufferT>
463447
inline GridHandle<OtherBufferT> GridHandle<BufferT>::copy(const OtherBufferT& other) const
464448
{
465449
if constexpr (BufferHasDeviceSingle<BufferT>::value || BufferHasDeviceSingle<OtherBufferT>::value) {
466-
static_assert(util::is_same<OtherBufferT, BufferT>::value && BufferHasDeviceSingle<BufferT>::value,
467-
"GridHandle::copy to or from a single-space device buffer of a different buffer type "
468-
"is not supported yet: copy device-to-device with the same buffer type instead");
469450
(void)other;// the single-space copy allocates through the source's resource
470-
return this->template copy<OtherBufferT>();
451+
return this->template copy<OtherBufferT>();// the no-arg overload holds the supported-combination guard
471452
} else {
472453
if (mBuffer.size() == 0) return GridHandle<OtherBufferT>();// return an empty handle
473454
auto buffer = OtherBufferT::create(mBuffer.size(), &other);
@@ -498,19 +479,15 @@ template<typename BufferT>
498479
template<typename ValueT, typename U, typename util::disable_if<BufferHasDeviceSingle<U>::value, int>::type>
499480
inline const NanoGrid<ValueT>* GridHandle<BufferT>::grid(uint32_t n) const
500481
{
501-
const void *data = mBuffer.data();
502-
if (data == nullptr || n >= mMetaData.size() || mMetaData[n].gridType != toGridType<ValueT>()) return nullptr;
503-
return util::PtrAdd<NanoGrid<ValueT>>(data, mMetaData[n].offset);
482+
return this->template gridAt<ValueT>(mBuffer.data(), n);
504483
}// const NanoGrid<ValueT>* GridHandle<BufferT>::grid(uint32_t n) const
505484

506485
template<typename BufferT>
507486
template<typename ValueT, typename U>
508487
inline typename util::enable_if<BufferTraits<U>::hasDeviceDual, const NanoGrid<ValueT>*>::type
509488
GridHandle<BufferT>::deviceGrid(uint32_t n) const
510489
{
511-
const void *data = mBuffer.deviceData();
512-
if (data == nullptr || n >= mMetaData.size() || mMetaData[n].gridType != toGridType<ValueT>()) return nullptr;
513-
return util::PtrAdd<NanoGrid<ValueT>>(data, mMetaData[n].offset);
490+
return this->template gridAt<ValueT>(mBuffer.deviceData(), n);
514491
}// GridHandle<BufferT>::deviceGrid(uint32_t n) cons
515492

516493
template<typename BufferT>

nanovdb/nanovdb/HostBuffer.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@
8787
#include <cassert>// for assert
8888
#include <sstream>// for std::stringstream
8989
#include <cstring>// for memcpy
90+
#include <type_traits>// for std::void_t
9091

9192
#define checkPtr(ptr, msg) \
9293
{ \
@@ -102,6 +103,26 @@ struct BufferTraits
102103
static constexpr bool hasDeviceSingle = false;
103104
};
104105

106+
/// @brief Detects whether @c BufferTraits<BufferT> defines @c hasDeviceSingle,
107+
/// i.e. whether the buffer manages a single device-resident allocation.
108+
/// @details Defaults to false when the trait member is absent, so pre-existing
109+
/// BufferTraits specializations (in or out of tree) that only define
110+
/// hasDeviceDual keep compiling unchanged.
111+
template<typename BufferT, typename = void>
112+
struct BufferHasDeviceSingle { static constexpr bool value = false; };
113+
template<typename BufferT>
114+
struct BufferHasDeviceSingle<BufferT, std::void_t<decltype(BufferTraits<BufferT>::hasDeviceSingle)>>
115+
{ static constexpr bool value = BufferTraits<BufferT>::hasDeviceSingle; };
116+
117+
/// @brief Companion detection for BufferTraits<...>::hasHostSingle: a
118+
/// single-space buffer whose storage is host-accessible (e.g. a
119+
/// pinned-resource cuda::Buffer).
120+
template<typename BufferT, typename = void>
121+
struct BufferHasHostSingle { static constexpr bool value = false; };
122+
template<typename BufferT>
123+
struct BufferHasHostSingle<BufferT, std::void_t<decltype(BufferTraits<BufferT>::hasHostSingle)>>
124+
{ static constexpr bool value = BufferTraits<BufferT>::hasHostSingle; };
125+
105126
// ----------------------------> HostBuffer <--------------------------------------
106127

107128
/// @brief This is a buffer that contains a shared or private pool

nanovdb/nanovdb/cuda/Buffer.h

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,11 @@ class Buffer : private detail::StreamHolder<is_async_resource<R>::value>
6868
using ElementType = T;
6969
using ResourceType = R;
7070

71+
/// @brief Alias for a sibling buffer over the same resource with a
72+
/// different element type.
73+
template<typename U>
74+
using rebind = Buffer<U, R>;
75+
7176
private:
7277
R mResource;
7378
T* mData = nullptr;
@@ -430,11 +435,15 @@ struct BufferTraits;
430435
/// owns exactly one allocation, resident on the device, so the handle
431436
/// parses metadata through a device read and exposes only the device
432437
/// accessors. Requires byte-addressed storage.
433-
/// @note A buffer whose trait sets hasDeviceSingle must provide the interface
434-
/// the single-space GridHandle constructor consumes: ElementType and
435-
/// ResourceType typedefs, data(), size_bytes(), resource(), stream()
436-
/// (stream-ordered resources), and the (stream, resource, count, noInit)
437-
/// constructor shape.
438+
/// @note This trait doubles as the definition of the single-space
439+
/// device-buffer concept: a buffer whose BufferTraits specialization
440+
/// sets hasDeviceSingle guarantees ElementType and ResourceType
441+
/// typedefs, data(), size() and size_bytes() (byte-addressed elements,
442+
/// enforced by the consumer), resource(), copy(), clear(), and stream()
443+
/// when the resource is stream-ordered. Any consumer of hasDeviceSingle
444+
/// may rely on exactly this interface and nothing more; in particular,
445+
/// scratch allocates through resource() as a cuda::Buffer, so a
446+
/// conforming buffer never needs to be constructible by a consumer.
438447
template<typename T, typename R>
439448
struct BufferTraits<cuda::Buffer<T, R>>
440449
{

0 commit comments

Comments
 (0)