use alloc::{borrow::Cow, string::String};
use parking_lot::Mutex;
use windows::Win32::{Foundation, System::Diagnostics::Debug};
static EXCEPTION_HANDLER_COUNT: Mutex<usize> = Mutex::new(0);
pub fn register_exception_handler() {
let mut count_guard = EXCEPTION_HANDLER_COUNT.lock();
if *count_guard == 0 {
unsafe { Debug::AddVectoredExceptionHandler(0, Some(output_debug_string_handler)) };
}
*count_guard += 1;
}
pub fn unregister_exception_handler() {
let mut count_guard = EXCEPTION_HANDLER_COUNT.lock();
if *count_guard == 1 {
unsafe { Debug::RemoveVectoredExceptionHandler(output_debug_string_handler as *mut _) };
}
*count_guard -= 1;
}
const MESSAGE_PREFIXES: &[(&str, log::Level)] = &[
("CORRUPTION", log::Level::Error),
("ERROR", log::Level::Error),
("WARNING", log::Level::Warn),
("INFO", log::Level::Debug),
("MESSAGE", log::Level::Trace),
];
unsafe extern "system" fn output_debug_string_handler(
exception_info: *mut Debug::EXCEPTION_POINTERS,
) -> i32 {
let record = unsafe { &*(*exception_info).ExceptionRecord };
if record.NumberParameters != 2 {
return Debug::EXCEPTION_CONTINUE_SEARCH;
}
let message = match record.ExceptionCode {
Foundation::DBG_PRINTEXCEPTION_C => {
String::from_utf8_lossy(bytemuck::cast_slice(&record.ExceptionInformation))
}
Foundation::DBG_PRINTEXCEPTION_WIDE_C => Cow::Owned(String::from_utf16_lossy(
bytemuck::cast_slice(&record.ExceptionInformation),
)),
_ => return Debug::EXCEPTION_CONTINUE_SEARCH,
};
let message = match message.strip_prefix("D3D12 ") {
Some(msg) => msg
.trim_end_matches("\n\0")
.trim_end_matches("[ STATE_CREATION WARNING #0: UNKNOWN]"),
None => return Debug::EXCEPTION_CONTINUE_SEARCH,
};
let (message, level) = match MESSAGE_PREFIXES
.iter()
.find(|&&(prefix, _)| message.starts_with(prefix))
{
Some(&(prefix, level)) => (&message[prefix.len() + 2..], level),
None => (message, log::Level::Debug),
};
if level == log::Level::Warn && message.contains("#82") {
return Debug::EXCEPTION_CONTINUE_SEARCH;
}
if level == log::Level::Warn && message.contains("DRAW_EMPTY_SCISSOR_RECTANGLE") {
return Debug::EXCEPTION_CONTINUE_SEARCH;
}
let _ = std::panic::catch_unwind(|| {
log::log!(level, "{message}");
});
#[cfg(feature = "validation_canary")]
if cfg!(debug_assertions) && level == log::Level::Error {
use alloc::string::ToString as _;
crate::VALIDATION_CANARY.add(message.to_string());
}
Debug::EXCEPTION_CONTINUE_EXECUTION
}