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

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._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) 

27  

28import numpy 

29  

30from dataclasses import dataclass 

31  

32from cuda.core._utils.cuda_utils import check_or_create_options 

33from cuda.core.typing import ArrayFormatType 

34  

35  

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

50  

51  

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} 

59  

60  

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} 

72  

73  

74def _normalize_array_format(format): 

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

76  

77 Accepts, in order of preference: 

78  

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. 

84  

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

112  

113  

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

122  

123  

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

137  

138  

139@dataclass 

140class OpaqueArrayOptions: 

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

142  

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. 

157  

158 .. versionadded:: 1.1.0 

159 """ 

160  

161 shape: tuple[int, ...] 

162 format: object 

163 num_channels: int 

164 is_surface_load_store: bool = False 

165  

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

169  

170  

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

187  

188  

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

199  

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

234  

235  

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 ) 

278  

279  

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

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

282  

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 

292  

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

298  

299 try: 1afedbc

300 if to_array: 1afedbc

301 got_buffer = _fill_linear_endpoint( 1afebc

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

303 ) 

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

305 else: 

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

307 got_buffer = _fill_linear_endpoint( 1afedbc

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

309 ) 

310  

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

315 finally: 

316 if got_buffer: 1adbc

317 cpython.PyBuffer_Release(&pybuf) 1dbc

318  

319  

320cdef class OpaqueArray: 

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

322  

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. 

327  

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. 

336  

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. 

340  

341 .. versionadded:: 1.1.0 

342 """ 

343  

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

345 raise RuntimeError( 1!

346 "OpaqueArray cannot be instantiated directly. " 

347 "Use Device.create_opaque_array()." 

348 ) 

349  

350 @classmethod 

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

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

353  

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) 

367  

368 @property 

369 def handle(self): 

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

371 return as_intptr(self._handle) 1gFcq

372  

373 @property 

374 def is_closed(self) -> bool: 

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

376 return self._handle.get() == NULL 1p

377  

378 @property 

379 def shape(self): 

380 """Allocation shape, in elements.""" 

381 return self._shape 1gnGFc

382  

383 @property 

384 def format(self): 

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

386 return _CU_TO_ARRAYFORMAT[self._format] 1gijkloF

387  

388 @property 

389 def num_channels(self): 

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

391 return self._num_channels 1gF

392  

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

397  

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

403  

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

409  

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

420  

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

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

423  

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

435  

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

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

438  

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. 

447  

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

455  

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

463  

464 cpdef close(self): 

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

466  

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

473  

474 def __enter__(self): 

475 return self 

476  

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

478 self.close() 

479  

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 ) 

486  

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. 

490  

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

496  

497 cdef OpaqueArray self = OpaqueArray.__new__(OpaqueArray) 1GFc

498 self._handle = h 1GFc

499 self._device_id = device_id 1GFc

500  

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

505  

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

516  

517  

518def _create_opaque_array(options): 

519 """Allocate a new :class:`OpaqueArray` on the current device. 

520  

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

530  

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 ) 

536  

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 ) 

547  

548 cdef OpaqueArrayHandle h = create_array_handle(desc3d) 1uvwxygnijkloamfedbprzstAEBCDq

549 if not h: 1uvwxygnijkloamfedbprzstAEBCDq

550 HANDLE_RETURN(get_last_error()) 

551  

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