Coverage for cuda/core/texture/_array.pyx: 76.52%
247 statements
« prev ^ index » next coverage.py v7.16.2, created at 2026-10-16 01:23 +0000
« prev ^ index » next coverage.py v7.16.2, created at 2026-10-16 01:23 +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._rt 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): 1rxyzABhpijklqafmgedWMLNZOKnbsPJQIc0XRS123456T7Y89!uC#Uvw$D%V'HEFGot
88 return format 1rxyzABhpafmgedMLZOnbsPJQIc0RS123456T789!uC#Uvw$D%V'HEFGot
89 if isinstance(format, str): 1ijklqWNKXY
90 try: 1qN
91 return ArrayFormatType(format) 1qN
92 except ValueError as e: 1N
93 valid = ", ".join(repr(f.value) for f in ArrayFormatType) 1N
94 raise ValueError( 1N
95 f"format must be an ArrayFormatType or one of {{{valid}}}, got {format!r}" 1N
96 ) from e 1N
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: 1ijklWKXY
100 dt = numpy.dtype(format) 1ijklWKXY
101 except TypeError as e: 1WXY
102 raise ValueError( 1WXY
103 f"format must be an ArrayFormatType, str, or NumPy dtype, got {format!r}" 1WXY
104 ) from e 1WXY
105 try: 1ijklK
106 return _NUMPY_DTYPE_TO_ARRAYFORMAT[dt] 1ijklK
107 except KeyError as e: 1K
108 valid = ", ".join(repr(f.value) for f in ArrayFormatType) 1K
109 raise ValueError( 1K
110 f"NumPy dtype {dt!r} has no ArrayFormatType equivalent; " 1K
111 f"supported formats: {{{valid}}}" 1K
112 ) from e 1K
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) 1rxyzABhpijklqafmgedWMLNZOKnbsPJQIc0XRS123456T7Y89!uC#Uvw$D%V'HEFGot
120 if isinstance(num_channels, bool) or num_channels not in (1, 2, 4): 1rxyzABhpijklqafmgedMLZOnbsPJQIc0RS123456T789!uC#Uvw$D%V'HEFGot
121 raise ValueError(f"num_channels must be 1, 2, or 4, got {num_channels!r}") 1Z027
122 return fmt 1rxyzABhpijklqafmgedMLOnbsPJQIcRS13456T89!uC#Uvw$D%V'HEFGot
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: 1rxyzABhpijklqafmgedMLOnbsPJQIcRSTuCUvwDVHEFGot
129 shape_t = tuple(int(s) for s in shape) 1rxyzABhpijklqafmgedMLOnbsPJQIcRSTuCUvwDVHEFGot
130 except TypeError as e: 1M
131 raise TypeError(f"shape must be a tuple of ints, got {type(shape).__name__}") from e 1M
132 if not 1 <= len(shape_t) <= 3: 1rxyzABhpijklqafmgedLOnbsPJQIcRSTuCUvwDVHEFGot
133 raise ValueError(f"shape rank must be 1, 2, or 3, got {len(shape_t)}") 1O
134 for i, dim in enumerate(shape_t): 1rxyzABhpijklqafmgedLnbsPJQIcRSTuCUvwDVHEFGot
135 if dim < 1: 1rxyzABhpijklqafmgedLnbsPJQIcRSTuCUvwDVHEFGot
136 raise ValueError(f"shape[{i}] must be >= 1, got {dim}") 1LR
137 return shape_t 1rxyzABhpijklqafmgednbsPJQIcSTuCUvwDVHEFGot
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) 1rxyzABhpijklqafmgedWMLNZOKnbsuCvwDHEFGot
169 self.shape = _validate_array_shape(self.shape) 1rxyzABhpijklqafmgedMLOnbsuCvwDHEFGot
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: 1afgedbc
177 p.srcMemoryType = cydriver.CU_MEMORYTYPE_ARRAY 1afgedbc
178 p.srcArray = as_cu(arr._handle) 1afgedbc
179 p.srcXInBytes = 0 1afgedbc
180 p.srcY = 0 1afgedbc
181 p.srcZ = 0 1afgedbc
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 1fedbc
205 if not is_src: 1fedbc
206 flags |= cpython.PyBUF_WRITABLE 1fedbc
207 if cpython.PyObject_GetBuffer(obj, pybuf_out, flags) != 0: 1fedbc
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 1afgedbc
251 if isinstance(obj, Buffer): 1afgedbc
252 Buffer_check_open(<Buffer>obj) 1ag
253 if <size_t>(<Buffer>obj).size < required: 1ag
254 raise ValueError( 1g
255 f"Buffer size ({(<Buffer>obj).size} bytes) is smaller than " 1g
256 f"the array extent ({required} bytes)" 1g
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( 1fedbc
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 1afgedbc
291 cdef intptr_t stream_handle
292 cdef cydriver.CUstream c_stream
294 memset(¶ms, 0, sizeof(params)) 1afgedbc
295 width_bytes, height, depth = arr._extent_bytes() 1afgedbc
296 params.WidthInBytes = <size_t>width_bytes 1afgedbc
297 params.Height = <size_t>height 1afgedbc
298 params.Depth = <size_t>depth 1afgedbc
300 try: 1afgedbc
301 if to_array: 1afgedbc
302 got_buffer = _fill_linear_endpoint( 1afgebc
303 ¶ms, other, True, width_bytes, height, depth, &pybuf 1afgebc
304 )
305 _fill_array_endpoint(¶ms, arr, False) 1abc
306 else:
307 _fill_array_endpoint(¶ms, arr, True) 1afgedbc
308 got_buffer = _fill_linear_endpoint( 1afgedbc
309 ¶ms, other, False, width_bytes, height, depth, &pybuf 1afgedbc
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) 1hIct
374 @property
375 def is_closed(self) -> bool:
376 """Whether this array has been closed."""
377 return self._handle.get() == NULL 1s
379 @property
380 def shape(self):
381 """Allocation shape, in elements."""
382 return self._shape 1hpJIc
384 @property
385 def format(self):
386 """The element :class:`~cuda.core.typing.ArrayFormatType`."""
387 return _CU_TO_ARRAYFORMAT[self._format] 1hijklqI
389 @property
390 def num_channels(self):
391 """Channels per element (1, 2, or 4)."""
392 return self._num_channels 1hI
394 @property
395 def element_bytes(self):
396 """Bytes per element (format size * channels)."""
397 return _FORMAT_ELEM_SIZE[self._format] * self._num_channels 1hp
399 @property
400 def device(self):
401 """The :class:`Device` this array was allocated on."""
402 from cuda.core._device import Device 1ho
403 return Device(self._device_id) 1ho
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 1hpuCvwt
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) 1afgedbc
415 cdef size_t w = <size_t>self._shape[0] * <size_t>( 1afgedbc
416 _FORMAT_ELEM_SIZE[self._format] * self._num_channels 1afgedbc
417 )
418 cdef size_t h = <size_t>(self._shape[1] if rank >= 2 else 1) 1afgedbc
419 cdef size_t d = <size_t>(self._shape[2] if rank >= 3 else 1) 1afgedbc
420 return w, h, d 1afgedbc
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) 1afmgebsc
435 _copy3d(self, src, Stream_accept(stream), to_array=True) 1afmgebc
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) 1afmgedbc
454 _copy3d(self, dst, Stream_accept(stream), to_array=False) 1afmgedbc
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 1ha
461 for s in self._shape: 1ha
462 n *= <size_t>s 1ha
463 return n * <size_t>(_FORMAT_ELEM_SIZE[self._format] * self._num_channels) 1ha
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() 1rxyzABhpijklqafmgednbsJIcuvwDEFGot
475 def __enter__(self):
476 return self 1ro
478 def __exit__(self, exc_type, exc, tb):
479 self.close() 1ro
481 def __repr__(self):
482 return ( 1n
483 f"OpaqueArray(shape={self._shape}, " 1n
484 f"format={_CU_TO_ARRAYFORMAT[self._format].name}, " 1n
485 f"num_channels={self._num_channels})" 1n
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: 1JIc
496 HANDLE_RETURN(get_last_error())
498 cdef OpaqueArray self = OpaqueArray.__new__(OpaqueArray) 1JIc
499 self._handle = h 1JIc
500 self._device_id = device_id 1JIc
502 cdef cydriver.CUDA_ARRAY3D_DESCRIPTOR desc
503 cdef cydriver.CUarray raw = as_cu(h) 1JIc
504 with nogil: 1JIc
505 HANDLE_RETURN(cydriver.cuArray3DGetDescriptor(&desc, raw)) 1JIc
507 if desc.Depth > 0: 1JIc
508 self._shape = (int(desc.Width), int(desc.Height), int(desc.Depth))
509 elif desc.Height > 0: 1JIc
510 self._shape = (int(desc.Width), int(desc.Height)) 1JIc
511 else:
512 self._shape = (int(desc.Width),)
513 self._format = desc.Format 1JIc
514 self._num_channels = desc.NumChannels 1JIc
515 self._surface_load_store = bool(desc.Flags & cydriver.CUDA_ARRAY3D_SURFACE_LDST) 1JIc
516 return self 1JIc
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( 1rxyzABhpijklqafmgednbsuCvwDHEFGot
528 OpaqueArrayOptions, options, "Opaque array options" 1rxyzABhpijklqafmgednbsuCvwDHEFGot
529 )
530 shape_t = opts.shape 1rxyzABhpijklqafmgednbsuCvwDHEFGot
532 cdef cydriver.CUarray_format c_format = <cydriver.CUarray_format>_ARRAYFORMAT_TO_CU[opts.format] 1rxyzABhpijklqafmgednbsuCvwDHEFGot
533 cdef int rank = len(shape_t) 1rxyzABhpijklqafmgednbsuCvwDHEFGot
534 cdef unsigned int flags = (
535 cydriver.CUDA_ARRAY3D_SURFACE_LDST if opts.is_surface_load_store else 0 1rxyzABhpijklqafmgednbsuCvwDHEFGot
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], 1rxyzABhpijklqafmgednbsuCvwDHEFGot
542 Height=<size_t>(shape_t[1] if rank >= 2 else 0), 1rxyzABhpijklqafmgednbsuCvwDHEFGot
543 Depth=<size_t>(shape_t[2] if rank >= 3 else 0), 1rxyzABhpijklqafmgednbsuCvwDHEFGot
544 Format=c_format, 1rxyzABhpijklqafmgednbsuCvwDHEFGot
545 NumChannels=<unsigned int>opts.num_channels, 1rxyzABhpijklqafmgednbsuCvwDHEFGot
546 Flags=flags, 1rxyzABhpijklqafmgednbsuCvwDHEFGot
547 )
549 cdef OpaqueArrayHandle h = create_array_handle(ctx._h_context, desc3d) 1rxyzABhpijklqafmgednbsuCvwDHEFGot
550 if not h: 1rxyzABhpijklqafmgednbsuCvwDHEFGot
551 HANDLE_RETURN(get_last_error())
553 cdef OpaqueArray self = OpaqueArray.__new__(OpaqueArray) 1rxyzABhpijklqafmgednbsuCvwDHEFGot
554 self._handle = h 1rxyzABhpijklqafmgednbsuCvwDHEFGot
555 self._shape = shape_t 1rxyzABhpijklqafmgednbsuCvwDHEFGot
556 self._format = c_format 1rxyzABhpijklqafmgednbsuCvwDHEFGot
557 self._num_channels = opts.num_channels 1rxyzABhpijklqafmgednbsuCvwDHEFGot
558 self._surface_load_store = bool(opts.is_surface_load_store) 1rxyzABhpijklqafmgednbsuCvwDHEFGot
559 self._device_id = device_id 1rxyzABhpijklqafmgednbsuCvwDHEFGot
560 return self 1rxyzABhpijklqafmgednbsuCvwDHEFGot