Skip to content

Commit 264f2e6

Browse files
rparolinclaude
andcommitted
cuda.core: fix lint/mypy after merging main; add generated .pyi stubs
Post-merge cleanup so pre-commit.ci passes on the texture/surface stack: - Switch ReadMode/AddressMode/FilterMode/ArrayFormat to `from enum import IntEnum` so stubgen-pyx preserves the IntEnum base in the generated stubs (qualified `enum.IntEnum` was dropped, making members infer as `int` and failing mypy assignment checks). - Annotate TextureDescriptor.border_color as `tuple[float, ...] | None` (disallow_any_generics flagged the bare `tuple`). - Prefix unused pyglet event-handler args with `_` (ARG001) and lowercase in-function locals (N806) across the GL interop examples; drop dead pre-try buffer inits in texture_sample.main (F841). - Commit the auto-generated .pyi stubs for the new _array/_texture/_surface/ _mipmapped_array modules. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent fbe880a commit 264f2e6

18 files changed

Lines changed: 957 additions & 311 deletions

cuda_core/cuda/core/__init__.py

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ class _PatchedProperty(metaclass=_PatchedPropMeta):
6969

7070

7171
from cuda.core import checkpoint, system, utils
72+
from cuda.core._array import ArrayFormat, CUDAArray
7273
from cuda.core._context import Context, ContextOptions
7374
from cuda.core._device import Device
7475
from cuda.core._device_resources import (
@@ -78,17 +79,6 @@ class _PatchedProperty(metaclass=_PatchedPropMeta):
7879
WorkqueueResource,
7980
WorkqueueResourceOptions,
8081
)
81-
from cuda.core._array import CUDAArray, ArrayFormat
82-
from cuda.core._mipmapped_array import MipmappedArray
83-
from cuda.core._texture import (
84-
AddressMode,
85-
FilterMode,
86-
ReadMode,
87-
ResourceDescriptor,
88-
TextureDescriptor,
89-
TextureObject,
90-
)
91-
from cuda.core._surface import SurfaceObject
9282
from cuda.core._event import Event, EventOptions
9383
from cuda.core._graphics import GraphicsResource
9484
from cuda.core._host import Host
@@ -110,6 +100,7 @@ class _PatchedProperty(metaclass=_PatchedPropMeta):
110100
VirtualMemoryResource,
111101
VirtualMemoryResourceOptions,
112102
)
103+
from cuda.core._mipmapped_array import MipmappedArray
113104
from cuda.core._module import Kernel, ObjectCode
114105
from cuda.core._program import Program, ProgramOptions
115106
from cuda.core._stream import (
@@ -118,7 +109,16 @@ class _PatchedProperty(metaclass=_PatchedPropMeta):
118109
Stream,
119110
StreamOptions,
120111
)
112+
from cuda.core._surface import SurfaceObject
121113
from cuda.core._tensor_map import TensorMapDescriptor, TensorMapDescriptorOptions
114+
from cuda.core._texture import (
115+
AddressMode,
116+
FilterMode,
117+
ReadMode,
118+
ResourceDescriptor,
119+
TextureDescriptor,
120+
TextureObject,
121+
)
122122

123123
# isort: split
124124
# Must come after the cuda.core._* extension imports above: loading graph

cuda_core/cuda/core/_array.pyi

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
# This file was generated by stubgen-pyx v0.2.6 from cuda_core/cuda/core/_array.pyx
2+
3+
from __future__ import annotations
4+
5+
from enum import IntEnum
6+
7+
from cuda.bindings import cydriver
8+
9+
10+
class ArrayFormat(IntEnum):
11+
"""Element format for a :class:`CUDAArray` allocation.
12+
13+
Mirrors ``CUarray_format`` from the CUDA driver API.
14+
"""
15+
UINT8 = cydriver.CU_AD_FORMAT_UNSIGNED_INT8
16+
UINT16 = cydriver.CU_AD_FORMAT_UNSIGNED_INT16
17+
UINT32 = cydriver.CU_AD_FORMAT_UNSIGNED_INT32
18+
INT8 = cydriver.CU_AD_FORMAT_SIGNED_INT8
19+
INT16 = cydriver.CU_AD_FORMAT_SIGNED_INT16
20+
INT32 = cydriver.CU_AD_FORMAT_SIGNED_INT32
21+
FLOAT16 = cydriver.CU_AD_FORMAT_HALF
22+
FLOAT32 = cydriver.CU_AD_FORMAT_FLOAT
23+
24+
class CUDAArray:
25+
"""An opaque, hardware-laid-out GPU allocation for texture/surface access.
26+
27+
Distinct from :class:`Buffer`: a ``CUarray`` has no exposed device pointer
28+
and can only be accessed from kernels through a :class:`TextureObject` or
29+
:class:`SurfaceObject`. Its memory layout is chosen by the driver for 2D/3D
30+
spatial locality.
31+
32+
Construct via :meth:`from_descriptor`. Only plain 1D/2D/3D allocations are
33+
supported in this initial version; layered/cubemap/sparse variants will
34+
follow once their shape semantics are settled.
35+
"""
36+
37+
def close(self):
38+
"""Destroy the underlying ``CUarray`` if owned by this object."""
39+
40+
def __init__(self, *args, **kwargs):
41+
...
42+
43+
@classmethod
44+
def from_descriptor(cls, *, shape, format, num_channels, surface_load_store=False):
45+
"""Allocate a new CUDA array.
46+
47+
Parameters
48+
----------
49+
shape : tuple of int
50+
``(width,)``, ``(width, height)``, or ``(width, height, depth)``
51+
in elements.
52+
format : ArrayFormat
53+
Element format.
54+
num_channels : int
55+
Channels per element. Must be 1, 2, or 4.
56+
surface_load_store : bool
57+
If True, allocate with ``CUDA_ARRAY3D_SURFACE_LDST`` so the array
58+
can be bound as a :class:`SurfaceObject` for kernel-side writes.
59+
Default False.
60+
61+
Returns
62+
-------
63+
CUDAArray
64+
"""
65+
66+
@classmethod
67+
def _from_handle(cls, handle: int, owning: bool, *, device_id=None):
68+
"""Wrap an externally-allocated ``CUarray``.
69+
70+
Intended for graphics interop (``cuGraphicsSubResourceGetMappedArray``)
71+
where the array is owned by the graphics API. With ``owning=False``,
72+
:meth:`close` and ``__dealloc__`` will not free the handle. Shape,
73+
format, and channel count are queried from the driver.
74+
"""
75+
76+
@property
77+
def handle(self):
78+
"""The underlying ``CUarray`` as an integer."""
79+
80+
@property
81+
def shape(self):
82+
"""Allocation shape, in elements."""
83+
84+
@property
85+
def format(self):
86+
"""The element :class:`ArrayFormat`."""
87+
88+
@property
89+
def num_channels(self):
90+
"""Channels per element (1, 2, or 4)."""
91+
92+
@property
93+
def element_size(self):
94+
"""Bytes per element (format size * channels)."""
95+
96+
@property
97+
def device(self):
98+
"""The :class:`Device` this array was allocated on."""
99+
100+
@property
101+
def is_surface_load_store(self):
102+
"""True if this array was created with ``CUDA_ARRAY3D_SURFACE_LDST``
103+
and can be bound as a :class:`SurfaceObject`."""
104+
105+
def _extent_bytes(self):
106+
"""Return (width_bytes, height, depth) for cuMemcpy3D, with height/depth
107+
normalized to >=1 for lower-rank arrays."""
108+
109+
def copy_from(self, src, *, stream):
110+
"""Copy a full-array's worth of data into this array.
111+
112+
Parameters
113+
----------
114+
src : Buffer or buffer-protocol object
115+
Source data. Must contain at least ``self.size_bytes`` bytes
116+
of contiguous data.
117+
stream : Stream
118+
Stream to issue the copy on.
119+
"""
120+
121+
def copy_to(self, dst, *, stream):
122+
"""Copy a full-array's worth of data out of this array.
123+
124+
Parameters
125+
----------
126+
dst : Buffer or writable buffer-protocol object
127+
Destination. Must have at least ``self.size_bytes`` bytes of
128+
writable, contiguous space.
129+
stream : Stream
130+
Stream to issue the copy on.
131+
"""
132+
133+
@property
134+
def size_bytes(self):
135+
"""Total bytes of array storage (``prod(shape) * element_size``)."""
136+
137+
def __dealloc__(self):
138+
...
139+
140+
def __enter__(self):
141+
...
142+
143+
def __exit__(self, exc_type, exc, tb):
144+
...
145+
146+
def __repr__(self):
147+
...
148+
_FORMAT_ELEM_SIZE = {int(ArrayFormat.UINT8): 1, int(ArrayFormat.INT8): 1, int(ArrayFormat.UINT16): 2, int(ArrayFormat.INT16): 2, int(ArrayFormat.FLOAT16): 2, int(ArrayFormat.UINT32): 4, int(ArrayFormat.INT32): 4, int(ArrayFormat.FLOAT32): 4}

cuda_core/cuda/core/_array.pyx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,10 @@ from cuda.core._utils.cuda_utils cimport (
1717
_get_current_device_id,
1818
)
1919

20-
import enum
20+
from enum import IntEnum
2121

2222

23-
class ArrayFormat(enum.IntEnum):
23+
class ArrayFormat(IntEnum):
2424
"""Element format for a :class:`CUDAArray` allocation.
2525
2626
Mirrors ``CUarray_format`` from the CUDA driver API.
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
# This file was generated by stubgen-pyx v0.2.6 from cuda_core/cuda/core/_mipmapped_array.pyx
2+
3+
from __future__ import annotations
4+
5+
6+
class MipmappedArray:
7+
"""A mipmapped CUDA array for texture/surface access across levels.
8+
9+
Wraps ``CUmipmappedArray``. Each mip level is a distinct, hardware-laid-out
10+
allocation accessible only via a :class:`TextureObject` (or by retrieving
11+
the level's :class:`CUDAArray` and binding it as a :class:`SurfaceObject`).
12+
Destroying the :class:`MipmappedArray` destroys all level arrays
13+
implicitly, so the :class:`CUDAArray` instances returned by :meth:`get_level`
14+
are non-owning and hold a strong reference back to their parent.
15+
16+
Construct via :meth:`from_descriptor`.
17+
"""
18+
19+
def close(self):
20+
"""Destroy the underlying ``CUmipmappedArray`` if owned.
21+
22+
After ``close()`` any level :class:`CUDAArray` returned by :meth:`get_level`
23+
becomes invalid; callers must not access them.
24+
"""
25+
26+
def __init__(self, *args, **kwargs):
27+
...
28+
29+
@classmethod
30+
def from_descriptor(cls, *, shape, format, num_channels, num_levels, surface_load_store=False):
31+
"""Allocate a new mipmapped CUDA array.
32+
33+
Parameters
34+
----------
35+
shape : tuple of int
36+
``(width,)``, ``(width, height)``, or ``(width, height, depth)``
37+
in elements, for the base (level 0) mip.
38+
format : ArrayFormat
39+
Element format.
40+
num_channels : int
41+
Channels per element. Must be 1, 2, or 4.
42+
num_levels : int
43+
Number of mip levels to allocate; must be >= 1. The driver caps
44+
this at the log2 of the largest dimension; passing a larger value
45+
yields a driver error.
46+
surface_load_store : bool
47+
If True, allocate with ``CUDA_ARRAY3D_SURFACE_LDST`` so individual
48+
levels (obtained via :meth:`get_level`) can be bound as
49+
:class:`SurfaceObject` for kernel-side writes. Default False.
50+
51+
Returns
52+
-------
53+
MipmappedArray
54+
"""
55+
56+
def get_level(self, level):
57+
"""Return a non-owning :class:`CUDAArray` view of the given mip level.
58+
59+
Parameters
60+
----------
61+
level : int
62+
Mip level index in ``[0, num_levels)``.
63+
64+
Returns
65+
-------
66+
CUDAArray
67+
A non-owning :class:`CUDAArray` wrapping the level's ``CUarray``.
68+
The :class:`MipmappedArray` is kept alive for the lifetime of the
69+
returned :class:`CUDAArray`; the underlying storage is released only
70+
when this :class:`MipmappedArray` is destroyed.
71+
"""
72+
73+
@property
74+
def handle(self):
75+
"""The underlying ``CUmipmappedArray`` as an integer."""
76+
77+
@property
78+
def shape(self):
79+
"""Base-level (level 0) allocation shape, in elements."""
80+
81+
@property
82+
def format(self):
83+
"""The element :class:`ArrayFormat`."""
84+
85+
@property
86+
def num_channels(self):
87+
"""Channels per element (1, 2, or 4)."""
88+
89+
@property
90+
def num_levels(self):
91+
"""Number of mip levels."""
92+
93+
@property
94+
def is_surface_load_store(self):
95+
"""True if this mipmap (and each of its levels) was created with
96+
``CUDA_ARRAY3D_SURFACE_LDST`` and can back a :class:`SurfaceObject`."""
97+
98+
@property
99+
def device(self):
100+
"""The :class:`Device` this mipmap was allocated on."""
101+
102+
def __dealloc__(self):
103+
...
104+
105+
def __enter__(self):
106+
...
107+
108+
def __exit__(self, exc_type, exc, tb):
109+
...
110+
111+
def __repr__(self):
112+
...

cuda_core/cuda/core/_surface.pyi

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
# This file was generated by stubgen-pyx v0.2.6 from cuda_core/cuda/core/_surface.pyx
2+
3+
from __future__ import annotations
4+
5+
6+
class SurfaceObject:
7+
"""A bindless surface handle for kernel-side typed load/store.
8+
9+
Wraps ``cuSurfObjectCreate``. Unlike a :class:`TextureObject`, a surface
10+
has no sampling state (no filtering, no addressing modes, no normalization);
11+
kernels read and write through it using integer pixel coordinates.
12+
13+
The backing :class:`CUDAArray` must have been created with
14+
``surface_load_store=True`` and is kept alive for the lifetime of this
15+
object to prevent dangling handles.
16+
17+
Construct via :meth:`from_array` or :meth:`from_descriptor`. Passes to
18+
kernels as a 64-bit handle (via the ``handle`` property).
19+
"""
20+
21+
def close(self):
22+
"""Destroy the underlying ``CUsurfObject``."""
23+
24+
def __init__(self, *args, **kwargs):
25+
...
26+
27+
@classmethod
28+
def from_array(cls, array):
29+
"""Create a surface object directly from an :class:`CUDAArray`.
30+
31+
The array must have been created with ``surface_load_store=True``.
32+
"""
33+
34+
@classmethod
35+
def from_descriptor(cls, *, resource):
36+
"""Create a surface object from a :class:`ResourceDescriptor`.
37+
38+
Parameters
39+
----------
40+
resource : ResourceDescriptor
41+
Must wrap an :class:`CUDAArray` allocated with
42+
``surface_load_store=True``. Linear/pitch2d resources are not
43+
valid surface backings.
44+
"""
45+
46+
@property
47+
def handle(self):
48+
"""The underlying ``CUsurfObject`` as an integer (64-bit kernel arg)."""
49+
50+
@property
51+
def resource(self):
52+
"""The :class:`ResourceDescriptor` this surface was built from."""
53+
54+
@property
55+
def device(self):
56+
...
57+
58+
def __dealloc__(self):
59+
...
60+
61+
def __enter__(self):
62+
...
63+
64+
def __exit__(self, exc_type, exc, tb):
65+
...
66+
67+
def __repr__(self):
68+
...

0 commit comments

Comments
 (0)