Skip to content

Commit

Permalink
Factory out DXGI factory creation
Browse files Browse the repository at this point in the history
  • Loading branch information
cwfitzgerald committed Mar 1, 2022
1 parent b3c2e2c commit d2dfa50
Show file tree
Hide file tree
Showing 13 changed files with 302 additions and 158 deletions.
2 changes: 1 addition & 1 deletion wgpu-hal/src/auxil/dxgi/conv.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use winapi::shared::{dxgiformat};
use winapi::shared::dxgiformat;

pub fn map_texture_format(format: wgt::TextureFormat) -> dxgiformat::DXGI_FORMAT {
use wgt::TextureFormat as Tf;
Expand Down
98 changes: 98 additions & 0 deletions wgpu-hal/src/auxil/dxgi/exception.rs
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
}
117 changes: 117 additions & 0 deletions wgpu-hal/src/auxil/dxgi/factory.rs
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)))
}
3 changes: 3 additions & 0 deletions wgpu-hal/src/auxil/dxgi/mod.rs
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;
42 changes: 42 additions & 0 deletions wgpu-hal/src/auxil/dxgi/result.rs
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)
}
}
9 changes: 8 additions & 1 deletion wgpu-hal/src/dx11/instance.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
use crate::auxil;

impl crate::Instance<super::Api> for super::Instance {
unsafe fn init(desc: &crate::InstanceDescriptor) -> Result<Self, crate::InstanceError> {
todo!()
let (lib_dxgi, factory) = auxil::dxgi::factory::create_factory(
auxil::dxgi::factory::DxgiFactoryType::Factory1,
desc.flags,
)?;

Ok(super::Instance { lib_dxgi, factory })
}

unsafe fn create_surface(
Expand Down
11 changes: 10 additions & 1 deletion wgpu-hal/src/dx11/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
#![allow(dead_code)]
#![allow(unused_variables)]

use crate::auxil;

mod adapter;
mod command;
mod device;
Expand Down Expand Up @@ -34,7 +37,13 @@ impl crate::Api for Api {
type ComputePipeline = ComputePipeline;
}

pub struct Instance {}
pub struct Instance {
lib_dxgi: native::DxgiLib,
factory: auxil::dxgi::factory::DxgiFactory,
}

unsafe impl Send for Instance {}
unsafe impl Sync for Instance {}

pub struct Surface {}

Expand Down
4 changes: 2 additions & 2 deletions wgpu-hal/src/dx12/adapter.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::{
auxil,
dx12::{HResult as _, SurfaceTarget},
auxil::{self, dxgi::result::HResult as _},
dx12::SurfaceTarget,
};
use std::{mem, sync::Arc, thread};
use winapi::{
Expand Down
4 changes: 2 additions & 2 deletions wgpu-hal/src/dx12/command.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::auxil;
use crate::auxil::{self, dxgi::result::HResult as _};

use super::{conv, HResult as _};
use super::conv;
use std::{mem, ops::Range, ptr};
use winapi::um::d3d12;

Expand Down
2 changes: 1 addition & 1 deletion wgpu-hal/src/dx12/descriptor.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use super::HResult as _;
use crate::auxil::dxgi::result::HResult as _;
use bit_set::BitSet;
use parking_lot::Mutex;
use range_alloc::RangeAllocator;
Expand Down
7 changes: 5 additions & 2 deletions wgpu-hal/src/dx12/device.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
use crate::{auxil, FormatAspects};
use crate::{
auxil::{self, dxgi::result::HResult as _},
FormatAspects,
};

use super::{conv, descriptor, view, HResult as _};
use super::{conv, descriptor, view};
use parking_lot::Mutex;
use std::{ffi, mem, num::NonZeroU32, ptr, slice, sync::Arc};
use winapi::{
Expand Down
Loading

0 comments on commit d2dfa50

Please sign in to comment.