Skip to main content

baracuda_driver/
graphics.rs

1//! Graphics-API interop — register GL / D3D / VDPAU / EGL resources with
2//! CUDA for zero-copy compute on their memory.
3//!
4//! **Note:** For modern flows on Vulkan and D3D12, prefer
5//! [`crate::external::ExternalMemory`] — it's the forward-looking API
6//! NVIDIA recommends. The `cuGraphics*` functions here remain useful for:
7//!
8//! - OpenGL interop on drivers without `GL_EXT_memory_object_fd/win32`.
9//! - D3D9 / D3D10 / D3D11 legacy code paths.
10//! - VDPAU video-surface import on Linux.
11//! - EGL image / stream interop on Jetson.
12//!
13//! # Workflow
14//!
15//! 1. Register the graphics resource (`gl::register_buffer`,
16//!    `d3d11::register_resource`, ...) — returns a [`GraphicsResource`].
17//! 2. Call [`GraphicsResource::map`] on a stream to expose it to CUDA.
18//! 3. Call [`GraphicsResource::mapped_pointer`] or
19//!    [`GraphicsResource::mapped_array`] to get a usable handle.
20//! 4. ... compute ...
21//! 5. [`GraphicsResource::unmap`] to release it back to the graphics API.
22//!
23//! On drop, the resource is unregistered (and unmapped if still mapped,
24//! best-effort).
25
26use std::sync::Arc;
27
28use baracuda_cuda_sys::types::CUmipmappedArray;
29use baracuda_cuda_sys::{driver, CUarray, CUdeviceptr, CUgraphicsResource};
30
31use crate::context::Context;
32use crate::error::{check, Result};
33use crate::stream::Stream;
34
35pub use baracuda_cuda_sys::types::{
36    CUGLDeviceList as GLDeviceList, CUgraphicsMapResourceFlags as MapResourceFlags,
37    CUgraphicsRegisterFlags as RegisterFlags,
38};
39
40/// A CUDA-side handle to a graphics-API resource. Drops unregister the
41/// resource via `cuGraphicsUnregisterResource`.
42#[derive(Clone)]
43pub struct GraphicsResource {
44    inner: Arc<GraphicsResourceInner>,
45}
46
47struct GraphicsResourceInner {
48    handle: CUgraphicsResource,
49    #[allow(dead_code)]
50    context: Context,
51}
52
53unsafe impl Send for GraphicsResourceInner {}
54unsafe impl Sync for GraphicsResourceInner {}
55
56impl core::fmt::Debug for GraphicsResourceInner {
57    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
58        f.debug_struct("GraphicsResource")
59            .field("handle", &self.handle)
60            .finish_non_exhaustive()
61    }
62}
63
64impl core::fmt::Debug for GraphicsResource {
65    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
66        self.inner.fmt(f)
67    }
68}
69
70impl GraphicsResource {
71    /// Wrap a raw `CUgraphicsResource`. The resource is assumed to be
72    /// registered already (e.g. from `cu<API>Register*`).
73    ///
74    /// # Safety
75    ///
76    /// `handle` must be a live, registered resource. baracuda unregisters
77    /// it when this wrapper (and all clones) drop.
78    pub unsafe fn from_raw(context: &Context, handle: CUgraphicsResource) -> Self {
79        Self {
80            inner: Arc::new(GraphicsResourceInner {
81                handle,
82                context: context.clone(),
83            }),
84        }
85    }
86
87    #[inline]
88    pub fn as_raw(&self) -> CUgraphicsResource {
89        self.inner.handle
90    }
91
92    /// Change the map flags before mapping (e.g. READ_ONLY for shader
93    /// sampling, WRITE_DISCARD for compute output).
94    pub fn set_map_flags(&self, flags: u32) -> Result<()> {
95        let d = driver()?;
96        let cu = d.cu_graphics_resource_set_map_flags()?;
97        check(unsafe { cu(self.inner.handle, flags) })
98    }
99
100    /// Map the resource on `stream` so CUDA can access it. Pair with
101    /// [`Self::unmap`].
102    pub fn map(&self, stream: &Stream) -> Result<()> {
103        let d = driver()?;
104        let cu = d.cu_graphics_map_resources()?;
105        let mut arr = [self.inner.handle];
106        check(unsafe { cu(1, arr.as_mut_ptr(), stream.as_raw()) })
107    }
108
109    /// Unmap the resource on `stream`, releasing it back to the graphics API.
110    pub fn unmap(&self, stream: &Stream) -> Result<()> {
111        let d = driver()?;
112        let cu = d.cu_graphics_unmap_resources()?;
113        let mut arr = [self.inner.handle];
114        check(unsafe { cu(1, arr.as_mut_ptr(), stream.as_raw()) })
115    }
116
117    /// Retrieve the device pointer + size for a mapped buffer-type
118    /// resource (typical of GL buffer objects, D3D buffers).
119    pub fn mapped_pointer(&self) -> Result<(CUdeviceptr, usize)> {
120        let d = driver()?;
121        let cu = d.cu_graphics_resource_get_mapped_pointer()?;
122        let mut dptr = CUdeviceptr(0);
123        let mut size: usize = 0;
124        check(unsafe { cu(&mut dptr, &mut size, self.inner.handle) })?;
125        Ok((dptr, size))
126    }
127
128    /// Retrieve the subresource CUDA array for a mapped image / texture
129    /// resource (GL textures, D3D textures, VDPAU surfaces).
130    pub fn mapped_array(&self, array_index: u32, mip_level: u32) -> Result<CUarray> {
131        let d = driver()?;
132        let cu = d.cu_graphics_sub_resource_get_mapped_array()?;
133        let mut arr: CUarray = core::ptr::null_mut();
134        check(unsafe { cu(&mut arr, self.inner.handle, array_index, mip_level) })?;
135        Ok(arr)
136    }
137
138    /// Retrieve the mipmapped-array handle for a mapped mipmapped texture.
139    pub fn mapped_mipmapped_array(&self) -> Result<CUmipmappedArray> {
140        let d = driver()?;
141        let cu = d.cu_graphics_resource_get_mapped_mipmapped_array()?;
142        let mut mip: CUmipmappedArray = core::ptr::null_mut();
143        check(unsafe { cu(&mut mip, self.inner.handle) })?;
144        Ok(mip)
145    }
146
147    /// Bulk-map multiple resources in one call. More efficient than
148    /// calling [`Self::map`] per resource.
149    pub fn map_all(resources: &[Self], stream: &Stream) -> Result<()> {
150        if resources.is_empty() {
151            return Ok(());
152        }
153        let d = driver()?;
154        let cu = d.cu_graphics_map_resources()?;
155        let mut raws: Vec<CUgraphicsResource> = resources.iter().map(|r| r.as_raw()).collect();
156        check(unsafe {
157            cu(
158                raws.len() as core::ffi::c_uint,
159                raws.as_mut_ptr(),
160                stream.as_raw(),
161            )
162        })
163    }
164
165    /// Bulk-unmap.
166    pub fn unmap_all(resources: &[Self], stream: &Stream) -> Result<()> {
167        if resources.is_empty() {
168            return Ok(());
169        }
170        let d = driver()?;
171        let cu = d.cu_graphics_unmap_resources()?;
172        let mut raws: Vec<CUgraphicsResource> = resources.iter().map(|r| r.as_raw()).collect();
173        check(unsafe {
174            cu(
175                raws.len() as core::ffi::c_uint,
176                raws.as_mut_ptr(),
177                stream.as_raw(),
178            )
179        })
180    }
181}
182
183impl Drop for GraphicsResourceInner {
184    fn drop(&mut self) {
185        if self.handle.is_null() {
186            return;
187        }
188        if let Ok(d) = driver() {
189            if let Ok(cu) = d.cu_graphics_unregister_resource() {
190                let _ = unsafe { cu(self.handle) };
191            }
192        }
193    }
194}
195
196/// OpenGL-specific interop.
197pub mod gl {
198    use super::*;
199    use crate::device::Device;
200    use baracuda_cuda_sys::types::{GLenum, GLuint};
201
202    /// Initialize the GL interop subsystem. Must be called from a thread
203    /// with a current GL context. Deprecated but retained for legacy code.
204    pub fn init() -> Result<()> {
205        let d = driver()?;
206        let cu = d.cu_gl_init()?;
207        check(unsafe { cu() })
208    }
209
210    /// List CUDA devices associated with a given slice of the GL
211    /// rendering pipeline (current frame, next frame, or all devices).
212    pub fn get_devices(device_list: u32) -> Result<Vec<Device>> {
213        let d = driver()?;
214        let cu = d.cu_gl_get_devices()?;
215        // Query count first by passing cuda_device_count_in = 0.
216        let mut count: core::ffi::c_uint = 0;
217        let probe = unsafe { cu(&mut count, core::ptr::null_mut(), 0, device_list) };
218        // Some driver versions return SUCCESS with count set; others
219        // return NOT_INITIALIZED if there's no GL context. Treat the
220        // latter as "no GL devices" rather than error.
221        if !probe.is_success() && probe != baracuda_cuda_sys::CUresult::ERROR_NOT_INITIALIZED {
222            check(probe)?;
223        }
224        if count == 0 {
225            return Ok(Vec::new());
226        }
227        let mut raw = vec![baracuda_cuda_sys::CUdevice(0); count as usize];
228        check(unsafe {
229            cu(
230                &mut count,
231                raw.as_mut_ptr(),
232                raw.len() as core::ffi::c_uint,
233                device_list,
234            )
235        })?;
236        // SAFETY: `Device` is a `pub(crate)` newtype over `CUdevice`;
237        // constructing from driver-returned ordinals is safe.
238        Ok(raw.into_iter().map(Device).collect())
239    }
240
241    /// Register an OpenGL buffer object (VBO / SSBO / UBO / ...) for CUDA
242    /// access. `buffer` is the GL buffer name from `glGenBuffers`.
243    ///
244    /// # Safety
245    ///
246    /// `buffer` must be a valid GL buffer in a GL context that is current
247    /// on the calling thread. The buffer must outlive the returned
248    /// [`GraphicsResource`].
249    pub unsafe fn register_buffer(
250        context: &Context,
251        buffer: GLuint,
252        flags: u32,
253    ) -> Result<GraphicsResource> { unsafe {
254        context.set_current()?;
255        let d = driver()?;
256        let cu = d.cu_graphics_gl_register_buffer()?;
257        let mut resource: CUgraphicsResource = core::ptr::null_mut();
258        check(cu(&mut resource, buffer, flags))?;
259        Ok(GraphicsResource::from_raw(context, resource))
260    }}
261
262    /// Register an OpenGL texture / renderbuffer. `target` is the GL
263    /// binding target (`GL_TEXTURE_2D`, `GL_RENDERBUFFER`, ...).
264    ///
265    /// # Safety
266    ///
267    /// Same discipline as [`register_buffer`]: `image` must be live in a
268    /// current GL context, matching the declared `target`.
269    pub unsafe fn register_image(
270        context: &Context,
271        image: GLuint,
272        target: GLenum,
273        flags: u32,
274    ) -> Result<GraphicsResource> { unsafe {
275        context.set_current()?;
276        let d = driver()?;
277        let cu = d.cu_graphics_gl_register_image()?;
278        let mut resource: CUgraphicsResource = core::ptr::null_mut();
279        check(cu(&mut resource, image, target, flags))?;
280        Ok(GraphicsResource::from_raw(context, resource))
281    }}
282}
283
284/// Direct3D 9 interop (Windows).
285pub mod d3d9 {
286    use super::*;
287    use crate::device::Device;
288    use baracuda_cuda_sys::types::{ID3DDevice, ID3DResource};
289
290    /// Find the CUDA device powering a D3D9 adapter, by adapter name.
291    ///
292    /// # Safety
293    ///
294    /// `adapter_name` must be a NUL-terminated C string naming a live
295    /// DXGI adapter.
296    pub unsafe fn get_device(adapter_name: *const core::ffi::c_char) -> Result<Device> { unsafe {
297        let d = driver()?;
298        let cu = d.cu_d3d9_get_device()?;
299        let mut dev = baracuda_cuda_sys::CUdevice(0);
300        check(cu(&mut dev, adapter_name))?;
301        Ok(Device(dev))
302    }}
303
304    /// List CUDA devices associated with a given D3D9 device.
305    ///
306    /// # Safety
307    ///
308    /// `d3d_device` must be a valid `IDirect3DDevice9*` pointer.
309    pub unsafe fn get_devices(d3d_device: ID3DDevice, device_list: u32) -> Result<Vec<Device>> { unsafe {
310        let d = driver()?;
311        let cu = d.cu_d3d9_get_devices()?;
312        let mut count: core::ffi::c_uint = 0;
313        check(cu(
314            &mut count,
315            core::ptr::null_mut(),
316            0,
317            d3d_device,
318            device_list,
319        ))?;
320        if count == 0 {
321            return Ok(Vec::new());
322        }
323        let mut raw = vec![baracuda_cuda_sys::CUdevice(0); count as usize];
324        check(cu(
325            &mut count,
326            raw.as_mut_ptr(),
327            raw.len() as core::ffi::c_uint,
328            d3d_device,
329            device_list,
330        ))?;
331        Ok(raw.into_iter().map(Device).collect())
332    }}
333
334    /// Register a D3D9 resource (`IDirect3DResource9*`) for CUDA access.
335    ///
336    /// # Safety
337    ///
338    /// `resource` must be live for the duration of the returned
339    /// [`GraphicsResource`].
340    pub unsafe fn register_resource(
341        context: &Context,
342        resource: ID3DResource,
343        flags: u32,
344    ) -> Result<GraphicsResource> { unsafe {
345        context.set_current()?;
346        let d = driver()?;
347        let cu = d.cu_graphics_d3d9_register_resource()?;
348        let mut res: CUgraphicsResource = core::ptr::null_mut();
349        check(cu(&mut res, resource, flags))?;
350        Ok(GraphicsResource::from_raw(context, res))
351    }}
352}
353
354/// Direct3D 10 interop (Windows).
355pub mod d3d10 {
356    use super::*;
357    use crate::device::Device;
358    use baracuda_cuda_sys::types::{ID3DDevice, ID3DResource};
359
360    /// # Safety
361    ///
362    /// `adapter` must be a valid `IDXGIAdapter*`.
363    pub unsafe fn get_device(adapter: ID3DDevice) -> Result<Device> { unsafe {
364        let d = driver()?;
365        let cu = d.cu_d3d10_get_device()?;
366        let mut dev = baracuda_cuda_sys::CUdevice(0);
367        check(cu(&mut dev, adapter))?;
368        Ok(Device(dev))
369    }}
370
371    /// # Safety
372    ///
373    /// `d3d_device` must be a valid `ID3D10Device*`.
374    pub unsafe fn get_devices(d3d_device: ID3DDevice, device_list: u32) -> Result<Vec<Device>> { unsafe {
375        let d = driver()?;
376        let cu = d.cu_d3d10_get_devices()?;
377        let mut count: core::ffi::c_uint = 0;
378        check(cu(
379            &mut count,
380            core::ptr::null_mut(),
381            0,
382            d3d_device,
383            device_list,
384        ))?;
385        if count == 0 {
386            return Ok(Vec::new());
387        }
388        let mut raw = vec![baracuda_cuda_sys::CUdevice(0); count as usize];
389        check(cu(
390            &mut count,
391            raw.as_mut_ptr(),
392            raw.len() as core::ffi::c_uint,
393            d3d_device,
394            device_list,
395        ))?;
396        Ok(raw.into_iter().map(Device).collect())
397    }}
398
399    /// # Safety
400    ///
401    /// `resource` must be a live `ID3D10Resource*`.
402    pub unsafe fn register_resource(
403        context: &Context,
404        resource: ID3DResource,
405        flags: u32,
406    ) -> Result<GraphicsResource> { unsafe {
407        context.set_current()?;
408        let d = driver()?;
409        let cu = d.cu_graphics_d3d10_register_resource()?;
410        let mut res: CUgraphicsResource = core::ptr::null_mut();
411        check(cu(&mut res, resource, flags))?;
412        Ok(GraphicsResource::from_raw(context, res))
413    }}
414}
415
416/// VDPAU interop (Linux). Register VDPAU video / output surfaces from
417/// libvdpau into CUDA for zero-copy decode → compute pipelines.
418pub mod vdpau {
419    use super::*;
420    use crate::device::Device;
421    use baracuda_cuda_sys::types::{
422        VdpDevice, VdpGetProcAddress, VdpOutputSurface, VdpVideoSurface,
423    };
424
425    /// Look up the CUDA device powering a given VDPAU device.
426    ///
427    /// # Safety
428    ///
429    /// `vdp_device` and `vdp_get_proc_address` must come from a live
430    /// libvdpau `VdpDeviceCreateX11` call on the current X display.
431    pub unsafe fn get_device(
432        vdp_device: VdpDevice,
433        vdp_get_proc_address: VdpGetProcAddress,
434    ) -> Result<Device> { unsafe {
435        let d = driver()?;
436        let cu = d.cu_vdpau_get_device()?;
437        let mut dev = baracuda_cuda_sys::CUdevice(0);
438        check(cu(&mut dev, vdp_device, vdp_get_proc_address))?;
439        Ok(Device(dev))
440    }}
441
442    /// Register a VDPAU video surface (e.g. a decoded frame output) for
443    /// CUDA access.
444    ///
445    /// # Safety
446    ///
447    /// `vdp_surface` must be a live VDPAU video surface on the VDPAU
448    /// device associated with the current CUDA context.
449    pub unsafe fn register_video_surface(
450        context: &Context,
451        vdp_surface: VdpVideoSurface,
452        flags: u32,
453    ) -> Result<GraphicsResource> { unsafe {
454        context.set_current()?;
455        let d = driver()?;
456        let cu = d.cu_graphics_vdpau_register_video_surface()?;
457        let mut res: CUgraphicsResource = core::ptr::null_mut();
458        check(cu(&mut res, vdp_surface, flags))?;
459        Ok(GraphicsResource::from_raw(context, res))
460    }}
461
462    /// Register a VDPAU output surface.
463    ///
464    /// # Safety
465    ///
466    /// Same as [`register_video_surface`].
467    pub unsafe fn register_output_surface(
468        context: &Context,
469        vdp_surface: VdpOutputSurface,
470        flags: u32,
471    ) -> Result<GraphicsResource> { unsafe {
472        context.set_current()?;
473        let d = driver()?;
474        let cu = d.cu_graphics_vdpau_register_output_surface()?;
475        let mut res: CUgraphicsResource = core::ptr::null_mut();
476        check(cu(&mut res, vdp_surface, flags))?;
477        Ok(GraphicsResource::from_raw(context, res))
478    }}
479}
480
481/// EGL interop — primary path for Jetson video pipelines (NvMM streams,
482/// camera capture). Supports both image-based and EGLStream-based flows.
483pub mod egl {
484    use super::*;
485    use baracuda_cuda_sys::types::{CUeglFrame, EGLImageKHR, EGLStreamKHR, EGLSyncKHR};
486    use baracuda_cuda_sys::CUevent;
487    use core::ffi::c_void;
488
489    /// Register an `EGLImageKHR` for CUDA access.
490    ///
491    /// # Safety
492    ///
493    /// `image` must be a live `EGLImageKHR` (typically created via
494    /// `eglCreateImageKHR`). The image must outlive the returned resource.
495    pub unsafe fn register_image(
496        context: &Context,
497        image: EGLImageKHR,
498        flags: u32,
499    ) -> Result<GraphicsResource> { unsafe {
500        context.set_current()?;
501        let d = driver()?;
502        let cu = d.cu_graphics_egl_register_image()?;
503        let mut res: CUgraphicsResource = core::ptr::null_mut();
504        check(cu(&mut res, image, flags))?;
505        Ok(GraphicsResource::from_raw(context, res))
506    }}
507
508    /// Fetch the `CUeglFrame` (YUV / RGB descriptor) for a mapped EGL
509    /// resource. `index` and `mip_level` select the plane / mip.
510    pub fn mapped_frame(
511        resource: &GraphicsResource,
512        index: u32,
513        mip_level: u32,
514    ) -> Result<CUeglFrame> {
515        let d = driver()?;
516        let cu = d.cu_graphics_resource_get_mapped_egl_frame()?;
517        let mut frame = CUeglFrame::default();
518        check(unsafe { cu(&mut frame, resource.as_raw(), index, mip_level) })?;
519        Ok(frame)
520    }
521
522    /// Wrap an `EGLSyncKHR` as a CUDA event — lets CUDA wait on GPU work
523    /// submitted via EGL's synchronization primitives.
524    ///
525    /// # Safety
526    ///
527    /// `egl_sync` must be a live `EGLSyncKHR`.
528    pub unsafe fn event_from_sync(egl_sync: EGLSyncKHR, flags: u32) -> Result<CUevent> { unsafe {
529        let d = driver()?;
530        let cu = d.cu_event_create_from_egl_sync()?;
531        let mut event: CUevent = core::ptr::null_mut();
532        check(cu(&mut event, egl_sync, flags))?;
533        Ok(event)
534    }}
535
536    /// EGLStream consumer-side connection — CUDA receives frames from an
537    /// EGL producer (e.g. a camera or decoder).
538    ///
539    /// `connection` is an opaque pointer handed back by the driver;
540    /// baracuda does not wrap it in a typed struct, so you'll need to
541    /// keep the pointer alive yourself.
542    ///
543    /// # Safety
544    ///
545    /// `stream` must be a live `EGLStreamKHR`. `connection` must be a
546    /// pointer to a single `CUeglStreamConnection` slot.
547    pub unsafe fn stream_consumer_connect(
548        connection: *mut c_void,
549        stream: EGLStreamKHR,
550    ) -> Result<()> { unsafe {
551        let d = driver()?;
552        let cu = d.cu_egl_stream_consumer_connect()?;
553        check(cu(connection, stream))
554    }}
555
556    /// # Safety
557    ///
558    /// `connection` must be a connection previously set up by
559    /// [`stream_consumer_connect`].
560    pub unsafe fn stream_consumer_disconnect(connection: *mut c_void) -> Result<()> { unsafe {
561        let d = driver()?;
562        let cu = d.cu_egl_stream_consumer_disconnect()?;
563        check(cu(connection))
564    }}
565
566    /// Acquire the next frame from an EGLStream consumer. `timeout` is
567    /// in nanoseconds (0 = non-blocking).
568    ///
569    /// # Safety
570    ///
571    /// `connection` must be a connected consumer (see
572    /// [`stream_consumer_connect`]).
573    pub unsafe fn stream_consumer_acquire_frame(
574        context: &Context,
575        connection: *mut c_void,
576        cu_stream_out: *mut baracuda_cuda_sys::CUstream,
577        timeout: u32,
578    ) -> Result<GraphicsResource> { unsafe {
579        let d = driver()?;
580        let cu = d.cu_egl_stream_consumer_acquire_frame()?;
581        let mut resource: CUgraphicsResource = core::ptr::null_mut();
582        check(cu(connection, &mut resource, cu_stream_out, timeout))?;
583        Ok(GraphicsResource::from_raw(context, resource))
584    }}
585
586    /// # Safety
587    ///
588    /// `connection` must match the one used to acquire `resource`.
589    pub unsafe fn stream_consumer_release_frame(
590        connection: *mut c_void,
591        resource: &GraphicsResource,
592        cu_stream_inout: *mut baracuda_cuda_sys::CUstream,
593    ) -> Result<()> { unsafe {
594        let d = driver()?;
595        let cu = d.cu_egl_stream_consumer_release_frame()?;
596        check(cu(connection, resource.as_raw(), cu_stream_inout))
597    }}
598
599    /// EGLStream producer-side connection — CUDA feeds frames into an
600    /// EGL stream (typically to a compositor or encoder consumer).
601    ///
602    /// # Safety
603    ///
604    /// `stream` must be a live `EGLStreamKHR`. `connection` must be a
605    /// pointer to a single `CUeglStreamConnection` slot.
606    pub unsafe fn stream_producer_connect(
607        connection: *mut c_void,
608        stream: EGLStreamKHR,
609        width: i32,
610        height: i32,
611    ) -> Result<()> { unsafe {
612        let d = driver()?;
613        let cu = d.cu_egl_stream_producer_connect()?;
614        check(cu(connection, stream, width, height))
615    }}
616
617    /// # Safety
618    ///
619    /// `connection` must be a connected producer.
620    pub unsafe fn stream_producer_disconnect(connection: *mut c_void) -> Result<()> { unsafe {
621        let d = driver()?;
622        let cu = d.cu_egl_stream_producer_disconnect()?;
623        check(cu(connection))
624    }}
625
626    /// Push a frame to the EGL stream.
627    ///
628    /// # Safety
629    ///
630    /// `egl_frame` must be populated with live plane pointers pointing at
631    /// memory the producer owns until the consumer returns it via
632    /// [`stream_producer_return_frame`].
633    pub unsafe fn stream_producer_present_frame(
634        connection: *mut c_void,
635        egl_frame: CUeglFrame,
636        cu_stream_inout: *mut baracuda_cuda_sys::CUstream,
637    ) -> Result<()> { unsafe {
638        let d = driver()?;
639        let cu = d.cu_egl_stream_producer_present_frame()?;
640        check(cu(connection, egl_frame, cu_stream_inout))
641    }}
642
643    /// Reclaim a previously-presented frame. `egl_frame` is overwritten
644    /// with the plane descriptors of the returned frame.
645    ///
646    /// # Safety
647    ///
648    /// `connection` must be a connected producer.
649    pub unsafe fn stream_producer_return_frame(
650        connection: *mut c_void,
651        egl_frame: *mut CUeglFrame,
652        cu_stream_inout: *mut baracuda_cuda_sys::CUstream,
653    ) -> Result<()> { unsafe {
654        let d = driver()?;
655        let cu = d.cu_egl_stream_producer_return_frame()?;
656        check(cu(connection, egl_frame, cu_stream_inout))
657    }}
658}
659
660/// NvSci interop (Jetson / DRIVE platforms). Query attributes for
661/// `NvSciSyncObj`s from a CUDA device, then feed the resulting objects
662/// into [`crate::external::ExternalSemaphore::import`] with the
663/// `NVSCISYNC` handle type.
664pub mod nvsci {
665    use super::*;
666    use crate::device::Device;
667    use baracuda_cuda_sys::types::NvSciSyncAttrList;
668
669    /// `CUnvSciSyncAttr::SIGNAL` — see [`baracuda_cuda_sys::types::CUnvSciSyncAttr`].
670    pub const SIGNAL: i32 = baracuda_cuda_sys::types::CUnvSciSyncAttr::SIGNAL;
671    /// `CUnvSciSyncAttr::WAIT`.
672    pub const WAIT: i32 = baracuda_cuda_sys::types::CUnvSciSyncAttr::WAIT;
673
674    /// Fill `attr_list` with the NvSciSync attributes CUDA requires for
675    /// `device` to participate as a signaler or waiter.
676    ///
677    /// # Safety
678    ///
679    /// `attr_list` must be a live `NvSciSyncAttrList` created via
680    /// libnvsciSync's `NvSciSyncAttrListCreate`.
681    pub unsafe fn device_sync_attributes(
682        attr_list: NvSciSyncAttrList,
683        device: &Device,
684        direction: i32,
685    ) -> Result<()> { unsafe {
686        let d = driver()?;
687        let cu = d.cu_device_get_nv_sci_sync_attributes()?;
688        check(cu(attr_list, device.as_raw(), direction))
689    }}
690}
691
692/// Direct3D 11 interop (Windows) — the D3D path most still relevant today.
693pub mod d3d11 {
694    use super::*;
695    use crate::device::Device;
696    use baracuda_cuda_sys::types::{ID3DDevice, ID3DResource};
697
698    /// # Safety
699    ///
700    /// `adapter` must be a valid `IDXGIAdapter*`.
701    pub unsafe fn get_device(adapter: ID3DDevice) -> Result<Device> { unsafe {
702        let d = driver()?;
703        let cu = d.cu_d3d11_get_device()?;
704        let mut dev = baracuda_cuda_sys::CUdevice(0);
705        check(cu(&mut dev, adapter))?;
706        Ok(Device(dev))
707    }}
708
709    /// # Safety
710    ///
711    /// `d3d_device` must be a valid `ID3D11Device*`.
712    pub unsafe fn get_devices(d3d_device: ID3DDevice, device_list: u32) -> Result<Vec<Device>> { unsafe {
713        let d = driver()?;
714        let cu = d.cu_d3d11_get_devices()?;
715        let mut count: core::ffi::c_uint = 0;
716        check(cu(
717            &mut count,
718            core::ptr::null_mut(),
719            0,
720            d3d_device,
721            device_list,
722        ))?;
723        if count == 0 {
724            return Ok(Vec::new());
725        }
726        let mut raw = vec![baracuda_cuda_sys::CUdevice(0); count as usize];
727        check(cu(
728            &mut count,
729            raw.as_mut_ptr(),
730            raw.len() as core::ffi::c_uint,
731            d3d_device,
732            device_list,
733        ))?;
734        Ok(raw.into_iter().map(Device).collect())
735    }}
736
737    /// # Safety
738    ///
739    /// `resource` must be a live `ID3D11Resource*`.
740    pub unsafe fn register_resource(
741        context: &Context,
742        resource: ID3DResource,
743        flags: u32,
744    ) -> Result<GraphicsResource> { unsafe {
745        context.set_current()?;
746        let d = driver()?;
747        let cu = d.cu_graphics_d3d11_register_resource()?;
748        let mut res: CUgraphicsResource = core::ptr::null_mut();
749        check(cu(&mut res, resource, flags))?;
750        Ok(GraphicsResource::from_raw(context, res))
751    }}
752}