OpenVDB 14.0.0
Loading...
Searching...
No Matches
Buffer.h
Go to the documentation of this file.
1// Copyright Contributors to the OpenVDB Project
2// SPDX-License-Identifier: Apache-2.0
3//
4/// @file nanovdb/cuda/Buffer.h
5///
6/// @brief Typed containers for CUDA memory: the owning, resource-aware,
7/// stream-ordered cuda::Buffer and the non-owning cuda::BufferView.
8
9#ifndef NANOVDB_CUDA_BUFFER_H_HAS_BEEN_INCLUDED
10#define NANOVDB_CUDA_BUFFER_H_HAS_BEEN_INCLUDED
11
12#include <cuda_runtime_api.h>
15
16#include <cstddef>
17#include <limits>
18#include <stdexcept>
19#include <type_traits>
20#include <utility>
21
22namespace nanovdb {
23
24namespace cuda {
25
26/// @brief Tag type selecting the Buffer constructors that skip element
27/// initialization, leaving the contents indeterminate.
28struct NoInit {};
29inline constexpr NoInit noInit{};
30
31namespace detail {
32
33/// @brief Conditional stream storage for Buffer. The async specialization
34/// retains the stream of the most recent allocation; the synchronous
35/// specialization is an empty base, so a Buffer over a synchronous
36/// resource carries no stream state and exposes no stream API.
37template<bool IsAsync>
38struct StreamHolder {};
39
40template<>
41struct StreamHolder<true> { cudaStream_t mStream = 0; };
42
43} // namespace detail
44
45/// @brief Owning, typed container of @c T elements allocated from a memory
46/// resource @c R held by value.
47/// @tparam T element type; sizes are expressed in elements, not bytes
48/// @tparam R memory resource, either stream-ordered (AsyncResource concept,
49/// see is_async_resource) or synchronous (Resource concept, see
50/// is_resource). When @c R provides both interfaces the stream-ordered
51/// one is used. The resource must provide operator==.
52/// @details With a stream-ordered resource the Buffer retains the stream of
53/// the most recent allocation (or the one supplied via set_stream)
54/// and orders its deallocation on that stream. Buffer is move-only.
55/// @note Cross-stream ordering is the caller's, expressed with ordinary CUDA
56/// events -- the buffer deliberately tracks nothing. To hand a buffer's
57/// contents to work on another stream (a consumer library, a wrapped
58/// tensor), record after the last write and make the consumer wait:
59/// @code
60/// cudaEvent_t ready;
61/// cudaEventCreateWithFlags(&ready, cudaEventDisableTiming);
62/// cudaEventRecord(ready, producerStream); // after the last write
63/// cudaStreamWaitEvent(consumerStream, ready); // before the first read
64/// @endcode
65/// and order the buffer's destruction (which frees on its retained
66/// stream) after all consumers the same way, or synchronize.
67template<typename T, typename R = DeviceResource>
68class Buffer : private detail::StreamHolder<is_async_resource<R>::value>
69{
71 "Buffer requires R to model the AsyncResource or the Resource concept");
73 "a memory resource needs operator==: add friend bool operator==(const R&, const R&), true when memory "
74 "allocated through one may be deallocated through the other (always, for a stateless resource), "
75 "and operator!= for cuda::mr under C++17");
76 static_assert(std::is_trivially_copyable<T>::value,
77 "Buffer requires a trivially copyable T: elements are copied bytewise and never constructed or destroyed");
78
79 static constexpr bool IsAsync = is_async_resource<R>::value;
80
81public:
82 /// @brief Element and resource types, for generic code that rebinds one
83 /// or constructs sibling buffers over the same resource.
84 using ElementType = T;
85 using ResourceType = R;
86
87 /// @brief Alias for a sibling buffer over the same resource with a
88 /// different element type.
89 template<typename U>
91
92private:
93 R mResource;
94 T* mData = nullptr;
95 size_t mSize = 0; // element count
96
97public:
98 /// @brief Default c-tor of an empty buffer; performs no allocation.
99 Buffer() = default;
100
101 /// @brief C-tor allocating @c count uninitialized elements, stream-ordered
102 /// on @c stream. Parameter order follows cuda::buffer:
103 /// (stream, resource, count, no_init).
104 /// @param stream cuda stream the allocation is ordered on
105 /// @param resource resource instance the buffer takes ownership of
106 /// @param count number of elements
107 template<typename S = R, std::enable_if_t<is_async_resource<S>::value, int> = 0>
108 explicit Buffer(cudaStream_t stream, R resource, size_t count, NoInit)
109 : detail::StreamHolder<true>{stream}
110 , mResource(std::move(resource))
111 {
112 this->allocate(count, stream);
113 }
114
115 /// @brief Convenience c-tor using a default-constructed resource.
116 /// @param stream cuda stream the allocation is ordered on
117 /// @param count number of elements
118 /// @note There is deliberately no count c-tor without NoInit: implicit
119 /// initialization of freshly allocated memory costs a hidden fill
120 /// pass that the dominant allocate-then-overwrite pattern wastes,
121 /// so initialization is always explicit (matching cuda::buffer,
122 /// whose count c-tor likewise requires cuda::no_init).
123 template<typename S = R, std::enable_if_t<is_async_resource<S>::value, int> = 0>
124 Buffer(cudaStream_t stream, size_t count, NoInit) : Buffer(stream, R(), count, noInit) {}
125
126 /// @brief C-tor allocating @c count uninitialized elements from a
127 /// synchronous resource: the stream-less analog of
128 /// (stream, resource, count, no_init).
129 /// @param resource resource instance the buffer takes ownership of
130 /// @param count number of elements
131 template<typename S = R, std::enable_if_t<!is_async_resource<S>::value && is_resource<S>::value, int> = 0>
132 explicit Buffer(R resource, size_t count, NoInit)
133 : mResource(std::move(resource))
134 {
135 this->allocate(count, cudaStream_t{0});
136 }
137
138 /// @brief Convenience c-tor using a default-constructed resource.
139 /// @param count number of elements
140 template<typename S = R, std::enable_if_t<!is_async_resource<S>::value && is_resource<S>::value, int> = 0>
141 Buffer(size_t count, NoInit) : Buffer(R(), count, noInit) {}
142
143 /// @brief Explicitly disallow copy construction and assignment operation
144 Buffer(const Buffer&) = delete;
145 Buffer& operator=(const Buffer&) = delete;
146
147 /// @brief Move c-tor; steals the allocation (and retained stream, if any)
148 /// and leaves @c other empty.
149 Buffer(Buffer&& other) noexcept
151 , mResource(std::move(other.mResource))
152 , mData(other.mData)
153 , mSize(other.mSize)
154 {
155 other.mData = nullptr;
156 other.mSize = 0;
157 }
158
159 /// @brief Move assignment; frees the current allocation first, then steals
160 /// from @c other and leaves it empty. Self-move is a no-op.
161 Buffer& operator=(Buffer&& other) noexcept
162 {
163 if (this != &other) {
164 this->destroy();
165 static_cast<detail::StreamHolder<IsAsync>&>(*this) = other;
166 mResource = std::move(other.mResource);
167 mData = other.mData;
168 mSize = other.mSize;
169 other.mData = nullptr;
170 other.mSize = 0;
171 }
172 return *this;
173 }
174
175 /// @brief Returns a deep copy of this buffer, allocated from a copy of the
176 /// resource; the allocation and element copy are ordered on @c stream,
177 /// which becomes the copy's retained stream.
178 /// @param stream cuda stream the allocation and element copy are ordered on
179 template<typename S = R, std::enable_if_t<is_async_resource<S>::value, int> = 0>
180 Buffer copy(cudaStream_t stream) const
181 {
182 Buffer out(stream, mResource, mSize, noInit);
183 if (mData) cudaCheck(cudaMemcpyAsync(out.mData, mData, this->size_bytes(), cudaMemcpyDefault, stream));
184 return out;
185 }
186
187 /// @brief Returns a deep copy of this buffer ordered on the retained
188 /// stream, i.e. copy(this->stream()).
189 template<typename S = R, std::enable_if_t<is_async_resource<S>::value, int> = 0>
190 Buffer copy() const { return this->copy(this->stream()); }
191
192 /// @brief Returns a deep copy of this buffer, allocated from a copy of the
193 /// synchronous resource.
194 template<typename S = R, std::enable_if_t<!is_async_resource<S>::value && is_resource<S>::value, int> = 0>
195 Buffer copy() const
196 {
197 Buffer out(mResource, mSize, noInit);
198 if (mData) cudaCheck(cudaMemcpy(out.mData, mData, this->size_bytes(), cudaMemcpyDefault));
199 return out;
200 }
201
202 /// @brief D-tor. A stream-ordered resource frees on the retained stream;
203 /// a synchronous resource frees immediately.
204 ~Buffer() { this->destroy(); }
205
206 /// @brief Returns the retained stream, i.e. the stream the buffer's memory
207 /// will be freed on.
208 template<typename S = R, std::enable_if_t<is_async_resource<S>::value, int> = 0>
209 cudaStream_t stream() const { return this->mStream; }
210
211 /// @brief Replaces the retained stream without synchronizing; subsequent
212 /// deallocation (and destruction) is ordered on @c stream instead.
213 /// @param stream cuda stream subsequent deallocation is ordered on
214 /// @warning The caller is responsible for ordering @c stream after any
215 /// in-flight work that uses the buffer's memory. This deliberately
216 /// does not synchronize, matching cuda::buffer's set_stream, which
217 /// avoids implicit synchronization in fundamental primitives.
218 template<typename S = R, std::enable_if_t<is_async_resource<S>::value, int> = 0>
219 void set_stream(cudaStream_t stream) { this->mStream = stream; }
220
221 /// @brief Resizes the buffer to @c count elements, preserving the leading
222 /// min(old, new) elements. Every operation — the new allocation, the
223 /// prefix copy, and the free of the old block — is ordered on
224 /// @c stream, which becomes the retained stream: the prefix copy is
225 /// the old block's last use, so that is the stream its free must be
226 /// ordered on.
227 /// @param stream cuda stream the reallocation is ordered on
228 /// @param count number of elements
229 /// @warning The caller is responsible for ordering @c stream after any
230 /// in-flight work on the previously retained stream that uses the
231 /// buffer's memory.
232 template<typename S = R, std::enable_if_t<is_async_resource<S>::value, int> = 0>
233 void resize(cudaStream_t stream, size_t count)
234 {
235 if (count != mSize) {
236 // No member is mutated until every throwing operation has
237 // succeeded, so a failed resize leaves the buffer untouched --
238 // including its retained stream.
239 T* newData = count ? static_cast<T*>(mResource.allocate_async(checkedBytes(count), R::DEFAULT_ALIGNMENT, stream))
240 : nullptr;
241 if (newData && mData) {
242 const size_t prefix = count < mSize ? count : mSize;
243 try {
244 cudaCheck(cudaMemcpyAsync(newData, mData, prefix * sizeof(T), cudaMemcpyDefault, stream));
245 }
246 catch (...) {
247 mResource.deallocate_async(newData, checkedBytes(count), R::DEFAULT_ALIGNMENT, stream);
248 throw;
249 }
250 }
251 this->mStream = stream;
252 this->deallocate(mData, mSize); // ordered on stream: after the prefix copy
253 mData = newData;
254 mSize = count;
255 }
256 else {
257 this->mStream = stream; // no reallocation: set_stream semantics
258 }
259 }
260
261 /// @brief Resizes the buffer to @c count elements through the synchronous
262 /// resource, preserving the leading min(old, new) elements.
263 /// @param count number of elements
264 template<typename S = R, std::enable_if_t<!is_async_resource<S>::value && is_resource<S>::value, int> = 0>
265 void resize(size_t count)
266 {
267 if (count == mSize) return;
268 // No member is mutated until every throwing operation has succeeded,
269 // so a failed resize leaves the buffer untouched.
270 T* newData = count ? static_cast<T*>(mResource.allocate(checkedBytes(count), R::DEFAULT_ALIGNMENT))
271 : nullptr;
272 if (newData && mData) {
273 const size_t prefix = count < mSize ? count : mSize;
274 try {
275 cudaCheck(cudaMemcpy(newData, mData, prefix * sizeof(T), cudaMemcpyDefault));
276 }
277 catch (...) {
278 mResource.deallocate(newData, checkedBytes(count), R::DEFAULT_ALIGNMENT);
279 throw;
280 }
281 }
282 this->deallocate(mData, mSize);
283 mData = newData;
284 mSize = count;
285 }
286
287 /// @brief Returns a pointer to the elements, or nullptr if empty.
288 T* data() { return mData; }
289 const T* data() const { return mData; }
290
291 /// @brief Returns a copy of the resource; for a ResourceRef this refers
292 /// to the same underlying instance.
293 /// @note Requires R to be copy-constructible (the cuda::mr convention:
294 /// resources are cheap handles). A resource that owns its pool by
295 /// value hands the caller an independent copy of that pool.
296 R resource() const { return mResource; }
297
298 /// @brief Returns the number of elements.
299 size_t size() const { return mSize; }
300
301 /// @brief Returns the size of the buffer's allocation in bytes.
302 size_t size_bytes() const { return mSize * sizeof(T); }
303
304 /// @brief Returns true if this buffer manages no memory.
305 bool empty() const { return mSize == 0; }
306
307 /// @brief Frees the buffer memory (if any) and resets to the empty state.
308 /// A stream-ordered resource frees on the retained stream.
309 /// @note Spelled destroy to match cuda::buffer. This is the name to use.
310 void destroy()
311 {
312 this->deallocate(mData, mSize);
313 mData = nullptr;
314 mSize = 0;
315 }
316
317 /// @brief Frees the buffer memory (if any) on @c stream and resets to the
318 /// empty state. @c stream becomes the retained stream.
319 /// @param stream cuda stream the deallocation is ordered on
320 /// @warning The caller is responsible for ordering @c stream after any
321 /// in-flight work that uses the buffer's memory.
322 template<typename S = R, std::enable_if_t<is_async_resource<S>::value, int> = 0>
323 void destroy(cudaStream_t stream)
324 {
325 this->mStream = stream;
326 this->destroy();
327 }
328
329 /// @brief Exchanges the contents of this buffer with @c other. Neither
330 /// buffer allocates, frees, or copies element data.
331 /// @param other buffer to exchange contents with
332 void swap(Buffer& other) noexcept
333 {
334 auto& lhs = static_cast<detail::StreamHolder<IsAsync>&>(*this);
335 auto& rhs = static_cast<detail::StreamHolder<IsAsync>&>(other);
336 std::swap(lhs, rhs);
337 std::swap(mResource, other.mResource);
338 std::swap(mData, other.mData);
339 std::swap(mSize, other.mSize);
340 }
341
342private:
343 /// @brief Returns @c count * sizeof(T), throwing std::runtime_error if the
344 /// byte size would exceed PTRDIFF_MAX, which also rejects a byte size
345 /// that would overflow size_t and a negative count converted to
346 /// size_t.
347 /// @param count number of elements
348 static size_t checkedBytes(size_t count)
349 {
350 if (count > size_t(std::numeric_limits<std::ptrdiff_t>::max()) / sizeof(T))
351 throw std::runtime_error("nanovdb::cuda::Buffer: element count exceeds the maximum buffer size");
352 return count * sizeof(T);
353 }
354
355 /// @brief Allocates @c count elements through the resource and records the
356 /// new extent. A zero count allocates nothing.
357 /// @param count number of elements
358 /// @param stream cuda stream the allocation is ordered on; used by the
359 /// stream-ordered form and ignored by the synchronous one
360 void allocate(size_t count, cudaStream_t stream)
361 {
362 if (count) {
363 if constexpr (IsAsync)
364 mData = static_cast<T*>(mResource.allocate_async(checkedBytes(count), R::DEFAULT_ALIGNMENT, stream));
365 else
366 mData = static_cast<T*>(mResource.allocate(checkedBytes(count), R::DEFAULT_ALIGNMENT));
367 }
368 mSize = count;
369 }
370
371 /// @brief Frees @c count elements at @c p through the resource; the
372 /// stream-ordered form frees on the retained stream. Null is a no-op.
373 /// @param p pointer to the elements to free
374 /// @param count number of elements
375 void deallocate(T* p, size_t count)
376 {
377 if (!p) return;
378 if constexpr (IsAsync)
379 mResource.deallocate_async(p, count * sizeof(T), R::DEFAULT_ALIGNMENT, this->mStream);
380 else
381 mResource.deallocate(p, count * sizeof(T), R::DEFAULT_ALIGNMENT);
382 }
383}; // Buffer<T, R> class
384
385/// @brief Non-owning, trivially copyable view of a contiguous range of @c T
386/// elements, with span semantics.
387/// @tparam T element type; spell constness in the element type
388/// (e.g. BufferView<const std::byte> is the read-only form), since
389/// const on the view itself is shallow.
390template<typename T>
392{
393 T* mData = nullptr;
394 size_t mSize = 0; // element count
395
396public:
397 /// @brief Default c-tor of an empty view.
398 BufferView() = default;
399
400 /// @brief C-tor viewing a contiguous range; the caller guarantees the
401 /// underlying storage outlives every use of the view.
402 /// @param data pointer to the first element
403 /// @param count number of elements
404 /// @throw std::runtime_error if @c data is null while @c count is non-zero.
405 BufferView(T* data, size_t count) : mData(data), mSize(count)
406 {
407 if (data == nullptr && count != 0)
408 throw std::runtime_error("BufferView: null data with a non-zero element count");
409 }
410
411 /// @brief Returns a pointer to the viewed elements, or nullptr if empty.
412 T* data() const { return mData; }
413
414 /// @brief Returns the number of viewed elements.
415 size_t size() const { return mSize; }
416
417 /// @brief Returns the size of the viewed range in bytes.
418 size_t size_bytes() const { return mSize * sizeof(T); }
419
420 /// @brief Returns true if this view references no elements.
421 bool empty() const { return mSize == 0; }
422
423 /// @brief Detaches the view (nulls the pointer and zeroes the size)
424 /// without touching the underlying storage -- the view is
425 /// non-owning, so "destroying" it releases nothing. This is the
426 /// one deliberate deviation from std::span, required by the buffer
427 /// static interface the handles consume through reset().
428 void destroy()
429 {
430 mData = nullptr;
431 mSize = 0;
432 }
433}; // BufferView<T> class
434
435} // namespace cuda
436
437// Primary template defined in HostBuffer.h; declared here so this header
438// stays self-contained without pulling in the host-buffer machinery.
439template<typename BufferT>
440struct BufferTraits;
441
442/// @brief GridHandle support for the single-space cuda::Buffer: the buffer
443/// owns exactly one allocation, resident on the device, so the handle
444/// parses metadata through a device read and exposes only the device
445/// accessors. Requires byte-addressed storage.
446/// @note This trait doubles as the definition of the single-space
447/// device-buffer concept: a buffer whose BufferTraits specialization
448/// sets hasDeviceSingle guarantees ElementType and ResourceType
449/// typedefs, data(), size() and size_bytes() (byte-addressed elements,
450/// enforced by the consumer), resource(), copy(), destroy(), and stream()
451/// when the resource is stream-ordered. Any consumer of hasDeviceSingle
452/// may rely on exactly this interface and nothing more; in particular,
453/// scratch allocates through resource() as a cuda::Buffer, so a
454/// conforming buffer never needs to be constructible by a consumer.
455template<typename T, typename R>
456struct BufferTraits<cuda::Buffer<T, R>>
457{
458 // Device-resident storage; the byte-addressed requirement is enforced by
459 // the single-space GridHandle constructor, so trait queries stay
460 // answerable for any element type.
463 // A buffer over a host-accessible resource (e.g. PinnedResource) is
464 // host-readable single-space storage: GridHandle parses its metadata on
465 // the host, exposes the host accessors, and allocates reads and copies
466 // through the buffer's resource. A resource that is host- AND
467 // device-accessible (ManagedResource) sets both members: the handle
468 // parses metadata through the device (a host parse could race producer
469 // kernels) and exposes both accessor families.
471};
472
473} // namespace nanovdb
474
475#endif // end of NANOVDB_CUDA_BUFFER_H_HAS_BEEN_INCLUDED
BufferView()=default
Default c-tor of an empty view.
size_t size() const
Returns the number of viewed elements.
Definition Buffer.h:415
T * data() const
Returns a pointer to the viewed elements, or nullptr if empty.
Definition Buffer.h:412
BufferView(T *data, size_t count)
C-tor viewing a contiguous range; the caller guarantees the underlying storage outlives every use of ...
Definition Buffer.h:405
void destroy()
Detaches the view (nulls the pointer and zeroes the size) without touching the underlying storage – t...
Definition Buffer.h:428
bool empty() const
Returns true if this view references no elements.
Definition Buffer.h:421
size_t size_bytes() const
Returns the size of the viewed range in bytes.
Definition Buffer.h:418
Buffer(cudaStream_t stream, size_t count, NoInit)
Convenience c-tor using a default-constructed resource.
Definition Buffer.h:124
T ElementType
Element and resource types, for generic code that rebinds one or constructs sibling buffers over the ...
Definition Buffer.h:84
R ResourceType
Definition Buffer.h:85
Buffer(const Buffer &)=delete
Explicitly disallow copy construction and assignment operation.
size_t size() const
Returns the number of elements.
Definition Buffer.h:299
Buffer()=default
Default c-tor of an empty buffer; performs no allocation.
void swap(Buffer &other) noexcept
Exchanges the contents of this buffer with other. Neither buffer allocates, frees,...
Definition Buffer.h:332
void destroy()
Frees the buffer memory (if any) and resets to the empty state. A stream-ordered resource frees on th...
Definition Buffer.h:310
cudaStream_t stream() const
Definition Buffer.h:209
Buffer< U, R > rebind
Alias for a sibling buffer over the same resource with a different element type.
Definition Buffer.h:90
const T * data() const
Definition Buffer.h:289
bool empty() const
Returns true if this buffer manages no memory.
Definition Buffer.h:305
Buffer(cudaStream_t stream, R resource, size_t count, NoInit)
C-tor allocating count uninitialized elements, stream-ordered on stream. Parameter order follows cuda...
Definition Buffer.h:108
Buffer copy(cudaStream_t stream) const
Returns a deep copy of this buffer, allocated from a copy of the resource; the allocation and element...
Definition Buffer.h:180
void destroy(cudaStream_t stream)
Frees the buffer memory (if any) on stream and resets to the empty state. stream becomes the retained...
Definition Buffer.h:323
R resource() const
Definition Buffer.h:296
Buffer(R resource, size_t count, NoInit)
C-tor allocating count uninitialized elements from a synchronous resource: the stream-less analog of ...
Definition Buffer.h:132
void resize(cudaStream_t stream, size_t count)
Resizes the buffer to count elements, preserving the leading min(old, new) elements....
Definition Buffer.h:233
Buffer copy() const
Returns a deep copy of this buffer ordered on the retained stream, i.e. copy(this->stream()).
Definition Buffer.h:190
void resize(size_t count)
Resizes the buffer to count elements through the synchronous resource, preserving the leading min(old...
Definition Buffer.h:265
Buffer(size_t count, NoInit)
Convenience c-tor using a default-constructed resource.
Definition Buffer.h:141
~Buffer()
D-tor. A stream-ordered resource frees on the retained stream; a synchronous resource frees immediate...
Definition Buffer.h:204
T * data()
Returns a pointer to the elements, or nullptr if empty.
Definition Buffer.h:288
void set_stream(cudaStream_t stream)
Replaces the retained stream without synchronizing; subsequent deallocation (and destruction) is orde...
Definition Buffer.h:219
Buffer(Buffer &&other) noexcept
Move c-tor; steals the allocation (and retained stream, if any) and leaves other empty.
Definition Buffer.h:149
Buffer & operator=(Buffer &&other) noexcept
Move assignment; frees the current allocation first, then steals from other and leaves it empty....
Definition Buffer.h:161
size_t size_bytes() const
Returns the size of the buffer's allocation in bytes.
Definition Buffer.h:302
Buffer & operator=(const Buffer &)=delete
Definition VoxToNanoVDB.h:15
Definition Buffer.h:24
constexpr NoInit noInit
Definition Buffer.h:29
Defines a simple memory pool used to call cub functions that use dynamic temporary storage.
Definition GridHandle.h:34
Definition Coord.h:590
Cuda specific utility functions.
#define cudaCheck(ans)
Definition Util.h:49
static constexpr bool hasHostSingle
Definition Buffer.h:470
static constexpr bool hasDeviceSingle
Definition Buffer.h:461
Definition HostBuffer.h:101
Tag type selecting the Buffer constructors that skip element initialization, leaving the contents ind...
Definition Buffer.h:28
cudaStream_t mStream
Definition Buffer.h:41
Conditional stream storage for Buffer. The async specialization retains the stream of the most recent...
Definition Buffer.h:38
Detection trait: is_async_resource<R>::value is true iff R models the stream-ordered AsyncResource co...
Definition DeviceResource.h:113
Companion detection: is_device_accessible_resource<R>::value is true iff R declares static constexpr ...
Definition DeviceResource.h:142
Detection trait: is_equality_comparable_resource<R>::value is true iff R models a resource concept an...
Definition DeviceResource.h:175
Detection trait: is_host_accessible_resource<R>::value is true iff R declares static constexpr bool H...
Definition DeviceResource.h:130
Detection trait: is_resource<R>::value is true iff R models the synchronous Resource concept,...
Definition DeviceResource.h:160