Skip to content

Commit 8b20f77

Browse files
authored
cuda.core.texture: API-style follow-up before v1.1.0 (#2292) (#2307)
* cuda.core.texture: rename from_array -> from_opaque_array, element_size -> element_bytes (#2292) Addresses items 5 and 7 of the pre-v1.1.0 texture API-style review. - ResourceDescriptor.from_array -> ResourceDescriptor.from_opaque_array: the Array -> OpaqueArray rename removed "array" ambiguity, so the factory name should follow suit. - OpaqueArray.element_size -> OpaqueArray.element_bytes: the accessor returns a byte count, matching the _bytes convention already used by size_bytes and pitch_bytes. Both are renames of APIs not yet released (first ship in v1.1.0), so no deprecation shim is needed; the new names carry a versionadded:: 1.1.0 note. Call sites in the texture tests and the four texture examples are updated. * cuda.core.texture: move texture enums to cuda.core.typing as StrEnum (#2292) Addresses item 4 of the pre-v1.1.0 texture API-style review. The texture/surface enums were IntEnum subclasses backed by cydriver.CU_* values, defined in the cuda.core.texture root namespace and lacking the Type suffix. Every other cuda.core enum unified pre-1.0 lives in cuda.core.typing as a StrEnum with semantic string values and a Type suffix. Bring the texture enums in line: - ArrayFormat -> cuda.core.typing.ArrayFormatType - AddressMode -> cuda.core.typing.AddressModeType - FilterMode -> cuda.core.typing.FilterModeType - ReadMode -> cuda.core.typing.ReadModeType Internally the driver integer values are still needed, so _array and _texture carry private CU<->StrEnum bridge maps; OpaqueArray/MipmappedArray keep storing the driver int and convert on the .format boundary. Per the enum convention, plain strings are accepted anywhere an enum is expected (via _normalize_enum / _normalize_array_format), and invalid values raise ValueError. TextureDescriptor gains a __post_init__ that normalizes its filter_mode/read_mode/ mipmap_filter_mode fields at construction. Enums are dropped from the cuda.core.texture __all__ and documented under cuda.core.typing. Tests, examples, and docs are updated to the new names; the negative-path tests that previously expected a TypeError at texture creation now expect a ValueError at descriptor construction. * cuda.core.texture: accept NumPy dtypes for array format (#2292) Addresses item 3 of the pre-v1.1.0 texture API-style review. TensorMapDescriptorOptions.data_type already accepts NumPy dtype objects and keeps its enum as a fallback. Apply the same precedent to the texture format argument: OpaqueArray / MipmappedArray creation and ResourceDescriptor.from_linear / from_pitch2d now accept an ArrayFormatType, a plain str, or a NumPy dtype object (anything numpy.dtype() accepts). Each ArrayFormatType value is already spelled as a NumPy dtype name, so the eight formats map 1:1 via a new _NUMPY_DTYPE_TO_ARRAYFORMAT table. All four factories funnel through _validate_format_channels -> _normalize_array_format, so dtype support is added in one place. ml_dtypes is intentionally not imported: every current format is a standard NumPy dtype, and the ml_dtypes path only becomes relevant if/when a bfloat16 format is added. * cuda.core.texture: rename TextureDescriptor -> TextureObjectOptions (#2292) Addresses the rename portion of item 2 of the pre-v1.1.0 texture API-style review. CUDA_TEXTURE_DESC is pure creation input (it cannot be queried back from a texture object), so "descriptor" borrowed the driver naming inappropriately. TextureObjectOptions matches the XxxOptions convention used throughout cuda.core (StreamOptions, EventOptions, TensorMapDescriptorOptions, ...). Pure rename of the dataclass and all references across the implementation, stubs, __init__ __all__, docs, tests, and examples. The from_descriptor texture_descriptor= keyword is left untouched here; the whole creation entry point is replaced by Device.create_texture_object(*, resource, options) in a later commit. * cuda.core.texture: add Device.create_opaque_array / create_mipmapped_array (#2292) Addresses item 1 (array allocation) and item 2 (Options dataclasses) of the pre-v1.1.0 texture API-style review. Every other user-allocatable resource in cuda.core is created via Device.create_* (create_stream, create_event, ...) with an XxxOptions dataclass. The texture arrays used per-type classmethod factories (OpaqueArray.from_descriptor, MipmappedArray.from_descriptor) with loose kwargs instead. Move them in line: - add OpaqueArrayOptions and MipmappedArrayOptions dataclasses (validated in __post_init__, mirroring TensorMapDescriptorOptions); - add Device.create_opaque_array(options) and Device.create_mipmapped_array( options), which gate on _check_context_initialized() and delegate to private module factories that bind to the current device (same pattern as create_event / create_graph_builder); - remove the OpaqueArray.from_descriptor / MipmappedArray.from_descriptor classmethods; the private _from_handle graphics-interop path is retained. New entry points carry versionadded:: 1.1.0. Options are exported from cuda.core.texture and documented in api.rst. Tests and the texture examples are updated to Device().create_*_array(<Options>(...)). * cuda.core.texture: add Device.create_texture_object / create_surface_object (#2292) Addresses item 1 (object creation) and item 6 (drop SurfaceObject.from_array sugar) of the pre-v1.1.0 texture API-style review, completing the move to the Device.create_* allocation shape. - add Device.create_texture_object(*, resource, options) and Device.create_surface_object(*, resource), gating on _check_context_initialized() and delegating to private module factories that bind to the current device (same pattern as create_opaque_array); - remove TextureObject.from_descriptor, SurfaceObject.from_descriptor, and the speculative SurfaceObject.from_array sugar (a surface is now built from an explicit ResourceDescriptor.from_opaque_array(...) like every other backing); - rename the sampling-options argument from texture_descriptor to options on the new entry point, matching the create_* + XxxOptions convention (the queryable TextureObject.texture_descriptor property is unchanged). New entry points carry versionadded:: 1.1.0. Tests, examples, and api.rst are updated; a make_surface() helper mirrors make_texture() in the fluid example. * cuda.core.texture: apply pre-commit (ruff, ruff-format, stubgen) (#2292) Mechanical pre-commit pass over the #2292 texture API series: - ruff: drop now-unused imports in the texture example/test files and remove an unused _normalize_array_format import from _texture.pyx (cython-lint); - ruff-format: reflow the rewritten call sites in the tests and examples; - stubgen-pyx: regenerate the .pyi stubs from the updated .pyx sources. * cuda.core.texture: address API-review follow-ups (#2292) Apply the review findings from the texture API-style follow-up: - Rename TextureObject.texture_descriptor -> options (and the internal _texture_desc slot -> _options) to drop the borrowed "descriptor" naming, matching the TextureObjectOptions rename. - Make options a required argument on create_opaque_array / create_mipmapped_array (shape/format/num_channels have no defaults), so omitting it raises a clear error instead of a raw dataclass TypeError. create_texture_object keeps options=None (all-default sampling state). - Add return and resource/options type annotations to the four Device.create_* texture factories via a TYPE_CHECKING texture import (mirroring the existing GraphBuilder pattern). - Clarify in the create_* docstrings that the resource is created in the current CUDA context (call set_current first), mirroring create_stream / create_event. - Add versionadded:: 1.1.0 to OpaqueArrayOptions, MipmappedArrayOptions, TextureObjectOptions, and the new options property. - Fix a stale negative-path test whose regex no longer matched the shared check_or_create_options message; rename stale from_descriptor test names to create_*; realign the gl_interop_fluid API-MAP comment table after the enum renames. - Regenerate .pyi stubs. * cuda.core.texture: fix stale address_mode entry-rejection test (#2292) The StrEnum migration (item 4 of #2292) makes a plain str acceptable anywhere an AddressModeType is expected. A bad tuple entry like "bad" is therefore a valid *type* but an invalid *value*, so _normalize_enum rejects it with a ValueError (naming the offending position address_mode[1]), not a TypeError. Update the test to assert ValueError and rename it to test_address_mode_rejects_invalid_entry. This is the second of the two CI failures on PR #2307; the first (the stale options-type message) was fixed in the previous commit. * cuda.core.texture: register new StrEnums in enum-coverage test (#2292) test_enum_coverage.py::test_all_str_enums_in_cases requires every StrEnum in cuda.core to be declared either in _CASES (bound to a cuda_binding enum) or in _UNBOUND_STR_ENUMS. The four texture enums moved into cuda.core.typing (item 4 of #2292) were not registered, failing that guard. - AddressModeType / FilterModeType are 1:1 wrappers of CUaddress_mode / CUfilter_mode, so add mapping=None _CASES entries (count-check only; the texture mapping dicts store cydriver-derived ints, not driver.<Enum> members, so the isinstance-based mapping check does not apply). - ArrayFormatType exposes only the 8 NumPy-representable formats out of CUarray_format's ~67 members, so it is a curated subset rather than a 1:1 wrapper -> _UNBOUND_STR_ENUMS. - ReadModeType maps to the CU_TRSF_READ_AS_INTEGER flag bit, not a CUenum -> _UNBOUND_STR_ENUMS. This is the third local test failure; the other two (stale texture negative-path tests) were fixed in the two preceding commits. * cuda.core.texture: add versionadded markers and widen enum str annotations Address review feedback (Ralf): - Add ``.. versionadded:: 1.1.0`` to the four new texture enums in typing.py (ArrayFormatType, AddressModeType, FilterModeType, ReadModeType) and to the texture classes that lacked a class-level marker (ResourceDescriptor, TextureObject, OpaqueArray, MipmappedArray, SurfaceObject). Remove the redundant method-level markers now covered at class level. - Widen the enum-bearing TextureObjectOptions field annotations to include ``str`` (address_mode, filter_mode, read_mode, mipmap_filter_mode), matching the runtime string coercion this PR adds and existing convention elsewhere in cuda.core. Note "Plain strings are accepted" on the affected fields. - Regenerate .pyi stubs.
1 parent ed4e31e commit 8b20f77

21 files changed

Lines changed: 1515 additions & 929 deletions

‎cuda_core/cuda/core/_device.pyi‎

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ from cuda.core._memory._buffer import Buffer, MemoryResource
1212
from cuda.core._stream import IsStreamType, Stream
1313
from cuda.core._utils.cuda_utils import ComputeCapability
1414
from cuda.core.graph import GraphBuilder
15+
from cuda.core.texture import (MipmappedArray, MipmappedArrayOptions,
16+
OpaqueArray, OpaqueArrayOptions,
17+
ResourceDescriptor, SurfaceObject,
18+
TextureObject, TextureObjectOptions)
1519

1620

1721
class DeviceProperties:
@@ -909,5 +913,114 @@ class Device:
909913
Newly created graph builder object.
910914
911915
"""
916+
917+
def create_opaque_array(self, options: OpaqueArrayOptions) -> OpaqueArray:
918+
"""Create an :obj:`~cuda.core.texture.OpaqueArray` on the current device.
919+
920+
Allocates an opaque, hardware-laid-out CUDA array for texture/surface
921+
access. The array is created in the current CUDA context, so make this
922+
device current with :meth:`set_current` before calling (mirroring
923+
:meth:`create_stream` / :meth:`create_event`).
924+
925+
Note
926+
----
927+
Device must be initialized.
928+
929+
Parameters
930+
----------
931+
options : :obj:`~cuda.core.texture.OpaqueArrayOptions`
932+
Allocation options (shape, format, channels, surface flag).
933+
934+
Returns
935+
-------
936+
:obj:`~cuda.core.texture.OpaqueArray`
937+
Newly created opaque array.
938+
939+
.. versionadded:: 1.1.0
940+
"""
941+
942+
def create_mipmapped_array(self, options: MipmappedArrayOptions) -> MipmappedArray:
943+
"""Create a :obj:`~cuda.core.texture.MipmappedArray` on the current device.
944+
945+
Allocates a mipmapped CUDA array for texture/surface access across
946+
levels. The array is created in the current CUDA context, so make this
947+
device current with :meth:`set_current` before calling (mirroring
948+
:meth:`create_stream` / :meth:`create_event`).
949+
950+
Note
951+
----
952+
Device must be initialized.
953+
954+
Parameters
955+
----------
956+
options : :obj:`~cuda.core.texture.MipmappedArrayOptions`
957+
Allocation options (shape, format, channels, levels, surface flag).
958+
959+
Returns
960+
-------
961+
:obj:`~cuda.core.texture.MipmappedArray`
962+
Newly created mipmapped array.
963+
964+
.. versionadded:: 1.1.0
965+
"""
966+
967+
def create_texture_object(self, *, resource: ResourceDescriptor, options: TextureObjectOptions | None=None) -> TextureObject:
968+
"""Create a :obj:`~cuda.core.texture.TextureObject` on the current device.
969+
970+
Binds a resource (an :obj:`~cuda.core.texture.OpaqueArray` /
971+
:obj:`~cuda.core.texture.MipmappedArray` / linear or pitch2d
972+
:obj:`~cuda.core.Buffer`, wrapped in a
973+
:obj:`~cuda.core.texture.ResourceDescriptor`) as a bindless texture for
974+
kernel-side sampled reads. The object is created in the current CUDA
975+
context, so make this device current with :meth:`set_current` before
976+
calling (mirroring :meth:`create_stream` / :meth:`create_event`).
977+
978+
Note
979+
----
980+
Device must be initialized.
981+
982+
Parameters
983+
----------
984+
resource : :obj:`~cuda.core.texture.ResourceDescriptor`
985+
The memory backing the texture.
986+
options : :obj:`~cuda.core.texture.TextureObjectOptions`
987+
Sampling state (address/filter/read modes, normalization, etc.).
988+
989+
Returns
990+
-------
991+
:obj:`~cuda.core.texture.TextureObject`
992+
Newly created texture object.
993+
994+
.. versionadded:: 1.1.0
995+
"""
996+
997+
def create_surface_object(self, *, resource: ResourceDescriptor) -> SurfaceObject:
998+
"""Create a :obj:`~cuda.core.texture.SurfaceObject` on the current device.
999+
1000+
Binds an :obj:`~cuda.core.texture.OpaqueArray` (via a
1001+
:obj:`~cuda.core.texture.ResourceDescriptor`) as a bindless surface for
1002+
kernel-side typed load/store. The backing array must have been created
1003+
with ``is_surface_load_store=True``. The object is created in the
1004+
current CUDA context, so make this device current with
1005+
:meth:`set_current` before calling (mirroring :meth:`create_stream` /
1006+
:meth:`create_event`).
1007+
1008+
Note
1009+
----
1010+
Device must be initialized.
1011+
1012+
Parameters
1013+
----------
1014+
resource : :obj:`~cuda.core.texture.ResourceDescriptor`
1015+
Must wrap an :obj:`~cuda.core.texture.OpaqueArray` allocated with
1016+
``is_surface_load_store=True``.
1017+
1018+
Returns
1019+
-------
1020+
:obj:`~cuda.core.texture.SurfaceObject`
1021+
Newly created surface object.
1022+
1023+
.. versionadded:: 1.1.0
1024+
"""
9121025
_tls = threading.local()
9131026
_lock = threading.Lock()

‎cuda_core/cuda/core/_device.pyx‎

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,16 @@ from typing import TYPE_CHECKING
4444
if TYPE_CHECKING:
4545
import cuda.core.system # no-cython-lint
4646
from cuda.core.graph import GraphBuilder
47+
from cuda.core.texture import (
48+
MipmappedArray,
49+
MipmappedArrayOptions,
50+
OpaqueArray,
51+
OpaqueArrayOptions,
52+
ResourceDescriptor,
53+
SurfaceObject,
54+
TextureObject,
55+
TextureObjectOptions,
56+
)
4757

4858
# TODO: I prefer to type these as "cdef object" and avoid accessing them from within Python,
4959
# but it seems it is very convenient to expose them for testing purposes...
@@ -1456,6 +1466,133 @@ class Device:
14561466
self._check_context_initialized()
14571467
return GraphBuilder._init(self.create_stream())
14581468

1469+
def create_opaque_array(self, options: OpaqueArrayOptions) -> OpaqueArray:
1470+
"""Create an :obj:`~cuda.core.texture.OpaqueArray` on the current device.
1471+
1472+
Allocates an opaque, hardware-laid-out CUDA array for texture/surface
1473+
access. The array is created in the current CUDA context, so make this
1474+
device current with :meth:`set_current` before calling (mirroring
1475+
:meth:`create_stream` / :meth:`create_event`).
1476+
1477+
Note
1478+
----
1479+
Device must be initialized.
1480+
1481+
Parameters
1482+
----------
1483+
options : :obj:`~cuda.core.texture.OpaqueArrayOptions`
1484+
Allocation options (shape, format, channels, surface flag).
1485+
1486+
Returns
1487+
-------
1488+
:obj:`~cuda.core.texture.OpaqueArray`
1489+
Newly created opaque array.
1490+
1491+
.. versionadded:: 1.1.0
1492+
"""
1493+
from cuda.core.texture._array import _create_opaque_array
1494+
1495+
self._check_context_initialized()
1496+
return _create_opaque_array(options)
1497+
1498+
def create_mipmapped_array(self, options: MipmappedArrayOptions) -> MipmappedArray:
1499+
"""Create a :obj:`~cuda.core.texture.MipmappedArray` on the current device.
1500+
1501+
Allocates a mipmapped CUDA array for texture/surface access across
1502+
levels. The array is created in the current CUDA context, so make this
1503+
device current with :meth:`set_current` before calling (mirroring
1504+
:meth:`create_stream` / :meth:`create_event`).
1505+
1506+
Note
1507+
----
1508+
Device must be initialized.
1509+
1510+
Parameters
1511+
----------
1512+
options : :obj:`~cuda.core.texture.MipmappedArrayOptions`
1513+
Allocation options (shape, format, channels, levels, surface flag).
1514+
1515+
Returns
1516+
-------
1517+
:obj:`~cuda.core.texture.MipmappedArray`
1518+
Newly created mipmapped array.
1519+
1520+
.. versionadded:: 1.1.0
1521+
"""
1522+
from cuda.core.texture._mipmapped_array import _create_mipmapped_array
1523+
1524+
self._check_context_initialized()
1525+
return _create_mipmapped_array(options)
1526+
1527+
def create_texture_object(
1528+
self, *, resource: ResourceDescriptor, options: TextureObjectOptions | None = None
1529+
) -> TextureObject:
1530+
"""Create a :obj:`~cuda.core.texture.TextureObject` on the current device.
1531+
1532+
Binds a resource (an :obj:`~cuda.core.texture.OpaqueArray` /
1533+
:obj:`~cuda.core.texture.MipmappedArray` / linear or pitch2d
1534+
:obj:`~cuda.core.Buffer`, wrapped in a
1535+
:obj:`~cuda.core.texture.ResourceDescriptor`) as a bindless texture for
1536+
kernel-side sampled reads. The object is created in the current CUDA
1537+
context, so make this device current with :meth:`set_current` before
1538+
calling (mirroring :meth:`create_stream` / :meth:`create_event`).
1539+
1540+
Note
1541+
----
1542+
Device must be initialized.
1543+
1544+
Parameters
1545+
----------
1546+
resource : :obj:`~cuda.core.texture.ResourceDescriptor`
1547+
The memory backing the texture.
1548+
options : :obj:`~cuda.core.texture.TextureObjectOptions`
1549+
Sampling state (address/filter/read modes, normalization, etc.).
1550+
1551+
Returns
1552+
-------
1553+
:obj:`~cuda.core.texture.TextureObject`
1554+
Newly created texture object.
1555+
1556+
.. versionadded:: 1.1.0
1557+
"""
1558+
from cuda.core.texture._texture import _create_texture_object
1559+
1560+
self._check_context_initialized()
1561+
return _create_texture_object(resource, options)
1562+
1563+
def create_surface_object(self, *, resource: ResourceDescriptor) -> SurfaceObject:
1564+
"""Create a :obj:`~cuda.core.texture.SurfaceObject` on the current device.
1565+
1566+
Binds an :obj:`~cuda.core.texture.OpaqueArray` (via a
1567+
:obj:`~cuda.core.texture.ResourceDescriptor`) as a bindless surface for
1568+
kernel-side typed load/store. The backing array must have been created
1569+
with ``is_surface_load_store=True``. The object is created in the
1570+
current CUDA context, so make this device current with
1571+
:meth:`set_current` before calling (mirroring :meth:`create_stream` /
1572+
:meth:`create_event`).
1573+
1574+
Note
1575+
----
1576+
Device must be initialized.
1577+
1578+
Parameters
1579+
----------
1580+
resource : :obj:`~cuda.core.texture.ResourceDescriptor`
1581+
Must wrap an :obj:`~cuda.core.texture.OpaqueArray` allocated with
1582+
``is_surface_load_store=True``.
1583+
1584+
Returns
1585+
-------
1586+
:obj:`~cuda.core.texture.SurfaceObject`
1587+
Newly created surface object.
1588+
1589+
.. versionadded:: 1.1.0
1590+
"""
1591+
from cuda.core.texture._surface import _create_surface_object
1592+
1593+
self._check_context_initialized()
1594+
return _create_surface_object(resource)
1595+
14591596

14601597
cdef inline int Device_ensure_cuda_initialized() except? -1:
14611598
"""Initialize CUDA driver and check version compatibility (once per process)."""

‎cuda_core/cuda/core/texture/__init__.py‎

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,30 +10,31 @@
1010
1111
Import these types from here, e.g.::
1212
13-
from cuda.core.texture import OpaqueArray, TextureObject, TextureDescriptor
13+
from cuda.core.texture import OpaqueArray, TextureObject, TextureObjectOptions
14+
15+
The associated enumerations (:class:`~cuda.core.typing.ArrayFormatType`,
16+
:class:`~cuda.core.typing.AddressModeType`,
17+
:class:`~cuda.core.typing.FilterModeType`,
18+
:class:`~cuda.core.typing.ReadModeType`) live in :mod:`cuda.core.typing`
19+
alongside the other ``cuda.core`` enumerations.
1420
"""
1521

16-
from cuda.core.texture._array import ArrayFormat, OpaqueArray
17-
from cuda.core.texture._mipmapped_array import MipmappedArray
22+
from cuda.core.texture._array import OpaqueArray, OpaqueArrayOptions
23+
from cuda.core.texture._mipmapped_array import MipmappedArray, MipmappedArrayOptions
1824
from cuda.core.texture._surface import SurfaceObject
1925
from cuda.core.texture._texture import (
20-
AddressMode,
21-
FilterMode,
22-
ReadMode,
2326
ResourceDescriptor,
24-
TextureDescriptor,
2527
TextureObject,
28+
TextureObjectOptions,
2629
)
2730

2831
__all__ = [
29-
"AddressMode",
30-
"ArrayFormat",
31-
"FilterMode",
3232
"MipmappedArray",
33+
"MipmappedArrayOptions",
3334
"OpaqueArray",
34-
"ReadMode",
35+
"OpaqueArrayOptions",
3536
"ResourceDescriptor",
3637
"SurfaceObject",
37-
"TextureDescriptor",
3838
"TextureObject",
39+
"TextureObjectOptions",
3940
]

0 commit comments

Comments
 (0)