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