Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
162 changes: 133 additions & 29 deletions nanovdb/nanovdb/GridHandle.h

Large diffs are not rendered by default.

24 changes: 23 additions & 1 deletion nanovdb/nanovdb/HostBuffer.h
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@
#include <cassert>// for assert
#include <sstream>// for std::stringstream
#include <cstring>// for memcpy
#include <type_traits>// for std::void_t

#define checkPtr(ptr, msg) \
{ \
Expand All @@ -98,9 +99,30 @@ namespace nanovdb {
template<typename BufferT>
struct BufferTraits
{
static constexpr bool hasDeviceDual = false;
static constexpr bool hasDeviceDual = false;
static constexpr bool hasDeviceSingle = false;
};

/// @brief Detects whether @c BufferTraits<BufferT> defines @c hasDeviceSingle,
/// i.e. whether the buffer manages a single device-resident allocation.
/// @details Defaults to false when the trait member is absent, so pre-existing
/// BufferTraits specializations (in or out of tree) that only define
/// hasDeviceDual keep compiling unchanged.
template<typename BufferT, typename = void>
struct BufferHasDeviceSingle { static constexpr bool value = false; };
template<typename BufferT>
struct BufferHasDeviceSingle<BufferT, std::void_t<decltype(BufferTraits<BufferT>::hasDeviceSingle)>>
{ static constexpr bool value = BufferTraits<BufferT>::hasDeviceSingle; };

/// @brief Companion detection for BufferTraits<...>::hasHostSingle: a
/// single-space buffer whose storage is host-accessible (e.g. a
/// pinned-resource cuda::Buffer).
template<typename BufferT, typename = void>
struct BufferHasHostSingle { static constexpr bool value = false; };
template<typename BufferT>
struct BufferHasHostSingle<BufferT, std::void_t<decltype(BufferTraits<BufferT>::hasHostSingle)>>
{ static constexpr bool value = BufferTraits<BufferT>::hasHostSingle; };

// ----------------------------> HostBuffer <--------------------------------------

/// @brief This is a buffer that contains a shared or private pool
Expand Down
58 changes: 58 additions & 0 deletions nanovdb/nanovdb/cuda/Buffer.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,18 @@ class Buffer : private detail::StreamHolder<is_async_resource<R>::value>

static constexpr bool IsAsync = is_async_resource<R>::value;

public:
/// @brief Element and resource types, for generic code that rebinds one
/// or constructs sibling buffers over the same resource.
using ElementType = T;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💅 polish: Tiny one: a rebind alias here (template<class U> using rebind = Buffer<U, R>;) would let generic code construct sibling buffers without spelling the full type — makeMetaScratch spells cuda::Buffer<GridHandleMetaData, ResourceT> manually today, and the VBM follow-up will spell two more.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added in e86e55e. One deliberate non-use: the GridHandle constructor keeps spelling cuda::Buffer<std::byte, ResourceT> for its scratch rather than rebinding BufferT, so rebind stays out of the single-space concept — scratch only needs the buffer's resource, not a sibling of the buffer type.

using ResourceType = R;

/// @brief Alias for a sibling buffer over the same resource with a
/// different element type.
template<typename U>
using rebind = Buffer<U, R>;

private:
R mResource;
T* mData = nullptr;
size_t mSize = 0; // element count
Expand Down Expand Up @@ -156,6 +168,11 @@ class Buffer : private detail::StreamHolder<is_async_resource<R>::value>
return out;
}

/// @brief Returns a deep copy of this buffer ordered on the retained
/// stream, i.e. copy(this->stream()).
template<typename S = R, std::enable_if_t<is_async_resource<S>::value, int> = 0>
Buffer copy() const { return this->copy(this->stream()); }

/// @brief Returns a deep copy of this buffer, allocated from a copy of the
/// synchronous resource.
template<typename S = R, std::enable_if_t<!is_async_resource<S>::value && is_resource<S>::value, int> = 0>
Expand Down Expand Up @@ -255,6 +272,13 @@ class Buffer : private detail::StreamHolder<is_async_resource<R>::value>
T* data() { return mData; }
const T* data() const { return mData; }

/// @brief Returns a copy of the resource; for a ResourceRef this refers
/// to the same underlying instance.
/// @note Requires R to be copy-constructible (the cuda::mr convention:
/// resources are cheap handles). A resource that owns its pool by
/// value hands the caller an independent copy of that pool.
R resource() const { return mResource; }

/// @brief Returns the number of elements.
size_t size() const { return mSize; }

Expand Down Expand Up @@ -402,6 +426,40 @@ class BufferView

} // namespace cuda

// Primary template defined in HostBuffer.h; declared here so this header
// stays self-contained without pulling in the host-buffer machinery.
template<typename BufferT>
struct BufferTraits;

/// @brief GridHandle support for the single-space cuda::Buffer: the buffer
/// owns exactly one allocation, resident on the device, so the handle
/// parses metadata through a device read and exposes only the device
/// accessors. Requires byte-addressed storage.
/// @note This trait doubles as the definition of the single-space
/// device-buffer concept: a buffer whose BufferTraits specialization
/// sets hasDeviceSingle guarantees ElementType and ResourceType
/// typedefs, data(), size() and size_bytes() (byte-addressed elements,
/// enforced by the consumer), resource(), copy(), clear(), and stream()
/// when the resource is stream-ordered. Any consumer of hasDeviceSingle
/// may rely on exactly this interface and nothing more; in particular,
/// scratch allocates through resource() as a cuda::Buffer, so a
/// conforming buffer never needs to be constructible by a consumer.
template<typename T, typename R>
struct BufferTraits<cuda::Buffer<T, R>>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 suggestion: This @note is effectively the definition of a "single-space buffer concept" — it lists exactly the members a buffer must provide for hasDeviceSingle to be honored. Since other handle types will consume the same contract (VoxelBlockManagerHandle is next, per the #2232 plan), could we phrase it as such — name the concept, and state that any consumer of hasDeviceSingle may rely on exactly this interface? That lets the follow-up PRs cite it instead of reverse-engineering the GridHandle constructor.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in e86e55e — the @note now defines the single-space device-buffer concept and enumerates the exact interface a consumer of hasDeviceSingle may rely on. It also got tighter while writing it down: the metadata scratch now allocates through resource() as a cuda::Buffer, so the constructor shape is no longer part of the contract — a conforming buffer never needs to be constructible by a consumer.

{
static constexpr bool hasDeviceDual = false;
// Device-resident storage; the byte-addressed requirement is enforced by
// the single-space GridHandle constructor, so trait queries stay
// answerable for any element type.
static constexpr bool hasDeviceSingle = !cuda::is_host_accessible_resource<R>::value;
// A buffer over a host-accessible resource (e.g. PinnedResource) is
// host-readable, but GridHandle's host paths also require the create()
// static interface and byte-count size semantics that cuda::Buffer does
// not provide -- GridHandle rejects such buffers with a named error until
// that adaptation lands.
static constexpr bool hasHostSingle = cuda::is_host_accessible_resource<R>::value;
};

} // namespace nanovdb

#endif // end of NANOVDB_CUDA_BUFFER_H_HAS_BEEN_INCLUDED
17 changes: 17 additions & 0 deletions nanovdb/nanovdb/cuda/DeviceResource.h
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,17 @@ struct is_async_resource<R, std::void_t<
decltype(std::declval<R&>().deallocate(std::declval<void*>(), size_t{0}, size_t{0}))>>
: std::true_type {};

/// @brief Detection trait: @c is_host_accessible_resource<R>::value is true
/// iff @c R declares `static constexpr bool HOST_ACCESSIBLE = true`,
/// i.e. its allocations are mapped into the host address space (e.g.
/// PinnedResource). Defaults to false: allocations are device-resident.
/// @note Unlike the void_t detections above, this checks the member's VALUE:
/// a resource declaring HOST_ACCESSIBLE = false stays device-resident.
template<typename R, typename = void>
struct is_host_accessible_resource : std::false_type {};
template<typename R>
struct is_host_accessible_resource<R, typename std::enable_if<bool(R::HOST_ACCESSIBLE)>::type> : std::true_type {};

/// @brief Detection trait: @c is_resource<R>::value is true iff @c R models
/// the synchronous Resource concept, i.e. exposes
/// allocate(size_t, size_t) and deallocate(void*, size_t, size_t).
Expand Down Expand Up @@ -244,6 +255,9 @@ struct AsyncFromSync

static constexpr size_t DEFAULT_ALIGNMENT = R::DEFAULT_ALIGNMENT;

/// @brief The adapter is host-accessible iff the adapted resource is.
static constexpr bool HOST_ACCESSIBLE = is_host_accessible_resource<R>::value;

R resource;

/// @brief Allocates through the synchronous resource; the result is valid
Expand Down Expand Up @@ -286,6 +300,9 @@ struct ResourceRef

static constexpr size_t DEFAULT_ALIGNMENT = R::DEFAULT_ALIGNMENT;

/// @brief A reference is host-accessible iff the referenced resource is.
static constexpr bool HOST_ACCESSIBLE = is_host_accessible_resource<R>::value;

/// @brief Constructs a ref borrowing @c resource.
/// @param resource resource to allocate from; must outlive this ref
ResourceRef(R& resource) : mResource(&resource) {}
Expand Down
Loading
Loading