OpenVDB 14.0.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 through a single-space buffer's memory
9 resource, and transfers handles between address spaces.
10
11 \note This header is host-includable: it calls the CUDA runtime but
12 launches no kernels, so a plain C++ translation unit (linked
13 against the CUDA runtime) can allocate storage and transfer grid
14 handles with cuda::copyTo.
15*/
16
17#ifndef NANOVDB_CUDA_HANDLESTORAGE_H_HAS_BEEN_INCLUDED
18#define NANOVDB_CUDA_HANDLESTORAGE_H_HAS_BEEN_INCLUDED
19#define NANOVDB_CUDA_HANDLESTORAGE_DEFERS_GRIDHANDLE_CUH // see the end of nanovdb/GridHandle.h
20
21#include <nanovdb/GridHandle.h> // for the handle cuda::copyTo transfers
22#include <nanovdb/HostBuffer.h> // for the BufferTraits detectors
23#include <nanovdb/cuda/Buffer.h> // for noInit and the resource concepts
24#include <nanovdb/util/cuda/Util.h> // for cudaCheck
25
26#include <utility> // for std::move
27#include <vector> // for the adopted metadata
28
29#include <stdexcept> // for std::runtime_error
30#include <type_traits> // for std::is_default_constructible, std::enable_if, std::is_same, std::is_convertible, std::is_null_pointer
31
32namespace nanovdb {
33
34namespace cuda {
35
36namespace detail {
37
38/// @brief Allocates @c bytes of device-resident storage of the single-space
39/// buffer type @c BufferT on the current device through @c proto's
40/// resource -- on @c stream when the resource is stream-ordered.
41/// @param bytes size of the allocation
42/// @param proto prototype buffer or null: the source of the resource, which
43/// is default-constructed when @c proto is null
44/// @param stream stream the allocation is ordered on where supported
45template<typename BufferT>
46inline BufferT createDeviceStorage(uint64_t bytes, const BufferT* proto, cudaStream_t stream)
47{
49 "device grid storage requires a single-space device buffer, e.g. cuda::Buffer<std::byte>");
50 using ResourceT = typename BufferT::ResourceType;
51 if (!proto) {
52 if constexpr (std::is_default_constructible<ResourceT>::value) {
53 if constexpr (is_async_resource<ResourceT>::value) return BufferT(stream, bytes, noInit);
54 else return BufferT(bytes, noInit);
55 } else {
56 throw std::runtime_error("createDeviceStorage: a buffer over a non-default-constructible "
57 "resource requires a prototype buffer to take the resource from");
58 }
59 }
61 return BufferT(stream, proto->resource(), bytes, noInit);
62 } else {
63 (void)stream;
64 return BufferT(proto->resource(), bytes, noInit);
65 }
66}
67
68/// @brief The device address of a storage buffer made by
69/// createDeviceStorage: its one allocation is the device allocation.
70template<typename BufferT>
71inline void* deviceStorageData(BufferT& buffer)
72{
73 static_assert(BufferHasDeviceSingle<BufferT>::value, "device grid storage requires a single-space device buffer");
74 return buffer.data();
75}
76
77/// @brief True when @c BufferT is a stream rather than a buffer: cudaStream_t, or
78/// another non-buffer type that converts to it. nullptr is not a stream here.
79template<typename BufferT>
81{
82 static constexpr bool value = std::is_convertible<BufferT, cudaStream_t>::value && !std::is_null_pointer<BufferT>::value
85};
86/// @brief Return type of a tool's prototype-buffer form: @c T, except when
87/// @c BufferT deduced as a stream (cudaStream_t, or any other type that
88/// converts to it and is not a buffer, such as a stream wrapper), i.e.
89/// the call passed a stream where the prototype goes, which is the
90/// resource form's signature. A literal 0 or nullptr keeps the prototype
91/// form, whose static_assert asks for cudaStream_t(0).
92/// Removing the prototype form there leaves the resource form as the
93/// only candidate rather than relying on partial ordering, which
94/// compilers resolve differently for these overloads.
95template<typename BufferT, typename T>
96using PrototypeFormResult = typename std::enable_if<!IsStreamArgument<BufferT>::value, T>::type;
97
98/// @brief Orders the host after @c stream where a handle is about to be
99/// constructed from bytes still being written on it: a single-space
100/// buffer over a synchronous resource retains no stream, so the
101/// constructor's metadata parse (which runs on the default stream) is
102/// not otherwise ordered after the producer. A no-op for
103/// stream-ordered resources (the buffer retains the stream).
104template<typename BufferT>
105inline void orderBeforeHandleConstruction(cudaStream_t stream)
106{
109 cudaCheck(cudaStreamSynchronize(stream));
110 }
111 (void)stream;
112}
113
114/// @brief Allocates @c bytes of destination storage for a cross-space
115/// transfer: single-space buffers allocate through @c proto's resource
116/// (or a default-constructed resource without one), on @c stream when
117/// the resource is stream-ordered; buffers providing create() go
118/// through it.
119template<typename DstBufferT>
120inline DstBufferT makeTransferStorage(uint64_t bytes, cudaStream_t stream, const DstBufferT* proto)
121{
123 using ResourceT = typename DstBufferT::ResourceType;
124 if (!proto) {
125 // both branches of a plain conditional would instantiate the
126 // default-resource constructor, breaking non-default-constructible
127 // resources (e.g. ResourceRef) even for callers that pass a proto
128 if constexpr (std::is_default_constructible<ResourceT>::value) {
129 if constexpr (is_async_resource<ResourceT>::value) return DstBufferT(stream, bytes, noInit);
130 else return DstBufferT(bytes, noInit);
131 } else {
132 throw std::runtime_error("cuda::copyTo: a destination buffer over a non-default-constructible "
133 "resource requires a prototype buffer");
134 }
135 }
137 return DstBufferT(stream, proto->resource(), bytes, noInit);
138 } else {
139 (void)stream;
140 return DstBufferT(proto->resource(), bytes, noInit);
141 }
142 } else {
143 return DstBufferT::create(bytes, proto);
144 }
145}
146
147}// namespace detail
148
149/// @brief Deep-copies a grid handle into a different address space: the
150/// explicit, stream-carrying transfer between single-space device
151/// handles and host-readable handles (HostBuffer or a host-accessible
152/// single-space buffer such as a pinned-resource cuda::Buffer).
153/// @tparam DstBufferT destination buffer type (specify explicitly)
154/// @param src the handle to copy
155/// @param stream stream the copy is issued on; a device destination buffer
156/// with a stream-ordered resource retains it
157/// @warning Passing a stream other than the source buffer's retained stream
158/// makes the caller responsible for ordering: prior work on the
159/// source (and the source's later destruction, which frees on its
160/// own stream) must be ordered against @a stream by the caller,
161/// e.g. with cudaStreamWaitEvent or a synchronization. The
162/// stream-less overload below has no such requirement for a source
163/// with a retained stream. A source WITHOUT one (a synchronous
164/// resource, e.g. a pinned-resource buffer) is the caller's to keep
165/// alive under either overload: its destruction frees host memory
166/// immediately, unordered against the still-asynchronous copy, so
167/// synchronize @a stream before destroying such a source. (A
168/// pageable HostBuffer source is exempt: its copy degrades to
169/// synchronous behavior.)
170/// @param proto optional buffer whose resource (or pool, for buffers
171/// providing create()) allocates the destination storage; without it
172/// the destination resource is default-constructed
173/// @return a handle of the destination buffer type with equal contents; an
174/// empty @a src yields an empty handle, over @a proto's resource for
175/// a single-space destination when given
176/// @throw std::runtime_error if @a src is empty and DstBufferT is not
177/// default-constructible, unless it is single-space and @a proto is given
178/// @details A host-readable destination -- HostBuffer, pinned, or a
179/// both-space managed buffer -- synchronizes @c stream before
180/// returning, so its host accessors are immediately valid; a
181/// device-only destination is stream-ordered, so use its
182/// contents on @c stream or synchronize first. The destination
183/// reuses the source handle's metadata, which was checked when the
184/// source was made, so no kernel runs and this function is
185/// callable from host-only translation units. A
186/// pageable host source or destination (HostBuffer) degrades the
187/// copy to synchronous behavior; pinned single-space handles keep
188/// it asynchronous.
189template<typename DstBufferT, typename SrcBufferT>
190inline GridHandle<DstBufferT> copyTo(const GridHandle<SrcBufferT>& src, cudaStream_t stream, const DstBufferT* proto = nullptr)
191{
192 constexpr bool srcDev = BufferHasDeviceSingle<SrcBufferT>::value;
193 constexpr bool dstDev = BufferHasDeviceSingle<DstBufferT>::value;
194 static_assert(srcDev || dstDev,
195 "cuda::copyTo is for cross-space transfers involving a device buffer: use GridHandle::copy for host-to-host");
196 const uint64_t bytes = src.bufferSize();
197 if (bytes == 0u) {// an empty handle copies to an empty handle, over the prototype's resource where given
199 if (proto) return GridHandle<DstBufferT>::adopt(detail::makeTransferStorage<DstBufferT>(0, stream, proto), {});
200 }
201 if constexpr (std::is_default_constructible<DstBufferT>::value) {
202 return GridHandle<DstBufferT>();
203 } else {
204 throw std::runtime_error("cuda::copyTo: copying an empty handle into a buffer type that is not "
205 "default-constructible requires a single-space buffer type and a prototype buffer");
206 }
207 }
208 DstBufferT dst = detail::makeTransferStorage<DstBufferT>(bytes, stream, proto);
209 const void* srcPtr;
210 if constexpr (srcDev) srcPtr = src.deviceData();
211 else srcPtr = src.data();
212 cudaCheck(cudaMemcpyAsync(dst.data(), srcPtr, bytes, cudaMemcpyDefault, stream));
213 if constexpr (!dstDev || BufferHasHostSingle<DstBufferT>::value)
214 cudaCheck(cudaStreamSynchronize(stream)); // the host-readable result is the postcondition; covers the both-space (managed) destination, whose handle exposes host accessors immediately
215 // The copy reuses the source's layout unchecked, so no kernel runs here,
216 // which keeps this header host-includable. A device destination is
217 // stream-ordered: use it on @a stream, or synchronize.
218 return nanovdb::detail::LayoutCopy::make(std::move(dst), src);
219}// cuda::copyTo
220
221/// @brief Convenience overload issuing the copy on the source buffer's
222/// retained stream when it has one (any single-space source over a
223/// stream-ordered resource), the default stream otherwise.
224template<typename DstBufferT, typename SrcBufferT>
225inline GridHandle<DstBufferT> copyTo(const GridHandle<SrcBufferT>& src, const DstBufferT* proto = nullptr)
226{
227 cudaStream_t stream = 0;
228 if constexpr (BufferHasStream<SrcBufferT>::value) stream = src.buffer().stream();
229 return copyTo<DstBufferT>(src, stream, proto);
230}// cuda::copyTo (retained stream)
231
232
233}// namespace cuda
234
235}// namespace nanovdb
236
237#undef NANOVDB_CUDA_HANDLESTORAGE_DEFERS_GRIDHANDLE_CUH
238#if defined(__CUDACC__) && !defined(NANOVDB_TOOLS_CUDA_GRIDCHECKSUM_DEFERS_GRIDHANDLE_CUH)
239#include <nanovdb/cuda/GridHandle.cuh>
240#endif
241
242#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:164
BufferT & buffer()
Return a reference to the buffer.
Definition GridHandle.h:297
uint64_t bufferSize() const
Returns the size in bytes of the raw memory buffer managed by this GridHandle.
Definition GridHandle.h:324
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:316
static GridHandle adopt(BufferT &&buffer, std::vector< GridHandleMetaData > meta)
Constructs a handle over buffer from metadata the caller already knows to describe its contents,...
Definition GridHandle.h:213
void * data()
Returns a pointer to the host data; not available for a single-space device buffer,...
Definition GridHandle.h:307
Definition VoxToNanoVDB.h:15
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:105
void * deviceStorageData(BufferT &buffer)
The device address of a storage buffer made by createDeviceStorage: its one allocation is the device ...
Definition HandleStorage.h:71
BufferT createDeviceStorage(uint64_t bytes, const BufferT *proto, cudaStream_t stream)
Allocates bytes of device-resident storage of the single-space buffer type BufferT on the current dev...
Definition HandleStorage.h:46
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:120
typename std::enable_if<!IsStreamArgument< BufferT >::value, T >::type PrototypeFormResult
Return type of a tool's prototype-buffer form: T, except when BufferT deduced as a stream (cudaStream...
Definition HandleStorage.h:96
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:190
Defines a simple memory pool used to call cub functions that use dynamic temporary storage.
Definition GridHandle.h:34
Cuda specific utility functions.
#define cudaCheck(ans)
Definition Util.h:49
static constexpr bool value
Definition HostBuffer.h:111
static constexpr bool value
Definition HostBuffer.h:120
static constexpr bool value
Definition HostBuffer.h:137
True when BufferT is a stream rather than a buffer: cudaStream_t, or another non-buffer type that con...
Definition HandleStorage.h:81
static constexpr bool value
Definition HandleStorage.h:82
Detection trait: is_async_resource<R>::value is true iff R models the stream-ordered AsyncResource co...
Definition DeviceResource.h:113
static GridHandle< DstBufferT > make(DstBufferT &&buffer, const GridHandle< SrcBufferT > &src)
Definition GridHandle.h:513