Coverage for cuda/core/texture/_surface.pyx: 76.19%
42 statements
« prev ^ index » next coverage.py v7.15.0, created at 2026-07-03 01:38 +0000
« prev ^ index » next coverage.py v7.15.0, created at 2026-07-03 01:38 +0000
1# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2#
3# SPDX-License-Identifier: Apache-2.0
5from __future__ import annotations
7from libc.string cimport memset
9from cuda.bindings cimport cydriver
10from cuda.core.texture._array cimport OpaqueArray
11from cuda.core._resource_handles cimport (
12 SurfObjectHandle,
13 as_cu,
14 as_intptr,
15 create_surf_object_handle,
16 get_last_error,
17)
18from cuda.core.texture._texture import ResourceDescriptor
19from cuda.core._utils.cuda_utils cimport (
20 HANDLE_RETURN,
21 _get_current_device_id,
22)
25cdef class SurfaceObject:
26 """A bindless surface handle for kernel-side typed load/store.
28 Wraps ``cuSurfObjectCreate``. Unlike a :class:`TextureObject`, a surface
29 has no sampling state (no filtering, no addressing modes, no normalization);
30 kernels read and write through it using integer pixel coordinates.
32 The backing :class:`OpaqueArray` must have been created with
33 ``is_surface_load_store=True`` and is kept alive for the lifetime of this
34 object to prevent dangling handles.
36 Construct via :meth:`from_array` or :meth:`from_descriptor`. Passes to
37 kernels as a 64-bit handle (via the ``handle`` property).
38 """
40 def __init__(self, *args, **kwargs):
41 raise RuntimeError( 1h
42 "SurfaceObject cannot be instantiated directly. "
43 "Use SurfaceObject.from_array() or SurfaceObject.from_descriptor()."
44 )
46 @classmethod
47 def from_array(cls, array):
48 """Create a surface object directly from an :class:`OpaqueArray`.
50 The array must have been created with ``is_surface_load_store=True``.
51 """
52 if not isinstance(array, OpaqueArray): 1dabec
53 raise TypeError(f"array must be a OpaqueArray, got {type(array).__name__}")
54 return cls.from_descriptor(resource=ResourceDescriptor.from_array(array)) 1abec
56 @classmethod
57 def from_descriptor(cls, *, resource):
58 """Create a surface object from a :class:`ResourceDescriptor`.
60 Parameters
61 ----------
62 resource : ResourceDescriptor
63 Must wrap an :class:`OpaqueArray` allocated with
64 ``is_surface_load_store=True``. Linear/pitch2d resources are not
65 valid surface backings.
66 """
67 if not isinstance(resource, ResourceDescriptor): 1abfgec
68 raise TypeError(
69 f"resource must be a ResourceDescriptor, got "
70 f"{type(resource).__name__}"
71 )
72 if resource.kind != "array": 1abfgec
73 raise ValueError( 1fg
74 f"SurfaceObject requires an array-backed ResourceDescriptor, " 1fg
75 f"got kind={resource.kind!r}" 1fg
76 )
78 cdef OpaqueArray arr = <OpaqueArray>resource.source 1abec
79 if not arr.is_surface_load_store: 1abec
80 raise ValueError( 1e
81 "OpaqueArray must be created with is_surface_load_store=True to be "
82 "bound as a SurfaceObject"
83 )
85 cdef cydriver.CUDA_RESOURCE_DESC res_desc
86 memset(&res_desc, 0, sizeof(res_desc)) 1abc
87 res_desc.resType = cydriver.CU_RESOURCE_TYPE_ARRAY 1abc
88 res_desc.res.array.hArray = as_cu(arr._handle) 1abc
90 cdef SurfObjectHandle h = create_surf_object_handle(res_desc, arr._handle) 1abc
91 if not h: 1abc
92 HANDLE_RETURN(get_last_error())
94 cdef SurfaceObject self = cls.__new__(cls) 1abc
95 self._handle = h 1abc
96 self._source_ref = resource 1abc
97 self._device_id = _get_current_device_id() 1abc
98 return self 1abc
100 @property
101 def handle(self):
102 """The underlying ``CUsurfObject`` as an integer (64-bit kernel arg)."""
103 return as_intptr(self._handle) 1abc
105 @property
106 def resource(self):
107 """The :class:`ResourceDescriptor` this surface was built from."""
108 return self._source_ref 1a
110 @property
111 def device(self):
112 from cuda.core._device import Device
113 return Device(self._device_id)
115 cpdef close(self):
116 """Release this object's reference to the underlying ``CUsurfObject``.
118 Destruction (``cuSurfObjectDestroy``) and release of the backing array
119 happen via the handle's deleter when the last reference is dropped.
120 Idempotent.
121 """
122 self._handle.reset() 1abc
123 self._source_ref = None 1abc
125 def __enter__(self):
126 return self
128 def __exit__(self, exc_type, exc, tb):
129 self.close()
131 def __repr__(self):
132 return f"SurfaceObject(handle=0x{as_intptr(self._handle):x})"