Coverage for cuda/core/texture/_array.pyx: 90.16%
244 statements
« prev ^ index » next coverage.py v7.15.2, created at 2026-07-19 01:12 +0000
« prev ^ index » next coverage.py v7.15.2, created at 2026-07-19 01:12 +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
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): 1stuvwgmijklnbofedQHGTIFaJEKDcURLMVWXYZ0N1S234qx5Ory6P7CzABp
87 return format 1stuvwgmbofedHGTIaJEKDcULMVWXYZ0N1234qx5Ory6P7CzABp
88 if isinstance(format, str): 1ijklnQFRS
89 try: 1n
90 return ArrayFormatType(format) 1n
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: 1ijklQFRS
99 dt = numpy.dtype(format) 1ijklQFRS
100 except TypeError as e: 1QRS
101 raise ValueError( 1QRS
102 f"format must be an ArrayFormatType, str, or NumPy dtype, got {format!r}" 1QRS
103 ) from e 1QRS
104 try: 1ijklF
105 return _NUMPY_DTYPE_TO_ARRAYFORMAT[dt] 1ijklF
106 except KeyError as e: 1F
107 valid = ", ".join(repr(f.value) for f in ArrayFormatType) 1F
108 raise ValueError( 1F
109 f"NumPy dtype {dt!r} has no ArrayFormatType equivalent; " 1F
110 f"supported formats: {{{valid}}}" 1F
111 ) from e 1F
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) 1stuvwgmijklnbofedQHGTIFaJEKDcURLMVWXYZ0N1S234qx5Ory6P7CzABp
119 if isinstance(num_channels, bool) or num_channels not in (1, 2, 4): 1stuvwgmijklnbofedHGTIaJEKDcULMVWXYZ0N1234qx5Ory6P7CzABp
120 raise ValueError(f"num_channels must be 1, 2, or 4, got {num_channels!r}") 1TUW1
121 return fmt 1stuvwgmijklnbofedHGIaJEKDcLMVXYZ0N234qx5Ory6P7CzABp
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: 1stuvwgmijklnbofedHGIaJEKDcLMNqxOryPCzABp
128 shape_t = tuple(int(s) for s in shape) 1stuvwgmijklnbofedHGIaJEKDcLMNqxOryPCzABp
129 except TypeError as e: 1H
130 raise TypeError(f"shape must be a tuple of ints, got {type(shape).__name__}") from e 1H
131 if not 1 <= len(shape_t) <= 3: 1stuvwgmijklnbofedGIaJEKDcLMNqxOryPCzABp
132 raise ValueError(f"shape rank must be 1, 2, or 3, got {len(shape_t)}") 1I
133 for i, dim in enumerate(shape_t): 1hstuvwgmijklnbofedGaJEKDcLMNqxOryPCzABp
134 if dim < 1: 1stuvwgmijklnbofedGaJEKDcLMNqxOryPCzABp
135 raise ValueError(f"shape[{i}] must be >= 1, got {dim}") 1GL
136 return shape_t 1stuvwgmijklnbofedaJEKDcMNqxOryPCzABp
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) 1stuvwgmijklnbofedQHGTIFaqxryCzABp
168 self.shape = _validate_array_shape(self.shape) 1stuvwgmijklnbofedHGIaqxryCzABp
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: 1bfedac
176 p.srcMemoryType = cydriver.CU_MEMORYTYPE_ARRAY 1bfedac
177 p.srcArray = as_cu(arr._handle) 1bfedac
178 p.srcXInBytes = 0 1bfedac
179 p.srcY = 0 1bfedac
180 p.srcZ = 0 1bfedac
181 else:
182 p.dstMemoryType = cydriver.CU_MEMORYTYPE_ARRAY 1bac
183 p.dstArray = as_cu(arr._handle) 1bac
184 p.dstXInBytes = 0 1bac
185 p.dstY = 0 1bac
186 p.dstZ = 0 1bac
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 1edac
204 if not is_src: 1edac
205 flags |= cpython.PyBUF_WRITABLE 1edac
206 if cpython.PyObject_GetBuffer(obj, pybuf_out, flags) != 0: 1edac
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: 1edac
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: 1dac
218 p.srcMemoryType = cydriver.CU_MEMORYTYPE_HOST 1ac
219 p.srcHost = pybuf_out.buf 1ac
220 p.srcPitch = width_bytes 1ac
221 p.srcHeight = height 1ac
222 p.srcXInBytes = 0 1ac
223 p.srcY = 0 1ac
224 p.srcZ = 0 1ac
225 else:
226 p.dstMemoryType = cydriver.CU_MEMORYTYPE_HOST 1dac
227 p.dstHost = pybuf_out.buf 1dac
228 p.dstPitch = width_bytes 1dac
229 p.dstHeight = height 1dac
230 p.dstXInBytes = 0 1dac
231 p.dstY = 0 1dac
232 p.dstZ = 0 1dac
233 return 1 1dac
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 1bfedac
250 if isinstance(obj, Buffer): 1bfedac
251 if <size_t>(<Buffer>obj).size < required: 1bf
252 raise ValueError( 1f
253 f"Buffer size ({(<Buffer>obj).size} bytes) is smaller than " 1f
254 f"the array extent ({required} bytes)" 1f
255 )
256 ptr = int((<Buffer>obj).handle) 1b
257 if is_src: 1b
258 p.srcMemoryType = cydriver.CU_MEMORYTYPE_DEVICE 1b
259 p.srcDevice = <cydriver.CUdeviceptr>ptr 1b
260 p.srcPitch = width_bytes 1b
261 p.srcHeight = height 1b
262 p.srcXInBytes = 0 1b
263 p.srcY = 0 1b
264 p.srcZ = 0 1b
265 else:
266 p.dstMemoryType = cydriver.CU_MEMORYTYPE_DEVICE 1b
267 p.dstDevice = <cydriver.CUdeviceptr>ptr 1b
268 p.dstPitch = width_bytes 1b
269 p.dstHeight = height 1b
270 p.dstXInBytes = 0 1b
271 p.dstY = 0 1b
272 p.dstZ = 0 1b
273 return 0 1b
274 return _fill_host_endpoint( 1edac
275 p, obj, is_src, width_bytes, height, required, pybuf_out
276 )
279cdef _copy3d(OpaqueArray arr, object other, Stream stream, bint to_array):
280 """Issue a full-array async 3D memcpy between ``arr`` and ``other``.
282 Direction is determined by ``to_array``: True copies *into* arr, False
283 copies *out of* arr. ``stream`` must already be a concrete :class:`Stream`
284 (callers coerce via :func:`Stream_accept`).
285 """
286 cdef cydriver.CUDA_MEMCPY3D params
287 cdef cpython.Py_buffer pybuf
288 cdef int got_buffer = 0 1bfedac
289 cdef intptr_t stream_handle
290 cdef cydriver.CUstream c_stream
292 memset(¶ms, 0, sizeof(params)) 1bfedac
293 width_bytes, height, depth = arr._extent_bytes() 1bfedac
294 params.WidthInBytes = <size_t>width_bytes 1bfedac
295 params.Height = <size_t>height 1bfedac
296 params.Depth = <size_t>depth 1bfedac
298 try: 1bfedac
299 if to_array: 1bfedac
300 got_buffer = _fill_linear_endpoint( 1bfeac
301 ¶ms, other, True, width_bytes, height, depth, &pybuf 1bfeac
302 )
303 _fill_array_endpoint(¶ms, arr, False) 1bac
304 else:
305 _fill_array_endpoint(¶ms, arr, True) 1bfedac
306 got_buffer = _fill_linear_endpoint( 1bfedac
307 ¶ms, other, False, width_bytes, height, depth, &pybuf 1bfedac
308 )
310 stream_handle = int((<Stream>stream).handle) 1bdac
311 c_stream = <cydriver.CUstream><void*>stream_handle 1bdac
312 with nogil: 1bdac
313 HANDLE_RETURN(cydriver.cuMemcpy3DAsync(¶ms, c_stream)) 1bdac
314 finally:
315 if got_buffer: 1bdac
316 cpython.PyBuffer_Release(&pybuf) 1dac
319cdef class OpaqueArray:
320 """An opaque, hardware-laid-out GPU allocation for texture/surface access.
322 Distinct from :class:`Buffer`: a ``CUarray`` has no exposed device pointer
323 and can only be accessed from kernels through a :class:`TextureObject` or
324 :class:`SurfaceObject`. Its memory layout is chosen by the driver for 2D/3D
325 spatial locality.
327 **Copy-only interop.** Because the layout is opaque and there is no linear
328 device pointer, a ``OpaqueArray`` cannot expose ``__cuda_array_interface__`` /
329 DLPack and cannot be shared zero-copy with NumPy, CuPy, numba-cuda, or
330 PyTorch. Moving data in or out is therefore always a copy: use
331 :meth:`copy_from` / :meth:`copy_to` against a linear :class:`Buffer` or a
332 host buffer-protocol object. There is no allocation helper — allocate the
333 linear :class:`Buffer` yourself (e.g. ``mr.allocate(arr.size_bytes,
334 stream=s)``) and copy.
336 Construct via :meth:`cuda.core.Device.create_opaque_array`. Only plain
337 1D/2D/3D allocations are supported in this initial version; layered/cubemap/
338 sparse variants will follow once their shape semantics are settled.
340 .. versionadded:: 1.1.0
341 """
343 def __init__(self, *args, **kwargs):
344 raise RuntimeError( 18
345 "OpaqueArray cannot be instantiated directly. "
346 "Use Device.create_opaque_array()."
347 )
349 @classmethod
350 def _from_handle(cls, intptr_t handle, bint owning, *, device_id=None):
351 """Wrap an externally-allocated ``CUarray``.
353 Intended for graphics interop (``cuGraphicsSubResourceGetMappedArray``)
354 where the array is owned by the graphics API. With ``owning=False`` the
355 underlying ``CUarray`` is never destroyed by this object. Shape, format,
356 and channel count are queried from the driver.
357 """
358 cdef cydriver.CUarray raw = <cydriver.CUarray><void*>handle
359 cdef OpaqueArrayHandle h
360 if owning:
361 h = create_array_handle_owning(raw)
362 else:
363 h = create_array_handle_ref(raw)
364 cdef int dev = _get_current_device_id() if device_id is None else int(device_id)
365 return _array_from_handle(h, dev)
367 @property
368 def handle(self):
369 """The underlying ``CUarray`` as an integer."""
370 return as_intptr(self._handle) 1gDcp
372 @property
373 def shape(self):
374 """Allocation shape, in elements."""
375 return self._shape 1gmEDc
377 @property
378 def format(self):
379 """The element :class:`~cuda.core.typing.ArrayFormatType`."""
380 return _CU_TO_ARRAYFORMAT[self._format] 1gijklnD
382 @property
383 def num_channels(self):
384 """Channels per element (1, 2, or 4)."""
385 return self._num_channels 1gD
387 @property
388 def element_bytes(self):
389 """Bytes per element (format size * channels)."""
390 return _FORMAT_ELEM_SIZE[self._format] * self._num_channels 1gm
392 @property
393 def device(self):
394 """The :class:`Device` this array was allocated on."""
395 from cuda.core._device import Device 1g
396 return Device(self._device_id) 1g
398 @property
399 def is_surface_load_store(self):
400 """True if this array was created with ``CUDA_ARRAY3D_SURFACE_LDST``
401 and can be bound as a :class:`SurfaceObject`."""
402 return self._surface_load_store 1gmqxrp
404 def _extent_bytes(self):
405 """Return (width_bytes, height, depth) for cuMemcpy3D, with height/depth
406 normalized to >=1 for lower-rank arrays."""
407 cdef int rank = len(self._shape) 1bfedac
408 cdef size_t w = <size_t>self._shape[0] * <size_t>( 1bfedac
409 _FORMAT_ELEM_SIZE[self._format] * self._num_channels 1bfedac
410 )
411 cdef size_t h = <size_t>(self._shape[1] if rank >= 2 else 1) 1bfedac
412 cdef size_t d = <size_t>(self._shape[2] if rank >= 3 else 1) 1bfedac
413 return w, h, d 1bfedac
415 def copy_from(self, src, *, stream) -> None:
416 """Copy a full-array's worth of data into this array.
418 Parameters
419 ----------
420 src : Buffer or buffer-protocol object
421 Source data. Must contain at least ``self.size_bytes`` bytes
422 of contiguous data.
423 stream : Stream or GraphBuilder
424 Stream to issue the copy on. A :class:`~cuda.core.graph.GraphBuilder`
425 is accepted so the copy can be captured into a graph.
426 """
427 _copy3d(self, src, Stream_accept(stream), to_array=True) 1bofeac
429 def copy_to(self, dst, *, stream):
430 """Copy a full-array's worth of data out of this array.
432 Parameters
433 ----------
434 dst : Buffer or writable buffer-protocol object
435 Destination. Must have at least ``self.size_bytes`` bytes of
436 writable, contiguous space.
437 stream : Stream or GraphBuilder
438 Stream to issue the copy on. A :class:`~cuda.core.graph.GraphBuilder`
439 is accepted so the copy can be captured into a graph.
441 Returns
442 -------
443 The ``dst`` object, for parity with :meth:`Buffer.copy_to`.
444 """
445 _copy3d(self, dst, Stream_accept(stream), to_array=False) 1bofedac
446 return dst 1bdac
448 @property
449 def size_bytes(self):
450 """Total bytes of array storage (``prod(shape) * element_bytes``)."""
451 cdef size_t n = 1 1gb
452 for s in self._shape: 1gb
453 n *= <size_t>s 1gb
454 return n * <size_t>(_FORMAT_ELEM_SIZE[self._format] * self._num_channels) 1gb
456 cpdef close(self):
457 """Release this object's reference to the underlying ``CUarray``.
459 Destruction (``cuArrayDestroy``) happens via the handle's deleter when
460 the last reference is dropped; for a non-owning handle (graphics interop
461 or a mipmap-level view) nothing is destroyed. Idempotent: a second call
462 (or destruction after ``close()``) is a no-op.
463 """
464 self._handle.reset() 1stuvwgmijklnbofedaEDcqryzABp
466 def __enter__(self):
467 return self
469 def __exit__(self, exc_type, exc, tb):
470 self.close()
472 def __repr__(self):
473 return (
474 f"OpaqueArray(shape={self._shape}, "
475 f"format={_CU_TO_ARRAYFORMAT[self._format].name}, "
476 f"num_channels={self._num_channels})"
477 )
480cdef OpaqueArray _array_from_handle(OpaqueArrayHandle h, int device_id):
481 """Wrap an existing OpaqueArrayHandle as a OpaqueArray, querying the driver for the
482 array's shape/format/channels/surface-flag metadata.
484 Any owning/non-owning semantics and parent (mipmap) dependency are already
485 captured structurally inside ``h``'s C++ box.
486 """
487 if not h: 1EDc
488 HANDLE_RETURN(get_last_error())
490 cdef OpaqueArray self = OpaqueArray.__new__(OpaqueArray) 1EDc
491 self._handle = h 1EDc
492 self._device_id = device_id 1EDc
494 cdef cydriver.CUDA_ARRAY3D_DESCRIPTOR desc
495 cdef cydriver.CUarray raw = as_cu(h) 1EDc
496 with nogil: 1EDc
497 HANDLE_RETURN(cydriver.cuArray3DGetDescriptor(&desc, raw)) 1EDc
499 if desc.Depth > 0: 1EDc
500 self._shape = (int(desc.Width), int(desc.Height), int(desc.Depth))
501 elif desc.Height > 0: 1EDc
502 self._shape = (int(desc.Width), int(desc.Height)) 1EDc
503 else:
504 self._shape = (int(desc.Width),)
505 self._format = desc.Format 1EDc
506 self._num_channels = desc.NumChannels 1EDc
507 self._surface_load_store = bool(desc.Flags & cydriver.CUDA_ARRAY3D_SURFACE_LDST) 1EDc
508 return self 1EDc
511def _create_opaque_array(options):
512 """Allocate a new :class:`OpaqueArray` on the current device.
514 Backs :meth:`cuda.core.Device.create_opaque_array`. ``options`` is an
515 :class:`OpaqueArrayOptions` (or a mapping accepted by it); it is validated
516 at construction, so ``shape`` is already a normalized tuple and ``format``
517 an :class:`~cuda.core.typing.ArrayFormatType`.
518 """
519 cdef object opts = check_or_create_options( 1stuvwgmijklnbofedaqxryCzABp
520 OpaqueArrayOptions, options, "Opaque array options" 1stuvwgmijklnbofedaqxryCzABp
521 )
522 shape_t = opts.shape 1stuvwgmijklnbofedaqxryCzABp
524 cdef cydriver.CUarray_format c_format = <cydriver.CUarray_format>_ARRAYFORMAT_TO_CU[opts.format] 1stuvwgmijklnbofedaqxryCzABp
525 cdef cydriver.CUDA_ARRAY3D_DESCRIPTOR desc3d
526 cdef int rank = len(shape_t) 1stuvwgmijklnbofedaqxryCzABp
527 cdef unsigned int flags = (
528 cydriver.CUDA_ARRAY3D_SURFACE_LDST if opts.is_surface_load_store else 0 1stuvwgmijklnbofedaqxryCzABp
529 )
531 # cuArray3DCreate handles 1D/2D/3D uniformly (Height/Depth 0 sentinels),
532 # so a single descriptor + create_array_handle covers every shape.
533 memset(&desc3d, 0, sizeof(desc3d)) 1stuvwgmijklnbofedaqxryCzABp
534 desc3d.Width = <size_t>shape_t[0] 1stuvwgmijklnbofedaqxryCzABp
535 desc3d.Height = <size_t>(shape_t[1] if rank >= 2 else 0) 1stuvwgmijklnbofedaqxryCzABp
536 desc3d.Depth = <size_t>(shape_t[2] if rank >= 3 else 0) 1stuvwgmijklnbofedaqxryCzABp
537 desc3d.Format = c_format 1stuvwgmijklnbofedaqxryCzABp
538 desc3d.NumChannels = <unsigned int>opts.num_channels 1stuvwgmijklnbofedaqxryCzABp
539 desc3d.Flags = flags 1stuvwgmijklnbofedaqxryCzABp
541 cdef OpaqueArrayHandle h = create_array_handle(desc3d) 1stuvwgmijklnbofedaqxryCzABp
542 if not h: 1stuvwgmijklnbofedaqxryCzABp
543 HANDLE_RETURN(get_last_error())
545 cdef OpaqueArray self = OpaqueArray.__new__(OpaqueArray) 1stuvwgmijklnbofedaqxryCzABp
546 self._handle = h 1stuvwgmijklnbofedaqxryCzABp
547 self._shape = shape_t 1stuvwgmijklnbofedaqxryCzABp
548 self._format = c_format 1stuvwgmijklnbofedaqxryCzABp
549 self._num_channels = opts.num_channels 1stuvwgmijklnbofedaqxryCzABp
550 self._surface_load_store = bool(opts.is_surface_load_store) 1stuvwgmijklnbofedaqxryCzABp
551 self._device_id = _get_current_device_id() 1stuvwgmijklnbofedaqxryCzABp
552 return self 1stuvwgmijklnbofedaqxryCzABp