-
Notifications
You must be signed in to change notification settings - Fork 953
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
b3c2e2c
commit d2dfa50
Showing
13 changed files
with
302 additions
and
158 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
use std::{borrow::Cow, slice}; | ||
|
||
use parking_lot::{lock_api::RawMutex, Mutex}; | ||
use winapi::{ | ||
um::{errhandlingapi, winnt}, | ||
vc::excpt, | ||
}; | ||
|
||
// This is a mutex as opposed to an atomic as we need to completely | ||
// lock everyone out until we have registered or unregistered the | ||
// exception handler, otherwise really nasty races could happen. | ||
// | ||
// By routing all the registration through these functions we can guarentee | ||
// there is either 1 or 0 exception handlers registered, not multiple. | ||
static EXCEPTION_HANLDER_COUNT: Mutex<usize> = Mutex::const_new(parking_lot::RawMutex::INIT, 0); | ||
|
||
pub fn register_exception_handler() { | ||
let mut count_guard = EXCEPTION_HANLDER_COUNT.lock(); | ||
if *count_guard == 0 { | ||
unsafe { | ||
errhandlingapi::AddVectoredExceptionHandler(0, Some(output_debug_string_handler)) | ||
}; | ||
} | ||
*count_guard += 1; | ||
} | ||
|
||
pub fn unregister_exception_handler() { | ||
let mut count_guard = EXCEPTION_HANLDER_COUNT.lock(); | ||
if *count_guard == 1 { | ||
unsafe { | ||
errhandlingapi::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::Info), | ||
("MESSAGE", log::Level::Debug), | ||
]; | ||
|
||
unsafe extern "system" fn output_debug_string_handler( | ||
exception_info: *mut winnt::EXCEPTION_POINTERS, | ||
) -> i32 { | ||
// See https://stackoverflow.com/a/41480827 | ||
let record = &*(*exception_info).ExceptionRecord; | ||
if record.NumberParameters != 2 { | ||
return excpt::EXCEPTION_CONTINUE_SEARCH; | ||
} | ||
let message = match record.ExceptionCode { | ||
winnt::DBG_PRINTEXCEPTION_C => String::from_utf8_lossy(slice::from_raw_parts( | ||
record.ExceptionInformation[1] as *const u8, | ||
record.ExceptionInformation[0], | ||
)), | ||
winnt::DBG_PRINTEXCEPTION_WIDE_C => { | ||
Cow::Owned(String::from_utf16_lossy(slice::from_raw_parts( | ||
record.ExceptionInformation[1] as *const u16, | ||
record.ExceptionInformation[0], | ||
))) | ||
} | ||
_ => return excpt::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 excpt::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") { | ||
// This is are useless spammy warnings (#820, #821): | ||
// "The application did not pass any clear value to resource creation" | ||
return excpt::EXCEPTION_CONTINUE_SEARCH; | ||
} | ||
|
||
let _ = std::panic::catch_unwind(|| { | ||
log::log!(level, "{}", message); | ||
}); | ||
|
||
if cfg!(debug_assertions) && level == log::Level::Error { | ||
// Set canary and continue | ||
crate::VALIDATION_CANARY.set(); | ||
} | ||
|
||
excpt::EXCEPTION_CONTINUE_EXECUTION | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
use winapi::shared::dxgi1_2; | ||
|
||
use super::result::HResult as _; | ||
|
||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] | ||
pub enum DxgiFactoryType { | ||
Factory1, | ||
Factory2, | ||
Factory4, | ||
} | ||
|
||
pub enum DxgiFactory { | ||
Factory1(native::Factory1), | ||
Factory2(native::Factory2), | ||
Factory4(native::Factory4), | ||
} | ||
|
||
impl DxgiFactory { | ||
#[track_caller] | ||
pub fn unwrap_factory4(self) -> native::Factory4 { | ||
match self { | ||
DxgiFactory::Factory1(_) => panic!("unwrapping a factory4, got a factory1"), | ||
DxgiFactory::Factory2(_) => panic!("unwrapping a factory4, got a factory2"), | ||
DxgiFactory::Factory4(f) => f, | ||
} | ||
} | ||
} | ||
|
||
/// Tries to create a IDXGIFactory4, then a IDXGIFactory2, then a IDXGIFactory1, | ||
/// returning the one that succeeds, or if the required_factory_type fails to be | ||
/// created. | ||
pub fn create_factory( | ||
required_factory_type: DxgiFactoryType, | ||
instance_flags: crate::InstanceFlags, | ||
) -> Result<(native::DxgiLib, DxgiFactory), crate::InstanceError> { | ||
let lib_dxgi = native::DxgiLib::new().map_err(|_| crate::InstanceError)?; | ||
|
||
let mut factory_flags = native::FactoryCreationFlags::empty(); | ||
|
||
if instance_flags.contains(crate::InstanceFlags::VALIDATION) { | ||
// The `DXGI_CREATE_FACTORY_DEBUG` flag is only allowed to be passed to | ||
// `CreateDXGIFactory2` if the debug interface is actually available. So | ||
// we check for whether it exists first. | ||
match lib_dxgi.get_debug_interface1() { | ||
Ok(pair) => match pair.into_result() { | ||
Ok(debug_controller) => { | ||
unsafe { debug_controller.destroy() }; | ||
factory_flags |= native::FactoryCreationFlags::DEBUG; | ||
} | ||
Err(err) => { | ||
log::warn!("Unable to enable DXGI debug interface: {}", err); | ||
} | ||
}, | ||
Err(err) => { | ||
log::warn!("Debug interface function for DXGI not found: {:?}", err); | ||
} | ||
} | ||
|
||
// Intercept `OutputDebugString` calls | ||
super::exception::register_exception_handler(); | ||
} | ||
|
||
// Try to create IDXGIFactory4 | ||
match lib_dxgi.create_factory2(factory_flags) { | ||
Ok(pair) => match pair.into_result() { | ||
Ok(factory) => return Ok((lib_dxgi, DxgiFactory::Factory4(factory))), | ||
// We hard error here as we _should have_ been able to make a factory4 but couldn't. | ||
Err(err) => { | ||
log::error!("Failed to create IDXGIFactory4: {}", err); | ||
return Err(crate::InstanceError); | ||
} | ||
}, | ||
// If we require factory4, hard error. | ||
Err(err) if required_factory_type == DxgiFactoryType::Factory4 => { | ||
log::error!("IDXGIFactory1 creation function not found: {:?}", err); | ||
return Err(crate::InstanceError); | ||
} | ||
// If we don't print it to info as all win7 will hit this case. | ||
Err(err) => { | ||
log::info!("IDXGIFactory1 creation function not found: {:?}", err); | ||
} | ||
}; | ||
|
||
// Try to create IDXGIFactory1 | ||
let factory1 = match lib_dxgi.create_factory1() { | ||
Ok(pair) => match pair.into_result() { | ||
Ok(factory) => factory, | ||
Err(err) => { | ||
log::error!("Failed to create IDXGIFactory1: {}", err); | ||
return Err(crate::InstanceError); | ||
} | ||
}, | ||
// We always require at least factory1, so hard error | ||
Err(err) => { | ||
log::error!("IDXGIFactory1 creation function not found: {:?}", err); | ||
return Err(crate::InstanceError); | ||
} | ||
}; | ||
|
||
// Try to cast the IDXGIFactory1 into IDXGIFactory2 | ||
let factory2 = unsafe { factory1.cast::<dxgi1_2::IDXGIFactory2>().into_result() }; | ||
match factory2 { | ||
Ok(factory2) => return Ok((lib_dxgi, DxgiFactory::Factory2(factory2))), | ||
// If we require factory2, hard error. | ||
Err(err) if required_factory_type == DxgiFactoryType::Factory2 => { | ||
log::warn!("Failed to cast IDXGIFactory1 to IDXGIFactory2: {:?}", err); | ||
return Err(crate::InstanceError); | ||
} | ||
// If we don't print it to info. | ||
Err(err) => { | ||
log::info!("Failed to cast IDXGIFactory1 to IDXGIFactory2: {:?}", err); | ||
} | ||
} | ||
|
||
// We tried to create 4 and 2, but only succeeded with 1. | ||
Ok((lib_dxgi, DxgiFactory::Factory1(factory1))) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,4 @@ | ||
pub mod conv; | ||
pub mod exception; | ||
pub mod factory; | ||
pub mod result; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
use std::borrow::Cow; | ||
|
||
use winapi::shared::winerror; | ||
|
||
pub(crate) trait HResult<O> { | ||
fn into_result(self) -> Result<O, Cow<'static, str>>; | ||
fn into_device_result(self, description: &str) -> Result<O, crate::DeviceError>; | ||
} | ||
impl HResult<()> for i32 { | ||
fn into_result(self) -> Result<(), Cow<'static, str>> { | ||
if self >= 0 { | ||
return Ok(()); | ||
} | ||
let description = match self { | ||
winerror::E_UNEXPECTED => "unexpected", | ||
winerror::E_NOTIMPL => "not implemented", | ||
winerror::E_OUTOFMEMORY => "out of memory", | ||
winerror::E_INVALIDARG => "invalid argument", | ||
_ => return Err(Cow::Owned(format!("0x{:X}", self as u32))), | ||
}; | ||
Err(Cow::Borrowed(description)) | ||
} | ||
fn into_device_result(self, description: &str) -> Result<(), crate::DeviceError> { | ||
self.into_result().map_err(|err| { | ||
log::error!("{} failed: {}", description, err); | ||
if self == winerror::E_OUTOFMEMORY { | ||
crate::DeviceError::OutOfMemory | ||
} else { | ||
crate::DeviceError::Lost | ||
} | ||
}) | ||
} | ||
} | ||
|
||
impl<T> HResult<T> for (T, i32) { | ||
fn into_result(self) -> Result<T, Cow<'static, str>> { | ||
self.1.into_result().map(|()| self.0) | ||
} | ||
fn into_device_result(self, description: &str) -> Result<T, crate::DeviceError> { | ||
self.1.into_device_result(description).map(|()| self.0) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.