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