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

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.string cimport memset 

8  

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) 

23  

24  

25cdef class SurfaceObject: 

26 """A bindless surface handle for kernel-side typed load/store. 

27  

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. 

31  

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. 

35  

36 Construct via :meth:`from_array` or :meth:`from_descriptor`. Passes to 

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

38 """ 

39  

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 ) 

45  

46 @classmethod 

47 def from_array(cls, array): 

48 """Create a surface object directly from an :class:`OpaqueArray`. 

49  

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

55  

56 @classmethod 

57 def from_descriptor(cls, *, resource): 

58 """Create a surface object from a :class:`ResourceDescriptor`. 

59  

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 ) 

77  

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 ) 

84  

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

89  

90 cdef SurfObjectHandle h = create_surf_object_handle(res_desc, arr._handle) 1abc

91 if not h: 1abc

92 HANDLE_RETURN(get_last_error()) 

93  

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

99  

100 @property 

101 def handle(self): 

102 """The underlying ``CUsurfObject`` as an integer (64-bit kernel arg).""" 

103 return as_intptr(self._handle) 1abc

104  

105 @property 

106 def resource(self): 

107 """The :class:`ResourceDescriptor` this surface was built from.""" 

108 return self._source_ref 1a

109  

110 @property 

111 def device(self): 

112 from cuda.core._device import Device 

113 return Device(self._device_id) 

114  

115 cpdef close(self): 

116 """Release this object's reference to the underlying ``CUsurfObject``. 

117  

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

124  

125 def __enter__(self): 

126 return self 

127  

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

129 self.close() 

130  

131 def __repr__(self): 

132 return f"SurfaceObject(handle=0x{as_intptr(self._handle):x})"