Coverage for cuda/core/texture/_array.pyx: 91.09%
247 statements
« prev ^ index » next coverage.py v7.16.0, created at 2026-09-10 02:27 +0000
« prev ^ index » next coverage.py v7.16.0, created at 2026-09-10 02:27 +0000
1# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2#
3# SPDX-License-Identifier: Apache-2.0
5from __future__ import annotations
7cimport cpython
8from libc.stdint cimport intptr_t
9from libc.string cimport memset
11from cuda.bindings cimport cydriver
12from cuda.core._context cimport Context
13from cuda.core._memory._buffer cimport Buffer, Buffer_check_open
14from cuda.core._resource_handles cimport (
15 OpaqueArrayHandle,
16 as_cu,
17 as_intptr,
18 create_array_handle,
19 create_array_handle_owning,
20 create_array_handle_ref,
21 get_last_error,
22)
23from cuda.core._stream cimport Stream, Stream_accept
24from cuda.core._utils.cuda_utils cimport (
25 HANDLE_RETURN,
26 _get_current_device_id,
27)
29import numpy
31from dataclasses import dataclass
33from cuda.core._utils.cuda_utils import check_or_create_options
34from cuda.core.typing import ArrayFormatType
37# Bridge between the public ArrayFormatType StrEnum and the driver
38# CUarray_format integer values. OpaqueArray stores the driver int internally
39# (see ._format), so all conversions funnel through these two maps.
40_ARRAYFORMAT_TO_CU = {
41 ArrayFormatType.UINT8: int(cydriver.CU_AD_FORMAT_UNSIGNED_INT8),
42 ArrayFormatType.UINT16: int(cydriver.CU_AD_FORMAT_UNSIGNED_INT16),
43 ArrayFormatType.UINT32: int(cydriver.CU_AD_FORMAT_UNSIGNED_INT32),
44 ArrayFormatType.INT8: int(cydriver.CU_AD_FORMAT_SIGNED_INT8),
45 ArrayFormatType.INT16: int(cydriver.CU_AD_FORMAT_SIGNED_INT16),
46 ArrayFormatType.INT32: int(cydriver.CU_AD_FORMAT_SIGNED_INT32),
47 ArrayFormatType.FLOAT16: int(cydriver.CU_AD_FORMAT_HALF),
48 ArrayFormatType.FLOAT32: int(cydriver.CU_AD_FORMAT_FLOAT),
49}
50_CU_TO_ARRAYFORMAT = {cu: fmt for fmt, cu in _ARRAYFORMAT_TO_CU.items()}
53# Every ArrayFormatType value is spelled as a NumPy dtype name, so the eight
54# formats map 1:1 to NumPy dtypes. This lets callers pass a dtype object (or
55# anything numpy.dtype() accepts) instead of the enum, matching the precedent
56# set by TensorMapDescriptorOptions.data_type.
57_NUMPY_DTYPE_TO_ARRAYFORMAT = {
58 numpy.dtype(fmt.value): fmt for fmt in ArrayFormatType
59}
62# Bytes per element (single channel), keyed by the driver CUarray_format int.
63_FORMAT_ELEM_SIZE = {
64 _ARRAYFORMAT_TO_CU[ArrayFormatType.UINT8]: 1,
65 _ARRAYFORMAT_TO_CU[ArrayFormatType.INT8]: 1,
66 _ARRAYFORMAT_TO_CU[ArrayFormatType.UINT16]: 2,
67 _ARRAYFORMAT_TO_CU[ArrayFormatType.INT16]: 2,
68 _ARRAYFORMAT_TO_CU[ArrayFormatType.FLOAT16]: 2,
69 _ARRAYFORMAT_TO_CU[ArrayFormatType.UINT32]: 4,
70 _ARRAYFORMAT_TO_CU[ArrayFormatType.INT32]: 4,
71 _ARRAYFORMAT_TO_CU[ArrayFormatType.FLOAT32]: 4,
72}
75def _normalize_array_format(format):
76 """Coerce ``format`` to an :class:`ArrayFormatType`.
78 Accepts, in order of preference:
80 * an :class:`ArrayFormatType`;
81 * a plain ``str`` naming one of its values (e.g. ``"float32"``);
82 * a NumPy dtype object (or anything ``numpy.dtype()`` accepts, such as
83 ``numpy.float32``) whose canonical dtype maps 1:1 to one of the eight
84 supported formats.
86 Raises :class:`ValueError` on anything else."""
87 if isinstance(format, ArrayFormatType): 1qwxyzAgoijklpamfedULKXMJbrNIOHcYVPQZ01234R5W678tB9Suv!C#T$GDEFns
88 return format 1qwxyzAgoamfedLKXMbrNIOHcYPQZ01234R5678tB9Suv!C#T$GDEFns
89 if isinstance(format, str): 1ijklpUJVW
90 try: 1p
91 return ArrayFormatType(format) 1p
92 except ValueError as e:
93 valid = ", ".join(repr(f.value) for f in ArrayFormatType)
94 raise ValueError(
95 f"format must be an ArrayFormatType or one of {{{valid}}}, got {format!r}"
96 ) from e
97 # Fall back to interpreting ``format`` as a NumPy dtype (dtype object,
98 # scalar type, etc.). Unknown dtypes are reported against the supported set.
99 try: 1ijklUJVW
100 dt = numpy.dtype(format) 1ijklUJVW
101 except TypeError as e: 1UVW
102 raise ValueError( 1UVW
103 f"format must be an ArrayFormatType, str, or NumPy dtype, got {format!r}" 1UVW
104 ) from e 1UVW
105 try: 1ijklJ
106 return _NUMPY_DTYPE_TO_ARRAYFORMAT[dt] 1ijklJ
107 except KeyError as e: 1J
108 valid = ", ".join(repr(f.value) for f in ArrayFormatType) 1J
109 raise ValueError( 1J
110 f"NumPy dtype {dt!r} has no ArrayFormatType equivalent; " 1J
111 f"supported formats: {{{valid}}}" 1J
112 ) from e 1J
115def _validate_format_channels(format, num_channels):
116 """Validate the ``(format, num_channels)`` pair shared by the array,
117 mipmap, and texture factories. Returns the normalized
118 :class:`ArrayFormatType`. Raises on an invalid combination."""
119 fmt = _normalize_array_format(format) 1qwxyzAgoijklpamfedULKXMJbrNIOHcYVPQZ01234R5W678tB9Suv!C#T$GDEFns
120 if isinstance(num_channels, bool) or num_channels not in (1, 2, 4): 1qwxyzAgoijklpamfedLKXMbrNIOHcYPQZ01234R5678tB9Suv!C#T$GDEFns
121 raise ValueError(f"num_channels must be 1, 2, or 4, got {num_channels!r}") 1XY05
122 return fmt 1qwxyzAgoijklpamfedLKMbrNIOHcPQZ1234R678tB9Suv!C#T$GDEFns
125def _validate_array_shape(shape):
126 """Coerce ``shape`` to a tuple of ints and validate rank (1-3) and that
127 every extent is >= 1. Returns the normalized tuple."""
128 try: 1qwxyzAgoijklpamfedLKMbrNIOHcPQRtBSuvCTGDEFns
129 shape_t = tuple(int(s) for s in shape) 1qwxyzAgoijklpamfedLKMbrNIOHcPQRtBSuvCTGDEFns
130 except TypeError as e: 1L
131 raise TypeError(f"shape must be a tuple of ints, got {type(shape).__name__}") from e 1L
132 if not 1 <= len(shape_t) <= 3: 1qwxyzAgoijklpamfedKMbrNIOHcPQRtBSuvCTGDEFns
133 raise ValueError(f"shape rank must be 1, 2, or 3, got {len(shape_t)}") 1hM
134 for i, dim in enumerate(shape_t): 1qwxyzAgoijklpamfedKbrNIOHcPQRtBSuvCTGDEFns
135 if dim < 1: 1qwxyzAgoijklpamfedKbrNIOHcPQRtBSuvCTGDEFns
136 raise ValueError(f"shape[{i}] must be >= 1, got {dim}") 1KP
137 return shape_t 1qwxyzAgoijklpamfedbrNIOHcQRtBSuvCTGDEFns
140@dataclass
141class OpaqueArrayOptions:
142 """Options for :meth:`cuda.core.Device.create_opaque_array`.
144 Attributes
145 ----------
146 shape : tuple of int
147 ``(width,)``, ``(width, height)``, or ``(width, height, depth)`` in
148 elements.
149 format : ArrayFormatType, str, or numpy.dtype
150 Element format. Accepts an :class:`~cuda.core.typing.ArrayFormatType`,
151 a plain string (e.g. ``"float32"``), or a NumPy dtype object.
152 num_channels : int
153 Channels per element. Must be 1, 2, or 4.
154 is_surface_load_store : bool
155 If True, allocate with ``CUDA_ARRAY3D_SURFACE_LDST`` so the array can be
156 bound as a :class:`~cuda.core.texture.SurfaceObject` for kernel-side
157 writes. Default False.
159 .. versionadded:: 1.1.0
160 """
162 shape: tuple[int, ...]
163 format: object
164 num_channels: int
165 is_surface_load_store: bool = False
167 def __post_init__(self):
168 self.format = _validate_format_channels(self.format, self.num_channels) 1qwxyzAgoijklpamfedULKXMJbrtBuvCGDEFns
169 self.shape = _validate_array_shape(self.shape) 1qwxyzAgoijklpamfedLKMbrtBuvCGDEFns
172cdef void _fill_array_endpoint(
173 cydriver.CUDA_MEMCPY3D* p, OpaqueArray arr, bint is_src
174) noexcept:
175 """Populate the src or dst array fields of a CUDA_MEMCPY3D struct."""
176 if is_src: 1afedbc
177 p.srcMemoryType = cydriver.CU_MEMORYTYPE_ARRAY 1afedbc
178 p.srcArray = as_cu(arr._handle) 1afedbc
179 p.srcXInBytes = 0 1afedbc
180 p.srcY = 0 1afedbc
181 p.srcZ = 0 1afedbc
182 else:
183 p.dstMemoryType = cydriver.CU_MEMORYTYPE_ARRAY 1abc
184 p.dstArray = as_cu(arr._handle) 1abc
185 p.dstXInBytes = 0 1abc
186 p.dstY = 0 1abc
187 p.dstZ = 0 1abc
190cdef int _fill_host_endpoint(
191 cydriver.CUDA_MEMCPY3D* p,
192 object obj,
193 bint is_src,
194 size_t width_bytes,
195 size_t height,
196 size_t required,
197 cpython.Py_buffer* pybuf_out,
198) except -1:
199 """Populate src/dst host fields from a buffer-protocol ``obj``.
201 Acquires a Py_buffer view; the caller is responsible for releasing it
202 (this function always returns with the view held when it returns 1).
203 """
204 cdef int flags = cpython.PyBUF_SIMPLE 1edbc
205 if not is_src: 1edbc
206 flags |= cpython.PyBUF_WRITABLE 1edbc
207 if cpython.PyObject_GetBuffer(obj, pybuf_out, flags) != 0: 1edbc
208 raise TypeError(
209 f"Source/destination must be a Buffer or a contiguous "
210 f"buffer-protocol object, got {type(obj).__name__}"
211 )
212 if <size_t>pybuf_out.len < required: 1edbc
213 cpython.PyBuffer_Release(pybuf_out) 1e
214 raise ValueError( 1e
215 f"Host buffer has {pybuf_out.len} bytes, smaller than the array " 1e
216 f"extent ({required} bytes)" 1e
217 )
218 if is_src: 1dbc
219 p.srcMemoryType = cydriver.CU_MEMORYTYPE_HOST 1bc
220 p.srcHost = pybuf_out.buf 1bc
221 p.srcPitch = width_bytes 1bc
222 p.srcHeight = height 1bc
223 p.srcXInBytes = 0 1bc
224 p.srcY = 0 1bc
225 p.srcZ = 0 1bc
226 else:
227 p.dstMemoryType = cydriver.CU_MEMORYTYPE_HOST 1dbc
228 p.dstHost = pybuf_out.buf 1dbc
229 p.dstPitch = width_bytes 1dbc
230 p.dstHeight = height 1dbc
231 p.dstXInBytes = 0 1dbc
232 p.dstY = 0 1dbc
233 p.dstZ = 0 1dbc
234 return 1 1dbc
237cdef int _fill_linear_endpoint(
238 cydriver.CUDA_MEMCPY3D* p,
239 object obj,
240 bint is_src,
241 size_t width_bytes,
242 size_t height,
243 size_t depth,
244 cpython.Py_buffer* pybuf_out,
245) except -1:
246 """Populate the src or dst linear fields. Returns 1 if pybuf_out was
247 filled (caller must release it), 0 otherwise.
248 """
249 cdef intptr_t ptr
250 cdef size_t required = width_bytes * height * depth 1afedbc
251 if isinstance(obj, Buffer): 1afedbc
252 Buffer_check_open(<Buffer>obj) 1af
253 if <size_t>(<Buffer>obj).size < required: 1af
254 raise ValueError( 1f
255 f"Buffer size ({(<Buffer>obj).size} bytes) is smaller than " 1f
256 f"the array extent ({required} bytes)" 1f
257 )
258 ptr = int((<Buffer>obj).handle) 1a
259 if is_src: 1a
260 p.srcMemoryType = cydriver.CU_MEMORYTYPE_DEVICE 1a
261 p.srcDevice = <cydriver.CUdeviceptr>ptr 1a
262 p.srcPitch = width_bytes 1a
263 p.srcHeight = height 1a
264 p.srcXInBytes = 0 1a
265 p.srcY = 0 1a
266 p.srcZ = 0 1a
267 else:
268 p.dstMemoryType = cydriver.CU_MEMORYTYPE_DEVICE 1a
269 p.dstDevice = <cydriver.CUdeviceptr>ptr 1a
270 p.dstPitch = width_bytes 1a
271 p.dstHeight = height 1a
272 p.dstXInBytes = 0 1a
273 p.dstY = 0 1a
274 p.dstZ = 0 1a
275 return 0 1a
276 return _fill_host_endpoint( 1edbc
277 p, obj, is_src, width_bytes, height, required, pybuf_out
278 )
281cdef _copy3d(OpaqueArray arr, object other, Stream stream, bint to_array):
282 """Issue a full-array async 3D memcpy between ``arr`` and ``other``.
284 Direction is determined by ``to_array``: True copies *into* arr, False
285 copies *out of* arr. ``stream`` must already be a concrete :class:`Stream`
286 (callers coerce via :func:`Stream_accept`).
287 """
288 cdef cydriver.CUDA_MEMCPY3D params
289 cdef cpython.Py_buffer pybuf
290 cdef int got_buffer = 0 1afedbc
291 cdef intptr_t stream_handle
292 cdef cydriver.CUstream c_stream
294 memset(¶ms, 0, sizeof(params)) 1afedbc
295 width_bytes, height, depth = arr._extent_bytes() 1afedbc
296 params.WidthInBytes = <size_t>width_bytes 1afedbc
297 params.Height = <size_t>height 1afedbc
298 params.Depth = <size_t>depth 1afedbc
300 try: 1afedbc
301 if to_array: 1afedbc
302 got_buffer = _fill_linear_endpoint( 1afebc
303 ¶ms, other, True, width_bytes, height, depth, &pybuf 1afebc
304 )
305 _fill_array_endpoint(¶ms, arr, False) 1abc
306 else:
307 _fill_array_endpoint(¶ms, arr, True) 1afedbc
308 got_buffer = _fill_linear_endpoint( 1afedbc
309 ¶ms, other, False, width_bytes, height, depth, &pybuf 1afedbc
310 )
312 stream_handle = int((<Stream>stream).handle) 1adbc
313 c_stream = <cydriver.CUstream><void*>stream_handle 1adbc
314 with nogil: 1adbc
315 HANDLE_RETURN(cydriver.cuMemcpy3DAsync(¶ms, c_stream)) 1adbc
316 finally:
317 if got_buffer: 1adbc
318 cpython.PyBuffer_Release(&pybuf) 1dbc
321cdef class OpaqueArray:
322 """An opaque, hardware-laid-out GPU allocation for texture/surface access.
324 Distinct from :class:`Buffer`: a ``CUarray`` has no exposed device pointer
325 and can only be accessed from kernels through a :class:`TextureObject` or
326 :class:`SurfaceObject`. Its memory layout is chosen by the driver for 2D/3D
327 spatial locality.
329 **Copy-only interop.** Because the layout is opaque and there is no linear
330 device pointer, a ``OpaqueArray`` cannot expose ``__cuda_array_interface__`` /
331 DLPack and cannot be shared zero-copy with NumPy, CuPy, numba-cuda, or
332 PyTorch. Moving data in or out is therefore always a copy: use
333 :meth:`copy_from` / :meth:`copy_to` against a linear :class:`Buffer` or a
334 host buffer-protocol object. There is no allocation helper — allocate the
335 linear :class:`Buffer` yourself (e.g. ``mr.allocate(arr.size_bytes,
336 stream=s)``) and copy.
338 Construct via :meth:`cuda.core.Device.create_opaque_array`. Only plain
339 1D/2D/3D allocations are supported in this initial version; layered/cubemap/
340 sparse variants will follow once their shape semantics are settled.
342 .. versionadded:: 1.1.0
343 """
345 def __init__(self, *args, **kwargs):
346 raise RuntimeError( 1%
347 "OpaqueArray cannot be instantiated directly. "
348 "Use Device.create_opaque_array()."
349 )
351 @classmethod
352 def _from_handle(cls, intptr_t handle, bint owning, *, device_id=None):
353 """Wrap an externally-allocated ``CUarray``.
355 Intended for graphics interop (``cuGraphicsSubResourceGetMappedArray``)
356 where the array is owned by the graphics API. With ``owning=False`` the
357 underlying ``CUarray`` is never destroyed by this object. Shape, format,
358 and channel count are queried from the driver.
359 """
360 cdef cydriver.CUarray raw = <cydriver.CUarray><void*>handle
361 cdef OpaqueArrayHandle h
362 if owning:
363 h = create_array_handle_owning(raw)
364 else:
365 h = create_array_handle_ref(raw)
366 cdef int dev = _get_current_device_id() if device_id is None else int(device_id)
367 return _array_from_handle(h, dev)
369 @property
370 def handle(self):
371 """The underlying ``CUarray`` as an integer."""
372 return as_intptr(self._handle) 1gHcs
374 @property
375 def is_closed(self) -> bool:
376 """Whether this array has been closed."""
377 return self._handle.get() == NULL 1r
379 @property
380 def shape(self):
381 """Allocation shape, in elements."""
382 return self._shape 1goIHc
384 @property
385 def format(self):
386 """The element :class:`~cuda.core.typing.ArrayFormatType`."""
387 return _CU_TO_ARRAYFORMAT[self._format] 1gijklpH
389 @property
390 def num_channels(self):
391 """Channels per element (1, 2, or 4)."""
392 return self._num_channels 1gH
394 @property
395 def element_bytes(self):
396 """Bytes per element (format size * channels)."""
397 return _FORMAT_ELEM_SIZE[self._format] * self._num_channels 1go
399 @property
400 def device(self):
401 """The :class:`Device` this array was allocated on."""
402 from cuda.core._device import Device 1gn
403 return Device(self._device_id) 1gn
405 @property
406 def is_surface_load_store(self):
407 """True if this array was created with ``CUDA_ARRAY3D_SURFACE_LDST``
408 and can be bound as a :class:`SurfaceObject`."""
409 return self._surface_load_store 1gotBuvs
411 def _extent_bytes(self):
412 """Return (width_bytes, height, depth) for cuMemcpy3D, with height/depth
413 normalized to >=1 for lower-rank arrays."""
414 cdef int rank = len(self._shape) 1afedbc
415 cdef size_t w = <size_t>self._shape[0] * <size_t>( 1afedbc
416 _FORMAT_ELEM_SIZE[self._format] * self._num_channels 1afedbc
417 )
418 cdef size_t h = <size_t>(self._shape[1] if rank >= 2 else 1) 1afedbc
419 cdef size_t d = <size_t>(self._shape[2] if rank >= 3 else 1) 1afedbc
420 return w, h, d 1afedbc
422 def copy_from(self, src, *, stream) -> None:
423 """Copy a full-array's worth of data into this array.
425 Parameters
426 ----------
427 src : Buffer or buffer-protocol object
428 Source data. Must contain at least ``self.size_bytes`` bytes
429 of contiguous data.
430 stream : Stream or GraphBuilder
431 Stream to issue the copy on. A :class:`~cuda.core.graph.GraphBuilder`
432 is accepted so the copy can be captured into a graph.
433 """
434 OpaqueArray_check_open(self) 1amfebrc
435 _copy3d(self, src, Stream_accept(stream), to_array=True) 1amfebc
437 def copy_to(self, dst, *, stream):
438 """Copy a full-array's worth of data out of this array.
440 Parameters
441 ----------
442 dst : Buffer or writable buffer-protocol object
443 Destination. Must have at least ``self.size_bytes`` bytes of
444 writable, contiguous space.
445 stream : Stream or GraphBuilder
446 Stream to issue the copy on. A :class:`~cuda.core.graph.GraphBuilder`
447 is accepted so the copy can be captured into a graph.
449 Returns
450 -------
451 The ``dst`` object, for parity with :meth:`Buffer.copy_to`.
452 """
453 OpaqueArray_check_open(self) 1amfedbc
454 _copy3d(self, dst, Stream_accept(stream), to_array=False) 1amfedbc
455 return dst 1adbc
457 @property
458 def size_bytes(self):
459 """Total bytes of array storage (``prod(shape) * element_bytes``)."""
460 cdef size_t n = 1 1ga
461 for s in self._shape: 1ga
462 n *= <size_t>s 1ga
463 return n * <size_t>(_FORMAT_ELEM_SIZE[self._format] * self._num_channels) 1ga
465 cpdef close(self):
466 """Release this object's reference to the underlying ``CUarray``.
468 Destruction (``cuArrayDestroy``) happens via the handle's deleter when
469 the last reference is dropped; for a non-owning handle (graphics interop
470 or a mipmap-level view) nothing is destroyed. Idempotent: a second call
471 (or destruction after ``close()``) is a no-op.
472 """
473 self._handle.reset() 1qwxyzAgoijklpamfedbrIHctuvCDEFns
475 def __enter__(self):
476 return self 1qn
478 def __exit__(self, exc_type, exc, tb):
479 self.close() 1qn
481 def __repr__(self):
482 return (
483 f"OpaqueArray(shape={self._shape}, "
484 f"format={_CU_TO_ARRAYFORMAT[self._format].name}, "
485 f"num_channels={self._num_channels})"
486 )
488cdef OpaqueArray _array_from_handle(OpaqueArrayHandle h, int device_id):
489 """Wrap an existing OpaqueArrayHandle as a OpaqueArray, querying the driver for the
490 array's shape/format/channels/surface-flag metadata.
492 Any owning/non-owning semantics and parent (mipmap) dependency are already
493 captured structurally inside ``h``'s C++ box.
494 """
495 if not h: 1IHc
496 HANDLE_RETURN(get_last_error())
498 cdef OpaqueArray self = OpaqueArray.__new__(OpaqueArray) 1IHc
499 self._handle = h 1IHc
500 self._device_id = device_id 1IHc
502 cdef cydriver.CUDA_ARRAY3D_DESCRIPTOR desc
503 cdef cydriver.CUarray raw = as_cu(h) 1IHc
504 with nogil: 1IHc
505 HANDLE_RETURN(cydriver.cuArray3DGetDescriptor(&desc, raw)) 1IHc
507 if desc.Depth > 0: 1IHc
508 self._shape = (int(desc.Width), int(desc.Height), int(desc.Depth))
509 elif desc.Height > 0: 1IHc
510 self._shape = (int(desc.Width), int(desc.Height)) 1IHc
511 else:
512 self._shape = (int(desc.Width),)
513 self._format = desc.Format 1IHc
514 self._num_channels = desc.NumChannels 1IHc
515 self._surface_load_store = bool(desc.Flags & cydriver.CUDA_ARRAY3D_SURFACE_LDST) 1IHc
516 return self 1IHc
519def _create_opaque_array(options, Context ctx, int device_id):
520 """Allocate a new :class:`OpaqueArray` on the specified device.
522 Backs :meth:`cuda.core.Device.create_opaque_array`. ``options`` is an
523 :class:`OpaqueArrayOptions` (or a mapping accepted by it); it is validated
524 at construction, so ``shape`` is already a normalized tuple and ``format``
525 an :class:`~cuda.core.typing.ArrayFormatType`.
526 """
527 cdef object opts = check_or_create_options( 1qwxyzAgoijklpamfedbrtBuvCGDEFns
528 OpaqueArrayOptions, options, "Opaque array options" 1qwxyzAgoijklpamfedbrtBuvCGDEFns
529 )
530 shape_t = opts.shape 1qwxyzAgoijklpamfedbrtBuvCGDEFns
532 cdef cydriver.CUarray_format c_format = <cydriver.CUarray_format>_ARRAYFORMAT_TO_CU[opts.format] 1qwxyzAgoijklpamfedbrtBuvCGDEFns
533 cdef int rank = len(shape_t) 1qwxyzAgoijklpamfedbrtBuvCGDEFns
534 cdef unsigned int flags = (
535 cydriver.CUDA_ARRAY3D_SURFACE_LDST if opts.is_surface_load_store else 0 1qwxyzAgoijklpamfedbrtBuvCGDEFns
536 )
538 # cuArray3DCreate handles 1D/2D/3D uniformly (Height/Depth 0 sentinels),
539 # so a single descriptor + create_array_handle covers every shape.
540 cdef cydriver.CUDA_ARRAY3D_DESCRIPTOR desc3d = cydriver.CUDA_ARRAY3D_DESCRIPTOR(
541 Width=<size_t>shape_t[0], 1qwxyzAgoijklpamfedbrtBuvCGDEFns
542 Height=<size_t>(shape_t[1] if rank >= 2 else 0), 1qwxyzAgoijklpamfedbrtBuvCGDEFns
543 Depth=<size_t>(shape_t[2] if rank >= 3 else 0), 1qwxyzAgoijklpamfedbrtBuvCGDEFns
544 Format=c_format, 1qwxyzAgoijklpamfedbrtBuvCGDEFns
545 NumChannels=<unsigned int>opts.num_channels, 1qwxyzAgoijklpamfedbrtBuvCGDEFns
546 Flags=flags, 1qwxyzAgoijklpamfedbrtBuvCGDEFns
547 )
549 cdef OpaqueArrayHandle h = create_array_handle(ctx._h_context, desc3d) 1qwxyzAgoijklpamfedbrtBuvCGDEFns
550 if not h: 1qwxyzAgoijklpamfedbrtBuvCGDEFns
551 HANDLE_RETURN(get_last_error())
553 cdef OpaqueArray self = OpaqueArray.__new__(OpaqueArray) 1qwxyzAgoijklpamfedbrtBuvCGDEFns
554 self._handle = h 1qwxyzAgoijklpamfedbrtBuvCGDEFns
555 self._shape = shape_t 1qwxyzAgoijklpamfedbrtBuvCGDEFns
556 self._format = c_format 1qwxyzAgoijklpamfedbrtBuvCGDEFns
557 self._num_channels = opts.num_channels 1qwxyzAgoijklpamfedbrtBuvCGDEFns
558 self._surface_load_store = bool(opts.is_surface_load_store) 1qwxyzAgoijklpamfedbrtBuvCGDEFns
559 self._device_id = device_id 1qwxyzAgoijklpamfedbrtBuvCGDEFns
560 return self 1qwxyzAgoijklpamfedbrtBuvCGDEFns