diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
commit | 26a029d407be480d791972afb5975cf62c9360a6 (patch) | |
tree | f435a8308119effd964b339f76abb83a57c29483 /third_party/rust/wgpu-hal/src/auxil/dxgi | |
parent | Initial commit. (diff) | |
download | firefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz firefox-26a029d407be480d791972afb5975cf62c9360a6.zip |
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/wgpu-hal/src/auxil/dxgi')
-rw-r--r-- | third_party/rust/wgpu-hal/src/auxil/dxgi/conv.rs | 270 | ||||
-rw-r--r-- | third_party/rust/wgpu-hal/src/auxil/dxgi/exception.rs | 105 | ||||
-rw-r--r-- | third_party/rust/wgpu-hal/src/auxil/dxgi/factory.rs | 261 | ||||
-rw-r--r-- | third_party/rust/wgpu-hal/src/auxil/dxgi/mod.rs | 5 | ||||
-rw-r--r-- | third_party/rust/wgpu-hal/src/auxil/dxgi/result.rs | 42 | ||||
-rw-r--r-- | third_party/rust/wgpu-hal/src/auxil/dxgi/time.rs | 94 |
6 files changed, 777 insertions, 0 deletions
diff --git a/third_party/rust/wgpu-hal/src/auxil/dxgi/conv.rs b/third_party/rust/wgpu-hal/src/auxil/dxgi/conv.rs new file mode 100644 index 0000000000..6af4b77bb3 --- /dev/null +++ b/third_party/rust/wgpu-hal/src/auxil/dxgi/conv.rs @@ -0,0 +1,270 @@ +use std::{ffi::OsString, os::windows::ffi::OsStringExt}; +use winapi::shared::dxgiformat; + +// Helper to convert DXGI adapter name to a normal string +pub fn map_adapter_name(name: [u16; 128]) -> String { + let len = name.iter().take_while(|&&c| c != 0).count(); + let name = OsString::from_wide(&name[..len]); + name.to_string_lossy().into_owned() +} + +pub fn map_texture_format_failable(format: wgt::TextureFormat) -> Option<dxgiformat::DXGI_FORMAT> { + use wgt::TextureFormat as Tf; + use winapi::shared::dxgiformat::*; + + Some(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::Rgb9e5Ufloat => DXGI_FORMAT_R9G9B9E5_SHAREDEXP, + Tf::Rgb10a2Uint => DXGI_FORMAT_R10G10B10A2_UINT, + 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::Stencil8 => DXGI_FORMAT_D24_UNORM_S8_UINT, + Tf::Depth16Unorm => DXGI_FORMAT_D16_UNORM, + Tf::Depth24Plus => DXGI_FORMAT_D24_UNORM_S8_UINT, + Tf::Depth24PlusStencil8 => DXGI_FORMAT_D24_UNORM_S8_UINT, + Tf::Depth32Float => DXGI_FORMAT_D32_FLOAT, + Tf::Depth32FloatStencil8 => DXGI_FORMAT_D32_FLOAT_S8X24_UINT, + Tf::NV12 => DXGI_FORMAT_NV12, + 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::Bc6hRgbFloat => 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: _, + } => return None, + }) +} + +pub fn map_texture_format(format: wgt::TextureFormat) -> dxgiformat::DXGI_FORMAT { + match map_texture_format_failable(format) { + Some(f) => f, + None => 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), + } +} + +// SRV and UAV can't use the depth or typeless formats +// see https://microsoft.github.io/DirectX-Specs/d3d/PlanarDepthStencilDDISpec.html#view-creation +pub fn map_texture_format_for_srv_uav( + format: wgt::TextureFormat, + aspect: crate::FormatAspects, +) -> Option<dxgiformat::DXGI_FORMAT> { + Some(match (format, aspect) { + (wgt::TextureFormat::Depth16Unorm, crate::FormatAspects::DEPTH) => { + dxgiformat::DXGI_FORMAT_R16_UNORM + } + (wgt::TextureFormat::Depth32Float, crate::FormatAspects::DEPTH) => { + dxgiformat::DXGI_FORMAT_R32_FLOAT + } + (wgt::TextureFormat::Depth32FloatStencil8, crate::FormatAspects::DEPTH) => { + dxgiformat::DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS + } + ( + wgt::TextureFormat::Depth24Plus | wgt::TextureFormat::Depth24PlusStencil8, + crate::FormatAspects::DEPTH, + ) => dxgiformat::DXGI_FORMAT_R24_UNORM_X8_TYPELESS, + + (wgt::TextureFormat::Depth32FloatStencil8, crate::FormatAspects::STENCIL) => { + dxgiformat::DXGI_FORMAT_X32_TYPELESS_G8X24_UINT + } + ( + wgt::TextureFormat::Stencil8 | wgt::TextureFormat::Depth24PlusStencil8, + crate::FormatAspects::STENCIL, + ) => dxgiformat::DXGI_FORMAT_X24_TYPELESS_G8_UINT, + + (_, crate::FormatAspects::DEPTH) + | (_, crate::FormatAspects::STENCIL) + | (_, crate::FormatAspects::DEPTH_STENCIL) => return None, + + _ => map_texture_format(format), + }) +} + +// see https://microsoft.github.io/DirectX-Specs/d3d/PlanarDepthStencilDDISpec.html#planar-layout-for-staging-from-buffer +pub fn map_texture_format_for_copy( + format: wgt::TextureFormat, + aspect: crate::FormatAspects, +) -> Option<dxgiformat::DXGI_FORMAT> { + Some(match (format, aspect) { + (wgt::TextureFormat::Depth16Unorm, crate::FormatAspects::DEPTH) => { + dxgiformat::DXGI_FORMAT_R16_UNORM + } + ( + wgt::TextureFormat::Depth32Float | wgt::TextureFormat::Depth32FloatStencil8, + crate::FormatAspects::DEPTH, + ) => dxgiformat::DXGI_FORMAT_R32_FLOAT, + + ( + wgt::TextureFormat::Stencil8 + | wgt::TextureFormat::Depth24PlusStencil8 + | wgt::TextureFormat::Depth32FloatStencil8, + crate::FormatAspects::STENCIL, + ) => dxgiformat::DXGI_FORMAT_R8_UINT, + + (format, crate::FormatAspects::COLOR) => map_texture_format(format), + + _ => return None, + }) +} + +pub fn map_texture_format_for_resource( + format: wgt::TextureFormat, + usage: crate::TextureUses, + has_view_formats: bool, + casting_fully_typed_format_supported: bool, +) -> dxgiformat::DXGI_FORMAT { + use wgt::TextureFormat as Tf; + use winapi::shared::dxgiformat::*; + + if casting_fully_typed_format_supported { + map_texture_format(format) + + // We might view this resource as srgb or non-srgb + } else if has_view_formats { + match format { + Tf::Rgba8Unorm | Tf::Rgba8UnormSrgb => DXGI_FORMAT_R8G8B8A8_TYPELESS, + Tf::Bgra8Unorm | Tf::Bgra8UnormSrgb => DXGI_FORMAT_B8G8R8A8_TYPELESS, + Tf::Bc1RgbaUnorm | Tf::Bc1RgbaUnormSrgb => DXGI_FORMAT_BC1_TYPELESS, + Tf::Bc2RgbaUnorm | Tf::Bc2RgbaUnormSrgb => DXGI_FORMAT_BC2_TYPELESS, + Tf::Bc3RgbaUnorm | Tf::Bc3RgbaUnormSrgb => DXGI_FORMAT_BC3_TYPELESS, + Tf::Bc7RgbaUnorm | Tf::Bc7RgbaUnormSrgb => DXGI_FORMAT_BC7_TYPELESS, + format => map_texture_format(format), + } + + // We might view this resource as SRV/UAV but also as DSV + } else if format.is_depth_stencil_format() + && usage.intersects( + crate::TextureUses::RESOURCE + | crate::TextureUses::STORAGE_READ + | crate::TextureUses::STORAGE_READ_WRITE, + ) + { + match format { + Tf::Depth16Unorm => DXGI_FORMAT_R16_TYPELESS, + Tf::Depth32Float => DXGI_FORMAT_R32_TYPELESS, + Tf::Depth32FloatStencil8 => DXGI_FORMAT_R32G8X24_TYPELESS, + Tf::Stencil8 | Tf::Depth24Plus | Tf::Depth24PlusStencil8 => DXGI_FORMAT_R24G8_TYPELESS, + _ => unreachable!(), + } + } else { + map_texture_format(format) + } +} + +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: wgt::CompositeAlphaMode) -> d3d12::AlphaMode { + d3d12::AlphaMode::Ignore +} diff --git a/third_party/rust/wgpu-hal/src/auxil/dxgi/exception.rs b/third_party/rust/wgpu-hal/src/auxil/dxgi/exception.rs new file mode 100644 index 0000000000..70db8b2d0d --- /dev/null +++ b/third_party/rust/wgpu-hal/src/auxil/dxgi/exception.rs @@ -0,0 +1,105 @@ +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 guarantee +// there is either 1 or 0 exception handlers registered, not multiple. +static EXCEPTION_HANDLER_COUNT: Mutex<usize> = Mutex::const_new(parking_lot::RawMutex::INIT, 0); + +pub fn register_exception_handler() { + let mut count_guard = EXCEPTION_HANDLER_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_HANDLER_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 = unsafe { &*(*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(unsafe { + slice::from_raw_parts( + record.ExceptionInformation[1] as *const u8, + record.ExceptionInformation[0], + ) + }), + winnt::DBG_PRINTEXCEPTION_WIDE_C => Cow::Owned(String::from_utf16_lossy(unsafe { + 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; + } + + if level == log::Level::Warn && message.contains("DRAW_EMPTY_SCISSOR_RECTANGLE") { + // This is normal, WebGPU allows passing empty scissor rectangles. + 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.add(message.to_string()); + } + + excpt::EXCEPTION_CONTINUE_EXECUTION +} diff --git a/third_party/rust/wgpu-hal/src/auxil/dxgi/factory.rs b/third_party/rust/wgpu-hal/src/auxil/dxgi/factory.rs new file mode 100644 index 0000000000..38fdd17c89 --- /dev/null +++ b/third_party/rust/wgpu-hal/src/auxil/dxgi/factory.rs @@ -0,0 +1,261 @@ +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 { + Factory2, + Factory4, + Factory6, +} + +fn should_keep_adapter(adapter: &dxgi::IDXGIAdapter1) -> bool { + let mut desc = unsafe { std::mem::zeroed() }; + unsafe { adapter.GetDesc1(&mut desc) }; + + // The Intel Haswell family of iGPUs had support for the D3D12 API but it was later + // removed due to a security vulnerability. + // + // We are explicitly filtering out all the devices in the family because we are now + // getting reports of device loss at a later time than at device creation time (`D3D12CreateDevice`). + // + // See https://www.intel.com/content/www/us/en/support/articles/000057520/graphics.html + // This list of device IDs is from https://dgpu-docs.intel.com/devices/hardware-table.html + let haswell_device_ids = [ + 0x0422, 0x0426, 0x042A, 0x042B, 0x042E, 0x0C22, 0x0C26, 0x0C2A, 0x0C2B, 0x0C2E, 0x0A22, + 0x0A2A, 0x0A2B, 0x0D2A, 0x0D2B, 0x0D2E, 0x0A26, 0x0A2E, 0x0D22, 0x0D26, 0x0412, 0x0416, + 0x0D12, 0x041A, 0x041B, 0x0C12, 0x0C16, 0x0C1A, 0x0C1B, 0x0C1E, 0x0A12, 0x0A1A, 0x0A1B, + 0x0D16, 0x0D1A, 0x0D1B, 0x0D1E, 0x041E, 0x0A16, 0x0A1E, 0x0402, 0x0406, 0x040A, 0x040B, + 0x040E, 0x0C02, 0x0C06, 0x0C0A, 0x0C0B, 0x0C0E, 0x0A02, 0x0A06, 0x0A0A, 0x0A0B, 0x0A0E, + 0x0D02, 0x0D06, 0x0D0A, 0x0D0B, 0x0D0E, + ]; + if desc.VendorId == 0x8086 && haswell_device_ids.contains(&desc.DeviceId) { + return false; + } + + // If run completely headless, windows will show two different WARP adapters, one + // which is lying about being an integrated card. This is so that programs + // that ignore software adapters will actually run on headless/gpu-less machines. + // + // We don't want that and discorage that kind of filtering anyway, so we skip the integrated WARP. + if desc.VendorId == 5140 && (desc.Flags & dxgi::DXGI_ADAPTER_FLAG_SOFTWARE) == 0 { + let adapter_name = super::conv::map_adapter_name(desc.Description); + if adapter_name.contains("Microsoft Basic Render Driver") { + return false; + } + } + + true +} + +pub fn enumerate_adapters(factory: d3d12::DxgiFactory) -> Vec<d3d12::DxgiAdapter> { + 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 IDXGIAdapter4 directly + let mut adapter4 = d3d12::ComPtr::<dxgi1_6::IDXGIAdapter4>::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; + } + + if !should_keep_adapter(&adapter4) { + continue; + } + + adapters.push(d3d12::DxgiAdapter::Adapter4(adapter4)); + continue; + } + + profiling::scope!("IDXGIFactory1::EnumAdapters1"); + let mut adapter1 = d3d12::ComPtr::<dxgi::IDXGIAdapter1>::null(); + let hr = unsafe { factory.EnumAdapters1(cur_index, adapter1.mut_self()) }; + + if hr == winerror::DXGI_ERROR_NOT_FOUND { + break; + } + if let Err(err) = hr.into_result() { + log::error!("Failed enumerating adapters: {}", err); + break; + } + + if !should_keep_adapter(&adapter1) { + continue; + } + + // Do the most aggressive casts first, skipping Adapter4 as we definitely don't have dxgi1_6. + + // Adapter1 -> Adapter3 + unsafe { + match adapter1.cast::<dxgi1_4::IDXGIAdapter3>().into_result() { + Ok(adapter3) => { + adapters.push(d3d12::DxgiAdapter::Adapter3(adapter3)); + continue; + } + Err(err) => { + log::warn!("Failed casting Adapter1 to Adapter3: {}", err); + } + } + } + + // Adapter1 -> Adapter2 + unsafe { + match adapter1.cast::<dxgi1_2::IDXGIAdapter2>().into_result() { + Ok(adapter2) => { + adapters.push(d3d12::DxgiAdapter::Adapter2(adapter2)); + continue; + } + Err(err) => { + log::warn!("Failed casting Adapter1 to Adapter2: {}", err); + } + } + } + + adapters.push(d3d12::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: wgt::InstanceFlags, +) -> Result<(d3d12::DxgiLib, d3d12::DxgiFactory), crate::InstanceError> { + let lib_dxgi = d3d12::DxgiLib::new().map_err(|e| { + crate::InstanceError::with_source(String::from("failed to load dxgi.dll"), e) + })?; + + let mut factory_flags = d3d12::FactoryCreationFlags::empty(); + + if instance_flags.contains(wgt::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) => { + factory_flags |= d3d12::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) => { + // err is a Cow<str>, not an Error implementor + return Err(crate::InstanceError::new(format!( + "failed to create IDXGIFactory4: {err:?}" + ))); + } + }, + // If we require factory4, hard error. + Err(err) if required_factory_type == DxgiFactoryType::Factory4 => { + return Err(crate::InstanceError::with_source( + String::from("IDXGIFactory1 creation function not found"), + err, + )); + } + // If we don't print it to warn as all win7 will hit this case. + Err(err) => { + log::warn!("IDXGIFactory1 creation function not found: {err:?}"); + None + } + }; + + if let Some(factory4) = factory4 { + // Try to cast the IDXGIFactory4 into IDXGIFactory6 + let factory6 = unsafe { factory4.cast::<dxgi1_6::IDXGIFactory6>().into_result() }; + match factory6 { + Ok(factory6) => { + return Ok((lib_dxgi, d3d12::DxgiFactory::Factory6(factory6))); + } + // If we require factory6, hard error. + Err(err) if required_factory_type == DxgiFactoryType::Factory6 => { + // err is a Cow<str>, not an Error implementor + return Err(crate::InstanceError::new(format!( + "failed to cast IDXGIFactory4 to IDXGIFactory6: {err:?}" + ))); + } + // If we don't print it to warn. + Err(err) => { + log::warn!("Failed to cast IDXGIFactory4 to IDXGIFactory6: {:?}", err); + return Ok((lib_dxgi, d3d12::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) => { + // err is a Cow<str>, not an Error implementor + return Err(crate::InstanceError::new(format!( + "failed to create IDXGIFactory1: {err:?}" + ))); + } + }, + // We always require at least factory1, so hard error + Err(err) => { + return Err(crate::InstanceError::with_source( + String::from("IDXGIFactory1 creation function not found"), + err, + )); + } + }; + + // 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, d3d12::DxgiFactory::Factory2(factory2))); + } + // If we require factory2, hard error. + Err(err) if required_factory_type == DxgiFactoryType::Factory2 => { + // err is a Cow<str>, not an Error implementor + return Err(crate::InstanceError::new(format!( + "failed to cast IDXGIFactory1 to IDXGIFactory2: {err:?}" + ))); + } + // If we don't print it to warn. + Err(err) => { + log::warn!("Failed to cast IDXGIFactory1 to IDXGIFactory2: {:?}", err); + } + } + + // We tried to create 4 and 2, but only succeeded with 1. + Ok((lib_dxgi, d3d12::DxgiFactory::Factory1(factory1))) +} diff --git a/third_party/rust/wgpu-hal/src/auxil/dxgi/mod.rs b/third_party/rust/wgpu-hal/src/auxil/dxgi/mod.rs new file mode 100644 index 0000000000..559969633c --- /dev/null +++ b/third_party/rust/wgpu-hal/src/auxil/dxgi/mod.rs @@ -0,0 +1,5 @@ +pub mod conv; +pub mod exception; +pub mod factory; +pub mod result; +pub mod time; diff --git a/third_party/rust/wgpu-hal/src/auxil/dxgi/result.rs b/third_party/rust/wgpu-hal/src/auxil/dxgi/result.rs new file mode 100644 index 0000000000..db013d2dec --- /dev/null +++ b/third_party/rust/wgpu-hal/src/auxil/dxgi/result.rs @@ -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) + } +} diff --git a/third_party/rust/wgpu-hal/src/auxil/dxgi/time.rs b/third_party/rust/wgpu-hal/src/auxil/dxgi/time.rs new file mode 100644 index 0000000000..fd99c097d7 --- /dev/null +++ b/third_party/rust/wgpu-hal/src/auxil/dxgi/time.rs @@ -0,0 +1,94 @@ +#![allow(dead_code)] // IPresentationManager is unused currently + +use std::mem; + +use winapi::um::{ + profileapi::{QueryPerformanceCounter, QueryPerformanceFrequency}, + winnt::LARGE_INTEGER, +}; + +pub enum PresentationTimer { + /// DXGI uses QueryPerformanceCounter + Dxgi { + /// How many ticks of QPC per second + frequency: u64, + }, + /// IPresentationManager uses QueryInterruptTimePrecise + #[allow(non_snake_case)] + IPresentationManager { + fnQueryInterruptTimePrecise: unsafe extern "system" fn(*mut winapi::ctypes::c_ulonglong), + }, +} + +impl std::fmt::Debug for PresentationTimer { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match *self { + Self::Dxgi { frequency } => f + .debug_struct("DXGI") + .field("frequency", &frequency) + .finish(), + Self::IPresentationManager { + fnQueryInterruptTimePrecise, + } => f + .debug_struct("IPresentationManager") + .field( + "QueryInterruptTimePrecise", + &(fnQueryInterruptTimePrecise as usize), + ) + .finish(), + } + } +} + +impl PresentationTimer { + /// Create a presentation timer using QueryPerformanceFrequency (what DXGI uses for presentation times) + pub fn new_dxgi() -> Self { + let mut frequency: LARGE_INTEGER = unsafe { mem::zeroed() }; + let success = unsafe { QueryPerformanceFrequency(&mut frequency) }; + assert_ne!(success, 0); + + Self::Dxgi { + frequency: unsafe { *frequency.QuadPart() } as u64, + } + } + + /// Create a presentation timer using QueryInterruptTimePrecise (what IPresentationManager uses for presentation times) + /// + /// Panics if QueryInterruptTimePrecise isn't found (below Win10) + pub fn new_ipresentation_manager() -> Self { + // We need to load this explicitly, as QueryInterruptTimePrecise is only available on Windows 10+ + // + // Docs say it's in kernel32.dll, but it's actually in kernelbase.dll. + let kernelbase = + libloading::os::windows::Library::open_already_loaded("kernelbase.dll").unwrap(); + // No concerns about lifetimes here as kernelbase is always there. + let ptr = unsafe { kernelbase.get(b"QueryInterruptTimePrecise").unwrap() }; + Self::IPresentationManager { + fnQueryInterruptTimePrecise: *ptr, + } + } + + /// Gets the current time in nanoseconds. + pub fn get_timestamp_ns(&self) -> u128 { + // Always do u128 math _after_ hitting the timing function. + match *self { + PresentationTimer::Dxgi { frequency } => { + let mut counter: LARGE_INTEGER = unsafe { mem::zeroed() }; + let success = unsafe { QueryPerformanceCounter(&mut counter) }; + assert_ne!(success, 0); + + // counter * (1_000_000_000 / freq) but re-ordered to make more precise + (unsafe { *counter.QuadPart() } as u128 * 1_000_000_000) / frequency as u128 + } + PresentationTimer::IPresentationManager { + fnQueryInterruptTimePrecise, + } => { + let mut counter = 0; + unsafe { fnQueryInterruptTimePrecise(&mut counter) }; + + // QueryInterruptTimePrecise uses units of 100ns for its tick. + counter as u128 * 100 + } + } + } +} |