diff --git a/wgpu-core/src/pipeline_cache.rs b/wgpu-core/src/pipeline_cache.rs index ca090e2e5fd..884ebae07b1 100644 --- a/wgpu-core/src/pipeline_cache.rs +++ b/wgpu-core/src/pipeline_cache.rs @@ -319,6 +319,7 @@ mod tests { vendor: 0x0002_FEED, device: 0xFEFE_FEFE, device_type: wgt::DeviceType::Other, + device_pci_bus_id: String::new(), driver: String::new(), driver_info: String::new(), backend: wgt::Backend::Vulkan, diff --git a/wgpu-hal/Cargo.toml b/wgpu-hal/Cargo.toml index bb300f61078..3a64b8e44bc 100644 --- a/wgpu-hal/Cargo.toml +++ b/wgpu-hal/Cargo.toml @@ -143,6 +143,8 @@ dx12 = [ "dep:range-alloc", "dep:windows-core", "dep:gpu-allocator", + "gpu-allocator/d3d12", + "windows/Win32_Devices_DeviceAndDriverInstallation", "windows/Win32_Graphics_Direct3D_Fxc", "windows/Win32_Graphics_Direct3D_Dxc", "windows/Win32_Graphics_Direct3D", diff --git a/wgpu-hal/src/dx12/adapter.rs b/wgpu-hal/src/dx12/adapter.rs index 7fb921b1049..8835df12d6a 100644 --- a/wgpu-hal/src/dx12/adapter.rs +++ b/wgpu-hal/src/dx12/adapter.rs @@ -6,6 +6,12 @@ use parking_lot::Mutex; use windows::{ core::Interface as _, Win32::{ + Devices::DeviceAndDriverInstallation::{ + SetupDiDestroyDeviceInfoList, SetupDiEnumDeviceInfo, SetupDiGetClassDevsW, + SetupDiGetDeviceRegistryPropertyW, DIGCF_PRESENT, GUID_DEVCLASS_DISPLAY, HDEVINFO, + SPDRP_ADDRESS, SPDRP_BUSNUMBER, SPDRP_HARDWAREID, SP_DEVINFO_DATA, + }, + Foundation::{GetLastError, ERROR_NO_MORE_ITEMS}, Graphics::{Direct3D, Direct3D12, Dxgi}, UI::WindowsAndMessaging, }, @@ -127,6 +133,7 @@ impl super::Adapter { } else { wgt::DeviceType::DiscreteGpu }, + device_pci_bus_id: get_adapter_pci_info(desc.VendorId, desc.DeviceId), driver: { if let Ok(i) = unsafe { adapter.CheckInterfaceSupport(&Dxgi::IDXGIDevice::IID) } { const MASK: i64 = 0xFFFF; @@ -1023,3 +1030,147 @@ impl crate::Adapter for super::Adapter { wgt::PresentationTimestamp(self.presentation_timer.get_timestamp_ns()) } } + +fn get_adapter_pci_info(vendor_id: u32, device_id: u32) -> String { + // SAFETY: SetupDiGetClassDevsW is called with valid parameters + let device_info_set = unsafe { + match SetupDiGetClassDevsW(Some(&GUID_DEVCLASS_DISPLAY), None, None, DIGCF_PRESENT) { + Ok(set) => set, + Err(_) => return String::new(), + } + }; + + struct DeviceInfoSetGuard(HDEVINFO); + impl Drop for DeviceInfoSetGuard { + fn drop(&mut self) { + // SAFETY: device_info_set is a valid HDEVINFO and is only dropped once via this guard + unsafe { + let _ = SetupDiDestroyDeviceInfoList(self.0); + } + } + } + let _guard = DeviceInfoSetGuard(device_info_set); + + let mut device_index = 0u32; + loop { + let mut device_info_data = SP_DEVINFO_DATA { + cbSize: size_of::() as u32, + ..Default::default() + }; + + // SAFETY: device_info_set is a valid HDEVINFO, device_index starts at 0 and + // device_info_data is properly initialized above + unsafe { + if SetupDiEnumDeviceInfo(device_info_set, device_index, &mut device_info_data).is_err() + { + if GetLastError() == ERROR_NO_MORE_ITEMS { + break; + } + device_index += 1; + continue; + } + } + + let mut hardware_id_size = 0u32; + // SAFETY: device_info_set and device_info_data are valid + unsafe { + let _ = SetupDiGetDeviceRegistryPropertyW( + device_info_set, + &device_info_data, + SPDRP_HARDWAREID, + None, + None, + Some(&mut hardware_id_size), + ); + } + + if hardware_id_size == 0 { + device_index += 1; + continue; + } + + let mut hardware_id_buffer = vec![0u8; hardware_id_size as usize]; + // SAFETY: device_info_set and device_info_data are valid + unsafe { + if SetupDiGetDeviceRegistryPropertyW( + device_info_set, + &device_info_data, + SPDRP_HARDWAREID, + None, + Some(&mut hardware_id_buffer), + Some(&mut hardware_id_size), + ) + .is_err() + { + device_index += 1; + continue; + } + } + + let hardware_id_u16: Vec = hardware_id_buffer + .chunks_exact(2) + .map(|chunk| u16::from_le_bytes([chunk[0], chunk[1]])) + .collect(); + let hardware_ids: Vec = hardware_id_u16 + .split(|&c| c == 0) + .filter(|s| !s.is_empty()) + .map(|s| String::from_utf16_lossy(s).to_uppercase()) + .collect(); + + // https://learn.microsoft.com/en-us/windows-hardware/drivers/install/identifiers-for-pci-devices + let expected_id = format!("PCI\\VEN_{vendor_id:04X}&DEV_{device_id:04X}"); + if !hardware_ids.iter().any(|id| id.contains(&expected_id)) { + device_index += 1; + continue; + } + + let mut bus_buffer = [0u8; 4]; + let mut data_size = bus_buffer.len() as u32; + // SAFETY: device_info_set and device_info_data are valid + let bus_number = unsafe { + if SetupDiGetDeviceRegistryPropertyW( + device_info_set, + &device_info_data, + SPDRP_BUSNUMBER, + None, + Some(&mut bus_buffer), + Some(&mut data_size), + ) + .is_err() + { + device_index += 1; + continue; + } + u32::from_le_bytes(bus_buffer) + }; + + let mut addr_buffer = [0u8; 4]; + let mut addr_size = addr_buffer.len() as u32; + // SAFETY: device_info_set and device_info_data are valid + unsafe { + if SetupDiGetDeviceRegistryPropertyW( + device_info_set, + &device_info_data, + SPDRP_ADDRESS, + None, + Some(&mut addr_buffer), + Some(&mut addr_size), + ) + .is_err() + { + device_index += 1; + continue; + } + } + let address = u32::from_le_bytes(addr_buffer); + + // https://learn.microsoft.com/en-us/windows-hardware/drivers/kernel/obtaining-device-configuration-information-at-irql---dispatch-level + let device = (address >> 16) & 0x0000FFFF; + let function = address & 0x0000FFFF; + + // domain:bus:device.function + return format!("{:04x}:{:02x}:{:02x}.{:x}", 0, bus_number, device, function); + } + + String::new() +} diff --git a/wgpu-hal/src/gles/adapter.rs b/wgpu-hal/src/gles/adapter.rs index 806f5567ba2..fc00fe03917 100644 --- a/wgpu-hal/src/gles/adapter.rs +++ b/wgpu-hal/src/gles/adapter.rs @@ -187,6 +187,7 @@ impl super::Adapter { device: 0, device_type: inferred_device_type, driver: "".to_owned(), + device_pci_bus_id: String::new(), driver_info: version, backend: wgt::Backend::Gl, } diff --git a/wgpu-hal/src/metal/mod.rs b/wgpu-hal/src/metal/mod.rs index 00223b2f778..ed2f938eb84 100644 --- a/wgpu-hal/src/metal/mod.rs +++ b/wgpu-hal/src/metal/mod.rs @@ -156,6 +156,7 @@ impl crate::Instance for Instance { vendor: 0, device: 0, device_type: shared.private_caps.device_type(), + device_pci_bus_id: String::new(), driver: String::new(), driver_info: String::new(), backend: wgt::Backend::Metal, diff --git a/wgpu-hal/src/noop/mod.rs b/wgpu-hal/src/noop/mod.rs index 4d06e04331e..83f274d8be4 100644 --- a/wgpu-hal/src/noop/mod.rs +++ b/wgpu-hal/src/noop/mod.rs @@ -137,6 +137,7 @@ pub fn adapter_info() -> wgt::AdapterInfo { vendor: 0, device: 0, device_type: wgt::DeviceType::Cpu, + device_pci_bus_id: String::new(), driver: String::from("wgpu"), driver_info: String::new(), backend: wgt::Backend::Noop, diff --git a/wgpu-hal/src/vulkan/adapter.rs b/wgpu-hal/src/vulkan/adapter.rs index 9c007bf6ef9..2e75ddc018d 100644 --- a/wgpu-hal/src/vulkan/adapter.rs +++ b/wgpu-hal/src/vulkan/adapter.rs @@ -962,6 +962,10 @@ pub struct PhysicalDeviceProperties { /// `VK_EXT_mesh_shader` extension. mesh_shader: Option>, + /// Additional `vk::PhysicalDevice` properties from the + /// `VK_EXT_pci_bus_info` extension. + pci_bus_info: Option>, + /// The device API version. /// /// Which is the version of Vulkan supported for device-level functionality. @@ -1392,6 +1396,8 @@ impl super::InstanceShared { >= vk::API_VERSION_1_3 || capabilities.supports_extension(ext::subgroup_size_control::NAME); let supports_robustness2 = capabilities.supports_extension(ext::robustness2::NAME); + let supports_pci_bus_info = + capabilities.supports_extension(ext::pci_bus_info::NAME); let supports_acceleration_structure = capabilities.supports_extension(khr::acceleration_structure::NAME); @@ -1448,6 +1454,13 @@ impl super::InstanceShared { properties2 = properties2.push_next(next); } + if supports_pci_bus_info { + let next = capabilities + .pci_bus_info + .insert(vk::PhysicalDevicePCIBusInfoPropertiesEXT::default()); + properties2 = properties2.push_next(next); + } + if supports_mesh_shader { let next = capabilities .mesh_shader @@ -1662,6 +1675,16 @@ impl super::Instance { vk::PhysicalDeviceType::CPU => wgt::DeviceType::Cpu, _ => wgt::DeviceType::Other, }, + device_pci_bus_id: phd_capabilities + .pci_bus_info + .filter(|info| info.pci_bus != 0 || info.pci_device != 0) + .map(|info| { + format!( + "{:04x}:{:02x}:{:02x}.{}", + info.pci_domain, info.pci_bus, info.pci_device, info.pci_function + ) + }) + .unwrap_or_default(), driver: { phd_capabilities .driver diff --git a/wgpu-info/src/human.rs b/wgpu-info/src/human.rs index f0930bd0212..34fd6ae313b 100644 --- a/wgpu-info/src/human.rs +++ b/wgpu-info/src/human.rs @@ -99,6 +99,7 @@ fn print_adapter(output: &mut impl io::Write, report: &AdapterReport, idx: usize writeln!(output, "\t Name: {}", info.name)?; writeln!(output, "\t VendorID: {:#X?}", info.vendor)?; writeln!(output, "\t DeviceID: {:#X?}", info.device)?; + writeln!(output, "\t DevicePCIBusId: {}", print_empty_string(&info.device_pci_bus_id))?; writeln!(output, "\t Type: {:?}", info.device_type)?; writeln!(output, "\t Driver: {}", print_empty_string(&info.driver))?; writeln!(output, "\t DriverInfo: {}", print_empty_string(&info.driver_info))?; diff --git a/wgpu-types/src/lib.rs b/wgpu-types/src/lib.rs index 75b19db42a1..5fc25936e9f 100644 --- a/wgpu-types/src/lib.rs +++ b/wgpu-types/src/lib.rs @@ -1409,6 +1409,13 @@ pub struct AdapterInfo { pub device: u32, /// Type of device pub device_type: DeviceType, + /// [`Backend`]-specific PCI bus ID of the adapter. + /// + /// * For [`Backend::Vulkan`], [`VkPhysicalDevicePCIBusInfoPropertiesEXT`] is used, + /// if available, in the form `bus:device.function`, e.g. `0000:01:00.0`. + /// + /// [`VkPhysicalDevicePCIBusInfoPropertiesEXT`]: https://registry.khronos.org/vulkan/specs/latest/man/html/VkPhysicalDevicePCIBusInfoPropertiesEXT.html + pub device_pci_bus_id: String, /// Driver name pub driver: String, /// Driver info diff --git a/wgpu/src/backend/webgpu.rs b/wgpu/src/backend/webgpu.rs index be5418bb1bc..25c1e142e1e 100644 --- a/wgpu/src/backend/webgpu.rs +++ b/wgpu/src/backend/webgpu.rs @@ -1707,6 +1707,7 @@ impl dispatch::AdapterInterface for WebAdapter { vendor: 0, device: 0, device_type: wgt::DeviceType::Other, + device_pci_bus_id: String::new(), driver: String::new(), driver_info: String::new(), backend: wgt::Backend::BrowserWebGpu,