Skip to main content

baracuda_runtime/
external.rs

1//! External memory / semaphore interop via the Runtime API.
2//!
3//! Mirrors [`baracuda_driver::external`]. Because `cudaExternalMemory_t`
4//! and `CUexternalMemory` are typedef-compatible (same underlying C
5//! pointer), the two wrappers are interchangeable at the handle level —
6//! this module exists so Runtime-API users don't have to pull in the
7//! Driver crate just for external-resource import.
8//!
9//! Struct layouts (`CUDA_EXTERNAL_MEMORY_HANDLE_DESC` etc.) are shared
10//! with the Driver API — populate using the same typed builders in
11//! [`baracuda_cuda_sys::types`].
12
13use std::sync::Arc;
14
15use baracuda_cuda_sys::runtime::{cudaExternalMemory_t, cudaExternalSemaphore_t, runtime};
16use baracuda_cuda_sys::types::{
17    CUDA_EXTERNAL_MEMORY_BUFFER_DESC, CUDA_EXTERNAL_MEMORY_HANDLE_DESC,
18    CUDA_EXTERNAL_SEMAPHORE_HANDLE_DESC, CUDA_EXTERNAL_SEMAPHORE_SIGNAL_PARAMS,
19    CUDA_EXTERNAL_SEMAPHORE_WAIT_PARAMS,
20};
21
22use crate::error::{Result, check};
23use crate::stream::Stream;
24
25/// An imported external-memory handle (Vulkan `VkDeviceMemory`, D3D12
26/// heap / resource, NvSciBuf, DMA-buf FD, ...). Destroyed on drop.
27#[derive(Clone)]
28pub struct ExternalMemory {
29    inner: Arc<ExternalMemoryInner>,
30}
31
32struct ExternalMemoryInner {
33    handle: cudaExternalMemory_t,
34}
35
36unsafe impl Send for ExternalMemoryInner {}
37unsafe impl Sync for ExternalMemoryInner {}
38
39impl core::fmt::Debug for ExternalMemoryInner {
40    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
41        f.debug_struct("ExternalMemory")
42            .field("handle", &self.handle)
43            .finish_non_exhaustive()
44    }
45}
46
47impl core::fmt::Debug for ExternalMemory {
48    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
49        self.inner.fmt(f)
50    }
51}
52
53impl ExternalMemory {
54    /// Import an external-memory handle described by `desc`.
55    ///
56    /// # Safety
57    ///
58    /// `desc.handle` must describe a live OS object that the process can
59    /// access (file descriptor, NT HANDLE, NvSciBufObj, ...). CUDA
60    /// retains a reference to the underlying memory until this
61    /// `ExternalMemory` drops.
62    pub unsafe fn import(desc: &CUDA_EXTERNAL_MEMORY_HANDLE_DESC) -> Result<Self> {
63        unsafe {
64            let r = runtime()?;
65            let cu = r.cuda_import_external_memory()?;
66            let mut handle: cudaExternalMemory_t = core::ptr::null_mut();
67            check(cu(&mut handle, desc))?;
68            Ok(Self {
69                inner: Arc::new(ExternalMemoryInner { handle }),
70            })
71        }
72    }
73
74    /// Expose a subregion of the imported memory as a device pointer
75    /// valid in the *current* CUDA context.
76    pub fn mapped_buffer(
77        &self,
78        offset: u64,
79        size: u64,
80        flags: u32,
81    ) -> Result<*mut core::ffi::c_void> {
82        let r = runtime()?;
83        let cu = r.cuda_external_memory_get_mapped_buffer()?;
84        let desc = CUDA_EXTERNAL_MEMORY_BUFFER_DESC {
85            offset,
86            size,
87            flags,
88            reserved: [0; 16],
89        };
90        let mut ptr: *mut core::ffi::c_void = core::ptr::null_mut();
91        check(unsafe { cu(&mut ptr, self.inner.handle, &desc) })?;
92        Ok(ptr)
93    }
94
95    /// Raw handle. Interchangeable with `CUexternalMemory` at the ABI
96    /// level — cast via `as baracuda_cuda_sys::CUexternalMemory` if you
97    /// need to interop with the Driver-side wrapper.
98    #[inline]
99    pub fn as_raw(&self) -> cudaExternalMemory_t {
100        self.inner.handle
101    }
102}
103
104impl Drop for ExternalMemoryInner {
105    fn drop(&mut self) {
106        if self.handle.is_null() {
107            return;
108        }
109        if let Ok(r) = runtime() {
110            if let Ok(cu) = r.cuda_destroy_external_memory() {
111                let _ = unsafe { cu(self.handle) };
112            }
113        }
114    }
115}
116
117/// An imported external-semaphore handle. Destroyed on drop.
118#[derive(Clone)]
119pub struct ExternalSemaphore {
120    inner: Arc<ExternalSemaphoreInner>,
121}
122
123struct ExternalSemaphoreInner {
124    handle: cudaExternalSemaphore_t,
125}
126
127unsafe impl Send for ExternalSemaphoreInner {}
128unsafe impl Sync for ExternalSemaphoreInner {}
129
130impl core::fmt::Debug for ExternalSemaphoreInner {
131    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
132        f.debug_struct("ExternalSemaphore")
133            .field("handle", &self.handle)
134            .finish_non_exhaustive()
135    }
136}
137
138impl core::fmt::Debug for ExternalSemaphore {
139    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
140        self.inner.fmt(f)
141    }
142}
143
144impl ExternalSemaphore {
145    /// Import an external-semaphore handle.
146    ///
147    /// # Safety
148    ///
149    /// Same discipline as [`ExternalMemory::import`].
150    pub unsafe fn import(desc: &CUDA_EXTERNAL_SEMAPHORE_HANDLE_DESC) -> Result<Self> {
151        unsafe {
152            let r = runtime()?;
153            let cu = r.cuda_import_external_semaphore()?;
154            let mut handle: cudaExternalSemaphore_t = core::ptr::null_mut();
155            check(cu(&mut handle, desc))?;
156            Ok(Self {
157                inner: Arc::new(ExternalSemaphoreInner { handle }),
158            })
159        }
160    }
161
162    /// Enqueue a signal of fence value `value` on `stream`.
163    pub fn signal_fence_async(&self, value: u64, stream: &Stream) -> Result<()> {
164        let r = runtime()?;
165        let cu = r.cuda_signal_external_semaphores_async()?;
166        let params = CUDA_EXTERNAL_SEMAPHORE_SIGNAL_PARAMS::fence_value(value);
167        check(unsafe { cu(&self.inner.handle, &params, 1, stream.as_raw()) })
168    }
169
170    /// Enqueue a wait for fence value `value` on `stream`.
171    pub fn wait_fence_async(&self, value: u64, stream: &Stream) -> Result<()> {
172        let r = runtime()?;
173        let cu = r.cuda_wait_external_semaphores_async()?;
174        let params = CUDA_EXTERNAL_SEMAPHORE_WAIT_PARAMS::fence_value(value);
175        check(unsafe { cu(&self.inner.handle, &params, 1, stream.as_raw()) })
176    }
177
178    /// Raw `cudaExternalSemaphore_t` handle. Use with care — owned by `self`.
179    #[inline]
180    pub fn as_raw(&self) -> cudaExternalSemaphore_t {
181        self.inner.handle
182    }
183}
184
185impl Drop for ExternalSemaphoreInner {
186    fn drop(&mut self) {
187        if self.handle.is_null() {
188            return;
189        }
190        if let Ok(r) = runtime() {
191            if let Ok(cu) = r.cuda_destroy_external_semaphore() {
192                let _ = unsafe { cu(self.handle) };
193            }
194        }
195    }
196}