Skip to main content

baracuda_runtime/
graphics.rs

1//! Graphics-API interop (Runtime API).
2//!
3//! Mirrors [`baracuda_driver::graphics`](../../../baracuda_driver/graphics/index.html)
4//! on the Runtime side. Runtime contexts are implicit, so `register_*`
5//! doesn't take a `Context` — it uses the current device's primary
6//! context.
7//!
8//! For modern Vulkan / D3D12 flows, prefer
9//! [`crate::external::ExternalMemory`].
10//!
11//! # Workflow
12//!
13//! 1. Register the graphics resource (`gl::register_buffer`,
14//!    `d3d11::register_resource`, ...) — returns a [`GraphicsResource`].
15//! 2. Call [`GraphicsResource::map`] on a stream to hand it to CUDA.
16//! 3. [`GraphicsResource::mapped_pointer`] or
17//!    [`GraphicsResource::mapped_array`] → usable device memory.
18//! 4. ... compute ...
19//! 5. [`GraphicsResource::unmap`] releases it back to the graphics API.
20//!
21//! Drop unregisters the resource automatically.
22
23use std::sync::Arc;
24
25use baracuda_cuda_sys::CUgraphicsResource;
26use baracuda_cuda_sys::runtime::runtime;
27
28use crate::error::{Result, check};
29use crate::stream::Stream;
30
31pub use baracuda_cuda_sys::types::{
32    CUgraphicsMapResourceFlags as MapResourceFlags, CUgraphicsRegisterFlags as RegisterFlags,
33};
34
35/// Runtime-side handle to a graphics-API resource. Drop unregisters it.
36#[derive(Clone)]
37pub struct GraphicsResource {
38    inner: Arc<GraphicsResourceInner>,
39}
40
41struct GraphicsResourceInner {
42    handle: CUgraphicsResource,
43}
44
45unsafe impl Send for GraphicsResourceInner {}
46unsafe impl Sync for GraphicsResourceInner {}
47
48impl core::fmt::Debug for GraphicsResourceInner {
49    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
50        f.debug_struct("GraphicsResource")
51            .field("handle", &self.handle)
52            .finish_non_exhaustive()
53    }
54}
55
56impl core::fmt::Debug for GraphicsResource {
57    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
58        self.inner.fmt(f)
59    }
60}
61
62impl GraphicsResource {
63    /// Wrap an already-registered resource.
64    ///
65    /// # Safety
66    ///
67    /// `handle` must be a live resource from a `cuda*Register*` call.
68    /// baracuda unregisters it when the last clone drops.
69    pub unsafe fn from_raw(handle: CUgraphicsResource) -> Self {
70        Self {
71            inner: Arc::new(GraphicsResourceInner { handle }),
72        }
73    }
74
75    /// Raw `CUgraphicsResource` handle. Use with care — owned by `self`.
76    #[inline]
77    pub fn as_raw(&self) -> CUgraphicsResource {
78        self.inner.handle
79    }
80
81    /// `cudaGraphicsResourceSetMapFlags`.
82    pub fn set_map_flags(&self, flags: u32) -> Result<()> {
83        let r = runtime()?;
84        let cu = r.cuda_graphics_resource_set_map_flags()?;
85        check(unsafe { cu(self.inner.handle, flags) })
86    }
87
88    /// Map the resource onto `stream`. Pair with [`Self::unmap`].
89    pub fn map(&self, stream: &Stream) -> Result<()> {
90        let r = runtime()?;
91        let cu = r.cuda_graphics_map_resources()?;
92        let mut arr = [self.inner.handle];
93        check(unsafe { cu(1, arr.as_mut_ptr(), stream.as_raw()) })
94    }
95
96    /// Unmap the resource on `stream`.
97    pub fn unmap(&self, stream: &Stream) -> Result<()> {
98        let r = runtime()?;
99        let cu = r.cuda_graphics_unmap_resources()?;
100        let mut arr = [self.inner.handle];
101        check(unsafe { cu(1, arr.as_mut_ptr(), stream.as_raw()) })
102    }
103
104    /// Fetch the mapped device pointer + size (buffer-type resources).
105    pub fn mapped_pointer(&self) -> Result<(*mut core::ffi::c_void, usize)> {
106        let r = runtime()?;
107        let cu = r.cuda_graphics_resource_get_mapped_pointer()?;
108        let mut dptr: *mut core::ffi::c_void = core::ptr::null_mut();
109        let mut size: usize = 0;
110        check(unsafe { cu(&mut dptr, &mut size, self.inner.handle) })?;
111        Ok((dptr, size))
112    }
113
114    /// Fetch a subresource array (image-type resources).
115    pub fn mapped_array(&self, array_index: u32, mip_level: u32) -> Result<*mut core::ffi::c_void> {
116        let r = runtime()?;
117        let cu = r.cuda_graphics_sub_resource_get_mapped_array()?;
118        let mut arr: *mut core::ffi::c_void = core::ptr::null_mut();
119        check(unsafe { cu(&mut arr, self.inner.handle, array_index, mip_level) })?;
120        Ok(arr)
121    }
122
123    /// Fetch the mipmapped-array handle for a mapped mipmapped texture.
124    pub fn mapped_mipmapped_array(&self) -> Result<*mut core::ffi::c_void> {
125        let r = runtime()?;
126        let cu = r.cuda_graphics_resource_get_mapped_mipmapped_array()?;
127        let mut mip: *mut core::ffi::c_void = core::ptr::null_mut();
128        check(unsafe { cu(&mut mip, self.inner.handle) })?;
129        Ok(mip)
130    }
131
132    /// Bulk-map multiple resources in one call.
133    pub fn map_all(resources: &[Self], stream: &Stream) -> Result<()> {
134        if resources.is_empty() {
135            return Ok(());
136        }
137        let r = runtime()?;
138        let cu = r.cuda_graphics_map_resources()?;
139        let mut raws: Vec<CUgraphicsResource> = resources.iter().map(|x| x.as_raw()).collect();
140        check(unsafe {
141            cu(
142                raws.len() as core::ffi::c_int,
143                raws.as_mut_ptr(),
144                stream.as_raw(),
145            )
146        })
147    }
148
149    /// Bulk-unmap.
150    pub fn unmap_all(resources: &[Self], stream: &Stream) -> Result<()> {
151        if resources.is_empty() {
152            return Ok(());
153        }
154        let r = runtime()?;
155        let cu = r.cuda_graphics_unmap_resources()?;
156        let mut raws: Vec<CUgraphicsResource> = resources.iter().map(|x| x.as_raw()).collect();
157        check(unsafe {
158            cu(
159                raws.len() as core::ffi::c_int,
160                raws.as_mut_ptr(),
161                stream.as_raw(),
162            )
163        })
164    }
165}
166
167impl Drop for GraphicsResourceInner {
168    fn drop(&mut self) {
169        if self.handle.is_null() {
170            return;
171        }
172        if let Ok(r) = runtime() {
173            if let Ok(cu) = r.cuda_graphics_unregister_resource() {
174                let _ = unsafe { cu(self.handle) };
175            }
176        }
177    }
178}
179
180/// OpenGL interop.
181pub mod gl {
182    use super::*;
183    use baracuda_cuda_sys::types::{GLenum, GLuint};
184
185    /// List CUDA device ordinals associated with the current GL context.
186    /// `device_list` selects which slice of the rendering pipeline
187    /// (cudaGLDeviceList_all, _current_frame, _next_frame).
188    pub fn get_devices(device_list: u32) -> Result<Vec<i32>> {
189        let r = runtime()?;
190        let cu = r.cuda_gl_get_devices()?;
191        let mut count: core::ffi::c_uint = 0;
192        let probe_rc = unsafe { cu(&mut count, core::ptr::null_mut(), 0, device_list) };
193        // Treat "no GL context" as "no devices".
194        if probe_rc != baracuda_cuda_sys::runtime::cudaError_t::Success {
195            return Ok(Vec::new());
196        }
197        if count == 0 {
198            return Ok(Vec::new());
199        }
200        let mut out = vec![0i32; count as usize];
201        check(unsafe {
202            cu(
203                &mut count,
204                out.as_mut_ptr(),
205                out.len() as core::ffi::c_uint,
206                device_list,
207            )
208        })?;
209        out.truncate(count as usize);
210        Ok(out)
211    }
212
213    /// Register a GL buffer object (VBO / SSBO / ...) for CUDA access.
214    ///
215    /// # Safety
216    ///
217    /// `buffer` must be a live GL buffer in a GL context current on the
218    /// calling thread. The buffer must outlive the returned resource.
219    pub unsafe fn register_buffer(buffer: GLuint, flags: u32) -> Result<GraphicsResource> {
220        unsafe {
221            let r = runtime()?;
222            let cu = r.cuda_graphics_gl_register_buffer()?;
223            let mut res: CUgraphicsResource = core::ptr::null_mut();
224            check(cu(&mut res, buffer, flags))?;
225            Ok(GraphicsResource::from_raw(res))
226        }
227    }
228
229    /// Register a GL texture / renderbuffer. `target` is the GL binding
230    /// target (`GL_TEXTURE_2D`, `GL_RENDERBUFFER`, ...).
231    ///
232    /// # Safety
233    ///
234    /// Same as [`register_buffer`].
235    pub unsafe fn register_image(
236        image: GLuint,
237        target: GLenum,
238        flags: u32,
239    ) -> Result<GraphicsResource> {
240        unsafe {
241            let r = runtime()?;
242            let cu = r.cuda_graphics_gl_register_image()?;
243            let mut res: CUgraphicsResource = core::ptr::null_mut();
244            check(cu(&mut res, image, target, flags))?;
245            Ok(GraphicsResource::from_raw(res))
246        }
247    }
248}
249
250/// Direct3D 9 interop (Windows).
251pub mod d3d9 {
252    use super::*;
253
254    /// CUDA device ordinal powering a given D3D9 adapter name.
255    ///
256    /// # Safety
257    ///
258    /// `adapter_name` must be a live NUL-terminated C string naming a
259    /// DXGI adapter.
260    pub unsafe fn get_device(adapter_name: *const core::ffi::c_char) -> Result<i32> {
261        unsafe {
262            let r = runtime()?;
263            let cu = r.cuda_d3d9_get_device()?;
264            let mut dev: core::ffi::c_int = 0;
265            check(cu(&mut dev, adapter_name))?;
266            Ok(dev)
267        }
268    }
269
270    /// List CUDA devices associated with a D3D9 device.
271    ///
272    /// # Safety
273    ///
274    /// `d3d_device` must be a live `IDirect3DDevice9*`.
275    pub unsafe fn get_devices(
276        d3d_device: *mut core::ffi::c_void,
277        device_list: u32,
278    ) -> Result<Vec<i32>> {
279        unsafe {
280            let r = runtime()?;
281            let cu = r.cuda_d3d9_get_devices()?;
282            let mut count: core::ffi::c_uint = 0;
283            check(cu(
284                &mut count,
285                core::ptr::null_mut(),
286                0,
287                d3d_device,
288                device_list,
289            ))?;
290            if count == 0 {
291                return Ok(Vec::new());
292            }
293            let mut out = vec![0i32; count as usize];
294            check(cu(
295                &mut count,
296                out.as_mut_ptr(),
297                out.len() as core::ffi::c_uint,
298                d3d_device,
299                device_list,
300            ))?;
301            out.truncate(count as usize);
302            Ok(out)
303        }
304    }
305
306    /// Register a D3D9 resource (`IDirect3DResource9*`).
307    ///
308    /// # Safety
309    ///
310    /// `resource` must outlive the returned [`GraphicsResource`].
311    pub unsafe fn register_resource(
312        resource: *mut core::ffi::c_void,
313        flags: u32,
314    ) -> Result<GraphicsResource> {
315        unsafe {
316            let r = runtime()?;
317            let cu = r.cuda_graphics_d3d9_register_resource()?;
318            let mut res: CUgraphicsResource = core::ptr::null_mut();
319            check(cu(&mut res, resource, flags))?;
320            Ok(GraphicsResource::from_raw(res))
321        }
322    }
323}
324
325/// Direct3D 10 interop (Windows).
326pub mod d3d10 {
327    use super::*;
328
329    /// # Safety
330    ///
331    /// `adapter` must be a valid `IDXGIAdapter*`.
332    pub unsafe fn get_device(adapter: *mut core::ffi::c_void) -> Result<i32> {
333        unsafe {
334            let r = runtime()?;
335            let cu = r.cuda_d3d10_get_device()?;
336            let mut dev: core::ffi::c_int = 0;
337            check(cu(&mut dev, adapter))?;
338            Ok(dev)
339        }
340    }
341
342    /// # Safety
343    ///
344    /// `d3d_device` must be a valid `ID3D10Device*`.
345    pub unsafe fn get_devices(
346        d3d_device: *mut core::ffi::c_void,
347        device_list: u32,
348    ) -> Result<Vec<i32>> {
349        unsafe {
350            let r = runtime()?;
351            let cu = r.cuda_d3d10_get_devices()?;
352            let mut count: core::ffi::c_uint = 0;
353            check(cu(
354                &mut count,
355                core::ptr::null_mut(),
356                0,
357                d3d_device,
358                device_list,
359            ))?;
360            if count == 0 {
361                return Ok(Vec::new());
362            }
363            let mut out = vec![0i32; count as usize];
364            check(cu(
365                &mut count,
366                out.as_mut_ptr(),
367                out.len() as core::ffi::c_uint,
368                d3d_device,
369                device_list,
370            ))?;
371            out.truncate(count as usize);
372            Ok(out)
373        }
374    }
375
376    /// # Safety
377    ///
378    /// `resource` must be a live `ID3D10Resource*`.
379    pub unsafe fn register_resource(
380        resource: *mut core::ffi::c_void,
381        flags: u32,
382    ) -> Result<GraphicsResource> {
383        unsafe {
384            let r = runtime()?;
385            let cu = r.cuda_graphics_d3d10_register_resource()?;
386            let mut res: CUgraphicsResource = core::ptr::null_mut();
387            check(cu(&mut res, resource, flags))?;
388            Ok(GraphicsResource::from_raw(res))
389        }
390    }
391}
392
393/// Direct3D 11 interop (Windows) — the D3D path most still relevant today.
394pub mod d3d11 {
395    use super::*;
396
397    /// # Safety
398    ///
399    /// `adapter` must be a valid `IDXGIAdapter*`.
400    pub unsafe fn get_device(adapter: *mut core::ffi::c_void) -> Result<i32> {
401        unsafe {
402            let r = runtime()?;
403            let cu = r.cuda_d3d11_get_device()?;
404            let mut dev: core::ffi::c_int = 0;
405            check(cu(&mut dev, adapter))?;
406            Ok(dev)
407        }
408    }
409
410    /// # Safety
411    ///
412    /// `d3d_device` must be a valid `ID3D11Device*`.
413    pub unsafe fn get_devices(
414        d3d_device: *mut core::ffi::c_void,
415        device_list: u32,
416    ) -> Result<Vec<i32>> {
417        unsafe {
418            let r = runtime()?;
419            let cu = r.cuda_d3d11_get_devices()?;
420            let mut count: core::ffi::c_uint = 0;
421            check(cu(
422                &mut count,
423                core::ptr::null_mut(),
424                0,
425                d3d_device,
426                device_list,
427            ))?;
428            if count == 0 {
429                return Ok(Vec::new());
430            }
431            let mut out = vec![0i32; count as usize];
432            check(cu(
433                &mut count,
434                out.as_mut_ptr(),
435                out.len() as core::ffi::c_uint,
436                d3d_device,
437                device_list,
438            ))?;
439            out.truncate(count as usize);
440            Ok(out)
441        }
442    }
443
444    /// # Safety
445    ///
446    /// `resource` must be a live `ID3D11Resource*`.
447    pub unsafe fn register_resource(
448        resource: *mut core::ffi::c_void,
449        flags: u32,
450    ) -> Result<GraphicsResource> {
451        unsafe {
452            let r = runtime()?;
453            let cu = r.cuda_graphics_d3d11_register_resource()?;
454            let mut res: CUgraphicsResource = core::ptr::null_mut();
455            check(cu(&mut res, resource, flags))?;
456            Ok(GraphicsResource::from_raw(res))
457        }
458    }
459}
460
461/// VDPAU interop (Linux video-decode pipelines).
462pub mod vdpau {
463    use super::*;
464
465    /// # Safety
466    ///
467    /// `vdp_device` and `vdp_get_proc_address` must come from libvdpau's
468    /// `VdpDeviceCreateX11` call on the current X display.
469    pub unsafe fn get_device(
470        vdp_device: *mut core::ffi::c_void,
471        vdp_get_proc_address: *mut core::ffi::c_void,
472    ) -> Result<i32> {
473        unsafe {
474            let r = runtime()?;
475            let cu = r.cuda_vdpau_get_device()?;
476            let mut dev: core::ffi::c_int = 0;
477            check(cu(&mut dev, vdp_device, vdp_get_proc_address))?;
478            Ok(dev)
479        }
480    }
481
482    /// Register a VDPAU video surface (decoded frame).
483    ///
484    /// # Safety
485    ///
486    /// `vdp_surface` must live on the VDPAU device bound to the current
487    /// CUDA context.
488    pub unsafe fn register_video_surface(
489        vdp_surface: *mut core::ffi::c_void,
490        flags: u32,
491    ) -> Result<GraphicsResource> {
492        unsafe {
493            let r = runtime()?;
494            let cu = r.cuda_graphics_vdpau_register_video_surface()?;
495            let mut res: CUgraphicsResource = core::ptr::null_mut();
496            check(cu(&mut res, vdp_surface, flags))?;
497            Ok(GraphicsResource::from_raw(res))
498        }
499    }
500
501    /// Register a VDPAU output surface.
502    ///
503    /// # Safety
504    ///
505    /// Same as [`register_video_surface`].
506    pub unsafe fn register_output_surface(
507        vdp_surface: *mut core::ffi::c_void,
508        flags: u32,
509    ) -> Result<GraphicsResource> {
510        unsafe {
511            let r = runtime()?;
512            let cu = r.cuda_graphics_vdpau_register_output_surface()?;
513            let mut res: CUgraphicsResource = core::ptr::null_mut();
514            check(cu(&mut res, vdp_surface, flags))?;
515            Ok(GraphicsResource::from_raw(res))
516        }
517    }
518}
519
520/// EGL interop — Jetson camera / video pipelines via EGLImage + EGLStream.
521pub mod egl {
522    use super::*;
523    use baracuda_cuda_sys::runtime::cudaEvent_t;
524    use core::ffi::c_void;
525
526    /// Register an `EGLImageKHR`.
527    ///
528    /// # Safety
529    ///
530    /// `image` must be a live `EGLImageKHR`.
531    pub unsafe fn register_image(image: *mut c_void, flags: u32) -> Result<GraphicsResource> {
532        unsafe {
533            let r = runtime()?;
534            let cu = r.cuda_graphics_egl_register_image()?;
535            let mut res: CUgraphicsResource = core::ptr::null_mut();
536            check(cu(&mut res, image, flags))?;
537            Ok(GraphicsResource::from_raw(res))
538        }
539    }
540
541    /// Fill `egl_frame_out` with the `cudaEglFrame` describing the
542    /// mapped resource (YUV planes, RGB surface, etc).
543    ///
544    /// # Safety
545    ///
546    /// `egl_frame_out` must point at a writable `cudaEglFrame` slot.
547    pub unsafe fn mapped_frame(
548        resource: &GraphicsResource,
549        egl_frame_out: *mut c_void,
550        index: u32,
551        mip_level: u32,
552    ) -> Result<()> {
553        unsafe {
554            let r = runtime()?;
555            let cu = r.cuda_graphics_resource_get_mapped_egl_frame()?;
556            check(cu(egl_frame_out, resource.as_raw(), index, mip_level))
557        }
558    }
559
560    /// Wrap an `EGLSyncKHR` as a CUDA event.
561    ///
562    /// # Safety
563    ///
564    /// `egl_sync` must be a live `EGLSyncKHR`.
565    pub unsafe fn event_from_sync(egl_sync: *mut c_void, flags: u32) -> Result<cudaEvent_t> {
566        unsafe {
567            let r = runtime()?;
568            let cu = r.cuda_event_create_from_egl_sync()?;
569            let mut event: cudaEvent_t = core::ptr::null_mut();
570            check(cu(&mut event, egl_sync, flags))?;
571            Ok(event)
572        }
573    }
574
575    /// Connect CUDA as an EGLStream consumer.
576    ///
577    /// # Safety
578    ///
579    /// `connection` must point at a writable `cudaEglStreamConnection`
580    /// slot; `egl_stream` must be a live `EGLStreamKHR`.
581    pub unsafe fn stream_consumer_connect(
582        connection: *mut c_void,
583        egl_stream: *mut c_void,
584    ) -> Result<()> {
585        unsafe {
586            let r = runtime()?;
587            let cu = r.cuda_egl_stream_consumer_connect()?;
588            check(cu(connection, egl_stream))
589        }
590    }
591
592    /// # Safety
593    ///
594    /// `connection` must be a connected consumer.
595    pub unsafe fn stream_consumer_disconnect(connection: *mut c_void) -> Result<()> {
596        unsafe {
597            let r = runtime()?;
598            let cu = r.cuda_egl_stream_consumer_disconnect()?;
599            check(cu(connection))
600        }
601    }
602
603    /// # Safety
604    ///
605    /// `connection` must be a connected consumer. `stream_out` must
606    /// point at a writable `cudaStream_t` slot.
607    pub unsafe fn stream_consumer_acquire_frame(
608        connection: *mut c_void,
609        stream_out: *mut baracuda_cuda_sys::runtime::cudaStream_t,
610        timeout: u32,
611    ) -> Result<GraphicsResource> {
612        unsafe {
613            let r = runtime()?;
614            let cu = r.cuda_egl_stream_consumer_acquire_frame()?;
615            let mut res: CUgraphicsResource = core::ptr::null_mut();
616            check(cu(connection, &mut res, stream_out, timeout))?;
617            Ok(GraphicsResource::from_raw(res))
618        }
619    }
620
621    /// # Safety
622    ///
623    /// `connection` must match the one used to acquire `resource`.
624    pub unsafe fn stream_consumer_release_frame(
625        connection: *mut c_void,
626        resource: &GraphicsResource,
627        stream_inout: *mut baracuda_cuda_sys::runtime::cudaStream_t,
628    ) -> Result<()> {
629        unsafe {
630            let r = runtime()?;
631            let cu = r.cuda_egl_stream_consumer_release_frame()?;
632            check(cu(connection, resource.as_raw(), stream_inout))
633        }
634    }
635
636    /// Connect CUDA as an EGLStream producer.
637    ///
638    /// # Safety
639    ///
640    /// Same as [`stream_consumer_connect`].
641    pub unsafe fn stream_producer_connect(
642        connection: *mut c_void,
643        egl_stream: *mut c_void,
644        width: i32,
645        height: i32,
646    ) -> Result<()> {
647        unsafe {
648            let r = runtime()?;
649            let cu = r.cuda_egl_stream_producer_connect()?;
650            check(cu(connection, egl_stream, width, height))
651        }
652    }
653
654    /// # Safety
655    ///
656    /// `connection` must be a connected producer.
657    pub unsafe fn stream_producer_disconnect(connection: *mut c_void) -> Result<()> {
658        unsafe {
659            let r = runtime()?;
660            let cu = r.cuda_egl_stream_producer_disconnect()?;
661            check(cu(connection))
662        }
663    }
664
665    /// Push a frame to the EGLStream.
666    ///
667    /// # Safety
668    ///
669    /// `egl_frame` must be a populated `cudaEglFrame`.
670    pub unsafe fn stream_producer_present_frame(
671        connection: *mut c_void,
672        egl_frame: *mut c_void,
673        stream_inout: *mut baracuda_cuda_sys::runtime::cudaStream_t,
674    ) -> Result<()> {
675        unsafe {
676            let r = runtime()?;
677            let cu = r.cuda_egl_stream_producer_present_frame()?;
678            check(cu(connection, egl_frame, stream_inout))
679        }
680    }
681
682    /// Reclaim a previously-presented frame.
683    ///
684    /// # Safety
685    ///
686    /// `connection` must be a connected producer.
687    pub unsafe fn stream_producer_return_frame(
688        connection: *mut c_void,
689        egl_frame_out: *mut c_void,
690        stream_inout: *mut baracuda_cuda_sys::runtime::cudaStream_t,
691    ) -> Result<()> {
692        unsafe {
693            let r = runtime()?;
694            let cu = r.cuda_egl_stream_producer_return_frame()?;
695            check(cu(connection, egl_frame_out, stream_inout))
696        }
697    }
698}
699
700/// NvSci interop — query sync-object attributes CUDA expects from
701/// NvSciSync signalers / waiters. Feed the resulting
702/// `NvSciSyncAttrList` into libnvsciSync's reconcile + alloc, then
703/// import the resulting object via
704/// [`crate::external::ExternalSemaphore::import`] with the
705/// `NvSciSync` handle type.
706pub mod nvsci {
707    use super::*;
708    use crate::device::Device;
709
710    /// `CUnvSciSyncAttr::SIGNAL`.
711    pub const SIGNAL: i32 = 0;
712    /// `CUnvSciSyncAttr::WAIT`.
713    pub const WAIT: i32 = 1;
714
715    /// `cudaDeviceGetNvSciSyncAttributes`.
716    ///
717    /// # Safety
718    ///
719    /// `attr_list` must be a live `NvSciSyncAttrList` created by
720    /// libnvsciSync's `NvSciSyncAttrListCreate`.
721    pub unsafe fn device_sync_attributes(
722        attr_list: *mut core::ffi::c_void,
723        device: &Device,
724        direction: i32,
725    ) -> Result<()> {
726        unsafe {
727            let r = runtime()?;
728            let cu = r.cuda_device_get_nv_sci_sync_attributes()?;
729            check(cu(attr_list, device.ordinal(), direction))
730        }
731    }
732}