1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
//! RenderDoc application bindings for Rust

#![deny(missing_docs)]

#[cfg(any(target_os = "macos", target_os = "ios"))]
compile_error!("RenderDoc does not support this platform.");

#[macro_use]
extern crate bitflags;
extern crate libloading;
#[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate renderdoc_derive;
extern crate renderdoc_sys;

#[cfg(feature = "glutin")]
extern crate glutin;
#[cfg(target_os = "windows")]
extern crate winapi;
#[cfg(target_os = "windows")]
extern crate wio;

pub use self::entry::{ApiVersion, V100, V110, V111, V112, V120};

use std::os::raw::{c_ulonglong, c_void};
use std::u32;

#[cfg(feature = "glutin")]
use glutin::VirtualKeyCode;
#[cfg(windows)]
use winapi::shared::guiddef::GUID;
#[cfg(windows)]
use wio::com::ComPtr;

pub mod api;
pub mod entry;
pub mod prelude;

/// Magic value used for when applications pass a path where shader debug
/// information can be found to match up with a stripped shader.
///
/// Windows GUID representation intended for consumption by D3D.
#[cfg(windows)]
pub const SHADER_MAGIC_DEBUG_VALUE_STRUCT: GUID = GUID {
    Data1: 0xeab25520,
    Data2: 0x6670,
    Data3: 0x4865,
    Data4: [0x84, 0x29, 0x6c, 0x8, 0x51, 0x54, 0x00, 0xff],
};

/// Magic value used for when applications pass a path where shader debug
/// information can be found to match up with a stripped shader.
///
/// Raw byte array representation (assuming x86 endianness).
pub const SHADER_MAGIC_DEBUG_VALUE_BYTE_ARRAY: &[u8] = &[
    0x20, 0x55, 0xb2, 0xea, 0x70, 0x66, 0x65, 0x48, 0x84, 0x29, 0x6c, 0x8, 0x51, 0x54, 0x00, 0xff,
];

/// Magic value used for when applications pass a path where shader debug
/// information can be found to match up with a stripped shader.
///
/// Truncated version when only a `uint64_t` is available (e.g. Vulkan tags).
pub const SHADER_MAGIC_DEBUG_VALUE_TRUNCATED: c_ulonglong = 0x4856670eab25520;

/// RenderDoc capture options.
#[repr(u32)]
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum CaptureOption {
    /// Let the application enable vertical synchronization.
    AllowVSync = 0,
    /// Let the application enter fullscreen mode.
    AllowFullscreen = 1,
    /// Record API debugging events and messages.
    ///
    /// This option also goes by the deprecated name of `DebugDeviceMode`.
    ApiValidation = 2,
    /// Capture CPU callstacks for API events.
    CaptureCallstacks = 3,
    /// When capturing CPU callstacks, only capture them from drawcalls.
    ///
    /// This option does nothing without the above option being enabled.
    CaptureCallstacksOnlyDraws = 4,
    /// Specify a delay, measured in seconds, to wait for a debugger to attach
    /// to the application after being injected.
    DelayForDebugger = 5,
    /// Verify any writes to mapped buffers by checking the memory after the
    /// bounds of the returned pointer to detect any modification.
    VerifyMapWrites = 6,
    /// Hooks any system API calls that create child processes and injects
    /// RenderDoc into them recursively with the same options.
    HookIntoChildren = 7,
    /// Reference all resources available at the time of capture.
    ///
    /// By default, RenderDoc only includes resources in the final capture file
    /// necessary for that frame. This option allows you to override that
    /// behavior.
    RefAllResources = 8,
    /// Save the initial state for all resources, regardless of usage.
    ///
    /// By default, RenderDoc skips saving initial states for resources where
    /// the previous contents don't appear to be used (assuming that writes
    /// before reads indicate the previous contents aren't used).
    SaveAllInitials = 9,
    /// Capture all command lists generated from the start of the application.
    ///
    /// In APIs that allow for recording of command lists to be replayed later,
    /// RenderDoc may choose to not capture command lists before a frame capture
    /// is triggered to reduce overhead. This means any command lists that are
    /// recorded one and replayed many times will not be available, potentially
    /// causing a failure to capture.
    ///
    /// Note that this is only true for APIs where multithreading is difficult
    /// or otherwise discouraged. Newer APIs, e.g. Vulkan and D3D12, will ignore
    /// this option and always capture all command lists since they are heavily
    /// oriented around them and the associated overhead is mostly reduced due
    /// to superior API design.
    CaptureAllCmdLists = 10,
    /// Mute API debug output when `CaptureOption::ApiValidation` is enabled.
    DebugOutputMute = 11,
}

/// Raw mutable pointer to the API's root handle.
///
/// For example, this could be a pointer to an `ID3D11Device`,
/// `HGLRC`/`GLXContext`, `ID3D12Device`, etc.
#[derive(Clone, Debug, Hash, Eq, PartialEq)]
pub struct DevicePointer(pub(crate) *const c_void);

impl From<*const c_void> for DevicePointer {
    fn from(ptr: *const c_void) -> Self {
        DevicePointer(ptr)
    }
}

impl From<*mut c_void> for DevicePointer {
    fn from(ptr: *mut c_void) -> Self {
        DevicePointer(ptr)
    }
}

#[cfg(windows)]
impl From<winapi::shared::windef::HGLRC> for DevicePointer {
    fn from(ctx: winapi::shared::windef::HGLRC) -> Self {
        DevicePointer(ctx as *mut _ as *const c_void)
    }
}

#[cfg(windows)]
impl From<*mut winapi::um::d3d11::ID3D11Device> for DevicePointer {
    fn from(ctx: *mut winapi::um::d3d11::ID3D11Device) -> Self {
        DevicePointer(ctx as *mut _ as *const c_void)
    }
}

#[cfg(windows)]
impl From<ComPtr<winapi::um::d3d11::ID3D11Device>> for DevicePointer {
    fn from(ctx: ComPtr<winapi::um::d3d11::ID3D11Device>) -> Self {
        unsafe { DevicePointer(ctx.as_raw() as *mut _ as *const c_void) }
    }
}

#[cfg(windows)]
impl From<*mut winapi::um::d3d12::ID3D12Device> for DevicePointer {
    fn from(ctx: *mut winapi::um::d3d12::ID3D12Device) -> Self {
        DevicePointer(ctx as *mut _ as *const c_void)
    }
}

#[cfg(windows)]
impl From<ComPtr<winapi::um::d3d12::ID3D12Device>> for DevicePointer {
    fn from(ctx: ComPtr<winapi::um::d3d12::ID3D12Device>) -> Self {
        unsafe { DevicePointer(ctx.as_raw() as *mut _ as *const c_void) }
    }
}

#[cfg(feature = "glutin")]
impl<'a> From<&'a glutin::Context> for DevicePointer {
    fn from(ctx: &'a glutin::Context) -> Self {
        use glutin::os::GlContextExt;

        #[cfg(unix)]
        unsafe {
            use glutin::os::unix::RawHandle;
            match ctx.raw_handle() {
                RawHandle::Glx(glx) => DevicePointer::from(glx),
                _ => panic!("RenderDoc only supports GLX contexts on Unix!"),
            }
        }

        #[cfg(windows)]
        unsafe {
            use glutin::os::windows::RawHandle;
            match ctx.raw_handle() {
                RawHandle::Wgl(wgl) => DevicePointer::from(wgl),
                _ => panic!("RenderDoc only supports WGL contexts on Windows!"),
            }
        }
    }
}

/// User input key codes.
#[allow(missing_docs)]
#[repr(u32)]
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum InputButton {
    /// The '0' key over the letters.
    Key0 = 0x30,
    /// The '1' key over the letters.
    Key1 = 0x31,
    /// The '2' key over the letters.
    Key2 = 0x32,
    /// The '3' key over the letters.
    Key3 = 0x33,
    /// The '4' key over the letters.
    Key4 = 0x34,
    /// The '5' key over the letters.
    Key5 = 0x35,
    /// The '6' key over the letters.
    Key6 = 0x36,
    /// The '7' key over the letters.
    Key7 = 0x37,
    /// The '8' key over the letters.
    Key8 = 0x38,
    /// The '9' key over the letters.
    Key9 = 0x39,

    A = 0x41,
    B = 0x42,
    C = 0x43,
    D = 0x44,
    E = 0x45,
    F = 0x46,
    G = 0x47,
    H = 0x48,
    I = 0x49,
    J = 0x4A,
    K = 0x4B,
    L = 0x4C,
    M = 0x4D,
    N = 0x4E,
    O = 0x4F,
    P = 0x50,
    Q = 0x51,
    R = 0x52,
    S = 0x53,
    T = 0x54,
    U = 0x55,
    V = 0x56,
    W = 0x57,
    X = 0x58,
    Y = 0x59,
    Z = 0x5A,

    /// Leave the rest of the ASCII range free, in case the RenderDoc developers
    /// decide to use it later.
    NonPrintable = 0x100,

    /// Division key on the numpad.
    Divide,
    /// Multiplication key on the numpad.
    Multiply,
    /// Subtraction key on the numpad.
    Subtract,
    /// Addition key on the numpad.
    Plus,

    F1,
    F2,
    F3,
    F4,
    F5,
    F6,
    F7,
    F8,
    F9,
    F10,
    F11,
    F12,

    Home,
    End,
    Insert,
    Delete,
    PageUp,
    PageDn,

    Backspace,
    Tab,
    PrtScrn,
    Pause,

    Max,
}

#[cfg(feature = "glutin")]
impl From<glutin::VirtualKeyCode> for InputButton {
    fn from(code: glutin::VirtualKeyCode) -> InputButton {
        match code {
            VirtualKeyCode::Key1 => InputButton::Key1,
            VirtualKeyCode::Key2 => InputButton::Key2,
            VirtualKeyCode::Key3 => InputButton::Key3,
            VirtualKeyCode::Key4 => InputButton::Key4,
            VirtualKeyCode::Key5 => InputButton::Key5,
            VirtualKeyCode::Key6 => InputButton::Key6,
            VirtualKeyCode::Key7 => InputButton::Key7,
            VirtualKeyCode::Key8 => InputButton::Key8,
            VirtualKeyCode::Key9 => InputButton::Key9,
            VirtualKeyCode::Key0 => InputButton::Key0,
            VirtualKeyCode::A => InputButton::A,
            VirtualKeyCode::B => InputButton::B,
            VirtualKeyCode::C => InputButton::C,
            VirtualKeyCode::D => InputButton::D,
            VirtualKeyCode::E => InputButton::E,
            VirtualKeyCode::F => InputButton::F,
            VirtualKeyCode::G => InputButton::G,
            VirtualKeyCode::H => InputButton::H,
            VirtualKeyCode::I => InputButton::I,
            VirtualKeyCode::J => InputButton::J,
            VirtualKeyCode::K => InputButton::K,
            VirtualKeyCode::L => InputButton::L,
            VirtualKeyCode::M => InputButton::M,
            VirtualKeyCode::N => InputButton::N,
            VirtualKeyCode::O => InputButton::O,
            VirtualKeyCode::P => InputButton::P,
            VirtualKeyCode::Q => InputButton::Q,
            VirtualKeyCode::R => InputButton::R,
            VirtualKeyCode::S => InputButton::S,
            VirtualKeyCode::T => InputButton::T,
            VirtualKeyCode::U => InputButton::U,
            VirtualKeyCode::V => InputButton::V,
            VirtualKeyCode::W => InputButton::W,
            VirtualKeyCode::X => InputButton::X,
            VirtualKeyCode::Y => InputButton::Y,
            VirtualKeyCode::Z => InputButton::Z,
            VirtualKeyCode::Divide => InputButton::Divide,
            VirtualKeyCode::Multiply => InputButton::Multiply,
            VirtualKeyCode::Subtract => InputButton::Subtract,
            VirtualKeyCode::Add => InputButton::Plus,
            VirtualKeyCode::F1 => InputButton::F1,
            VirtualKeyCode::F2 => InputButton::F2,
            VirtualKeyCode::F3 => InputButton::F3,
            VirtualKeyCode::F4 => InputButton::F4,
            VirtualKeyCode::F5 => InputButton::F5,
            VirtualKeyCode::F6 => InputButton::F6,
            VirtualKeyCode::F7 => InputButton::F7,
            VirtualKeyCode::F8 => InputButton::F8,
            VirtualKeyCode::F9 => InputButton::F9,
            VirtualKeyCode::F10 => InputButton::F10,
            VirtualKeyCode::F11 => InputButton::F11,
            VirtualKeyCode::F12 => InputButton::F12,
            VirtualKeyCode::Home => InputButton::Home,
            VirtualKeyCode::End => InputButton::End,
            VirtualKeyCode::Insert => InputButton::Insert,
            VirtualKeyCode::Delete => InputButton::Delete,
            VirtualKeyCode::PageUp => InputButton::PageUp,
            VirtualKeyCode::PageDown => InputButton::PageDn,
            VirtualKeyCode::Back => InputButton::Backspace,
            VirtualKeyCode::Tab => InputButton::Tab,
            VirtualKeyCode::Snapshot => InputButton::PrtScrn,
            VirtualKeyCode::Pause => InputButton::Pause,
            _ => InputButton::Max,
        }
    }
}

bitflags! {
    /// Bit flags for customizing the RenderDoc overlay.
    pub struct OverlayBits: u32 {
        /// Controls whether the overlay is enabled or disabled globally.
        const ENABLED = 0x1;
        /// Shows the average, minimum, and maximum sampled frame rate.
        const FRAME_RATE = 0x2;
        /// Shows the current frame number.
        const FRAME_NUMBER = 0x4;
        /// Shows a list of recent captures, out of the total captures made.
        const CAPTURE_LIST = 0x8;
        /// Sets the default configuration for the overlay.
        const DEFAULT = (0x1 | 0x2 | 0x4 | 0x8);
        /// Enables all overlay configuration bits.
        const ALL = u32::MAX;
        /// Disables all overlay configuration bits.
        const NONE = u32::MIN;
    }
}

/// Raw mutable pointer to the OS-provided window handle.
pub type WindowHandle = *const c_void;

/// An instance of the RenderDoc API with baseline version `V`.
#[derive(Clone, Debug, RenderDoc)]
#[renderdoc_convert(V100, V110, V111, V112, V120)]
pub struct RenderDoc<V: ApiVersion>(V::Entry);

impl<V: ApiVersion> RenderDoc<V> {
    /// Initializes a new instance of the RenderDoc API.
    pub fn new() -> Result<RenderDoc<V>, String> {
        let api = V::load()?;
        Ok(RenderDoc(api))
    }

    /// Returns the raw entry point of the API.
    ///
    /// # Safety
    ///
    /// Using the entry point structure directly will discard any thread safety
    /// provided by default with this library.
    pub unsafe fn raw_api(&self) -> V::Entry {
        self.0.clone()
    }
}

#[cfg(test)]
mod tests {
    use super::api::*;
    use super::*;

    #[test]
    fn get_set_capture_option_f32() {
        let mut rd: RenderDoc<V110> = RenderDoc::new().expect("Failed to init");

        let delay = rd.get_capture_option_f32(CaptureOption::DelayForDebugger);
        assert_eq!(delay, 0.0f32);

        rd.set_capture_option_f32(CaptureOption::DelayForDebugger, 2.5f32);
        let delay = rd.get_capture_option_f32(CaptureOption::DelayForDebugger);
        assert_eq!(delay, 2.0f32);
    }

    #[test]
    fn get_set_capture_option_u32() {
        let rd: RenderDoc<V110> = RenderDoc::new().expect("Failed to init");

        let vsync = rd.get_capture_option_u32(CaptureOption::AllowVSync);
        assert_eq!(vsync, 1u32);

        let is_full = rd.get_capture_option_u32(CaptureOption::AllowFullscreen);
        assert_eq!(is_full, 1u32);

        let api_val_mode = rd.get_capture_option_u32(CaptureOption::ApiValidation);
        let debug_mode = rd.get_capture_option_u32(CaptureOption::ApiValidation);
        assert_eq!(api_val_mode, 0u32);
        assert_eq!(api_val_mode, debug_mode);

        let cc = rd.get_capture_option_u32(CaptureOption::CaptureCallstacks);
        assert_eq!(cc, 0u32);

        let cc_draw = rd.get_capture_option_u32(CaptureOption::CaptureCallstacksOnlyDraws);
        assert_eq!(cc_draw, 0u32);

        let ver_map = rd.get_capture_option_u32(CaptureOption::VerifyMapWrites);
        assert_eq!(ver_map, 0u32);

        let hook_in = rd.get_capture_option_u32(CaptureOption::HookIntoChildren);
        assert_eq!(hook_in, 0u32);

        let ref_all = rd.get_capture_option_u32(CaptureOption::RefAllResources);
        assert_eq!(ref_all, 0u32);

        let intls = rd.get_capture_option_u32(CaptureOption::SaveAllInitials);
        assert_eq!(intls, 0u32);

        let cmds = rd.get_capture_option_u32(CaptureOption::CaptureAllCmdLists);
        assert_eq!(cmds, 0u32);

        let is_muted = rd.get_capture_option_u32(CaptureOption::DebugOutputMute);
        assert_eq!(is_muted, 1u32);
    }
}