Coverage for cuda/core/texture/_texture.pyx: 79.08%

306 statements  

« prev     ^ index     » next       coverage.py v7.17.2, created at 2026-11-05 01:11 +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  

7from libc.stdint cimport intptr_t 

8from libc.string cimport memset 

9  

10from cuda.bindings cimport cydriver 

11from cuda.core._context cimport Context 

12from cuda.core.texture._array cimport OpaqueArray, OpaqueArray_check_open 

13from cuda.core.texture._array import ( 

14 _ARRAYFORMAT_TO_CU, 

15 _CU_TO_ARRAYFORMAT, 

16 _FORMAT_ELEM_SIZE, 

17 _validate_format_channels, 

18) 

19from cuda.core._memory._buffer cimport Buffer, Buffer_check_open 

20from cuda.core.texture._mipmapped_array cimport MipmappedArray, MipmappedArray_check_open 

21from cuda.core.texture._mipmapped_array import MipmappedArray as _PyMipmappedArray 

22from cuda.core._rt cimport ( 

23 ContextHandle, 

24 TexObjectHandle, 

25 as_cu, 

26 as_intptr, 

27 create_tex_object_handle_array, 

28 create_tex_object_handle_linear, 

29 create_tex_object_handle_mipmap, 

30 get_array_context, 

31 get_last_error, 

32 get_mipmapped_array_context, 

33) 

34from cuda.core._utils.cuda_utils cimport HANDLE_RETURN 

35  

36from cuda.core.typing import AddressModeType, FilterModeType, ReadModeType 

37  

38from dataclasses import dataclass 

39  

40from cuda.core._utils.cuda_utils import check_or_create_options 

41  

42  

43# Driver texture-descriptor flag bits (CU_TRSF_*). 

44_TRSF_READ_AS_INTEGER = 0x01 

45_TRSF_NORMALIZED_COORDINATES = 0x02 

46_TRSF_SRGB = 0x10 

47_TRSF_DISABLE_TRILINEAR_OPTIMIZATION = 0x20 

48_TRSF_SEAMLESS_CUBEMAP = 0x40 

49  

50  

51# Bridge between the public sampling StrEnums and the driver integer values. 

52_ADDRESSMODE_TO_CU = { 

53 AddressModeType.WRAP: int(cydriver.CU_TR_ADDRESS_MODE_WRAP), 

54 AddressModeType.CLAMP: int(cydriver.CU_TR_ADDRESS_MODE_CLAMP), 

55 AddressModeType.MIRROR: int(cydriver.CU_TR_ADDRESS_MODE_MIRROR), 

56 AddressModeType.BORDER: int(cydriver.CU_TR_ADDRESS_MODE_BORDER), 

57} 

58_FILTERMODE_TO_CU = { 

59 FilterModeType.POINT: int(cydriver.CU_TR_FILTER_MODE_POINT), 

60 FilterModeType.LINEAR: int(cydriver.CU_TR_FILTER_MODE_LINEAR), 

61} 

62  

63  

64def _normalize_enum(name, value, enum_type): 

65 """Coerce ``value`` to ``enum_type`` (a StrEnum), accepting a plain str.""" 

66 if isinstance(value, enum_type): 1vespturdjgbhcfakmQJMnPil

67 return value 1vespturdjgbhcfakmJMnPil

68 try: 1pQJM

69 return enum_type(value) 1pQJM

70 except ValueError as e: 1pQJM

71 valid = ", ".join(repr(m.value) for m in enum_type) 1pQJM

72 raise ValueError( 1pQJM

73 f"{name} must be a {enum_type.__name__} or one of {{{valid}}}, got {value!r}" 1pQJM

74 ) from e 1pQJM

75  

76  

77class ResourceDescriptor: 

78 """Describes the memory backing a :class:`TextureObject`. 

79  

80 Construct via the ``from_*`` classmethods: 

81  

82 - :meth:`from_opaque_array` wraps a :class:`OpaqueArray` (works for both 

83 :class:`TextureObject` and :class:`SurfaceObject`). 

84 - :meth:`from_mipmapped_array` wraps a :class:`MipmappedArray` for mipmapped 

85 sampling (texture only, not surface). 

86 - :meth:`from_linear` wraps a :class:`Buffer` as a typed 1D fetch. Texture 

87 objects built from a linear resource do not support filtering, 

88 normalized coordinates, or addressing modes. 

89 - :meth:`from_pitch2d` wraps a :class:`Buffer` as a row-pitched 2D image. 

90 Supports filtering and 2D addressing, but only 2D access. 

91  

92 Linear and pitch2D resources cannot back a :class:`SurfaceObject` — those 

93 require an :class:`OpaqueArray` allocated with ``is_surface_load_store=True``. 

94  

95 .. versionadded:: 1.1.0 

96 """ 

97  

98 __slots__ = ( 

99 "_kind", "_source", 

100 "_format", "_num_channels", 

101 "_size_bytes", 

102 "_width", "_height", "_pitch_bytes", 

103 ) 

104  

105 def __init__(self): 

106 raise RuntimeError( 1X

107 "ResourceDescriptor cannot be instantiated directly. " 

108 "Use ResourceDescriptor.from_* factories." 

109 ) 

110  

111 @classmethod 

112 def from_opaque_array(cls, array): 

113 """Build a resource descriptor backed by a :class:`OpaqueArray`.""" 

114 if not isinstance(array, OpaqueArray): 1vespturyzCdDEFjghkmAnil

115 raise TypeError(f"array must be a OpaqueArray, got {type(array).__name__}") 

116 OpaqueArray_check_open(<OpaqueArray>array) 1vespturyzCdDEFjghkmAnil

117 self = cls.__new__(cls) 1vespturyzCdDEFjghkmAnil

118 self._kind = "array" 1vespturyzCdDEFjghkmAnil

119 self._source = array 1vespturyzCdDEFjghkmAnil

120 self._format = None 1vespturyzCdDEFjghkmAnil

121 self._num_channels = None 1vespturyzCdDEFjghkmAnil

122 self._size_bytes = None 1vespturyzCdDEFjghkmAnil

123 self._width = None 1vespturyzCdDEFjghkmAnil

124 self._height = None 1vespturyzCdDEFjghkmAnil

125 self._pitch_bytes = None 1vespturyzCdDEFjghkmAnil

126 return self 1vespturyzCdDEFjghkmAnil

127  

128 @classmethod 

129 def from_mipmapped_array(cls, mipmapped_array): 

130 """Build a resource descriptor backed by a :class:`MipmappedArray`. 

131  

132 Suitable for binding to a :class:`TextureObject` for mipmapped 

133 sampling. Not valid as a :class:`SurfaceObject` backing: surfaces 

134 require a single :class:`OpaqueArray` level (obtain via 

135 :meth:`MipmappedArray.get_level`). 

136 """ 

137 if not isinstance(mipmapped_array, _PyMipmappedArray): 1rRGKf

138 raise TypeError( 1R

139 f"mipmapped_array must be a MipmappedArray, got " 1R

140 f"{type(mipmapped_array).__name__}" 1R

141 ) 

142 MipmappedArray_check_open(<MipmappedArray>mipmapped_array) 1rGKf

143 self = cls.__new__(cls) 1rGKf

144 self._kind = "mipmapped_array" 1rGKf

145 self._source = mipmapped_array 1rGKf

146 self._format = None 1rGKf

147 self._num_channels = None 1rGKf

148 self._size_bytes = None 1rGKf

149 self._width = None 1rGKf

150 self._height = None 1rGKf

151 self._pitch_bytes = None 1rGKf

152 return self 1rGKf

153  

154 @classmethod 

155 def from_linear(cls, buffer, *, format, num_channels, size_bytes=None): 

156 """Build a resource descriptor for a linear (typed 1D) texture fetch. 

157  

158 Parameters 

159 ---------- 

160 buffer : Buffer 

161 Device-memory backing. Must remain alive for the lifetime of any 

162 :class:`TextureObject` built from this descriptor. 

163 format : ArrayFormatType, str, or numpy.dtype 

164 Element format. Accepts an :class:`~cuda.core.typing.ArrayFormatType`, 

165 a plain string (e.g. ``"float32"``), or a NumPy dtype object. 

166 num_channels : int 

167 Channels per element. Must be 1, 2, or 4. 

168 size_bytes : int, optional 

169 Bytes of ``buffer`` to bind. Defaults to ``buffer.size``. Must not 

170 exceed it. 

171  

172 Notes 

173 ----- 

174 Texture objects built from a linear resource ignore the 

175 :class:`TextureObjectOptions` addressing/filtering fields — kernels read 

176 through a typed 1D fetch with bounds checking only. 

177 """ 

178 if not isinstance(buffer, Buffer): 1wSVHNLxoqbc

179 raise TypeError(f"buffer must be a Buffer, got {type(buffer).__name__}") 1V

180 Buffer_check_open(<Buffer>buffer) 1wSHNLxoqbc

181 fmt = _validate_format_channels(format, num_channels) 1wSHNLxoqbc

182 cu_format = _ARRAYFORMAT_TO_CU[fmt] 1wHNLxoqbc

183  

184 buf_size = int(buffer.size) 1wHNLxoqbc

185 elem = _FORMAT_ELEM_SIZE[cu_format] * int(num_channels) 1wHNLxoqbc

186 if size_bytes is None: 1wHNLxoqbc

187 size = buf_size 1wqbc

188 else: 

189 size = int(size_bytes) 1HNLxo

190 if size > buf_size: 1HNLxo

191 raise ValueError( 1N

192 f"size_bytes ({size}) exceeds buffer.size ({buf_size})" 1N

193 ) 

194 if size < elem: 1wHLxoqbc

195 raise ValueError( 1L

196 f"size_bytes ({size}) must be at least one element ({elem} bytes)" 1L

197 ) 

198 if size % elem != 0: 1wHxoqbc

199 raise ValueError( 1H

200 f"size_bytes ({size}) must be a multiple of element size " 1H

201 f"({elem} bytes for {fmt.name} x {num_channels})" 1H

202 ) 

203  

204 self = cls.__new__(cls) 1wxoqbc

205 self._kind = "linear" 1wxoqbc

206 self._source = buffer 1wxoqbc

207 self._format = cu_format 1wxoqbc

208 self._num_channels = int(num_channels) 1wxoqbc

209 self._size_bytes = size 1wxoqbc

210 self._width = None 1wxoqbc

211 self._height = None 1wxoqbc

212 self._pitch_bytes = None 1wxoqbc

213 return self 1wxoqbc

214  

215 @classmethod 

216 def from_pitch2d( 

217 cls, buffer, *, format, num_channels, width, height, pitch_bytes 

218 ): 

219 """Build a resource descriptor for a row-pitched 2D image. 

220  

221 Parameters 

222 ---------- 

223 buffer : Buffer 

224 Device-memory backing. Must remain alive for the lifetime of any 

225 :class:`TextureObject` built from this descriptor. 

226 format : ArrayFormatType, str, or numpy.dtype 

227 Element format. Accepts an :class:`~cuda.core.typing.ArrayFormatType`, 

228 a plain string (e.g. ``"float32"``), or a NumPy dtype object. 

229 num_channels : int 

230 Channels per element. Must be 1, 2, or 4. 

231 width : int 

232 Image width, in elements. 

233 height : int 

234 Image height, in rows. 

235 pitch_bytes : int 

236 Distance between consecutive rows, in bytes. Must be at least 

237 ``width * format_size * num_channels`` and meet the driver's 

238 ``CU_DEVICE_ATTRIBUTE_TEXTURE_PITCH_ALIGNMENT``. 

239 """ 

240 if not isinstance(buffer, Buffer): 1TUWOBIoqa

241 raise TypeError(f"buffer must be a Buffer, got {type(buffer).__name__}") 1W

242 Buffer_check_open(<Buffer>buffer) 1TUOBIoqa

243 fmt = _validate_format_channels(format, num_channels) 1TUOBIoqa

244 cu_format = _ARRAYFORMAT_TO_CU[fmt] 1OBIoqa

245  

246 w = int(width) 1OBIoqa

247 h = int(height) 1OBIoqa

248 p = int(pitch_bytes) 1OBIoqa

249 if w < 1: 1OBIoqa

250 raise ValueError(f"width must be >= 1, got {w}") 1O

251 if h < 1: 1OBIoqa

252 raise ValueError(f"height must be >= 1, got {h}") 1O

253 elem = _FORMAT_ELEM_SIZE[cu_format] * int(num_channels) 1BIoqa

254 min_pitch = w * elem 1BIoqa

255 if p < min_pitch: 1BIoqa

256 raise ValueError( 1I

257 f"pitch_bytes ({p}) must be >= width * element_bytes ({min_pitch})" 1I

258 ) 

259 if p * h > int(buffer.size): 1Boqa

260 raise ValueError( 1B

261 f"pitch_bytes * height ({p * h}) exceeds buffer.size ({int(buffer.size)})" 1B

262 ) 

263  

264 self = cls.__new__(cls) 1oqa

265 self._kind = "pitch2d" 1oqa

266 self._source = buffer 1oqa

267 self._format = cu_format 1oqa

268 self._num_channels = int(num_channels) 1oqa

269 self._size_bytes = None 1oqa

270 self._width = w 1oqa

271 self._height = h 1oqa

272 self._pitch_bytes = p 1oqa

273 return self 1oqa

274  

275 @property 

276 def kind(self): 

277 return self._kind 1vespturwGCdDEqKFjgbhcfakmnil

278  

279 @property 

280 def source(self): 

281 return self._source 1vespturyzwGoCdDEFjgbhcfakmnil

282  

283 @property 

284 def format(self): 

285 """The element :class:`~cuda.core.typing.ArrayFormatType` (``None`` for array-backed).""" 

286 return None if self._format is None else _CU_TO_ARRAYFORMAT[self._format] 1woa

287  

288 @property 

289 def num_channels(self): 

290 """Channels per element (``None`` for array-backed).""" 

291 return self._num_channels 1wo

292  

293 @property 

294 def size_bytes(self): 

295 """Bytes bound for a linear resource (``None`` for other kinds).""" 

296 return self._size_bytes 1o

297  

298 @property 

299 def width(self): 

300 """Pitch2D image width, in elements (``None`` for other kinds).""" 

301 return self._width 1o

302  

303 @property 

304 def height(self): 

305 """Pitch2D image height, in rows (``None`` for other kinds).""" 

306 return self._height 1o

307  

308 @property 

309 def pitch_bytes(self): 

310 """Pitch2D row pitch, in bytes (``None`` for other kinds).""" 

311 return self._pitch_bytes 1o

312  

313 def __repr__(self): 

314 if self._kind == "linear": 1yzwda

315 return ( 1w

316 f"ResourceDescriptor(kind='linear', format={self.format.name}, " 1w

317 f"num_channels={self._num_channels}, size_bytes={self._size_bytes})" 1w

318 ) 

319 if self._kind == "pitch2d": 1yzda

320 return ( 1a

321 f"ResourceDescriptor(kind='pitch2d', format={self.format.name}, " 1a

322 f"num_channels={self._num_channels}, " 1a

323 f"width={self._width}, height={self._height}, " 1a

324 f"pitch_bytes={self._pitch_bytes})" 1a

325 ) 

326 return f"ResourceDescriptor(kind={self._kind!r})" 1yzd

327  

328  

329@dataclass 

330class TextureObjectOptions: 

331 """Sampling state for a :class:`TextureObject` (mirrors ``CUDA_TEXTURE_DESC``). 

332  

333 Attributes 

334 ---------- 

335 address_mode : AddressModeType or tuple of AddressModeType 

336 Boundary behavior per axis. May be a single 

337 :class:`~cuda.core.typing.AddressModeType` (applied to all axes) or a 

338 tuple of 1-3 entries (one per dimension). Plain strings are accepted. 

339 filter_mode : FilterModeType 

340 Texel sampling mode. Default ``POINT``. Plain strings are accepted. 

341 read_mode : ReadModeType 

342 How sampled integer values are returned. Default ``ELEMENT_TYPE``. 

343 Plain strings are accepted. 

344 normalized_coords : bool 

345 If True, coordinates are in ``[0, 1]`` instead of pixel indices. 

346 srgb : bool 

347 If True, perform sRGB → linear conversion on read (8-bit formats only). 

348 disable_trilinear_optimization : bool 

349 If True, request exact trilinear filtering. 

350 seamless_cubemap : bool 

351 If True, enable seamless cubemap edge filtering. 

352 max_anisotropy : int 

353 Maximum anisotropy; 0 disables anisotropic filtering. 

354 mipmap_filter_mode : FilterModeType 

355 Filtering between mipmap levels. Default ``POINT``. Plain strings are 

356 accepted. 

357 mipmap_level_bias : float 

358 min_mipmap_level_clamp : float 

359 max_mipmap_level_clamp : float 

360 border_color : tuple of float or None 

361 4-tuple used when ``address_mode`` includes ``BORDER``; ``None`` means 

362 zero. 

363  

364 .. versionadded:: 1.1.0 

365 """ 

366  

367 address_mode: AddressModeType | str | tuple[AddressModeType | str, ...] = AddressModeType.CLAMP 

368 filter_mode: FilterModeType | str = FilterModeType.POINT 

369 read_mode: ReadModeType | str = ReadModeType.ELEMENT_TYPE 

370 normalized_coords: bool = False 

371 srgb: bool = False 

372 disable_trilinear_optimization: bool = False 

373 seamless_cubemap: bool = False 

374 max_anisotropy: int = 0 

375 mipmap_filter_mode: FilterModeType | str = FilterModeType.POINT 

376 mipmap_level_bias: float = 0.0 

377 min_mipmap_level_clamp: float = 0.0 

378 max_mipmap_level_clamp: float = 0.0 

379 border_color: tuple[float, ...] | None = None 

380  

381 def __post_init__(self): 

382 self.filter_mode = _normalize_enum("filter_mode", self.filter_mode, FilterModeType) 1vespturdjgbhcfakmQJMnPil

383 self.read_mode = _normalize_enum("read_mode", self.read_mode, ReadModeType) 1vespturdjgbhcfakmJMnPil

384 self.mipmap_filter_mode = _normalize_enum( 1vespturdjgbhcfakmJnPil

385 "mipmap_filter_mode", self.mipmap_filter_mode, FilterModeType 1vespturdjgbhcfakmJnPil

386 ) 

387  

388  

389def _normalize_address_modes(address_mode): 

390 """Return a 3-tuple of :class:`AddressModeType` values from a scalar or 

391 1-3 tuple. Individual entries may be plain strings.""" 

392 if isinstance(address_mode, (AddressModeType, str)): 1esptudjgbhcfakmnil

393 m = _normalize_enum("address_mode", address_mode, AddressModeType) 1edjgbhcfakmnil

394 return (m, m, m) 1edjgbhcfakmnil

395 try: 1esptu

396 modes = tuple(address_mode) 1esptu

397 except TypeError as e: 1t

398 raise TypeError( 1t

399 "address_mode must be an AddressModeType or a tuple of AddressModeType" 

400 ) from e 1t

401 if not 1 <= len(modes) <= 3: 1espu

402 raise ValueError( 1su

403 f"address_mode tuple must have 1-3 entries, got {len(modes)}" 1su

404 ) 

405 modes = tuple( 1ep

406 _normalize_enum(f"address_mode[{i}]", m, AddressModeType) 1ep

407 for i, m in enumerate(modes) 1ep

408 ) 

409 # Pad to 3 entries by repeating the last one. 

410 padded = list(modes) + [modes[-1]] * (3 - len(modes)) 1e

411 return tuple(padded) 1e

412  

413  

414cdef class TextureObject: 

415 """A bindless texture handle for kernel-side sampled reads. 

416  

417 Wraps ``cuTexObjectCreate``. The underlying memory resource (e.g. the 

418 :class:`OpaqueArray` referenced by the descriptor) is kept alive for the 

419 lifetime of this object to prevent dangling handles. 

420  

421 Construct via :meth:`cuda.core.Device.create_texture_object`. Passes to 

422 kernels as a 64-bit handle (via the ``handle`` property). 

423  

424 .. versionadded:: 1.1.0 

425 """ 

426  

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

428 raise RuntimeError( 1Y

429 "TextureObject cannot be instantiated directly. " 

430 "Use Device.create_texture_object()." 

431 ) 

432  

433 @property 

434 def handle(self): 

435 """The underlying ``CUtexObject`` as an integer (64-bit kernel arg).""" 

436 return as_intptr(self._handle) 1edghcfakl

437  

438 @property 

439 def is_closed(self) -> bool: 

440 """Whether this texture object has been closed.""" 

441 return self._handle.get() == NULL 1dj

442  

443 @property 

444 def resource(self): 

445 """The :class:`ResourceDescriptor` this texture was built from.""" 

446 return self._source_ref 1hcf

447  

448 @property 

449 def options(self): 

450 """The :class:`TextureObjectOptions` this texture was built from.""" 

451 return self._options 1h

452  

453 @property 

454 def device(self): 

455 from cuda.core._device import Device 1dbi

456 return Device(self._device_id) 1dbi

457  

458 cpdef close(self): 

459 """Release this object's reference to the underlying ``CUtexObject``. 

460  

461 Destruction (``cuTexObjectDestroy``) and release of the backing resource 

462 happen via the handle's deleter when the last reference is dropped. 

463 Idempotent. 

464 """ 

465 self._handle.reset() 1edjgbhcfakil

466 self._source_ref = None 1edjgbhcfakil

467  

468 def __enter__(self): 

469 return self 1dgbi

470  

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

472 self.close() 1dgbi

473  

474 def __repr__(self): 

475 return f"TextureObject(handle=0x{as_intptr(self._handle):x})" 1d

476  

477  

478def _create_texture_object( 

479 resource, options, Context ctx, int device_id): 

480 """Create a :class:`TextureObject` on the specified device. 

481  

482 Backs :meth:`cuda.core.Device.create_texture_object`. ``resource`` is a 

483 :class:`ResourceDescriptor`; ``options`` is a :class:`TextureObjectOptions` 

484 (or a mapping accepted by it). 

485 """ 

486 if not isinstance(resource, ResourceDescriptor): 1vespturdjgbhcfakmAnPil

487 raise TypeError( 1P

488 f"resource must be a ResourceDescriptor, got " 1P

489 f"{type(resource).__name__}" 1P

490 ) 

491 cdef object opts = check_or_create_options( 1vespturdjgbhcfakmAnil

492 TextureObjectOptions, options, "Texture object options" 1vespturdjgbhcfakmAnil

493 ) 

494  

495 cdef cydriver.CUDA_RESOURCE_DESC res_desc 

496 cdef cydriver.CUDA_TEXTURE_DESC tex_desc 

497 memset(&res_desc, 0, sizeof(res_desc)) 1vespturdjgbhcfakmnil

498 memset(&tex_desc, 0, sizeof(tex_desc)) 1vespturdjgbhcfakmnil

499  

500 # --- Resource descriptor --- 

501 cdef OpaqueArray arr 

502 cdef MipmappedArray mip 

503 cdef Buffer buf 

504 cdef intptr_t devptr 

505 cdef ContextHandle resource_context 

506 cdef int resource_device_id 

507 if resource.kind == "array": 1vespturdjgbhcfakmnil

508 arr = <OpaqueArray>resource.source 1vespturdjghkmnil

509 OpaqueArray_check_open(arr) 1vespturdjghkmnil

510 resource_context = get_array_context(arr._handle) 1vesptudjghkmnil

511 resource_device_id = arr._device_id 1vesptudjghkmnil

512 res_desc.resType = cydriver.CU_RESOURCE_TYPE_ARRAY 1vesptudjghkmnil

513 res_desc.res.array.hArray = as_cu(arr._handle) 1vesptudjghkmnil

514 elif resource.kind == "mipmapped_array": 1rbcfa

515 mip = <MipmappedArray>resource.source 1rf

516 MipmappedArray_check_open(mip) 1rf

517 resource_context = get_mipmapped_array_context(mip._handle) 1f

518 resource_device_id = mip._device_id 1f

519 res_desc.resType = cydriver.CU_RESOURCE_TYPE_MIPMAPPED_ARRAY 1f

520 res_desc.res.mipmap.hMipmappedArray = as_cu(mip._handle) 1f

521 elif resource.kind == "linear": 1bca

522 buf = <Buffer>resource.source 1bc

523 Buffer_check_open(buf) 1bc

524 resource_device_id = buf.device_id # -1 for memory not bound to a device 1bc

525 devptr = int(buf.handle) 1bc

526 res_desc.resType = cydriver.CU_RESOURCE_TYPE_LINEAR 1bc

527 res_desc.res.linear.devPtr = <cydriver.CUdeviceptr>devptr 1bc

528 res_desc.res.linear.format = <cydriver.CUarray_format><int>resource._format 1bc

529 res_desc.res.linear.numChannels = <unsigned int>resource._num_channels 1bc

530 res_desc.res.linear.sizeInBytes = <size_t>resource._size_bytes 1bc

531 elif resource.kind == "pitch2d": 1a

532 buf = <Buffer>resource.source 1a

533 Buffer_check_open(buf) 1a

534 resource_device_id = buf.device_id # -1 for memory not bound to a device 1a

535 devptr = int(buf.handle) 1a

536 res_desc.resType = cydriver.CU_RESOURCE_TYPE_PITCH2D 1a

537 res_desc.res.pitch2D.devPtr = <cydriver.CUdeviceptr>devptr 1a

538 res_desc.res.pitch2D.format = <cydriver.CUarray_format><int>resource._format 1a

539 res_desc.res.pitch2D.numChannels = <unsigned int>resource._num_channels 1a

540 res_desc.res.pitch2D.width = <size_t>resource._width 1a

541 res_desc.res.pitch2D.height = <size_t>resource._height 1a

542 res_desc.res.pitch2D.pitchInBytes = <size_t>resource._pitch_bytes 1a

543 else: 

544 raise NotImplementedError( 

545 f"ResourceDescriptor kind {resource.kind!r} is not yet supported" 

546 ) 

547 if resource_device_id >= 0 and resource_device_id != device_id: 1vesptudjgbhcfakmnil

548 raise ValueError( 

549 f"resource belongs to device {resource_device_id}, " 

550 f"but texture creation was requested on device {device_id}" 

551 ) 

552 if resource_context and as_cu(resource_context) != as_cu(ctx._h_context): 1vesptudjgbhcfakmnil

553 raise ValueError("resource is not compatible with this Device object") 1v

554  

555 # --- Texture descriptor --- 

556 # filter_mode/read_mode/mipmap_filter_mode are normalized to their 

557 # StrEnum types by TextureObjectOptions.__post_init__; address_mode is 

558 # normalized (and str-coerced) here. 

559 modes = _normalize_address_modes(opts.address_mode) 1esptudjgbhcfakmnil

560 tex_desc.addressMode[0] = <cydriver.CUaddress_mode>_ADDRESSMODE_TO_CU[modes[0]] 1edjgbhcfakmnil

561 tex_desc.addressMode[1] = <cydriver.CUaddress_mode>_ADDRESSMODE_TO_CU[modes[1]] 1edjgbhcfakmnil

562 tex_desc.addressMode[2] = <cydriver.CUaddress_mode>_ADDRESSMODE_TO_CU[modes[2]] 1edjgbhcfakmnil

563  

564 tex_desc.filterMode = <cydriver.CUfilter_mode>_FILTERMODE_TO_CU[opts.filter_mode] 1edjgbhcfakmnil

565  

566 cdef unsigned int flags = 0 1edjgbhcfakmnil

567 # CU_TRSF_READ_AS_INTEGER suppresses normalization, so it maps to 

568 # ReadModeType.ELEMENT_TYPE. 

569 if opts.read_mode == ReadModeType.ELEMENT_TYPE: 1edjgbhcfakmnil

570 flags |= _TRSF_READ_AS_INTEGER 1ejbhcfakmnil

571 if opts.normalized_coords: 1edjgbhcfakmnil

572 flags |= _TRSF_NORMALIZED_COORDINATES 1hf

573 if opts.srgb: 1edjgbhcfakmnil

574 flags |= _TRSF_SRGB 1d

575 if opts.disable_trilinear_optimization: 1edjgbhcfakmnil

576 flags |= _TRSF_DISABLE_TRILINEAR_OPTIMIZATION 1f

577 if opts.seamless_cubemap: 1edjgbhcfakmnil

578 flags |= _TRSF_SEAMLESS_CUBEMAP 

579 tex_desc.flags = flags 1edjgbhcfakmnil

580  

581 if opts.max_anisotropy < 0: 1edjgbhcfakmnil

582 raise ValueError("max_anisotropy must be >= 0") 1n

583 tex_desc.maxAnisotropy = <unsigned int>opts.max_anisotropy 1edjgbhcfakmil

584  

585 tex_desc.mipmapFilterMode = <cydriver.CUfilter_mode>_FILTERMODE_TO_CU[opts.mipmap_filter_mode] 1edjgbhcfakmil

586 tex_desc.mipmapLevelBias = <float>opts.mipmap_level_bias 1edjgbhcfakmil

587 tex_desc.minMipmapLevelClamp = <float>opts.min_mipmap_level_clamp 1edjgbhcfakmil

588 tex_desc.maxMipmapLevelClamp = <float>opts.max_mipmap_level_clamp 1edjgbhcfakmil

589  

590 cdef int i 

591 if opts.border_color is None: 1edjgbhcfakmil

592 for i in range(4): 1edjbhcfakil

593 tex_desc.borderColor[i] = 0.0 1edjbhcfakil

594 else: 

595 bc = tuple(opts.border_color) 1gm

596 if len(bc) != 4: 1gm

597 raise ValueError( 1m

598 f"border_color must have 4 elements, got {len(bc)}" 1m

599 ) 

600 for i in range(4): 1g

601 tex_desc.borderColor[i] = <float>bc[i] 1g

602  

603 cdef TexObjectHandle h 

604 if resource.kind == "array": 1edjgbhcfakil

605 h = create_tex_object_handle_array( 1edjghkil

606 ctx._h_context, res_desc, tex_desc, arr._handle) 1edjghkil

607 elif resource.kind == "mipmapped_array": 1bcfa

608 h = create_tex_object_handle_mipmap( 1f

609 ctx._h_context, res_desc, tex_desc, mip._handle) 1f

610 else: # linear or pitch2d — both backed by a device Buffer 

611 h = create_tex_object_handle_linear( 1bca

612 ctx._h_context, res_desc, tex_desc, buf._h_ptr) 1bca

613 if not h: 1edjgbhcfakil

614 HANDLE_RETURN(get_last_error()) 

615  

616 cdef TextureObject self = TextureObject.__new__(TextureObject) 1edjgbhcfakil

617 self._handle = h 1edjgbhcfakil

618 self._source_ref = resource 1edjgbhcfakil

619 self._options = opts 1edjgbhcfakil

620 self._device_id = device_id 1edjgbhcfakil

621 return self 1edjgbhcfakil