OpenVDB 13.1.0
Loading...
Searching...
No Matches
HandleStorage.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/cuda/HandleStorage.h
6
7 \brief Allocates the device-resident storage behind a GridHandle or
8 NodeManagerHandle for any buffer family: through the static
9 create() interface for buffers that provide it (the dual-space
10 DeviceBuffer family), and through the buffer's memory resource
11 for a single-space cuda::Buffer. This is the bridge that lets
12 every tool entry point accept either buffer family.
13
14 \note This header is host-includable: it calls the CUDA runtime but
15 launches no kernels, so a plain C++ translation unit (linked
16 against the CUDA runtime) can allocate storage and transfer grid
17 handles with cuda::copyTo.
18*/
19
20#ifndef NANOVDB_CUDA_HANDLESTORAGE_H_HAS_BEEN_INCLUDED
21#define NANOVDB_CUDA_HANDLESTORAGE_H_HAS_BEEN_INCLUDED
22
23#include <nanovdb/GridHandle.h> // for the handle cuda::copyTo transfers
24#include <nanovdb/HostBuffer.h> // for the BufferTraits detectors
25#include <nanovdb/cuda/Buffer.h> // for noInit and the resource concepts
26#include <nanovdb/util/cuda/Util.h> // for cudaCheck
27
28#include <utility> // for std::move
29#include <vector> // for the adopted metadata
30
31#include <stdexcept> // for std::runtime_error
32#include <type_traits> // for std::is_default_constructible
33
34namespace nanovdb {
35
36namespace cuda {
37
38namespace detail {
39
40/// @brief Allocates @c bytes of device-resident storage of buffer type
41/// @c BufferT: single-space buffers allocate through @c pool's
42/// resource -- on @c stream when the resource is stream-ordered --
43/// and every other buffer type goes through its static
44/// create(bytes, pool, device, stream) interface.
45/// @param bytes size of the allocation
46/// @param proto prototype buffer or null: passed through as the pool for
47/// create()-style buffers; the source of the resource for
48/// single-space buffers, whose resource is default-constructed when
49/// @c proto is null
50/// @param device device the storage lives on; single-space buffers
51/// allocate on the current device, which every call site has
52/// already made current
53/// @param stream stream the allocation is ordered on where supported
54template<typename BufferT>
55inline BufferT createDeviceStorage(uint64_t bytes, const BufferT* proto, int device, cudaStream_t stream)
56{
58 using ResourceT = typename BufferT::ResourceType;
59 (void)device;
60 if (!proto) {
61 if constexpr (std::is_default_constructible<ResourceT>::value) {
62 if constexpr (is_async_resource<ResourceT>::value) return BufferT(stream, bytes, noInit);
63 else return BufferT(bytes, noInit);
64 } else {
65 throw std::runtime_error("createDeviceStorage: a buffer over a non-default-constructible "
66 "resource requires a prototype buffer to take the resource from");
67 }
68 }
70 return BufferT(stream, proto->resource(), bytes, noInit);
71 } else {
72 (void)stream;
73 return BufferT(proto->resource(), bytes, noInit);
74 }
75 } else {
76 return BufferT::create(bytes, proto, device, stream);
77 }
78}
79
80/// @brief The device address of a storage buffer made by
81/// createDeviceStorage: data() for a single-space buffer, whose one
82/// allocation is the device allocation, and deviceData() for the
83/// dual-space family.
84template<typename BufferT>
85inline void* deviceStorageData(BufferT& buffer)
86{
87 if constexpr (BufferHasDeviceSingle<BufferT>::value) return buffer.data();
88 else return buffer.deviceData();
89}
90
91/// @brief Orders the host after @c stream where a handle is about to be
92/// constructed from bytes still being written on it: a single-space
93/// buffer over a synchronous resource retains no stream, so the
94/// constructor's metadata parse (which runs on the default stream) is
95/// not otherwise ordered after the producer. A no-op for dual-space
96/// buffers (their constructor path predates this bridge) and for
97/// stream-ordered resources (the buffer retains the stream).
98template<typename BufferT>
99inline void orderBeforeHandleConstruction(cudaStream_t stream)
100{
103 cudaCheck(cudaStreamSynchronize(stream));
104 }
105 (void)stream;
106}
107
108/// @brief Allocates @c bytes of destination storage for a cross-space
109/// transfer: single-space buffers allocate through @c proto's resource
110/// (or a default-constructed resource without one), on @c stream when
111/// the resource is stream-ordered; buffers providing create() go
112/// through it.
113template<typename DstBufferT>
114inline DstBufferT makeTransferStorage(uint64_t bytes, cudaStream_t stream, const DstBufferT* proto)
115{
117 using ResourceT = typename DstBufferT::ResourceType;
118 if (!proto) {
119 // both branches of a plain conditional would instantiate the
120 // default-resource constructor, breaking non-default-constructible
121 // resources (e.g. ResourceRef) even for callers that pass a proto
122 if constexpr (std::is_default_constructible<ResourceT>::value) {
123 if constexpr (is_async_resource<ResourceT>::value) return DstBufferT(stream, bytes, noInit);
124 else return DstBufferT(bytes, noInit);
125 } else {
126 throw std::runtime_error("cuda::copyTo: a destination buffer over a non-default-constructible "
127 "resource requires a prototype buffer");
128 }
129 }
131 return DstBufferT(stream, proto->resource(), bytes, noInit);
132 } else {
133 (void)stream;
134 return DstBufferT(proto->resource(), bytes, noInit);
135 }
136 } else {
137 return DstBufferT::create(bytes, proto);
138 }
139}
140
141
142/// @brief The one gateway for constructing a GridHandle from a buffer plus
143/// metadata that is already known to be valid -- adopted from another
144/// handle, whose own construction from raw bytes did the validation.
146{
147 template<typename BufferT>
148 static GridHandle<BufferT> make(BufferT&& buffer, std::vector<GridHandleMetaData> meta)
149 {
150 return GridHandle<BufferT>(std::move(buffer), std::move(meta));
151 }
152
153 template<typename BufferT>
154 static const std::vector<GridHandleMetaData>& meta(const GridHandle<BufferT>& handle)
155 {
156 return handle.mMetaData;
157 }
158};
159
160}// namespace detail
161/// @brief Deep-copies a grid handle into a different address space: the
162/// explicit, stream-carrying transfer between single-space device
163/// handles and host-readable handles (HostBuffer or a host-accessible
164/// single-space buffer such as a pinned-resource cuda::Buffer).
165/// @tparam DstBufferT destination buffer type (specify explicitly)
166/// @param src the handle to copy; must not be dual-space (use
167/// deviceUpload/deviceDownload on those)
168/// @param stream stream the copy is issued on; a device destination buffer
169/// with a stream-ordered resource retains it
170/// @warning Passing a stream other than the source buffer's retained stream
171/// makes the caller responsible for ordering: prior work on the
172/// source (and the source's later destruction, which frees on its
173/// own stream) must be ordered against @a stream by the caller,
174/// e.g. with cudaStreamWaitEvent or a synchronization. The
175/// stream-less overload below has no such requirement for a source
176/// with a retained stream. A source WITHOUT one (a synchronous
177/// resource, e.g. a pinned-resource buffer) is the caller's to keep
178/// alive under either overload: its destruction frees host memory
179/// immediately, unordered against the still-asynchronous copy, so
180/// synchronize @a stream before destroying such a source. (A
181/// pageable HostBuffer source is exempt: its copy degrades to
182/// synchronous behavior.)
183/// @param proto optional buffer whose resource (or pool, for buffers
184/// providing create()) allocates the destination storage; without it
185/// the destination resource is default-constructed
186/// @return a handle of the destination buffer type with equal contents
187/// @details A host-readable destination -- HostBuffer, pinned, or a
188/// both-space managed buffer -- synchronizes @c stream before
189/// returning, so its host accessors are immediately valid; a
190/// device-only destination is stream-ordered, so use its
191/// contents on @c stream or synchronize first. The metadata is
192/// adopted from the source handle -- it was validated when that
193/// handle was constructed from raw bytes -- so no kernel runs and
194/// this function is callable from host-only translation units. A
195/// pageable host source or destination (HostBuffer) degrades the
196/// copy to synchronous behavior; pinned single-space handles keep
197/// it asynchronous.
198template<typename DstBufferT, typename SrcBufferT>
199inline GridHandle<DstBufferT> copyTo(const GridHandle<SrcBufferT>& src, cudaStream_t stream, const DstBufferT* proto = nullptr)
200{
201 constexpr bool srcDev = BufferHasDeviceSingle<SrcBufferT>::value;
202 constexpr bool dstDev = BufferHasDeviceSingle<DstBufferT>::value;
204 "cuda::copyTo does not support dual-space buffers: use deviceUpload/deviceDownload on the handle");
205 static_assert(srcDev || dstDev,
206 "cuda::copyTo is for cross-space transfers involving a device buffer: use GridHandle::copy for host-to-host");
207 const uint64_t bytes = src.bufferSize();
208 if (bytes == 0u) {
209 if constexpr (std::is_default_constructible<DstBufferT>::value) {
210 return GridHandle<DstBufferT>();
211 } else {
212 throw std::runtime_error("cuda::copyTo: an empty handle cannot be copied to a buffer type "
213 "that is not default-constructible");
214 }
215 }
216 DstBufferT dst = detail::makeTransferStorage<DstBufferT>(bytes, stream, proto);
217 const void* srcPtr;
218 if constexpr (srcDev) srcPtr = src.deviceData();
219 else srcPtr = src.data();
220 cudaCheck(cudaMemcpyAsync(dst.data(), srcPtr, bytes, cudaMemcpyDefault, stream));
221 if constexpr (!dstDev || BufferHasHostSingle<DstBufferT>::value)
222 cudaCheck(cudaStreamSynchronize(stream)); // the host-readable result is the postcondition; covers the both-space (managed) destination, whose handle exposes host accessors immediately
223 // A handle-to-handle copy adopts the source's metadata, which was
224 // validated when that handle was constructed from raw bytes -- no kernel
225 // runs here, which is what keeps this header host-includable. A device
226 // destination is stream-ordered: use it on @a stream, or synchronize.
227 return detail::HandleFactory::make(std::move(dst), detail::HandleFactory::meta(src));
228}// cuda::copyTo
229
230/// @brief Convenience overload issuing the copy on the source buffer's
231/// retained stream when it has one (any single-space source over a
232/// stream-ordered resource), the default stream otherwise.
233template<typename DstBufferT, typename SrcBufferT>
234inline GridHandle<DstBufferT> copyTo(const GridHandle<SrcBufferT>& src, const DstBufferT* proto = nullptr)
235{
236 cudaStream_t stream = 0;
237 if constexpr (BufferHasStream<SrcBufferT>::value) stream = src.buffer().stream();
238 return copyTo<DstBufferT>(src, stream, proto);
239}// cuda::copyTo (retained stream)
240
241
242}// namespace cuda
243
244}// namespace nanovdb
245
246#endif // NANOVDB_CUDA_HANDLESTORAGE_H_HAS_BEEN_INCLUDED
Typed containers for CUDA memory: the owning, resource-aware, stream-ordered cuda::Buffer and the non...
Defines GridHandle, which manages a memory buffer containing one or more NanoVDB grids: host-resident...
HostBuffer - a buffer that contains a shared or private bump pool to either externally or internally ...
This class serves to manage a buffer containing one or more NanoVDB Grids.
Definition GridHandle.h:109
BufferT & buffer()
Return a reference to the buffer.
Definition GridHandle.h:215
uint64_t bufferSize() const
Definition GridHandle.h:256
util::enable_if< BufferTraits< U >::hasDeviceDual, constvoid * >::type deviceData() const
Definition GridHandle.h:232
void * data()
Returns a pointer to the host data; not available for a single-space device buffer,...
Definition GridHandle.h:225
Definition GridHandle.h:37
void orderBeforeHandleConstruction(cudaStream_t stream)
Orders the host after stream where a handle is about to be constructed from bytes still being written...
Definition HandleStorage.h:99
void * deviceStorageData(BufferT &buffer)
The device address of a storage buffer made by createDeviceStorage: data() for a single-space buffer,...
Definition HandleStorage.h:85
BufferT createDeviceStorage(uint64_t bytes, const BufferT *proto, int device, cudaStream_t stream)
Allocates bytes of device-resident storage of buffer type BufferT: single-space buffers allocate thro...
Definition HandleStorage.h:55
DstBufferT makeTransferStorage(uint64_t bytes, cudaStream_t stream, const DstBufferT *proto)
Allocates bytes of destination storage for a cross-space transfer: single-space buffers allocate thro...
Definition HandleStorage.h:114
Definition GridHandle.h:37
constexpr NoInit noInit
Definition Buffer.h:29
GridHandle< DstBufferT > copyTo(const GridHandle< SrcBufferT > &src, cudaStream_t stream, const DstBufferT *proto=nullptr)
Deep-copies a grid handle into a different address space: the explicit, stream-carrying transfer betw...
Definition HandleStorage.h:199
Defines a simple memory pool used to call cub functions that use dynamic temporary storage.
Definition GridHandle.h:31
Cuda specific utility functions.
#define cudaCheck(ans)
Definition Util.h:49
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 hasDeviceDual
Definition HostBuffer.h:102
The one gateway for constructing a GridHandle from a buffer plus metadata that is already known to be...
Definition HandleStorage.h:146
static GridHandle< BufferT > make(BufferT &&buffer, std::vector< GridHandleMetaData > meta)
Definition HandleStorage.h:148
static const std::vector< GridHandleMetaData > & meta(const GridHandle< BufferT > &handle)
Definition HandleStorage.h:154
Detection trait: is_async_resource<R>::value is true iff R models the stream-ordered AsyncResource co...
Definition DeviceResource.h:118