Skip to main content

baracuda_runtime/
array.rs

1//! CUDA arrays + texture / surface objects (Runtime API).
2//!
3//! Mirrors [`baracuda_driver::array`]. An [`Array`] is an opaque
4//! on-device layout optimized for texture fetches. [`TextureObject`] /
5//! [`SurfaceObject`] wrap the CUDA 5+ object-based texture API; the
6//! legacy reference-based API is intentionally not wrapped.
7
8use core::ffi::c_void;
9use std::sync::Arc;
10
11use baracuda_cuda_sys::runtime::runtime;
12use baracuda_cuda_sys::runtime::types::{
13    cudaArray_t, cudaChannelFormatDesc, cudaChannelFormatKind, cudaExtent, cudaMipmappedArray_t,
14    cudaResourceDesc, cudaResourceViewDesc, cudaSurfaceObject_t, cudaTextureDesc,
15    cudaTextureObject_t,
16};
17
18use crate::error::{check, Result};
19
20/// Construct a `cudaChannelFormatDesc` with 1/2/4 channels of `bits` bits
21/// of the given `kind` (matches the `cudaCreateChannelDesc<T>()` helpers
22/// in CUDA headers).
23pub fn channel_desc(
24    bits_x: i32,
25    bits_y: i32,
26    bits_z: i32,
27    bits_w: i32,
28    kind: i32,
29) -> cudaChannelFormatDesc {
30    cudaChannelFormatDesc {
31        x: bits_x,
32        y: bits_y,
33        z: bits_z,
34        w: bits_w,
35        kind,
36    }
37}
38
39/// `cudaCreateChannelDesc<u8>` — one 8-bit unsigned channel.
40#[inline]
41pub fn channel_desc_u8() -> cudaChannelFormatDesc {
42    channel_desc(8, 0, 0, 0, cudaChannelFormatKind::UNSIGNED)
43}
44
45/// `cudaCreateChannelDesc<f32>` — one 32-bit float channel.
46#[inline]
47pub fn channel_desc_f32() -> cudaChannelFormatDesc {
48    channel_desc(32, 0, 0, 0, cudaChannelFormatKind::FLOAT)
49}
50
51/// A 2-D / 3-D CUDA array handle.
52#[derive(Clone)]
53pub struct Array {
54    inner: Arc<ArrayInner>,
55}
56
57struct ArrayInner {
58    handle: cudaArray_t,
59    width: usize,
60    height: usize,
61    depth: usize,
62    desc: cudaChannelFormatDesc,
63}
64
65unsafe impl Send for ArrayInner {}
66unsafe impl Sync for ArrayInner {}
67
68impl core::fmt::Debug for ArrayInner {
69    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
70        f.debug_struct("Array")
71            .field("width", &self.width)
72            .field("height", &self.height)
73            .field("depth", &self.depth)
74            .field("desc", &self.desc)
75            .finish_non_exhaustive()
76    }
77}
78
79impl core::fmt::Debug for Array {
80    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
81        self.inner.fmt(f)
82    }
83}
84
85impl Array {
86    /// Allocate a 2-D array `width × height` with the given channel
87    /// descriptor. `flags = 0` for the default layout.
88    pub fn new_2d(
89        desc: &cudaChannelFormatDesc,
90        width: usize,
91        height: usize,
92        flags: u32,
93    ) -> Result<Self> {
94        let r = runtime()?;
95        let cu = r.cuda_malloc_array()?;
96        let mut arr: cudaArray_t = core::ptr::null_mut();
97        check(unsafe {
98            cu(
99                &mut arr,
100                desc as *const cudaChannelFormatDesc as *const c_void,
101                width,
102                height,
103                flags,
104            )
105        })?;
106        Ok(Self {
107            inner: Arc::new(ArrayInner {
108                handle: arr,
109                width,
110                height,
111                depth: 0,
112                desc: *desc,
113            }),
114        })
115    }
116
117    /// Allocate a 3-D array with the given extent + descriptor.
118    /// `flags = 0` for the default.
119    pub fn new_3d(desc: &cudaChannelFormatDesc, extent: cudaExtent, flags: u32) -> Result<Self> {
120        let r = runtime()?;
121        let cu = r.cuda_malloc_3d_array()?;
122        let mut arr: cudaArray_t = core::ptr::null_mut();
123        check(unsafe {
124            cu(
125                &mut arr,
126                desc as *const cudaChannelFormatDesc as *const c_void,
127                &extent as *const cudaExtent as *const c_void,
128                flags,
129            )
130        })?;
131        Ok(Self {
132            inner: Arc::new(ArrayInner {
133                handle: arr,
134                width: extent.width,
135                height: extent.height,
136                depth: extent.depth,
137                desc: *desc,
138            }),
139        })
140    }
141
142    /// Wrap an already-allocated `cudaArray_t`.
143    ///
144    /// # Safety
145    ///
146    /// `handle` must be a live CUDA array. The wrapper frees it on drop.
147    pub unsafe fn from_raw(
148        handle: cudaArray_t,
149        desc: cudaChannelFormatDesc,
150        width: usize,
151        height: usize,
152        depth: usize,
153    ) -> Self {
154        Self {
155            inner: Arc::new(ArrayInner {
156                handle,
157                width,
158                height,
159                depth,
160                desc,
161            }),
162        }
163    }
164
165    #[inline]
166    pub fn as_raw(&self) -> cudaArray_t {
167        self.inner.handle
168    }
169
170    #[inline]
171    pub fn width(&self) -> usize {
172        self.inner.width
173    }
174    #[inline]
175    pub fn height(&self) -> usize {
176        self.inner.height
177    }
178    #[inline]
179    pub fn depth(&self) -> usize {
180        self.inner.depth
181    }
182    #[inline]
183    pub fn desc(&self) -> &cudaChannelFormatDesc {
184        &self.inner.desc
185    }
186
187    /// Query the runtime-reported channel desc + extent.
188    pub fn info(&self) -> Result<(cudaChannelFormatDesc, cudaExtent, u32)> {
189        let r = runtime()?;
190        let cu = r.cuda_array_get_info()?;
191        let mut desc = cudaChannelFormatDesc::default();
192        let mut extent = cudaExtent::default();
193        let mut flags: core::ffi::c_uint = 0;
194        check(unsafe {
195            cu(
196                &mut desc as *mut cudaChannelFormatDesc as *mut c_void,
197                &mut extent as *mut cudaExtent as *mut c_void,
198                &mut flags,
199                self.inner.handle,
200            )
201        })?;
202        Ok((desc, extent, flags))
203    }
204}
205
206impl Drop for ArrayInner {
207    fn drop(&mut self) {
208        if self.handle.is_null() {
209            return;
210        }
211        if let Ok(r) = runtime() {
212            if let Ok(cu) = r.cuda_free_array() {
213                let _ = unsafe { cu(self.handle) };
214            }
215        }
216    }
217}
218
219/// A mipmapped CUDA array.
220#[derive(Clone)]
221pub struct MipmappedArray {
222    inner: Arc<MipmappedArrayInner>,
223}
224
225struct MipmappedArrayInner {
226    handle: cudaMipmappedArray_t,
227}
228
229unsafe impl Send for MipmappedArrayInner {}
230unsafe impl Sync for MipmappedArrayInner {}
231
232impl core::fmt::Debug for MipmappedArray {
233    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
234        f.debug_struct("MipmappedArray")
235            .field("handle", &self.inner.handle)
236            .finish()
237    }
238}
239
240impl MipmappedArray {
241    /// Allocate a mipmapped array.
242    pub fn new(
243        desc: &cudaChannelFormatDesc,
244        extent: cudaExtent,
245        num_levels: u32,
246        flags: u32,
247    ) -> Result<Self> {
248        let r = runtime()?;
249        let cu = r.cuda_malloc_mipmapped_array()?;
250        let mut h: cudaMipmappedArray_t = core::ptr::null_mut();
251        check(unsafe {
252            cu(
253                &mut h,
254                desc as *const cudaChannelFormatDesc as *const c_void,
255                &extent as *const cudaExtent as *const c_void,
256                num_levels,
257                flags,
258            )
259        })?;
260        Ok(Self {
261            inner: Arc::new(MipmappedArrayInner { handle: h }),
262        })
263    }
264
265    #[inline]
266    pub fn as_raw(&self) -> cudaMipmappedArray_t {
267        self.inner.handle
268    }
269
270    /// Fetch the `level`-th mipmap as a regular `cudaArray_t` (view; does
271    /// NOT free on drop — the parent mipmapped array owns it).
272    pub fn level(&self, level: u32) -> Result<cudaArray_t> {
273        let r = runtime()?;
274        let cu = r.cuda_get_mipmapped_array_level()?;
275        let mut out: cudaArray_t = core::ptr::null_mut();
276        check(unsafe { cu(&mut out, self.inner.handle, level) })?;
277        Ok(out)
278    }
279}
280
281impl Drop for MipmappedArrayInner {
282    fn drop(&mut self) {
283        if let Ok(r) = runtime() {
284            if let Ok(cu) = r.cuda_free_mipmapped_array() {
285                let _ = unsafe { cu(self.handle) };
286            }
287        }
288    }
289}
290
291/// A texture object — a read-only sampler bound to an array (or linear
292/// device memory). Pass `as_raw()` as a u64 kernel argument.
293pub struct TextureObject {
294    handle: cudaTextureObject_t,
295    // Keep the backing array alive for the lifetime of the texture.
296    _backing: Option<Array>,
297}
298
299impl core::fmt::Debug for TextureObject {
300    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
301        f.debug_struct("TextureObject")
302            .field("handle", &self.handle)
303            .finish()
304    }
305}
306
307impl TextureObject {
308    /// Create a texture object over an array with the given sampler + view.
309    pub fn new(
310        array: &Array,
311        tex_desc: &cudaTextureDesc,
312        view_desc: Option<&cudaResourceViewDesc>,
313    ) -> Result<Self> {
314        let res_desc = cudaResourceDesc::from_array(array.as_raw());
315        let r = runtime()?;
316        let cu = r.cuda_create_texture_object()?;
317        let mut obj: cudaTextureObject_t = 0;
318        let view_ptr = view_desc
319            .map(|v| v as *const cudaResourceViewDesc as *const c_void)
320            .unwrap_or(core::ptr::null());
321        check(unsafe {
322            cu(
323                &mut obj,
324                &res_desc as *const cudaResourceDesc as *const c_void,
325                tex_desc as *const cudaTextureDesc as *const c_void,
326                view_ptr,
327            )
328        })?;
329        Ok(Self {
330            handle: obj,
331            _backing: Some(array.clone()),
332        })
333    }
334
335    /// Create a texture over a raw `cudaResourceDesc` (e.g. a linear
336    /// memory slab).
337    ///
338    /// # Safety
339    ///
340    /// `res_desc`'s backing memory must outlive the returned texture.
341    pub unsafe fn from_resource(
342        res_desc: &cudaResourceDesc,
343        tex_desc: &cudaTextureDesc,
344        view_desc: Option<&cudaResourceViewDesc>,
345    ) -> Result<Self> { unsafe {
346        let r = runtime()?;
347        let cu = r.cuda_create_texture_object()?;
348        let mut obj: cudaTextureObject_t = 0;
349        let view_ptr = view_desc
350            .map(|v| v as *const cudaResourceViewDesc as *const c_void)
351            .unwrap_or(core::ptr::null());
352        check(cu(
353            &mut obj,
354            res_desc as *const cudaResourceDesc as *const c_void,
355            tex_desc as *const cudaTextureDesc as *const c_void,
356            view_ptr,
357        ))?;
358        Ok(Self {
359            handle: obj,
360            _backing: None,
361        })
362    }}
363
364    #[inline]
365    pub fn as_raw(&self) -> cudaTextureObject_t {
366        self.handle
367    }
368
369    /// Query the resource descriptor the texture was created with.
370    pub fn resource_desc(&self) -> Result<cudaResourceDesc> {
371        let r = runtime()?;
372        let cu = r.cuda_get_texture_object_resource_desc()?;
373        let mut d = cudaResourceDesc::default();
374        check(unsafe { cu(&mut d as *mut cudaResourceDesc as *mut c_void, self.handle) })?;
375        Ok(d)
376    }
377
378    /// Query the sampler (filter/address/normalize) state.
379    pub fn texture_desc(&self) -> Result<cudaTextureDesc> {
380        let r = runtime()?;
381        let cu = r.cuda_get_texture_object_texture_desc()?;
382        let mut d = cudaTextureDesc::default();
383        check(unsafe { cu(&mut d as *mut cudaTextureDesc as *mut c_void, self.handle) })?;
384        Ok(d)
385    }
386}
387
388impl Drop for TextureObject {
389    fn drop(&mut self) {
390        if let Ok(r) = runtime() {
391            if let Ok(cu) = r.cuda_destroy_texture_object() {
392                let _ = unsafe { cu(self.handle) };
393            }
394        }
395    }
396}
397
398/// A surface object — writable array access from kernels.
399pub struct SurfaceObject {
400    handle: cudaSurfaceObject_t,
401    _backing: Option<Array>,
402}
403
404impl core::fmt::Debug for SurfaceObject {
405    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
406        f.debug_struct("SurfaceObject")
407            .field("handle", &self.handle)
408            .finish()
409    }
410}
411
412impl SurfaceObject {
413    /// Create a surface object over a CUDA array.
414    pub fn new(array: &Array) -> Result<Self> {
415        let res_desc = cudaResourceDesc::from_array(array.as_raw());
416        let r = runtime()?;
417        let cu = r.cuda_create_surface_object()?;
418        let mut obj: cudaSurfaceObject_t = 0;
419        check(unsafe {
420            cu(
421                &mut obj,
422                &res_desc as *const cudaResourceDesc as *const c_void,
423            )
424        })?;
425        Ok(Self {
426            handle: obj,
427            _backing: Some(array.clone()),
428        })
429    }
430
431    #[inline]
432    pub fn as_raw(&self) -> cudaSurfaceObject_t {
433        self.handle
434    }
435
436    /// Query the resource descriptor the surface was created with.
437    pub fn resource_desc(&self) -> Result<cudaResourceDesc> {
438        let r = runtime()?;
439        let cu = r.cuda_get_surface_object_resource_desc()?;
440        let mut d = cudaResourceDesc::default();
441        check(unsafe { cu(&mut d as *mut cudaResourceDesc as *mut c_void, self.handle) })?;
442        Ok(d)
443    }
444}
445
446impl Drop for SurfaceObject {
447    fn drop(&mut self) {
448        if let Ok(r) = runtime() {
449            if let Ok(cu) = r.cuda_destroy_surface_object() {
450                let _ = unsafe { cu(self.handle) };
451            }
452        }
453    }
454}