dxwr 0.4.3

Direct3D12 and DXGI wrapper library
Documentation
use std::sync::{LazyLock, Mutex, OnceLock};
use windows::Win32::Foundation::{DBG_PRINTEXCEPTION_C, DBG_PRINTEXCEPTION_WIDE_C};
use windows::Win32::Graphics::Direct3D12::*;
use windows::Win32::System::Diagnostics::Debug::*;

#[inline]
pub fn enable_debug_layer() -> windows::core::Result<()> {
    unsafe {
        let debug: ID3D12Debug = {
            let mut p: Option<ID3D12Debug> = None;
            D3D12GetDebugInterface(&mut p).map(|_| p.unwrap())?
        };
        debug.EnableDebugLayer();
    }
    Ok(())
}

fn call_dbg_handlers(msg: &str) {
    let Some(handlers) = DBG_HANDLERS.lock().ok() else {
        return;
    };
    for handler in handlers.iter() {
        handler(msg);
    }
}

unsafe extern "system" fn exception_handler_proc(pointers: *mut EXCEPTION_POINTERS) -> i32 {
    unsafe {
        let Some(pointers) = pointers.as_ref() else {
            return EXCEPTION_CONTINUE_EXECUTION;
        };
        let Some(record) = pointers.ExceptionRecord.as_ref() else {
            return EXCEPTION_CONTINUE_EXECUTION;
        };
        if record.NumberParameters < 2 {
            return EXCEPTION_CONTINUE_SEARCH;
        }
        match record.ExceptionCode {
            DBG_PRINTEXCEPTION_C => {
                let len = record.ExceptionInformation[0];
                if len >= 2 {
                    let data = record.ExceptionInformation[1] as *const u8;
                    let s = std::slice::from_raw_parts(data, len - 1);
                    if let Ok(msg) = std::str::from_utf8(s) {
                        call_dbg_handlers(msg);
                    }
                }
            }
            DBG_PRINTEXCEPTION_WIDE_C => {
                let len = record.ExceptionInformation[0];
                if len >= 2 {
                    let data = record.ExceptionInformation[1] as *const u16;
                    let s = std::slice::from_raw_parts(data, len - 1);
                    if let Ok(msg) = String::from_utf16(s) {
                        call_dbg_handlers(&msg);
                    }
                }
            }
            _ => {}
        }
        EXCEPTION_CONTINUE_SEARCH
    }
}

type DebugHandler = Box<dyn Fn(&str) + Send + Sync + 'static>;
static DBG_HANDLERS: LazyLock<Mutex<Vec<DebugHandler>>> = LazyLock::new(|| {
    unsafe {
        AddVectoredExceptionHandler(0, Some(exception_handler_proc));
    }
    Mutex::new(vec![])
});

pub fn register_output_debug_string_handler(f: impl Fn(&str) + Send + Sync + 'static) {
    let mut handlers = DBG_HANDLERS.lock().unwrap();
    handlers.push(Box::new(f));
}

#[inline]
pub fn output_debug_string_to_stderr() {
    static LOCK: OnceLock<()> = OnceLock::new();
    LOCK.get_or_init(|| {
        register_output_debug_string_handler(|msg| {
            eprintln!("{msg}");
        });
    });
}

#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
pub enum DredEnablement {
    SystemControlled,
    ForceOn,
    ForceOff,
}

impl Default for DredEnablement {
    #[inline]
    fn default() -> Self {
        Self::SystemControlled
    }
}

impl From<DredEnablement> for D3D12_DRED_ENABLEMENT {
    #[inline]
    fn from(value: DredEnablement) -> Self {
        match value {
            DredEnablement::SystemControlled => D3D12_DRED_ENABLEMENT_SYSTEM_CONTROLLED,
            DredEnablement::ForceOn => D3D12_DRED_ENABLEMENT_FORCED_ON,
            DredEnablement::ForceOff => D3D12_DRED_ENABLEMENT_FORCED_OFF,
        }
    }
}

pub struct DeviceRemovedExtendedDataSettings(ID3D12DeviceRemovedExtendedDataSettings1);

impl DeviceRemovedExtendedDataSettings {
    #[inline]
    pub fn new() -> windows::core::Result<Self> {
        let settings: ID3D12DeviceRemovedExtendedDataSettings1 = unsafe {
            let mut p: Option<_> = None;
            D3D12GetDebugInterface(&mut p).map(|_| p.unwrap())?
        };
        Ok(Self(settings))
    }

    #[inline]
    pub fn set_auto_breadcrumbs_enablement(self, enablement: DredEnablement) -> Self {
        unsafe {
            self.0.SetAutoBreadcrumbsEnablement(enablement.into());
        }
        self
    }

    #[inline]
    pub fn set_page_fault_enablement(self, enablement: DredEnablement) -> Self {
        unsafe {
            self.0.SetPageFaultEnablement(enablement.into());
        }
        self
    }

    #[inline]
    pub fn set_watson_dump_enablement(self, enablement: DredEnablement) -> Self {
        unsafe {
            self.0.SetWatsonDumpEnablement(enablement.into());
        }
        self
    }

    #[inline]
    pub fn set_breadcrumb_context_enablement(self, enablement: DredEnablement) -> Self {
        unsafe {
            self.0.SetBreadcrumbContextEnablement(enablement.into());
        }
        self
    }
}