wgpu_hal/auxil/
renderdoc.rs

1//! RenderDoc integration - <https://renderdoc.org/>
2#![cfg_attr(not(any(feature = "gles", feature = "vulkan")), allow(dead_code))]
3
4use alloc::format;
5use alloc::string::String;
6use core::{ffi, ptr};
7
8/// The dynamically loaded RenderDoc API function table
9#[repr(C)]
10#[derive(Debug)]
11pub struct RenderDocApi {
12    api: renderdoc_sys::RENDERDOC_API_1_4_1,
13    lib: libloading::Library,
14}
15
16unsafe impl Send for RenderDocApi {}
17unsafe impl Sync for RenderDocApi {}
18
19/// RenderDoc API type
20#[derive(Debug)]
21pub enum RenderDoc {
22    /// RenderDoc functionality is available
23    Available {
24        /// RenderDoc API with function pointers
25        api: RenderDocApi,
26    },
27    /// RenderDoc functionality is _not_ available
28    NotAvailable {
29        /// A description why renderdoc functionality is not available
30        reason: String,
31    },
32}
33
34// TODO: replace with libloading API once supported
35#[cfg(unix)]
36const RTLD_NOLOAD: i32 = 0x4;
37
38impl RenderDoc {
39    pub unsafe fn new() -> Self {
40        type GetApiFn = unsafe extern "C" fn(version: u32, out: *mut *mut ffi::c_void) -> i32;
41
42        #[cfg(windows)]
43        let renderdoc_filename = "renderdoc.dll";
44        #[cfg(all(unix, not(target_os = "android")))]
45        let renderdoc_filename = "librenderdoc.so";
46        #[cfg(target_os = "android")]
47        let renderdoc_filename = "libVkLayer_GLES_RenderDoc.so";
48
49        #[cfg(unix)]
50        let renderdoc_result: Result<libloading::Library, libloading::Error> = unsafe {
51            libloading::os::unix::Library::open(
52                Some(renderdoc_filename),
53                libloading::os::unix::RTLD_NOW | RTLD_NOLOAD,
54            )
55        }
56        .map(|lib| lib.into());
57
58        #[cfg(windows)]
59        let renderdoc_result: Result<libloading::Library, libloading::Error> =
60            libloading::os::windows::Library::open_already_loaded(renderdoc_filename)
61                .map(|lib| lib.into());
62
63        let renderdoc_lib = match renderdoc_result {
64            Ok(lib) => lib,
65            Err(e) => {
66                return RenderDoc::NotAvailable {
67                    reason: format!(
68                        "Unable to load renderdoc library '{renderdoc_filename}': {e:?}"
69                    ),
70                }
71            }
72        };
73
74        let get_api: libloading::Symbol<GetApiFn> =
75            match unsafe { renderdoc_lib.get(c"RENDERDOC_GetAPI".to_bytes()) } {
76                Ok(api) => api,
77                Err(e) => {
78                    return RenderDoc::NotAvailable {
79                        reason: format!(
80                            "Unable to get RENDERDOC_GetAPI from renderdoc library '{renderdoc_filename}': {e:?}"
81                        ),
82                    }
83                }
84            };
85        let mut obj = ptr::null_mut();
86        match unsafe { get_api(10401, &mut obj) } {
87            1 => RenderDoc::Available {
88                api: RenderDocApi {
89                    api: unsafe { *obj.cast::<renderdoc_sys::RENDERDOC_API_1_4_1>() },
90                    lib: renderdoc_lib,
91                },
92            },
93            return_value => RenderDoc::NotAvailable {
94                reason: format!(
95                    "Unable to get API from renderdoc library '{renderdoc_filename}': {return_value}"
96                ),
97            },
98        }
99    }
100}
101
102impl Default for RenderDoc {
103    fn default() -> Self {
104        if !cfg!(debug_assertions) {
105            return RenderDoc::NotAvailable {
106                reason: "RenderDoc support is only enabled with 'debug_assertions'".into(),
107            };
108        }
109        unsafe { Self::new() }
110    }
111}
112/// An implementation specific handle
113pub type Handle = *mut ffi::c_void;
114
115impl RenderDoc {
116    /// Start a RenderDoc frame capture
117    pub unsafe fn start_frame_capture(&self, device_handle: Handle, window_handle: Handle) -> bool {
118        match *self {
119            Self::Available { api: ref entry } => {
120                unsafe { entry.api.StartFrameCapture.unwrap()(device_handle, window_handle) };
121                true
122            }
123            Self::NotAvailable { ref reason } => {
124                log::warn!("Could not start RenderDoc frame capture: {reason}");
125                false
126            }
127        }
128    }
129
130    /// End a RenderDoc frame capture
131    pub unsafe fn end_frame_capture(&self, device_handle: Handle, window_handle: Handle) {
132        match *self {
133            Self::Available { api: ref entry } => {
134                unsafe { entry.api.EndFrameCapture.unwrap()(device_handle, window_handle) };
135            }
136            Self::NotAvailable { ref reason } => {
137                log::warn!("Could not end RenderDoc frame capture: {reason}")
138            }
139        };
140    }
141}