diff --git a/Cargo.lock b/Cargo.lock index 745c4d5d15..8173bd49f8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -436,8 +436,7 @@ checksum = "b365fabc795046672053e29c954733ec3b05e4be654ab130fe8f1f94d7051f35" [[package]] name = "d3d12" version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2daefd788d1e96e0a9d66dee4b828b883509bc3ea9ce30665f04c3246372690c" +source = "git+https://github.com/gfx-rs/d3d12-rs.git?rev=ffe5e261da0a6cb85332b82ab310abd2a7e849f6#ffe5e261da0a6cb85332b82ab310abd2a7e849f6" dependencies = [ "bitflags", "libloading", diff --git a/Cargo.toml b/Cargo.toml index 0c7770ed44..7d3044ae3d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,6 +28,7 @@ default-members = ["wgpu", "wgpu-hal", "wgpu-info"] [patch.crates-io] #naga = { path = "../naga" } #glow = { path = "../glow" } +#d3d12 = { path = "../d3d12-rs" } #metal = { path = "../metal-rs" } #web-sys = { path = "../wasm-bindgen/crates/web-sys" } #js-sys = { path = "../wasm-bindgen/crates/js-sys" } diff --git a/wgpu-core/Cargo.toml b/wgpu-core/Cargo.toml index 0766c85cab..4cd4770927 100644 --- a/wgpu-core/Cargo.toml +++ b/wgpu-core/Cargo.toml @@ -65,7 +65,7 @@ hal = { path = "../wgpu-hal", package = "wgpu-hal", version = "0.12", features = hal = { path = "../wgpu-hal", package = "wgpu-hal", version = "0.12", features = ["vulkan", "gles", "renderdoc"] } [target.'cfg(all(not(target_arch = "wasm32"), windows))'.dependencies] -hal = { path = "../wgpu-hal", package = "wgpu-hal", version = "0.12", features = ["vulkan", "dx12", "renderdoc"] } +hal = { path = "../wgpu-hal", package = "wgpu-hal", version = "0.12", features = ["vulkan", "dx12", "dx11", "renderdoc"] } [target.'cfg(target_os = "emscripten")'.dependencies] hal = { path = "../wgpu-hal", package = "wgpu-hal", version = "0.12", features = ["emscripten"] } diff --git a/wgpu-core/build.rs b/wgpu-core/build.rs index ae7e3870c2..445ea245d2 100644 --- a/wgpu-core/build.rs +++ b/wgpu-core/build.rs @@ -10,7 +10,7 @@ fn main() { vulkan: { all(not(wasm), any(windows, unix_wo_apple, feature = "vulkan-portability")) }, metal: { all(not(wasm), apple) }, dx12: { all(not(wasm), windows) }, - dx11: { all(false, not(wasm), windows) }, + dx11: { all(not(wasm), windows) }, gl: { any( unix_wo_apple, diff --git a/wgpu-core/src/hub.rs b/wgpu-core/src/hub.rs index 9f8b840b7f..8a56fd12f8 100644 --- a/wgpu-core/src/hub.rs +++ b/wgpu-core/src/hub.rs @@ -1042,10 +1042,16 @@ impl HalApi for hal::api::Dx12 { } } -/* #[cfg(dx11)] impl HalApi for hal::api::Dx11 { const VARIANT: Backend = Backend::Dx11; + fn create_instance_from_hal(name: &str, hal_instance: Self::Instance) -> Instance { + Instance { + name: name.to_owned(), + dx11: Some(hal_instance), + ..Default::default() + } + } fn hub(global: &Global) -> &Hub { &global.hubs.dx11 } @@ -1056,7 +1062,6 @@ impl HalApi for hal::api::Dx11 { surface.dx11.as_mut().unwrap() } } -*/ #[cfg(gl)] impl HalApi for hal::api::Gles { diff --git a/wgpu-core/src/instance.rs b/wgpu-core/src/instance.rs index b23a983d90..d93d5d3d08 100644 --- a/wgpu-core/src/instance.rs +++ b/wgpu-core/src/instance.rs @@ -508,6 +508,7 @@ impl Global { dx12: self.instance.dx12.as_ref().map(|inst| HalSurface { raw: { inst.create_surface_from_visual(visual as _) }, }), + dx11: None, #[cfg(gl)] gl: None, }; diff --git a/wgpu-core/src/lib.rs b/wgpu-core/src/lib.rs index 81b414429a..edef81afc9 100644 --- a/wgpu-core/src/lib.rs +++ b/wgpu-core/src/lib.rs @@ -211,8 +211,8 @@ macro_rules! gfx_select { wgt::Backend::Metal => $global.$method::<$crate::api::Metal>( $($param),* ), #[cfg(all(not(target_arch = "wasm32"), windows))] wgt::Backend::Dx12 => $global.$method::<$crate::api::Dx12>( $($param),* ), - //#[cfg(all(not(target_arch = "wasm32"), windows))] - //wgt::Backend::Dx11 => $global.$method::<$crate::api::Dx11>( $($param),* ), + #[cfg(all(not(target_arch = "wasm32"), windows))] + wgt::Backend::Dx11 => $global.$method::<$crate::api::Dx11>( $($param),* ), #[cfg(any( all(unix, not(target_os = "macos"), not(target_os = "ios")), feature = "angle", diff --git a/wgpu-hal/Cargo.toml b/wgpu-hal/Cargo.toml index 44d3532ac4..3022696a04 100644 --- a/wgpu-hal/Cargo.toml +++ b/wgpu-hal/Cargo.toml @@ -16,6 +16,7 @@ default = [] metal = ["naga/msl-out", "block", "foreign-types"] vulkan = ["naga/spv-out", "ash", "gpu-alloc", "gpu-descriptor", "libloading", "inplace_it"] gles = ["naga/glsl-out", "glow", "egl", "libloading"] +dx11 = ["naga/hlsl-out", "native", "libloading", "winapi/d3d11", "winapi/d3d11_1", "winapi/d3d11_2", "winapi/d3d11sdklayers", "winapi/dxgi1_6"] dx12 = ["naga/hlsl-out", "native", "bit-set", "range-alloc", "winapi/d3d12", "winapi/d3d12shader", "winapi/d3d12sdklayers", "winapi/dxgi1_6"] renderdoc = ["libloading", "renderdoc-sys"] emscripten = ["gles"] @@ -74,7 +75,8 @@ libloading = { version = "0.7", optional = true } [target.'cfg(windows)'.dependencies] winapi = { version = "0.3", features = ["libloaderapi", "windef", "winuser", "dcomp"] } -native = { package = "d3d12", version = "0.4.1", features = ["libloading"], optional = true } +# native = { package = "d3d12", version = "0.4.1", features = ["libloading"], optional = true } +native = { package = "d3d12", git = "https://github.com/gfx-rs/d3d12-rs.git", rev = "ffe5e261da0a6cb85332b82ab310abd2a7e849f6", features = ["libloading"], optional = true } [target.'cfg(any(target_os="macos", target_os="ios"))'.dependencies] mtl = { package = "metal", git = "https://github.com/gfx-rs/metal-rs", rev = "a357159" } diff --git a/wgpu-hal/src/auxil/dxgi/conv.rs b/wgpu-hal/src/auxil/dxgi/conv.rs new file mode 100644 index 0000000000..dfcb95b0fc --- /dev/null +++ b/wgpu-hal/src/auxil/dxgi/conv.rs @@ -0,0 +1,175 @@ +use winapi::shared::dxgiformat; + +pub fn map_texture_format(format: wgt::TextureFormat) -> dxgiformat::DXGI_FORMAT { + use wgt::TextureFormat as Tf; + use winapi::shared::dxgiformat::*; + + match format { + Tf::R8Unorm => DXGI_FORMAT_R8_UNORM, + Tf::R8Snorm => DXGI_FORMAT_R8_SNORM, + Tf::R8Uint => DXGI_FORMAT_R8_UINT, + Tf::R8Sint => DXGI_FORMAT_R8_SINT, + Tf::R16Uint => DXGI_FORMAT_R16_UINT, + Tf::R16Sint => DXGI_FORMAT_R16_SINT, + Tf::R16Unorm => DXGI_FORMAT_R16_UNORM, + Tf::R16Snorm => DXGI_FORMAT_R16_SNORM, + Tf::R16Float => DXGI_FORMAT_R16_FLOAT, + Tf::Rg8Unorm => DXGI_FORMAT_R8G8_UNORM, + Tf::Rg8Snorm => DXGI_FORMAT_R8G8_SNORM, + Tf::Rg8Uint => DXGI_FORMAT_R8G8_UINT, + Tf::Rg8Sint => DXGI_FORMAT_R8G8_SINT, + Tf::Rg16Unorm => DXGI_FORMAT_R16G16_UNORM, + Tf::Rg16Snorm => DXGI_FORMAT_R16G16_SNORM, + Tf::R32Uint => DXGI_FORMAT_R32_UINT, + Tf::R32Sint => DXGI_FORMAT_R32_SINT, + Tf::R32Float => DXGI_FORMAT_R32_FLOAT, + Tf::Rg16Uint => DXGI_FORMAT_R16G16_UINT, + Tf::Rg16Sint => DXGI_FORMAT_R16G16_SINT, + Tf::Rg16Float => DXGI_FORMAT_R16G16_FLOAT, + Tf::Rgba8Unorm => DXGI_FORMAT_R8G8B8A8_UNORM, + Tf::Rgba8UnormSrgb => DXGI_FORMAT_R8G8B8A8_UNORM_SRGB, + Tf::Bgra8UnormSrgb => DXGI_FORMAT_B8G8R8A8_UNORM_SRGB, + Tf::Rgba8Snorm => DXGI_FORMAT_R8G8B8A8_SNORM, + Tf::Bgra8Unorm => DXGI_FORMAT_B8G8R8A8_UNORM, + Tf::Rgba8Uint => DXGI_FORMAT_R8G8B8A8_UINT, + Tf::Rgba8Sint => DXGI_FORMAT_R8G8B8A8_SINT, + Tf::Rgb10a2Unorm => DXGI_FORMAT_R10G10B10A2_UNORM, + Tf::Rg11b10Float => DXGI_FORMAT_R11G11B10_FLOAT, + Tf::Rg32Uint => DXGI_FORMAT_R32G32_UINT, + Tf::Rg32Sint => DXGI_FORMAT_R32G32_SINT, + Tf::Rg32Float => DXGI_FORMAT_R32G32_FLOAT, + Tf::Rgba16Uint => DXGI_FORMAT_R16G16B16A16_UINT, + Tf::Rgba16Sint => DXGI_FORMAT_R16G16B16A16_SINT, + Tf::Rgba16Unorm => DXGI_FORMAT_R16G16B16A16_UNORM, + Tf::Rgba16Snorm => DXGI_FORMAT_R16G16B16A16_SNORM, + Tf::Rgba16Float => DXGI_FORMAT_R16G16B16A16_FLOAT, + Tf::Rgba32Uint => DXGI_FORMAT_R32G32B32A32_UINT, + Tf::Rgba32Sint => DXGI_FORMAT_R32G32B32A32_SINT, + Tf::Rgba32Float => DXGI_FORMAT_R32G32B32A32_FLOAT, + Tf::Depth32Float => DXGI_FORMAT_D32_FLOAT, + Tf::Depth24Plus => DXGI_FORMAT_D24_UNORM_S8_UINT, + Tf::Depth24PlusStencil8 => DXGI_FORMAT_D24_UNORM_S8_UINT, + Tf::Rgb9e5Ufloat => DXGI_FORMAT_R9G9B9E5_SHAREDEXP, + Tf::Bc1RgbaUnorm => DXGI_FORMAT_BC1_UNORM, + Tf::Bc1RgbaUnormSrgb => DXGI_FORMAT_BC1_UNORM_SRGB, + Tf::Bc2RgbaUnorm => DXGI_FORMAT_BC2_UNORM, + Tf::Bc2RgbaUnormSrgb => DXGI_FORMAT_BC2_UNORM_SRGB, + Tf::Bc3RgbaUnorm => DXGI_FORMAT_BC3_UNORM, + Tf::Bc3RgbaUnormSrgb => DXGI_FORMAT_BC3_UNORM_SRGB, + Tf::Bc4RUnorm => DXGI_FORMAT_BC4_UNORM, + Tf::Bc4RSnorm => DXGI_FORMAT_BC4_SNORM, + Tf::Bc5RgUnorm => DXGI_FORMAT_BC5_UNORM, + Tf::Bc5RgSnorm => DXGI_FORMAT_BC5_SNORM, + Tf::Bc6hRgbUfloat => DXGI_FORMAT_BC6H_UF16, + Tf::Bc6hRgbSfloat => DXGI_FORMAT_BC6H_SF16, + Tf::Bc7RgbaUnorm => DXGI_FORMAT_BC7_UNORM, + Tf::Bc7RgbaUnormSrgb => DXGI_FORMAT_BC7_UNORM_SRGB, + Tf::Etc2Rgb8Unorm + | Tf::Etc2Rgb8UnormSrgb + | Tf::Etc2Rgb8A1Unorm + | Tf::Etc2Rgb8A1UnormSrgb + | Tf::Etc2Rgba8Unorm + | Tf::Etc2Rgba8UnormSrgb + | Tf::EacR11Unorm + | Tf::EacR11Snorm + | Tf::EacRg11Unorm + | Tf::EacRg11Snorm + | Tf::Astc { + block: _, + channel: _, + } => unreachable!(), + } +} + +// Note: DXGI doesn't allow sRGB format on the swapchain, +// but creating RTV of swapchain buffers with sRGB works. +pub fn map_texture_format_nosrgb(format: wgt::TextureFormat) -> dxgiformat::DXGI_FORMAT { + match format { + wgt::TextureFormat::Bgra8UnormSrgb => dxgiformat::DXGI_FORMAT_B8G8R8A8_UNORM, + wgt::TextureFormat::Rgba8UnormSrgb => dxgiformat::DXGI_FORMAT_R8G8B8A8_UNORM, + _ => map_texture_format(format), + } +} + +//Note: SRV and UAV can't use the depth formats directly +//TODO: stencil views? +pub fn map_texture_format_nodepth(format: wgt::TextureFormat) -> dxgiformat::DXGI_FORMAT { + match format { + wgt::TextureFormat::Depth32Float => dxgiformat::DXGI_FORMAT_R32_FLOAT, + wgt::TextureFormat::Depth24Plus | wgt::TextureFormat::Depth24PlusStencil8 => { + dxgiformat::DXGI_FORMAT_R24_UNORM_X8_TYPELESS + } + _ => { + assert_eq!( + crate::FormatAspects::from(format), + crate::FormatAspects::COLOR + ); + map_texture_format(format) + } + } +} + +pub fn map_texture_format_depth_typeless(format: wgt::TextureFormat) -> dxgiformat::DXGI_FORMAT { + match format { + wgt::TextureFormat::Depth32Float => dxgiformat::DXGI_FORMAT_R32_TYPELESS, + wgt::TextureFormat::Depth24Plus | wgt::TextureFormat::Depth24PlusStencil8 => { + dxgiformat::DXGI_FORMAT_R24G8_TYPELESS + } + _ => unreachable!(), + } +} + +pub fn map_index_format(format: wgt::IndexFormat) -> dxgiformat::DXGI_FORMAT { + match format { + wgt::IndexFormat::Uint16 => dxgiformat::DXGI_FORMAT_R16_UINT, + wgt::IndexFormat::Uint32 => dxgiformat::DXGI_FORMAT_R32_UINT, + } +} + +pub fn map_vertex_format(format: wgt::VertexFormat) -> dxgiformat::DXGI_FORMAT { + use wgt::VertexFormat as Vf; + use winapi::shared::dxgiformat::*; + + match format { + Vf::Unorm8x2 => DXGI_FORMAT_R8G8_UNORM, + Vf::Snorm8x2 => DXGI_FORMAT_R8G8_SNORM, + Vf::Uint8x2 => DXGI_FORMAT_R8G8_UINT, + Vf::Sint8x2 => DXGI_FORMAT_R8G8_SINT, + Vf::Unorm8x4 => DXGI_FORMAT_R8G8B8A8_UNORM, + Vf::Snorm8x4 => DXGI_FORMAT_R8G8B8A8_SNORM, + Vf::Uint8x4 => DXGI_FORMAT_R8G8B8A8_UINT, + Vf::Sint8x4 => DXGI_FORMAT_R8G8B8A8_SINT, + Vf::Unorm16x2 => DXGI_FORMAT_R16G16_UNORM, + Vf::Snorm16x2 => DXGI_FORMAT_R16G16_SNORM, + Vf::Uint16x2 => DXGI_FORMAT_R16G16_UINT, + Vf::Sint16x2 => DXGI_FORMAT_R16G16_SINT, + Vf::Float16x2 => DXGI_FORMAT_R16G16_FLOAT, + Vf::Unorm16x4 => DXGI_FORMAT_R16G16B16A16_UNORM, + Vf::Snorm16x4 => DXGI_FORMAT_R16G16B16A16_SNORM, + Vf::Uint16x4 => DXGI_FORMAT_R16G16B16A16_UINT, + Vf::Sint16x4 => DXGI_FORMAT_R16G16B16A16_SINT, + Vf::Float16x4 => DXGI_FORMAT_R16G16B16A16_FLOAT, + Vf::Uint32 => DXGI_FORMAT_R32_UINT, + Vf::Sint32 => DXGI_FORMAT_R32_SINT, + Vf::Float32 => DXGI_FORMAT_R32_FLOAT, + Vf::Uint32x2 => DXGI_FORMAT_R32G32_UINT, + Vf::Sint32x2 => DXGI_FORMAT_R32G32_SINT, + Vf::Float32x2 => DXGI_FORMAT_R32G32_FLOAT, + Vf::Uint32x3 => DXGI_FORMAT_R32G32B32_UINT, + Vf::Sint32x3 => DXGI_FORMAT_R32G32B32_SINT, + Vf::Float32x3 => DXGI_FORMAT_R32G32B32_FLOAT, + Vf::Uint32x4 => DXGI_FORMAT_R32G32B32A32_UINT, + Vf::Sint32x4 => DXGI_FORMAT_R32G32B32A32_SINT, + Vf::Float32x4 => DXGI_FORMAT_R32G32B32A32_FLOAT, + Vf::Float64 | Vf::Float64x2 | Vf::Float64x3 | Vf::Float64x4 => unimplemented!(), + } +} + +pub fn map_acomposite_alpha_mode(mode: crate::CompositeAlphaMode) -> native::AlphaMode { + use crate::CompositeAlphaMode as Cam; + match mode { + Cam::Opaque => native::AlphaMode::Ignore, + Cam::PreMultiplied => native::AlphaMode::Premultiplied, + Cam::PostMultiplied => native::AlphaMode::Straight, + } +} diff --git a/wgpu-hal/src/auxil/dxgi/exception.rs b/wgpu-hal/src/auxil/dxgi/exception.rs new file mode 100644 index 0000000000..31d5e6933a --- /dev/null +++ b/wgpu-hal/src/auxil/dxgi/exception.rs @@ -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 = 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 +} diff --git a/wgpu-hal/src/auxil/dxgi/factory.rs b/wgpu-hal/src/auxil/dxgi/factory.rs new file mode 100644 index 0000000000..57c94544d9 --- /dev/null +++ b/wgpu-hal/src/auxil/dxgi/factory.rs @@ -0,0 +1,210 @@ +use winapi::{ + shared::{dxgi, dxgi1_2, dxgi1_4, dxgi1_6, winerror}, + Interface, +}; + +use super::result::HResult as _; + +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] +pub enum DxgiFactoryType { + Factory1, + Factory2, + Factory4, + Factory6, +} + +pub fn enumerate_adapters(factory: native::DxgiFactory) -> Vec { + let mut adapters = Vec::with_capacity(8); + + for cur_index in 0.. { + if let Some(factory6) = factory.as_factory6() { + profiling::scope!("IDXGIFactory6::EnumAdapterByGpuPreference"); + // We're already at dxgi1.6, we can grab IDXGIAdapater4 directly + let mut adapter4 = native::WeakPtr::::null(); + let hr = unsafe { + factory6.EnumAdapterByGpuPreference( + cur_index, + dxgi1_6::DXGI_GPU_PREFERENCE_HIGH_PERFORMANCE, + &dxgi1_6::IDXGIAdapter4::uuidof(), + adapter4.mut_void(), + ) + }; + + if hr == winerror::DXGI_ERROR_NOT_FOUND { + break; + } + if let Err(err) = hr.into_result() { + log::error!("Failed enumerating adapters: {}", err); + break; + } + + adapters.push(native::DxgiAdapter::Adapter4(adapter4)); + continue; + } + + profiling::scope!("IDXGIFactory1::EnumAdapters1"); + let mut adapter1 = native::WeakPtr::::null(); + let hr = unsafe { factory.EnumAdapters1(cur_index, adapter1.mut_void() as *mut *mut _) }; + + if hr == winerror::DXGI_ERROR_NOT_FOUND { + break; + } + if let Err(err) = hr.into_result() { + log::error!("Failed enumerating adapters: {}", err); + break; + } + + // Do the most aggressive casts first, skipping Adpater4 as we definitely don't have dxgi1_6. + + // Adapter1 -> Adapter3 + unsafe { + match adapter1.cast::().into_result() { + Ok(adapter3) => { + adapter1.destroy(); + adapters.push(native::DxgiAdapter::Adapter3(adapter3)); + continue; + } + Err(err) => { + log::info!("Failed casting Adapter1 to Adapter3: {}", err); + } + } + } + + // Adapter1 -> Adapter2 + unsafe { + match adapter1.cast::().into_result() { + Ok(adapter2) => { + adapter1.destroy(); + adapters.push(native::DxgiAdapter::Adapter2(adapter2)); + continue; + } + Err(err) => { + log::info!("Failed casting Adapter1 to Adapter2: {}", err); + } + } + } + + adapters.push(native::DxgiAdapter::Adapter1(adapter1)); + } + + adapters +} + +/// Tries to create a IDXGIFactory6, then 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, native::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 + let factory4 = match lib_dxgi.create_factory2(factory_flags) { + Ok(pair) => match pair.into_result() { + Ok(factory) => Some(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); + None + } + }; + + if let Some(factory4) = factory4 { + // Try to cast the IDXGIFactory4 into IDXGIFactory6 + let factory6 = unsafe { factory4.cast::().into_result() }; + match factory6 { + Ok(factory6) => { + unsafe { + factory4.destroy(); + } + return Ok((lib_dxgi, native::DxgiFactory::Factory6(factory6))); + } + // If we require factory6, hard error. + Err(err) if required_factory_type == DxgiFactoryType::Factory6 => { + log::warn!("Failed to cast IDXGIFactory4 to IDXGIFactory6: {:?}", err); + return Err(crate::InstanceError); + } + // If we don't print it to info. + Err(err) => { + log::info!("Failed to cast IDXGIFactory4 to IDXGIFactory6: {:?}", err); + return Ok((lib_dxgi, native::DxgiFactory::Factory4(factory4))); + } + } + } + + // 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::().into_result() }; + match factory2 { + Ok(factory2) => { + unsafe { + factory1.destroy(); + } + return Ok((lib_dxgi, native::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, native::DxgiFactory::Factory1(factory1))) +} diff --git a/wgpu-hal/src/auxil/dxgi/mod.rs b/wgpu-hal/src/auxil/dxgi/mod.rs new file mode 100644 index 0000000000..09a2b31bf3 --- /dev/null +++ b/wgpu-hal/src/auxil/dxgi/mod.rs @@ -0,0 +1,4 @@ +pub mod conv; +pub mod exception; +pub mod factory; +pub mod result; diff --git a/wgpu-hal/src/auxil/dxgi/result.rs b/wgpu-hal/src/auxil/dxgi/result.rs new file mode 100644 index 0000000000..db013d2dec --- /dev/null +++ b/wgpu-hal/src/auxil/dxgi/result.rs @@ -0,0 +1,42 @@ +use std::borrow::Cow; + +use winapi::shared::winerror; + +pub(crate) trait HResult { + fn into_result(self) -> Result>; + fn into_device_result(self, description: &str) -> Result; +} +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 HResult for (T, i32) { + fn into_result(self) -> Result> { + self.1.into_result().map(|()| self.0) + } + fn into_device_result(self, description: &str) -> Result { + self.1.into_device_result(description).map(|()| self.0) + } +} diff --git a/wgpu-hal/src/auxil/mod.rs b/wgpu-hal/src/auxil/mod.rs index 4d8cd0d2fa..f92fa2863e 100644 --- a/wgpu-hal/src/auxil/mod.rs +++ b/wgpu-hal/src/auxil/mod.rs @@ -1,3 +1,6 @@ +#[cfg(any(feature = "dx11", feature = "dx12"))] +pub(super) mod dxgi; + #[cfg(feature = "renderdoc")] pub(super) mod renderdoc; diff --git a/wgpu-hal/src/dx11/adapter.rs b/wgpu-hal/src/dx11/adapter.rs new file mode 100644 index 0000000000..2e4d3b6214 --- /dev/null +++ b/wgpu-hal/src/dx11/adapter.rs @@ -0,0 +1,274 @@ +use std::num::NonZeroU64; + +use winapi::um::{d3d11, d3dcommon}; + +impl crate::Adapter for super::Adapter { + unsafe fn open( + &self, + features: wgt::Features, + limits: &wgt::Limits, + ) -> Result, crate::DeviceError> { + todo!() + } + + unsafe fn texture_format_capabilities( + &self, + format: wgt::TextureFormat, + ) -> crate::TextureFormatCapabilities { + todo!() + } + + unsafe fn surface_capabilities( + &self, + surface: &super::Surface, + ) -> Option { + todo!() + } +} + +impl super::Adapter { + pub(super) fn expose( + instance: &super::library::D3D11Lib, + adapter: native::DxgiAdapter, + ) -> Option> { + use d3dcommon::{ + D3D_FEATURE_LEVEL_10_0 as FL10_0, D3D_FEATURE_LEVEL_10_1 as FL10_1, + D3D_FEATURE_LEVEL_11_0 as FL11_0, D3D_FEATURE_LEVEL_11_1 as FL11_1, + D3D_FEATURE_LEVEL_9_1 as FL9_1, D3D_FEATURE_LEVEL_9_2 as FL9_2, + D3D_FEATURE_LEVEL_9_3 as FL9_3, + }; + + let (device, feature_level) = instance.create_device(adapter)?; + + // + // Query Features from d3d11 + // + + let d3d9_features = unsafe { + device.check_feature_support::( + d3d11::D3D11_FEATURE_D3D9_OPTIONS1, + ) + }; + + let d3d10_features = unsafe { + device.check_feature_support::( + d3d11::D3D11_FEATURE_D3D10_X_HARDWARE_OPTIONS, + ) + }; + + let d3d11_features = unsafe { + device.check_feature_support::( + d3d11::D3D11_FEATURE_D3D11_OPTIONS, + ) + }; + + let d3d11_features1 = unsafe { + device.check_feature_support::( + d3d11::D3D11_FEATURE_D3D11_OPTIONS1, + ) + }; + + let d3d11_features2 = unsafe { + device.check_feature_support::( + d3d11::D3D11_FEATURE_D3D11_OPTIONS2, + ) + }; + + let d3d11_features3 = unsafe { + device.check_feature_support::( + d3d11::D3D11_FEATURE_D3D11_OPTIONS3, + ) + }; + + // + // Fill out features and downlevel features + // + // TODO(cwfitzgerald): Needed downlevel features: 3D dispatch + + let mut features = wgt::Features::DEPTH_CLIP_CONTROL + | wgt::Features::PUSH_CONSTANTS + | wgt::Features::POLYGON_MODE_LINE + | wgt::Features::CLEAR_TEXTURE + | wgt::Features::TEXTURE_FORMAT_16BIT_NORM + | wgt::Features::ADDRESS_MODE_CLAMP_TO_ZERO; + let mut downlevel = + wgt::DownlevelFlags::BASE_VERTEX | wgt::DownlevelFlags::READ_ONLY_DEPTH_STENCIL; + + // Features from queries + downlevel.set( + wgt::DownlevelFlags::NON_POWER_OF_TWO_MIPMAPPED_TEXTURES, + d3d9_features.FullNonPow2TextureSupported == 1, + ); + downlevel.set( + wgt::DownlevelFlags::COMPUTE_SHADERS, + d3d10_features.ComputeShaders_Plus_RawAndStructuredBuffers_Via_Shader_4_x == 1, + ); + + // Features from feature level + if feature_level >= FL9_2 { + downlevel |= wgt::DownlevelFlags::INDEPENDENT_BLEND; + // formally FL9_1 supports aniso 2, but we don't support that level of distinction + downlevel |= wgt::DownlevelFlags::ANISOTROPIC_FILTERING; + } + + if feature_level >= FL9_3 { + downlevel |= wgt::DownlevelFlags::COMPARISON_SAMPLERS; + } + + if feature_level >= FL10_0 { + downlevel |= wgt::DownlevelFlags::INDEPENDENT_BLEND; + downlevel |= wgt::DownlevelFlags::FRAGMENT_STORAGE; + downlevel |= wgt::DownlevelFlags::FRAGMENT_WRITABLE_STORAGE; + features |= wgt::Features::DEPTH_CLIP_CONTROL; + features |= wgt::Features::TIMESTAMP_QUERY; + features |= wgt::Features::PIPELINE_STATISTICS_QUERY; + } + + if feature_level >= FL10_1 { + downlevel |= wgt::DownlevelFlags::CUBE_ARRAY_TEXTURES; + } + + if feature_level >= FL11_0 { + downlevel |= wgt::DownlevelFlags::INDIRECT_EXECUTION; + features |= wgt::Features::TEXTURE_COMPRESSION_BC; + } + + if feature_level >= FL11_1 { + downlevel |= wgt::DownlevelFlags::VERTEX_STORAGE; + } + + // + // Fill out limits and alignments + // + + let max_texture_dimension_2d = match feature_level { + FL9_1 | FL9_2 => 2048, + FL9_3 => 4096, + FL10_0 | FL10_1 => 8192, + _ => d3d11::D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION, + }; + + let max_texture_dimension_3d = match feature_level { + FL9_1..=FL9_3 => 256, + _ => d3d11::D3D11_REQ_TEXTURE3D_U_V_OR_W_DIMENSION, + }; + let max_vertex_buffers = match feature_level { + FL9_1..=FL9_3 => 16, + _ => 32, + }; + let max_compute_workgroup_storage_size = match feature_level { + FL9_1..=FL9_3 => 0, + FL10_0 | FL10_1 => 4096 * 4, // This doesn't have an equiv SM4 constant :\ + _ => d3d11::D3D11_CS_TGSM_REGISTER_COUNT * 4, + }; + let max_workgroup_size_xy = match feature_level { + FL9_1..=FL9_3 => 0, + FL10_0 | FL10_1 => d3d11::D3D11_CS_4_X_THREAD_GROUP_MAX_X, + _ => d3d11::D3D11_CS_THREAD_GROUP_MAX_X, + }; + let max_workgroup_size_z = match feature_level { + FL9_1..=FL9_3 => 0, + FL10_0 | FL10_1 => 1, + _ => d3d11::D3D11_CS_THREAD_GROUP_MAX_Z, + }; + // let max_workgroup_count_z = match feature_level { + // FL9_1..=FL9_3 => 0, + // FL10_0 | FL10_1 => 1, + // _ => d3d11::D3D11_CS_THREAD_GROUP_MAX_Z, + // }; + + let max_sampled_textures = d3d11::D3D11_COMMONSHADER_INPUT_RESOURCE_REGISTER_COUNT; + let max_samplers = d3d11::D3D11_COMMONSHADER_SAMPLER_SLOT_COUNT; + let max_constant_buffers = d3d11::D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT - 1; + let max_uavs = if device.as_device1().is_some() { + d3d11::D3D11_1_UAV_SLOT_COUNT + } else { + d3d11::D3D11_PS_CS_UAV_REGISTER_COUNT + }; + let max_output_registers = d3d11::D3D11_VS_OUTPUT_REGISTER_COMPONENTS; + let max_compute_invocations_per_workgroup = + d3d11::D3D11_CS_THREAD_GROUP_MAX_THREADS_PER_GROUP; + let max_compute_workgroups_per_dimension = + d3d11::D3D11_CS_DISPATCH_MAX_THREAD_GROUPS_PER_DIMENSION; + + let limits = wgt::Limits { + max_texture_dimension_1d: max_texture_dimension_2d, + max_texture_dimension_2d: max_texture_dimension_2d, + max_texture_dimension_3d: max_texture_dimension_3d, + max_texture_array_layers: max_texture_dimension_3d, + max_bind_groups: u32::MAX, + max_dynamic_uniform_buffers_per_pipeline_layout: max_constant_buffers, + max_dynamic_storage_buffers_per_pipeline_layout: 0, + max_sampled_textures_per_shader_stage: max_sampled_textures, + max_samplers_per_shader_stage: max_samplers, + max_storage_buffers_per_shader_stage: max_uavs, + max_storage_textures_per_shader_stage: max_uavs, + max_uniform_buffers_per_shader_stage: max_constant_buffers, + max_uniform_buffer_binding_size: 1 << 16, + max_storage_buffer_binding_size: u32::MAX, + max_vertex_buffers: max_vertex_buffers, + max_vertex_attributes: max_vertex_buffers, + max_vertex_buffer_array_stride: u32::MAX, + max_push_constant_size: 1 << 16, + min_uniform_buffer_offset_alignment: 256, + min_storage_buffer_offset_alignment: 1, + max_inter_stage_shader_components: max_output_registers, + max_compute_workgroup_storage_size, + max_compute_invocations_per_workgroup, + max_compute_workgroup_size_x: max_workgroup_size_xy, + max_compute_workgroup_size_y: max_workgroup_size_xy, + max_compute_workgroup_size_z: max_workgroup_size_z, + max_compute_workgroups_per_dimension, + }; + + // + // Other capabilities + // + + let shader_model = match feature_level { + FL9_1..=FL9_3 => wgt::ShaderModel::Sm2, + FL10_0 | FL10_1 => wgt::ShaderModel::Sm4, + _ => wgt::ShaderModel::Sm5, + }; + + let device_info = wgt::AdapterInfo { + name: String::new(), + vendor: 0, + device: 0, + device_type: match d3d11_features2.UnifiedMemoryArchitecture { + 0 => wgt::DeviceType::DiscreteGpu, + 1 => wgt::DeviceType::IntegratedGpu, + _ => unreachable!(), + }, + backend: wgt::Backend::Dx11, + }; + + // + // Build up the structs + // + + let api_adapter = super::Adapter { device }; + + let alignments = crate::Alignments { + buffer_copy_offset: NonZeroU64::new(1).unwrap(), // todo + buffer_copy_pitch: NonZeroU64::new(1).unwrap(), // todo + }; + + let capabilities = crate::Capabilities { + limits, + alignments, + downlevel: wgt::DownlevelCapabilities { + flags: downlevel, + limits: wgt::DownlevelLimits {}, + shader_model, + }, + }; + + Some(crate::ExposedAdapter { + adapter: api_adapter, + info: device_info, + features, + capabilities, + }) + } +} diff --git a/wgpu-hal/src/dx11/command.rs b/wgpu-hal/src/dx11/command.rs new file mode 100644 index 0000000000..1c73f3c325 --- /dev/null +++ b/wgpu-hal/src/dx11/command.rs @@ -0,0 +1,268 @@ +impl crate::CommandEncoder for super::CommandEncoder { + unsafe fn begin_encoding(&mut self, label: crate::Label) -> Result<(), crate::DeviceError> { + todo!() + } + + unsafe fn discard_encoding(&mut self) { + todo!() + } + + unsafe fn end_encoding(&mut self) -> Result { + todo!() + } + + unsafe fn reset_all(&mut self, command_buffers: I) + where + I: Iterator, + { + todo!() + } + + unsafe fn transition_buffers<'a, T>(&mut self, barriers: T) + where + T: Iterator>, + { + todo!() + } + + unsafe fn transition_textures<'a, T>(&mut self, barriers: T) + where + T: Iterator>, + { + todo!() + } + + unsafe fn clear_buffer(&mut self, buffer: &super::Buffer, range: crate::MemoryRange) { + todo!() + } + + unsafe fn copy_buffer_to_buffer( + &mut self, + src: &super::Buffer, + dst: &super::Buffer, + regions: T, + ) where + T: Iterator, + { + todo!() + } + + unsafe fn copy_texture_to_texture( + &mut self, + src: &super::Texture, + src_usage: crate::TextureUses, + dst: &super::Texture, + regions: T, + ) where + T: Iterator, + { + todo!() + } + + unsafe fn copy_buffer_to_texture( + &mut self, + src: &super::Buffer, + dst: &super::Texture, + regions: T, + ) where + T: Iterator, + { + todo!() + } + + unsafe fn copy_texture_to_buffer( + &mut self, + src: &super::Texture, + src_usage: crate::TextureUses, + dst: &super::Buffer, + regions: T, + ) where + T: Iterator, + { + todo!() + } + + unsafe fn set_bind_group( + &mut self, + layout: &super::PipelineLayout, + index: u32, + group: &super::BindGroup, + dynamic_offsets: &[wgt::DynamicOffset], + ) { + todo!() + } + + unsafe fn set_push_constants( + &mut self, + layout: &super::PipelineLayout, + stages: wgt::ShaderStages, + offset: u32, + data: &[u32], + ) { + todo!() + } + + unsafe fn insert_debug_marker(&mut self, label: &str) { + todo!() + } + + unsafe fn begin_debug_marker(&mut self, group_label: &str) { + todo!() + } + + unsafe fn end_debug_marker(&mut self) { + todo!() + } + + unsafe fn begin_query(&mut self, set: &super::QuerySet, index: u32) { + todo!() + } + + unsafe fn end_query(&mut self, set: &super::QuerySet, index: u32) { + todo!() + } + + unsafe fn write_timestamp(&mut self, set: &super::QuerySet, index: u32) { + todo!() + } + + unsafe fn reset_queries(&mut self, set: &super::QuerySet, range: std::ops::Range) { + todo!() + } + + unsafe fn copy_query_results( + &mut self, + set: &super::QuerySet, + range: std::ops::Range, + buffer: &super::Buffer, + offset: wgt::BufferAddress, + stride: wgt::BufferSize, + ) { + todo!() + } + + unsafe fn begin_render_pass(&mut self, desc: &crate::RenderPassDescriptor) { + todo!() + } + + unsafe fn end_render_pass(&mut self) { + todo!() + } + + unsafe fn set_render_pipeline(&mut self, pipeline: &super::RenderPipeline) { + todo!() + } + + unsafe fn set_index_buffer<'a>( + &mut self, + binding: crate::BufferBinding<'a, super::Api>, + format: wgt::IndexFormat, + ) { + todo!() + } + + unsafe fn set_vertex_buffer<'a>( + &mut self, + index: u32, + binding: crate::BufferBinding<'a, super::Api>, + ) { + todo!() + } + + unsafe fn set_viewport(&mut self, rect: &crate::Rect, depth_range: std::ops::Range) { + todo!() + } + + unsafe fn set_scissor_rect(&mut self, rect: &crate::Rect) { + todo!() + } + + unsafe fn set_stencil_reference(&mut self, value: u32) { + todo!() + } + + unsafe fn set_blend_constants(&mut self, color: &[f32; 4]) { + todo!() + } + + unsafe fn draw( + &mut self, + start_vertex: u32, + vertex_count: u32, + start_instance: u32, + instance_count: u32, + ) { + todo!() + } + + unsafe fn draw_indexed( + &mut self, + start_index: u32, + index_count: u32, + base_vertex: i32, + start_instance: u32, + instance_count: u32, + ) { + todo!() + } + + unsafe fn draw_indirect( + &mut self, + buffer: &super::Buffer, + offset: wgt::BufferAddress, + draw_count: u32, + ) { + todo!() + } + + unsafe fn draw_indexed_indirect( + &mut self, + buffer: &super::Buffer, + offset: wgt::BufferAddress, + draw_count: u32, + ) { + todo!() + } + + unsafe fn draw_indirect_count( + &mut self, + buffer: &super::Buffer, + offset: wgt::BufferAddress, + count_buffer: &super::Buffer, + count_offset: wgt::BufferAddress, + max_count: u32, + ) { + todo!() + } + + unsafe fn draw_indexed_indirect_count( + &mut self, + buffer: &super::Buffer, + offset: wgt::BufferAddress, + count_buffer: &super::Buffer, + count_offset: wgt::BufferAddress, + max_count: u32, + ) { + todo!() + } + + unsafe fn begin_compute_pass(&mut self, desc: &crate::ComputePassDescriptor) { + todo!() + } + + unsafe fn end_compute_pass(&mut self) { + todo!() + } + + unsafe fn set_compute_pipeline(&mut self, pipeline: &super::ComputePipeline) { + todo!() + } + + unsafe fn dispatch(&mut self, count: [u32; 3]) { + todo!() + } + + unsafe fn dispatch_indirect(&mut self, buffer: &super::Buffer, offset: wgt::BufferAddress) { + todo!() + } +} diff --git a/wgpu-hal/src/dx11/device.rs b/wgpu-hal/src/dx11/device.rs new file mode 100644 index 0000000000..7b095ba1df --- /dev/null +++ b/wgpu-hal/src/dx11/device.rs @@ -0,0 +1,240 @@ +use std::{ffi::c_void, mem}; + +use winapi::um::d3d11; + +use crate::auxil::dxgi::result::HResult; + +impl crate::Device for super::Device { + unsafe fn exit(self, queue: super::Queue) { + todo!() + } + + unsafe fn create_buffer( + &self, + desc: &crate::BufferDescriptor, + ) -> Result { + todo!() + } + + unsafe fn destroy_buffer(&self, buffer: super::Buffer) { + todo!() + } + + unsafe fn map_buffer( + &self, + buffer: &super::Buffer, + range: crate::MemoryRange, + ) -> Result { + todo!() + } + + unsafe fn unmap_buffer(&self, buffer: &super::Buffer) -> Result<(), crate::DeviceError> { + todo!() + } + + unsafe fn flush_mapped_ranges(&self, buffer: &super::Buffer, ranges: I) + where + I: Iterator, + { + todo!() + } + + unsafe fn invalidate_mapped_ranges(&self, buffer: &super::Buffer, ranges: I) + where + I: Iterator, + { + todo!() + } + + unsafe fn create_texture( + &self, + desc: &crate::TextureDescriptor, + ) -> Result { + todo!() + } + + unsafe fn destroy_texture(&self, texture: super::Texture) { + todo!() + } + + unsafe fn create_texture_view( + &self, + texture: &super::Texture, + desc: &crate::TextureViewDescriptor, + ) -> Result { + todo!() + } + + unsafe fn destroy_texture_view(&self, view: super::TextureView) { + todo!() + } + + unsafe fn create_sampler( + &self, + desc: &crate::SamplerDescriptor, + ) -> Result { + todo!() + } + + unsafe fn destroy_sampler(&self, sampler: super::Sampler) { + todo!() + } + + unsafe fn create_command_encoder( + &self, + desc: &crate::CommandEncoderDescriptor, + ) -> Result { + todo!() + } + + unsafe fn destroy_command_encoder(&self, pool: super::CommandEncoder) { + todo!() + } + + unsafe fn create_bind_group_layout( + &self, + desc: &crate::BindGroupLayoutDescriptor, + ) -> Result { + todo!() + } + + unsafe fn destroy_bind_group_layout(&self, bg_layout: super::BindGroupLayout) { + todo!() + } + + unsafe fn create_pipeline_layout( + &self, + desc: &crate::PipelineLayoutDescriptor, + ) -> Result { + todo!() + } + + unsafe fn destroy_pipeline_layout(&self, pipeline_layout: super::PipelineLayout) { + todo!() + } + + unsafe fn create_bind_group( + &self, + desc: &crate::BindGroupDescriptor, + ) -> Result { + todo!() + } + + unsafe fn destroy_bind_group(&self, group: super::BindGroup) { + todo!() + } + + unsafe fn create_shader_module( + &self, + desc: &crate::ShaderModuleDescriptor, + shader: crate::ShaderInput, + ) -> Result { + todo!() + } + + unsafe fn destroy_shader_module(&self, module: super::ShaderModule) { + todo!() + } + + unsafe fn create_render_pipeline( + &self, + desc: &crate::RenderPipelineDescriptor, + ) -> Result { + todo!() + } + + unsafe fn destroy_render_pipeline(&self, pipeline: super::RenderPipeline) { + todo!() + } + + unsafe fn create_compute_pipeline( + &self, + desc: &crate::ComputePipelineDescriptor, + ) -> Result { + todo!() + } + + unsafe fn destroy_compute_pipeline(&self, pipeline: super::ComputePipeline) { + todo!() + } + + unsafe fn create_query_set( + &self, + desc: &wgt::QuerySetDescriptor, + ) -> Result { + todo!() + } + + unsafe fn destroy_query_set(&self, set: super::QuerySet) { + todo!() + } + + unsafe fn create_fence(&self) -> Result { + todo!() + } + + unsafe fn destroy_fence(&self, fence: super::Fence) { + todo!() + } + + unsafe fn get_fence_value( + &self, + fence: &super::Fence, + ) -> Result { + todo!() + } + + unsafe fn wait( + &self, + fence: &super::Fence, + value: crate::FenceValue, + timeout_ms: u32, + ) -> Result { + todo!() + } + + unsafe fn start_capture(&self) -> bool { + todo!() + } + + unsafe fn stop_capture(&self) { + todo!() + } +} + +impl crate::Queue for super::Queue { + unsafe fn submit( + &mut self, + command_buffers: &[&super::CommandBuffer], + signal_fence: Option<(&mut super::Fence, crate::FenceValue)>, + ) -> Result<(), crate::DeviceError> { + todo!() + } + + unsafe fn present( + &mut self, + surface: &mut super::Surface, + texture: super::SurfaceTexture, + ) -> Result<(), crate::SurfaceError> { + todo!() + } + + unsafe fn get_timestamp_period(&self) -> f32 { + todo!() + } +} + +impl super::D3D11Device { + #[allow(trivial_casts)] // come on + pub unsafe fn check_feature_support(&self, feature: d3d11::D3D11_FEATURE) -> T { + let mut value = mem::zeroed::(); + let ret = self.CheckFeatureSupport( + feature, + &mut value as *mut T as *mut c_void, + mem::size_of::() as u32, + ); + assert_eq!(ret.into_result(), Ok(())); + + value + } +} diff --git a/wgpu-hal/src/dx11/instance.rs b/wgpu-hal/src/dx11/instance.rs new file mode 100644 index 0000000000..ac0fdbe597 --- /dev/null +++ b/wgpu-hal/src/dx11/instance.rs @@ -0,0 +1,47 @@ +use crate::auxil; + +impl crate::Instance for super::Instance { + unsafe fn init(desc: &crate::InstanceDescriptor) -> Result { + let enable_dx11 = match std::env::var("WGPU_UNSTABLE_DX11_BACKEND") { + Ok(string) => string == "1" || string == "true", + Err(_) => false, + }; + + if !enable_dx11 { + return Err(crate::InstanceError); + } + + let lib_d3d11 = super::library::D3D11Lib::new().ok_or(crate::InstanceError)?; + + let (lib_dxgi, factory) = auxil::dxgi::factory::create_factory( + auxil::dxgi::factory::DxgiFactoryType::Factory1, + desc.flags, + )?; + + Ok(super::Instance { + lib_d3d11, + lib_dxgi, + factory, + }) + } + + unsafe fn create_surface( + &self, + rwh: &impl raw_window_handle::HasRawWindowHandle, + ) -> Result { + todo!() + } + + unsafe fn destroy_surface(&self, surface: super::Surface) { + todo!() + } + + unsafe fn enumerate_adapters(&self) -> Vec> { + let adapters = auxil::dxgi::factory::enumerate_adapters(self.factory); + + adapters + .into_iter() + .filter_map(|adapter| super::Adapter::expose(&self.lib_d3d11, adapter)) + .collect() + } +} diff --git a/wgpu-hal/src/dx11/library.rs b/wgpu-hal/src/dx11/library.rs new file mode 100644 index 0000000000..1b2defe4f8 --- /dev/null +++ b/wgpu-hal/src/dx11/library.rs @@ -0,0 +1,144 @@ +use std::ptr; + +use winapi::{ + shared::{ + dxgi, + minwindef::{HMODULE, UINT}, + winerror, + }, + um::{d3d11, d3d11_1, d3d11_2, d3dcommon}, +}; + +use crate::auxil::dxgi::result::HResult; + +type D3D11CreateDeviceFun = unsafe extern "system" fn( + *mut dxgi::IDXGIAdapter, + d3dcommon::D3D_DRIVER_TYPE, + HMODULE, + UINT, + *const d3dcommon::D3D_FEATURE_LEVEL, + UINT, + UINT, + *mut *mut d3d11::ID3D11Device, + *mut d3dcommon::D3D_FEATURE_LEVEL, + *mut *mut d3d11::ID3D11DeviceContext, +) -> native::HRESULT; + +pub(super) struct D3D11Lib { + // We use the os specific symbol to drop the lifetime parameter. + // + // SAFETY: we must ensure this outlives the Library. + d3d11_create_device: libloading::os::windows::Symbol, + + lib: libloading::Library, +} +impl D3D11Lib { + pub fn new() -> Option { + unsafe { + let lib = libloading::Library::new("d3d11.dll").ok()?; + + let d3d11_create_device = lib + .get::(b"D3D11CreateDevice") + .ok()? + .into_raw(); + + Some(Self { + lib, + d3d11_create_device, + }) + } + } + + pub fn create_device( + &self, + adapter: native::DxgiAdapter, + ) -> Option<(super::D3D11Device, d3dcommon::D3D_FEATURE_LEVEL)> { + let feature_levels = [ + d3dcommon::D3D_FEATURE_LEVEL_11_1, + d3dcommon::D3D_FEATURE_LEVEL_11_0, + d3dcommon::D3D_FEATURE_LEVEL_10_1, + d3dcommon::D3D_FEATURE_LEVEL_10_0, + d3dcommon::D3D_FEATURE_LEVEL_9_3, + d3dcommon::D3D_FEATURE_LEVEL_9_2, + d3dcommon::D3D_FEATURE_LEVEL_9_1, + ]; + + let mut device = native::WeakPtr::::null(); + let mut feature_level: d3dcommon::D3D_FEATURE_LEVEL = 0; + + // We need to try this twice. If the first time fails due to E_INVALIDARG + // we are running on a machine without a D3D11.1 runtime, and need to + // retry without the feature level 11_1 feature level. + // + // Why they thought this was a good API, who knows. + + let mut hr = unsafe { + (self.d3d11_create_device)( + adapter.as_mut_ptr() as *mut _, + d3dcommon::D3D_DRIVER_TYPE_UNKNOWN, + ptr::null_mut(), // software implementation DLL??? + 0, // flags + feature_levels.as_ptr(), + feature_levels.len() as u32, + d3d11::D3D11_SDK_VERSION, + device.mut_self(), + &mut feature_level, + ptr::null_mut(), // device context + ) + }; + + // Try again without FL11_1 + if hr == winerror::E_INVALIDARG { + hr = unsafe { + (self.d3d11_create_device)( + adapter.as_mut_ptr() as *mut _, + d3dcommon::D3D_DRIVER_TYPE_UNKNOWN, + ptr::null_mut(), // software implementation DLL??? + 0, // flags + feature_levels[1..].as_ptr(), + feature_levels[1..].len() as u32, + d3d11::D3D11_SDK_VERSION, + device.mut_self(), + &mut feature_level, + ptr::null_mut(), // device context + ) + }; + } + + // Any errors here are real and we should complain about + if let Err(err) = hr.into_result() { + log::error!("Failed to make a D3D11 device: {}", err); + return None; + } + + // We always try to upcast in highest -> lowest order + + // Device -> Device2 + unsafe { + match device.cast::().into_result() { + Ok(device2) => { + device.destroy(); + return Some((super::D3D11Device::Device2(device2), feature_level)); + } + Err(hr) => { + log::info!("Failed to cast device to ID3D11Device2: {}", hr) + } + } + } + + // Device -> Device1 + unsafe { + match device.cast::().into_result() { + Ok(device1) => { + device.destroy(); + return Some((super::D3D11Device::Device1(device1), feature_level)); + } + Err(hr) => { + log::info!("Failed to cast device to ID3D11Device1: {}", hr) + } + } + } + + Some((super::D3D11Device::Device(device), feature_level)) + } +} diff --git a/wgpu-hal/src/dx11/mod.rs b/wgpu-hal/src/dx11/mod.rs new file mode 100644 index 0000000000..ff9c49e0b3 --- /dev/null +++ b/wgpu-hal/src/dx11/mod.rs @@ -0,0 +1,135 @@ +#![allow(dead_code)] +#![allow(unused_variables)] + +use winapi::um::{d3d11, d3d11_1, d3d11_2}; + +mod adapter; +mod command; +mod device; +mod instance; +mod library; + +#[derive(Clone)] +pub struct Api; + +impl crate::Api for Api { + type Instance = Instance; + type Surface = Surface; + type Adapter = Adapter; + type Device = Device; + + type Queue = Queue; + type CommandEncoder = CommandEncoder; + type CommandBuffer = CommandBuffer; + + type Buffer = Buffer; + type Texture = Texture; + type SurfaceTexture = SurfaceTexture; + type TextureView = TextureView; + type Sampler = Sampler; + type QuerySet = QuerySet; + type Fence = Fence; + + type BindGroupLayout = BindGroupLayout; + type BindGroup = BindGroup; + type PipelineLayout = PipelineLayout; + type ShaderModule = ShaderModule; + type RenderPipeline = RenderPipeline; + type ComputePipeline = ComputePipeline; +} + +pub struct Instance { + lib_d3d11: library::D3D11Lib, + lib_dxgi: native::DxgiLib, + factory: native::DxgiFactory, +} + +unsafe impl Send for Instance {} +unsafe impl Sync for Instance {} + +pub struct Surface {} + +pub struct Adapter { + device: D3D11Device, +} + +unsafe impl Send for Adapter {} +unsafe impl Sync for Adapter {} + +native::weak_com_inheritance_chain! { + #[derive(Debug, Copy, Clone, PartialEq)] + enum D3D11Device { + Device(d3d11::ID3D11Device), from_device, as_device, device; + Device1(d3d11_1::ID3D11Device1), from_device1, as_device1, unwrap_device1; + Device2(d3d11_2::ID3D11Device2), from_device2, as_device2, unwrap_device2; + } +} + +pub struct Device {} + +unsafe impl Send for Device {} +unsafe impl Sync for Device {} + +pub struct Queue {} + +pub struct CommandEncoder {} + +pub struct CommandBuffer {} + +#[derive(Debug)] +pub struct Buffer {} +#[derive(Debug)] +pub struct Texture {} +#[derive(Debug)] +pub struct SurfaceTexture {} + +impl std::borrow::Borrow for SurfaceTexture { + fn borrow(&self) -> &Texture { + todo!() + } +} + +#[derive(Debug)] +pub struct TextureView {} +#[derive(Debug)] +pub struct Sampler {} +#[derive(Debug)] +pub struct QuerySet {} +#[derive(Debug)] +pub struct Fence {} +#[derive(Debug)] + +pub struct BindGroupLayout {} +#[derive(Debug)] +pub struct BindGroup {} +#[derive(Debug)] +pub struct PipelineLayout {} +#[derive(Debug)] +pub struct ShaderModule {} +pub struct RenderPipeline {} +pub struct ComputePipeline {} + +impl crate::Surface for Surface { + unsafe fn configure( + &mut self, + device: &Device, + config: &crate::SurfaceConfiguration, + ) -> Result<(), crate::SurfaceError> { + todo!() + } + + unsafe fn unconfigure(&mut self, device: &Device) { + todo!() + } + + unsafe fn acquire_texture( + &mut self, + timeout_ms: u32, + ) -> Result>, crate::SurfaceError> { + todo!() + } + + unsafe fn discard_texture(&mut self, texture: SurfaceTexture) { + todo!() + } +} diff --git a/wgpu-hal/src/dx12/adapter.rs b/wgpu-hal/src/dx12/adapter.rs index d7064d0183..88959bdb47 100644 --- a/wgpu-hal/src/dx12/adapter.rs +++ b/wgpu-hal/src/dx12/adapter.rs @@ -1,4 +1,7 @@ -use super::{conv, HResult as _, SurfaceTarget}; +use crate::{ + auxil::{self, dxgi::result::HResult as _}, + dx12::SurfaceTarget, +}; use std::{mem, sync::Arc, thread}; use winapi::{ shared::{dxgi, dxgi1_2, dxgi1_5, minwindef, windef, winerror}, @@ -40,14 +43,14 @@ impl super::Adapter { #[allow(trivial_casts)] pub(super) fn expose( - adapter: native::WeakPtr, + adapter: native::DxgiAdapter, library: &Arc, instance_flags: crate::InstanceFlags, ) -> Option> { // Create the device so that we can get the capabilities. let device = { profiling::scope!("ID3D12Device::create_device"); - match library.create_device(adapter, native::FeatureLevel::L11_0) { + match library.create_device(*adapter, native::FeatureLevel::L11_0) { Ok(pair) => match pair.into_result() { Ok(device) => device, Err(err) => { @@ -68,7 +71,7 @@ impl super::Adapter { // Acquire the device information. let mut desc: dxgi1_2::DXGI_ADAPTER_DESC2 = unsafe { mem::zeroed() }; unsafe { - adapter.GetDesc2(&mut desc); + adapter.unwrap_adapter2().GetDesc2(&mut desc); } let device_name = { @@ -317,7 +320,7 @@ impl crate::Adapter for super::Adapter { ) -> crate::TextureFormatCapabilities { use crate::TextureFormatCapabilities as Tfc; - let raw_format = conv::map_texture_format(format); + let raw_format = auxil::dxgi::conv::map_texture_format(format); let mut data = d3d12::D3D12_FEATURE_DATA_FORMAT_SUPPORT { Format: raw_format, Support1: mem::zeroed(), @@ -409,11 +412,7 @@ impl crate::Adapter for super::Adapter { let mut present_modes = vec![wgt::PresentMode::Fifo]; #[allow(trivial_casts)] - if let Ok(factory5) = surface - .factory - .cast::() - .into_result() - { + if let Some(factory5) = surface.factory.as_factory5() { let mut allow_tearing: minwindef::BOOL = minwindef::FALSE; let hr = factory5.CheckFeatureSupport( dxgi1_5::DXGI_FEATURE_PRESENT_ALLOW_TEARING, @@ -421,7 +420,6 @@ impl crate::Adapter for super::Adapter { mem::size_of::() as _, ); - factory5.destroy(); match hr.into_result() { Err(err) => log::warn!("Unable to check for tearing support: {}", err), Ok(()) => present_modes.push(wgt::PresentMode::Immediate), diff --git a/wgpu-hal/src/dx12/command.rs b/wgpu-hal/src/dx12/command.rs index 673ccccdf1..95546d213a 100644 --- a/wgpu-hal/src/dx12/command.rs +++ b/wgpu-hal/src/dx12/command.rs @@ -1,4 +1,6 @@ -use super::{conv, HResult as _}; +use crate::auxil::{self, dxgi::result::HResult as _}; + +use super::conv; use std::{mem, ops::Range, ptr}; use winapi::um::d3d12; @@ -22,7 +24,7 @@ impl crate::BufferTextureCopy { d3d12::D3D12_PLACED_SUBRESOURCE_FOOTPRINT { Offset: self.buffer_layout.offset, Footprint: d3d12::D3D12_SUBRESOURCE_FOOTPRINT { - Format: conv::map_texture_format(format), + Format: auxil::dxgi::conv::map_texture_format(format), Width: self.size.width, Height: self .buffer_layout @@ -834,7 +836,7 @@ impl crate::CommandEncoder for super::CommandEncoder { self.list.unwrap().set_index_buffer( binding.resolve_address(), binding.resolve_size() as u32, - conv::map_index_format(format), + auxil::dxgi::conv::map_index_format(format), ); } unsafe fn set_vertex_buffer<'a>( diff --git a/wgpu-hal/src/dx12/conv.rs b/wgpu-hal/src/dx12/conv.rs index 8c1cdb9ab6..6ea0339afb 100644 --- a/wgpu-hal/src/dx12/conv.rs +++ b/wgpu-hal/src/dx12/conv.rs @@ -1,182 +1,5 @@ use std::iter; -use winapi::{ - shared::{dxgi1_2, dxgiformat}, - um::{d3d12, d3dcommon}, -}; - -pub(super) fn map_texture_format(format: wgt::TextureFormat) -> dxgiformat::DXGI_FORMAT { - use wgt::TextureFormat as Tf; - use winapi::shared::dxgiformat::*; - - match format { - Tf::R8Unorm => DXGI_FORMAT_R8_UNORM, - Tf::R8Snorm => DXGI_FORMAT_R8_SNORM, - Tf::R8Uint => DXGI_FORMAT_R8_UINT, - Tf::R8Sint => DXGI_FORMAT_R8_SINT, - Tf::R16Uint => DXGI_FORMAT_R16_UINT, - Tf::R16Sint => DXGI_FORMAT_R16_SINT, - Tf::R16Unorm => DXGI_FORMAT_R16_UNORM, - Tf::R16Snorm => DXGI_FORMAT_R16_SNORM, - Tf::R16Float => DXGI_FORMAT_R16_FLOAT, - Tf::Rg8Unorm => DXGI_FORMAT_R8G8_UNORM, - Tf::Rg8Snorm => DXGI_FORMAT_R8G8_SNORM, - Tf::Rg8Uint => DXGI_FORMAT_R8G8_UINT, - Tf::Rg8Sint => DXGI_FORMAT_R8G8_SINT, - Tf::Rg16Unorm => DXGI_FORMAT_R16G16_UNORM, - Tf::Rg16Snorm => DXGI_FORMAT_R16G16_SNORM, - Tf::R32Uint => DXGI_FORMAT_R32_UINT, - Tf::R32Sint => DXGI_FORMAT_R32_SINT, - Tf::R32Float => DXGI_FORMAT_R32_FLOAT, - Tf::Rg16Uint => DXGI_FORMAT_R16G16_UINT, - Tf::Rg16Sint => DXGI_FORMAT_R16G16_SINT, - Tf::Rg16Float => DXGI_FORMAT_R16G16_FLOAT, - Tf::Rgba8Unorm => DXGI_FORMAT_R8G8B8A8_UNORM, - Tf::Rgba8UnormSrgb => DXGI_FORMAT_R8G8B8A8_UNORM_SRGB, - Tf::Bgra8UnormSrgb => DXGI_FORMAT_B8G8R8A8_UNORM_SRGB, - Tf::Rgba8Snorm => DXGI_FORMAT_R8G8B8A8_SNORM, - Tf::Bgra8Unorm => DXGI_FORMAT_B8G8R8A8_UNORM, - Tf::Rgba8Uint => DXGI_FORMAT_R8G8B8A8_UINT, - Tf::Rgba8Sint => DXGI_FORMAT_R8G8B8A8_SINT, - Tf::Rgb10a2Unorm => DXGI_FORMAT_R10G10B10A2_UNORM, - Tf::Rg11b10Float => DXGI_FORMAT_R11G11B10_FLOAT, - Tf::Rg32Uint => DXGI_FORMAT_R32G32_UINT, - Tf::Rg32Sint => DXGI_FORMAT_R32G32_SINT, - Tf::Rg32Float => DXGI_FORMAT_R32G32_FLOAT, - Tf::Rgba16Uint => DXGI_FORMAT_R16G16B16A16_UINT, - Tf::Rgba16Sint => DXGI_FORMAT_R16G16B16A16_SINT, - Tf::Rgba16Unorm => DXGI_FORMAT_R16G16B16A16_UNORM, - Tf::Rgba16Snorm => DXGI_FORMAT_R16G16B16A16_SNORM, - Tf::Rgba16Float => DXGI_FORMAT_R16G16B16A16_FLOAT, - Tf::Rgba32Uint => DXGI_FORMAT_R32G32B32A32_UINT, - Tf::Rgba32Sint => DXGI_FORMAT_R32G32B32A32_SINT, - Tf::Rgba32Float => DXGI_FORMAT_R32G32B32A32_FLOAT, - Tf::Depth32Float => DXGI_FORMAT_D32_FLOAT, - Tf::Depth24Plus => DXGI_FORMAT_D24_UNORM_S8_UINT, - Tf::Depth24PlusStencil8 => DXGI_FORMAT_D24_UNORM_S8_UINT, - Tf::Rgb9e5Ufloat => DXGI_FORMAT_R9G9B9E5_SHAREDEXP, - Tf::Bc1RgbaUnorm => DXGI_FORMAT_BC1_UNORM, - Tf::Bc1RgbaUnormSrgb => DXGI_FORMAT_BC1_UNORM_SRGB, - Tf::Bc2RgbaUnorm => DXGI_FORMAT_BC2_UNORM, - Tf::Bc2RgbaUnormSrgb => DXGI_FORMAT_BC2_UNORM_SRGB, - Tf::Bc3RgbaUnorm => DXGI_FORMAT_BC3_UNORM, - Tf::Bc3RgbaUnormSrgb => DXGI_FORMAT_BC3_UNORM_SRGB, - Tf::Bc4RUnorm => DXGI_FORMAT_BC4_UNORM, - Tf::Bc4RSnorm => DXGI_FORMAT_BC4_SNORM, - Tf::Bc5RgUnorm => DXGI_FORMAT_BC5_UNORM, - Tf::Bc5RgSnorm => DXGI_FORMAT_BC5_SNORM, - Tf::Bc6hRgbUfloat => DXGI_FORMAT_BC6H_UF16, - Tf::Bc6hRgbSfloat => DXGI_FORMAT_BC6H_SF16, - Tf::Bc7RgbaUnorm => DXGI_FORMAT_BC7_UNORM, - Tf::Bc7RgbaUnormSrgb => DXGI_FORMAT_BC7_UNORM_SRGB, - Tf::Etc2Rgb8Unorm - | Tf::Etc2Rgb8UnormSrgb - | Tf::Etc2Rgb8A1Unorm - | Tf::Etc2Rgb8A1UnormSrgb - | Tf::Etc2Rgba8Unorm - | Tf::Etc2Rgba8UnormSrgb - | Tf::EacR11Unorm - | Tf::EacR11Snorm - | Tf::EacRg11Unorm - | Tf::EacRg11Snorm => unreachable!(), - Tf::Astc { - block: _, - channel: _, - } => unreachable!(), - } -} - -//Note: DXGI doesn't allow sRGB format on the swapchain, -// but creating RTV of swapchain buffers with sRGB works. -pub fn map_texture_format_nosrgb(format: wgt::TextureFormat) -> dxgiformat::DXGI_FORMAT { - match format { - wgt::TextureFormat::Bgra8UnormSrgb => dxgiformat::DXGI_FORMAT_B8G8R8A8_UNORM, - wgt::TextureFormat::Rgba8UnormSrgb => dxgiformat::DXGI_FORMAT_R8G8B8A8_UNORM, - _ => map_texture_format(format), - } -} - -//Note: SRV and UAV can't use the depth formats directly -//TODO: stencil views? -pub fn map_texture_format_nodepth(format: wgt::TextureFormat) -> dxgiformat::DXGI_FORMAT { - match format { - wgt::TextureFormat::Depth32Float => dxgiformat::DXGI_FORMAT_R32_FLOAT, - wgt::TextureFormat::Depth24Plus | wgt::TextureFormat::Depth24PlusStencil8 => { - dxgiformat::DXGI_FORMAT_R24_UNORM_X8_TYPELESS - } - _ => { - assert_eq!( - crate::FormatAspects::from(format), - crate::FormatAspects::COLOR - ); - map_texture_format(format) - } - } -} - -pub fn map_texture_format_depth_typeless(format: wgt::TextureFormat) -> dxgiformat::DXGI_FORMAT { - match format { - wgt::TextureFormat::Depth32Float => dxgiformat::DXGI_FORMAT_R32_TYPELESS, - wgt::TextureFormat::Depth24Plus | wgt::TextureFormat::Depth24PlusStencil8 => { - dxgiformat::DXGI_FORMAT_R24G8_TYPELESS - } - _ => unreachable!(), - } -} - -pub fn map_index_format(format: wgt::IndexFormat) -> dxgiformat::DXGI_FORMAT { - match format { - wgt::IndexFormat::Uint16 => dxgiformat::DXGI_FORMAT_R16_UINT, - wgt::IndexFormat::Uint32 => dxgiformat::DXGI_FORMAT_R32_UINT, - } -} - -pub fn map_vertex_format(format: wgt::VertexFormat) -> dxgiformat::DXGI_FORMAT { - use wgt::VertexFormat as Vf; - use winapi::shared::dxgiformat::*; - - match format { - Vf::Unorm8x2 => DXGI_FORMAT_R8G8_UNORM, - Vf::Snorm8x2 => DXGI_FORMAT_R8G8_SNORM, - Vf::Uint8x2 => DXGI_FORMAT_R8G8_UINT, - Vf::Sint8x2 => DXGI_FORMAT_R8G8_SINT, - Vf::Unorm8x4 => DXGI_FORMAT_R8G8B8A8_UNORM, - Vf::Snorm8x4 => DXGI_FORMAT_R8G8B8A8_SNORM, - Vf::Uint8x4 => DXGI_FORMAT_R8G8B8A8_UINT, - Vf::Sint8x4 => DXGI_FORMAT_R8G8B8A8_SINT, - Vf::Unorm16x2 => DXGI_FORMAT_R16G16_UNORM, - Vf::Snorm16x2 => DXGI_FORMAT_R16G16_SNORM, - Vf::Uint16x2 => DXGI_FORMAT_R16G16_UINT, - Vf::Sint16x2 => DXGI_FORMAT_R16G16_SINT, - Vf::Float16x2 => DXGI_FORMAT_R16G16_FLOAT, - Vf::Unorm16x4 => DXGI_FORMAT_R16G16B16A16_UNORM, - Vf::Snorm16x4 => DXGI_FORMAT_R16G16B16A16_SNORM, - Vf::Uint16x4 => DXGI_FORMAT_R16G16B16A16_UINT, - Vf::Sint16x4 => DXGI_FORMAT_R16G16B16A16_SINT, - Vf::Float16x4 => DXGI_FORMAT_R16G16B16A16_FLOAT, - Vf::Uint32 => DXGI_FORMAT_R32_UINT, - Vf::Sint32 => DXGI_FORMAT_R32_SINT, - Vf::Float32 => DXGI_FORMAT_R32_FLOAT, - Vf::Uint32x2 => DXGI_FORMAT_R32G32_UINT, - Vf::Sint32x2 => DXGI_FORMAT_R32G32_SINT, - Vf::Float32x2 => DXGI_FORMAT_R32G32_FLOAT, - Vf::Uint32x3 => DXGI_FORMAT_R32G32B32_UINT, - Vf::Sint32x3 => DXGI_FORMAT_R32G32B32_SINT, - Vf::Float32x3 => DXGI_FORMAT_R32G32B32_FLOAT, - Vf::Uint32x4 => DXGI_FORMAT_R32G32B32A32_UINT, - Vf::Sint32x4 => DXGI_FORMAT_R32G32B32A32_SINT, - Vf::Float32x4 => DXGI_FORMAT_R32G32B32A32_FLOAT, - Vf::Float64 | Vf::Float64x2 | Vf::Float64x3 | Vf::Float64x4 => unimplemented!(), - } -} - -pub fn map_acomposite_alpha_mode(mode: crate::CompositeAlphaMode) -> dxgi1_2::DXGI_ALPHA_MODE { - use crate::CompositeAlphaMode as Cam; - match mode { - Cam::Opaque => dxgi1_2::DXGI_ALPHA_MODE_IGNORE, - Cam::PreMultiplied => dxgi1_2::DXGI_ALPHA_MODE_PREMULTIPLIED, - Cam::PostMultiplied => dxgi1_2::DXGI_ALPHA_MODE_STRAIGHT, - } -} +use winapi::um::{d3d12, d3dcommon}; pub fn map_buffer_usage_to_resource_flags(usage: crate::BufferUses) -> d3d12::D3D12_RESOURCE_FLAGS { let mut flags = 0; diff --git a/wgpu-hal/src/dx12/descriptor.rs b/wgpu-hal/src/dx12/descriptor.rs index 0a8bac4144..67c8eca4fe 100644 --- a/wgpu-hal/src/dx12/descriptor.rs +++ b/wgpu-hal/src/dx12/descriptor.rs @@ -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; diff --git a/wgpu-hal/src/dx12/device.rs b/wgpu-hal/src/dx12/device.rs index a29eeed45b..778bd2e51a 100644 --- a/wgpu-hal/src/dx12/device.rs +++ b/wgpu-hal/src/dx12/device.rs @@ -1,6 +1,9 @@ -use crate::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::{ @@ -432,13 +435,13 @@ impl crate::Device for super::Device { | crate::TextureUses::STORAGE_READ | crate::TextureUses::STORAGE_WRITE, ) { - conv::map_texture_format(desc.format) + auxil::dxgi::conv::map_texture_format(desc.format) } else { // This branch is needed if it's a depth texture, and it's ever needed to be viewed as SRV or UAV, // because then we'd create a non-depth format view of it. // Note: we can skip this branch if // `D3D12_FEATURE_D3D12_OPTIONS3::CastingFullyTypedFormatSupported` - conv::map_texture_format_depth_typeless(desc.format) + auxil::dxgi::conv::map_texture_format_depth_typeless(desc.format) }, SampleDesc: dxgitype::DXGI_SAMPLE_DESC { Count: desc.sample_count, @@ -1249,7 +1252,7 @@ impl crate::Device for super::Device { input_element_descs.push(d3d12::D3D12_INPUT_ELEMENT_DESC { SemanticName: NAGA_LOCATION_SEMANTIC.as_ptr() as *const _, SemanticIndex: attribute.shader_location, - Format: conv::map_vertex_format(attribute.format), + Format: auxil::dxgi::conv::map_vertex_format(attribute.format), InputSlot: i as u32, AlignedByteOffset: attribute.offset as u32, InputSlotClass: slot_class, @@ -1261,7 +1264,7 @@ impl crate::Device for super::Device { let mut rtv_formats = [dxgiformat::DXGI_FORMAT_UNKNOWN; d3d12::D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT as usize]; for (rtv_format, ct) in rtv_formats.iter_mut().zip(desc.color_targets) { - *rtv_format = conv::map_texture_format(ct.format); + *rtv_format = auxil::dxgi::conv::map_texture_format(ct.format); } let bias = desc @@ -1350,7 +1353,7 @@ impl crate::Device for super::Device { .depth_stencil .as_ref() .map_or(dxgiformat::DXGI_FORMAT_UNKNOWN, |ds| { - conv::map_texture_format(ds.format) + auxil::dxgi::conv::map_texture_format(ds.format) }), SampleDesc: dxgitype::DXGI_SAMPLE_DESC { Count: desc.multisample.count, diff --git a/wgpu-hal/src/dx12/instance.rs b/wgpu-hal/src/dx12/instance.rs index b8fa59cbc9..6129d53a71 100644 --- a/wgpu-hal/src/dx12/instance.rs +++ b/wgpu-hal/src/dx12/instance.rs @@ -1,81 +1,11 @@ -use super::{HResult as _, SurfaceTarget}; -use std::{borrow::Cow, slice, sync::Arc}; -use winapi::{ - shared::{dxgi, dxgi1_2, dxgi1_6, winerror}, - um::{errhandlingapi, winnt}, - vc::excpt, - Interface, -}; - -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 -} +use super::SurfaceTarget; +use crate::auxil::{self, dxgi::result::HResult as _}; +use std::sync::Arc; impl Drop for super::Instance { fn drop(&mut self) { - unsafe { - self.factory.destroy(); - errhandlingapi::RemoveVectoredExceptionHandler(output_debug_string_handler as *mut _); - } + unsafe { self.factory.destroy() }; + crate::auxil::dxgi::exception::unregister_exception_handler(); } } @@ -83,9 +13,6 @@ impl crate::Instance for super::Instance { unsafe fn init(desc: &crate::InstanceDescriptor) -> Result { let lib_main = native::D3D12Lib::new().map_err(|_| crate::InstanceError)?; - let lib_dxgi = native::DxgiLib::new().map_err(|_| crate::InstanceError)?; - let mut factory_flags = native::FactoryCreationFlags::empty(); - if desc.flags.contains(crate::InstanceFlags::VALIDATION) { // Enable debug layer match lib_main.get_debug_interface() { @@ -102,45 +29,16 @@ impl crate::Instance for super::Instance { log::warn!("Debug interface function for D3D12 not found: {:?}", err); } } - - // 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) => { - 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 - errhandlingapi::AddVectoredExceptionHandler(0, Some(output_debug_string_handler)); } - // Create DXGI factory - let factory = match lib_dxgi.create_factory2(factory_flags) { - Ok(pair) => match pair.into_result() { - Ok(factory) => factory, - Err(err) => { - log::warn!("Failed to create DXGI factory: {}", err); - return Err(crate::InstanceError); - } - }, - Err(err) => { - log::warn!("Factory creation function for DXGI not found: {:?}", err); - return Err(crate::InstanceError); - } - }; + // Create DXGIFactory4 + let (lib_dxgi, factory) = auxil::dxgi::factory::create_factory( + auxil::dxgi::factory::DxgiFactoryType::Factory4, + desc.flags, + )?; Ok(Self { + // The call to create_factory will only succeed if we get a factory4, so this is safe. factory, library: Arc::new(lib_main), _lib_dxgi: lib_dxgi, @@ -166,74 +64,11 @@ impl crate::Instance for super::Instance { } unsafe fn enumerate_adapters(&self) -> Vec> { - // Try to use high performance order by default (returns None on Windows < 1803) - let factory6 = match self.factory.cast::().into_result() { - Ok(f6) => { - // It's okay to decrement the refcount here because we - // have another reference to the factory already owned by `self`. - f6.destroy(); - Some(f6) - } - Err(err) => { - log::info!("Failed to cast DXGI to 1.6: {}", err); - None - } - }; - - // Enumerate adapters - let mut adapters = Vec::new(); - for cur_index in 0.. { - let raw = match factory6 { - Some(factory) => { - profiling::scope!("IDXGIFactory6::EnumAdapterByGpuPreference"); - let mut adapter2 = native::WeakPtr::::null(); - let hr = factory.EnumAdapterByGpuPreference( - cur_index, - dxgi1_6::DXGI_GPU_PREFERENCE_HIGH_PERFORMANCE, - &dxgi1_2::IDXGIAdapter2::uuidof(), - adapter2.mut_void(), - ); - - if hr == winerror::DXGI_ERROR_NOT_FOUND { - break; - } - if let Err(err) = hr.into_result() { - log::error!("Failed enumerating adapters: {}", err); - break; - } - - adapter2 - } - None => { - profiling::scope!("IDXGIFactory1::EnumAdapters1"); - let mut adapter1 = native::WeakPtr::::null(); - let hr = self - .factory - .EnumAdapters1(cur_index, adapter1.mut_void() as *mut *mut _); + let adapters = auxil::dxgi::factory::enumerate_adapters(self.factory); - if hr == winerror::DXGI_ERROR_NOT_FOUND { - break; - } - if let Err(err) = hr.into_result() { - log::error!("Failed enumerating adapters: {}", err); - break; - } - - match adapter1.cast::().into_result() { - Ok(adapter2) => { - adapter1.destroy(); - adapter2 - } - Err(err) => { - log::error!("Failed casting to Adapter2: {}", err); - break; - } - } - } - }; - - adapters.extend(super::Adapter::expose(raw, &self.library, self.flags)); - } adapters + .into_iter() + .filter_map(|raw| super::Adapter::expose(raw, &self.library, self.flags)) + .collect() } } diff --git a/wgpu-hal/src/dx12/mod.rs b/wgpu-hal/src/dx12/mod.rs index 7e1ec8e884..ffef5a3ada 100644 --- a/wgpu-hal/src/dx12/mod.rs +++ b/wgpu-hal/src/dx12/mod.rs @@ -41,11 +41,13 @@ mod device; mod instance; mod view; +use crate::auxil::{self, dxgi::result::HResult as _}; + use arrayvec::ArrayVec; use parking_lot::Mutex; -use std::{borrow::Cow, ffi, mem, num::NonZeroU32, ptr, sync::Arc}; +use std::{ffi, mem, num::NonZeroU32, sync::Arc}; use winapi::{ - shared::{dxgi, dxgi1_2, dxgi1_4, dxgitype, windef, winerror}, + shared::{dxgi, dxgi1_4, dxgitype, windef, winerror}, um::{d3d12, dcomp, synchapi, winbase, winnt}, Interface as _, }; @@ -79,51 +81,12 @@ impl crate::Api for Api { type ComputePipeline = ComputePipeline; } -trait HResult { - fn into_result(self) -> Result>; - fn into_device_result(self, description: &str) -> Result; -} -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 HResult for (T, i32) { - fn into_result(self) -> Result> { - self.1.into_result().map(|()| self.0) - } - fn into_device_result(self, description: &str) -> Result { - self.1.into_device_result(description).map(|()| self.0) - } -} - // Limited by D3D12's root signature size of 64. Each element takes 1 or 2 entries. const MAX_ROOT_ELEMENTS: usize = 64; const ZERO_BUFFER_SIZE: wgt::BufferAddress = 256 << 10; pub struct Instance { - factory: native::Factory4, + factory: native::DxgiFactory, library: Arc, _lib_dxgi: native::DxgiLib, flags: crate::InstanceFlags, @@ -163,7 +126,7 @@ enum SurfaceTarget { } pub struct Surface { - factory: native::WeakPtr, + factory: native::DxgiFactory, target: SurfaceTarget, swap_chain: Option, } @@ -197,7 +160,7 @@ struct Workarounds { } pub struct Adapter { - raw: native::WeakPtr, + raw: native::DxgiAdapter, device: native::Device, library: Arc, private_caps: PrivateCapabilities, @@ -592,7 +555,7 @@ impl crate::Surface for Surface { _ => {} } - let non_srgb_format = conv::map_texture_format_nosrgb(config.format); + let non_srgb_format = auxil::dxgi::conv::map_texture_format_nosrgb(config.format); let swap_chain = match self.swap_chain.take() { //Note: this path doesn't properly re-initialize all of the things @@ -615,54 +578,56 @@ impl crate::Surface for Surface { raw } None => { - let mut swap_chain1 = native::WeakPtr::::null(); - - let raw_desc = dxgi1_2::DXGI_SWAP_CHAIN_DESC1 { - AlphaMode: conv::map_acomposite_alpha_mode(config.composite_alpha_mode), - BufferCount: config.swap_chain_size, - Width: config.extent.width, - Height: config.extent.height, - Format: non_srgb_format, - Flags: flags, - BufferUsage: dxgitype::DXGI_USAGE_RENDER_TARGET_OUTPUT, - SampleDesc: dxgitype::DXGI_SAMPLE_DESC { - Count: 1, - Quality: 0, + let desc = native::SwapchainDesc { + alpha_mode: auxil::dxgi::conv::map_acomposite_alpha_mode( + config.composite_alpha_mode, + ), + width: config.extent.width, + height: config.extent.height, + format: non_srgb_format, + stereo: false, + sample: native::SampleDesc { + count: 1, + quality: 0, }, - Scaling: dxgi1_2::DXGI_SCALING_STRETCH, - Stereo: 0, - SwapEffect: dxgi::DXGI_SWAP_EFFECT_FLIP_DISCARD, + buffer_usage: dxgitype::DXGI_USAGE_RENDER_TARGET_OUTPUT, + buffer_count: config.swap_chain_size, + scaling: native::Scaling::Stretch, + swap_effect: native::SwapEffect::FlipDiscard, + flags, }; - - let hr = { - match self.target { - SurfaceTarget::WndHandle(wnd_handle) => { - profiling::scope!("IDXGIFactory4::CreateSwapChainForHwnd"); - self.factory.CreateSwapChainForHwnd( + let swap_chain1 = match self.target { + SurfaceTarget::Visual(_) => { + profiling::scope!("IDXGIFactory4::CreateSwapChainForComposition"); + self.factory + .unwrap_factory2() + .create_swapchain_for_composition( device.present_queue.as_mut_ptr() as *mut _, - wnd_handle, - &raw_desc, - ptr::null(), - ptr::null_mut(), - swap_chain1.mut_void() as *mut *mut _, + &desc, ) - } - SurfaceTarget::Visual(_) => { - profiling::scope!("IDXGIFactory4::CreateSwapChainForComposition"); - self.factory.CreateSwapChainForComposition( + .into_result() + } + SurfaceTarget::WndHandle(hwnd) => { + profiling::scope!("IDXGIFactory4::CreateSwapChainForHwnd"); + self.factory + .as_factory2() + .unwrap() + .create_swapchain_for_hwnd( device.present_queue.as_mut_ptr() as *mut _, - &raw_desc, - ptr::null_mut(), - swap_chain1.mut_void() as *mut *mut _, + hwnd, + &desc, ) - } + .into_result() } }; - if let Err(err) = hr.into_result() { - log::error!("SwapChain creation error: {}", err); - return Err(crate::SurfaceError::Other("swap chain creation")); - } + let swap_chain1 = match swap_chain1 { + Ok(s) => s, + Err(err) => { + log::error!("SwapChain creation error: {}", err); + return Err(crate::SurfaceError::Other("swap chain creation")); + } + }; match self.target { SurfaceTarget::WndHandle(_) => {} diff --git a/wgpu-hal/src/dx12/view.rs b/wgpu-hal/src/dx12/view.rs index 52b3e2f77f..81e187aaf1 100644 --- a/wgpu-hal/src/dx12/view.rs +++ b/wgpu-hal/src/dx12/view.rs @@ -1,4 +1,4 @@ -use super::conv; +use crate::auxil; use std::mem; use winapi::um::d3d12; @@ -19,8 +19,8 @@ impl crate::TextureViewDescriptor<'_> { pub(super) fn to_internal(&self, texture: &super::Texture) -> ViewDescriptor { ViewDescriptor { dimension: self.dimension, - format: conv::map_texture_format(self.format), - format_nodepth: conv::map_texture_format_nodepth(self.format), + format: auxil::dxgi::conv::map_texture_format(self.format), + format_nodepth: auxil::dxgi::conv::map_texture_format_nodepth(self.format), multisampled: texture.sample_count > 1, mip_level_base: self.range.base_mip_level, mip_level_count: match self.range.mip_level_count { diff --git a/wgpu-hal/src/lib.rs b/wgpu-hal/src/lib.rs index d6bd281677..891381e50a 100644 --- a/wgpu-hal/src/lib.rs +++ b/wgpu-hal/src/lib.rs @@ -52,6 +52,8 @@ compile_error!("Metal API enabled on non-Apple OS. If your project is not using #[cfg(all(feature = "dx12", not(windows)))] compile_error!("DX12 API enabled on non-Windows OS. If your project is not using resolver=\"2\" in Cargo.toml, it should."); +#[cfg(all(feature = "dx11", windows))] +mod dx11; #[cfg(all(feature = "dx12", windows))] mod dx12; mod empty; @@ -64,6 +66,8 @@ mod vulkan; pub mod auxil; pub mod api { + #[cfg(feature = "dx11")] + pub use super::dx11::Api as Dx11; #[cfg(feature = "dx12")] pub use super::dx12::Api as Dx12; pub use super::empty::Api as Empty;