use hal::adapter::{AdapterInfo, DeviceType}; use winapi::{ shared::{ dxgi, dxgi1_2, dxgi1_3, dxgi1_4, dxgi1_5, guiddef::{GUID, REFIID}, winerror, }, um::unknwnbase::IUnknown, Interface, }; use wio::com::ComPtr; use std::{ffi::OsString, mem, os::windows::ffi::OsStringExt, ptr}; #[derive(Debug, Copy, Clone)] pub(crate) enum DxgiVersion { /// Capable of the following interfaces: /// * IDXGIObject /// * IDXGIDeviceSubObject /// * IDXGIResource /// * IDXGIKeyedMutex /// * IDXGISurface /// * IDXGISurface1 /// * IDXGIOutput /// * IDXGISwapChain /// * IDXGIFactory /// * IDXGIDevice /// * IDXGIFactory1 /// * IDXGIAdapter1 /// * IDXGIDevice1 Dxgi1_0, /// Capable of the following interfaces: /// * IDXGIDisplayControl /// * IDXGIOutputDuplication /// * IDXGISurface2 /// * IDXGIResource1 /// * IDXGIDevice2 /// * IDXGISwapChain1 /// * IDXGIFactory2 /// * IDXGIAdapter2 /// * IDXGIOutput1 Dxgi1_2, /// Capable of the following interfaces: /// * IDXGIDevice3 /// * IDXGISwapChain2 /// * IDXGIOutput2 /// * IDXGIDecodeSwapChain /// * IDXGIFactoryMedia /// * IDXGISwapChainMedia /// * IDXGIOutput3 Dxgi1_3, /// Capable of the following interfaces: /// * IDXGISwapChain3 /// * IDXGIOutput4 /// * IDXGIFactory4 /// * IDXGIAdapter3 Dxgi1_4, /// Capable of the following interfaces: /// * IDXGIOutput5 /// * IDXGISwapChain4 /// * IDXGIDevice4 /// * IDXGIFactory5 Dxgi1_5, } type DxgiFun = unsafe extern "system" fn(REFIID, *mut *mut winapi::ctypes::c_void) -> winerror::HRESULT; fn create_dxgi_factory1( func: &DxgiFun, guid: &GUID, ) -> Result, winerror::HRESULT> { let mut factory: *mut IUnknown = ptr::null_mut(); let hr = unsafe { func(guid, &mut factory as *mut *mut _ as *mut *mut _) }; if winerror::SUCCEEDED(hr) { Ok(unsafe { ComPtr::from_raw(factory as *mut _) }) } else { Err(hr) } } pub(crate) fn get_dxgi_factory( ) -> Result<(libloading::Library, ComPtr, DxgiVersion), winerror::HRESULT> { // The returned Com-pointer is only safe to use for the lifetime of the Library. let library = libloading::Library::new("dxgi.dll").map_err(|_| -1)?; let func: libloading::Symbol = unsafe { library.get(b"CreateDXGIFactory1") }.map_err(|_| -1)?; // TODO: do we even need `create_dxgi_factory2`? if let Ok(factory) = create_dxgi_factory1(&func, &dxgi1_5::IDXGIFactory5::uuidof()) { return Ok((library, factory, DxgiVersion::Dxgi1_5)); } if let Ok(factory) = create_dxgi_factory1(&func, &dxgi1_4::IDXGIFactory4::uuidof()) { return Ok((library, factory, DxgiVersion::Dxgi1_4)); } if let Ok(factory) = create_dxgi_factory1(&func, &dxgi1_3::IDXGIFactory3::uuidof()) { return Ok((library, factory, DxgiVersion::Dxgi1_3)); } if let Ok(factory) = create_dxgi_factory1(&func, &dxgi1_2::IDXGIFactory2::uuidof()) { return Ok((library, factory, DxgiVersion::Dxgi1_2)); } if let Ok(factory) = create_dxgi_factory1(&func, &dxgi::IDXGIFactory1::uuidof()) { return Ok((library, factory, DxgiVersion::Dxgi1_0)); } // TODO: any reason why above would fail and this wouldnt? match create_dxgi_factory1(&func, &dxgi::IDXGIFactory::uuidof()) { Ok(factory) => Ok((library, factory, DxgiVersion::Dxgi1_0)), Err(hr) => Err(hr), } } fn enum_adapters1( idx: u32, factory: *mut dxgi::IDXGIFactory, ) -> Result, winerror::HRESULT> { let mut adapter: *mut dxgi::IDXGIAdapter = ptr::null_mut(); let hr = unsafe { (*(factory as *mut dxgi::IDXGIFactory1)) .EnumAdapters1(idx, &mut adapter as *mut *mut _ as *mut *mut _) }; if winerror::SUCCEEDED(hr) { Ok(unsafe { ComPtr::from_raw(adapter) }) } else { Err(hr) } } fn get_adapter_desc(adapter: *mut dxgi::IDXGIAdapter, version: DxgiVersion) -> AdapterInfo { match version { DxgiVersion::Dxgi1_0 => { let mut desc: dxgi::DXGI_ADAPTER_DESC1 = unsafe { mem::zeroed() }; unsafe { (*(adapter as *mut dxgi::IDXGIAdapter1)).GetDesc1(&mut desc); } let device_name = { let len = desc.Description.iter().take_while(|&&c| c != 0).count(); let name = ::from_wide(&desc.Description[..len]); name.to_string_lossy().into_owned() }; AdapterInfo { name: device_name, vendor: desc.VendorId as usize, device: desc.DeviceId as usize, device_type: if (desc.Flags & dxgi::DXGI_ADAPTER_FLAG_SOFTWARE) != 0 { DeviceType::VirtualGpu } else { DeviceType::DiscreteGpu }, } } DxgiVersion::Dxgi1_2 | DxgiVersion::Dxgi1_3 | DxgiVersion::Dxgi1_4 | DxgiVersion::Dxgi1_5 => { let mut desc: dxgi1_2::DXGI_ADAPTER_DESC2 = unsafe { mem::zeroed() }; unsafe { (*(adapter as *mut dxgi1_2::IDXGIAdapter2)).GetDesc2(&mut desc); } let device_name = { let len = desc.Description.iter().take_while(|&&c| c != 0).count(); let name = ::from_wide(&desc.Description[..len]); name.to_string_lossy().into_owned() }; AdapterInfo { name: device_name, vendor: desc.VendorId as usize, device: desc.DeviceId as usize, device_type: if (desc.Flags & dxgi::DXGI_ADAPTER_FLAG_SOFTWARE) != 0 { DeviceType::VirtualGpu } else { DeviceType::DiscreteGpu }, } } } } pub(crate) fn get_adapter( idx: u32, factory: *mut dxgi::IDXGIFactory, version: DxgiVersion, ) -> Result<(ComPtr, AdapterInfo), winerror::HRESULT> { let adapter = match version { DxgiVersion::Dxgi1_0 | DxgiVersion::Dxgi1_2 | DxgiVersion::Dxgi1_3 | DxgiVersion::Dxgi1_4 | DxgiVersion::Dxgi1_5 => enum_adapters1(idx, factory)?, }; let desc = get_adapter_desc(adapter.as_raw(), version); Ok((adapter, desc)) }