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

1# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 

2# 

3# SPDX-License-Identifier: Apache-2.0 

4  

5from __future__ import annotations 

6  

7cimport cpython 

8from libc.stdint cimport intptr_t 

9from libc.string cimport memset 

10  

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) 

28  

29import numpy 

30  

31from dataclasses import dataclass 

32  

33from cuda.core._utils.cuda_utils import check_or_create_options 

34from cuda.core.typing import ArrayFormatType 

35  

36  

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()} 

51  

52  

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} 

60  

61  

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} 

73  

74  

75def _normalize_array_format(format): 

76 """Coerce ``format`` to an :class:`ArrayFormatType`. 

77  

78 Accepts, in order of preference: 

79  

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. 

85  

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

113  

114  

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

123  

124  

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

138  

139  

140@dataclass 

141class OpaqueArrayOptions: 

142 """Options for :meth:`cuda.core.Device.create_opaque_array`. 

143  

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. 

158  

159 .. versionadded:: 1.1.0 

160 """ 

161  

162 shape: tuple[int, ...] 

163 format: object 

164 num_channels: int 

165 is_surface_load_store: bool = False 

166  

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

170  

171  

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

188  

189  

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``. 

200  

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

235  

236  

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 ) 

279  

280  

281cdef _copy3d(OpaqueArray arr, object other, Stream stream, bint to_array): 

282 """Issue a full-array async 3D memcpy between ``arr`` and ``other``. 

283  

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 

293  

294 memset(&params, 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

299  

300 try: 1afedbc

301 if to_array: 1afedbc

302 got_buffer = _fill_linear_endpoint( 1afebc

303 &params, other, True, width_bytes, height, depth, &pybuf 1afebc

304 ) 

305 _fill_array_endpoint(&params, arr, False) 1abc

306 else: 

307 _fill_array_endpoint(&params, arr, True) 1afedbc

308 got_buffer = _fill_linear_endpoint( 1afedbc

309 &params, other, False, width_bytes, height, depth, &pybuf 1afedbc

310 ) 

311  

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(&params, c_stream)) 1adbc

316 finally: 

317 if got_buffer: 1adbc

318 cpython.PyBuffer_Release(&pybuf) 1dbc

319  

320  

321cdef class OpaqueArray: 

322 """An opaque, hardware-laid-out GPU allocation for texture/surface access. 

323  

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. 

328  

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. 

337  

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. 

341  

342 .. versionadded:: 1.1.0 

343 """ 

344  

345 def __init__(self, *args, **kwargs): 

346 raise RuntimeError( 1%

347 "OpaqueArray cannot be instantiated directly. " 

348 "Use Device.create_opaque_array()." 

349 ) 

350  

351 @classmethod 

352 def _from_handle(cls, intptr_t handle, bint owning, *, device_id=None): 

353 """Wrap an externally-allocated ``CUarray``. 

354  

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) 

368  

369 @property 

370 def handle(self): 

371 """The underlying ``CUarray`` as an integer.""" 

372 return as_intptr(self._handle) 1gHcs

373  

374 @property 

375 def is_closed(self) -> bool: 

376 """Whether this array has been closed.""" 

377 return self._handle.get() == NULL 1r

378  

379 @property 

380 def shape(self): 

381 """Allocation shape, in elements.""" 

382 return self._shape 1goIHc

383  

384 @property 

385 def format(self): 

386 """The element :class:`~cuda.core.typing.ArrayFormatType`.""" 

387 return _CU_TO_ARRAYFORMAT[self._format] 1gijklpH

388  

389 @property 

390 def num_channels(self): 

391 """Channels per element (1, 2, or 4).""" 

392 return self._num_channels 1gH

393  

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

398  

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

404  

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

410  

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

421  

422 def copy_from(self, src, *, stream) -> None: 

423 """Copy a full-array's worth of data into this array. 

424  

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

436  

437 def copy_to(self, dst, *, stream): 

438 """Copy a full-array's worth of data out of this array. 

439  

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. 

448  

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

456  

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

464  

465 cpdef close(self): 

466 """Release this object's reference to the underlying ``CUarray``. 

467  

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

474  

475 def __enter__(self): 

476 return self 1qn

477  

478 def __exit__(self, exc_type, exc, tb): 

479 self.close() 1qn

480  

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 ) 

487  

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. 

491  

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()) 

497  

498 cdef OpaqueArray self = OpaqueArray.__new__(OpaqueArray) 1IHc

499 self._handle = h 1IHc

500 self._device_id = device_id 1IHc

501  

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

506  

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

517  

518  

519def _create_opaque_array(options, Context ctx, int device_id): 

520 """Allocate a new :class:`OpaqueArray` on the specified device. 

521  

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

531  

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 ) 

537  

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 ) 

548  

549 cdef OpaqueArrayHandle h = create_array_handle(ctx._h_context, desc3d) 1qwxyzAgoijklpamfedbrtBuvCGDEFns

550 if not h: 1qwxyzAgoijklpamfedbrtBuvCGDEFns

551 HANDLE_RETURN(get_last_error()) 

552  

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