1 use hal::adapter::{AdapterInfo, DeviceType};
2 
3 use winapi::{
4     shared::{
5         dxgi,
6         dxgi1_2,
7         dxgi1_3,
8         dxgi1_4,
9         dxgi1_5,
10         guiddef::{GUID, REFIID},
11         winerror,
12     },
13     um::unknwnbase::IUnknown,
14     Interface,
15 };
16 
17 use wio::com::ComPtr;
18 
19 use std::{ffi::OsString, mem, os::windows::ffi::OsStringExt, ptr};
20 
21 #[derive(Debug, Copy, Clone)]
22 pub(crate) enum DxgiVersion {
23     /// Capable of the following interfaces:
24     ///   * IDXGIObject
25     ///   * IDXGIDeviceSubObject
26     ///   * IDXGIResource
27     ///   * IDXGIKeyedMutex
28     ///   * IDXGISurface
29     ///   * IDXGISurface1
30     ///   * IDXGIOutput
31     ///   * IDXGISwapChain
32     ///   * IDXGIFactory
33     ///   * IDXGIDevice
34     ///   * IDXGIFactory1
35     ///   * IDXGIAdapter1
36     ///   * IDXGIDevice1
37     Dxgi1_0,
38 
39     /// Capable of the following interfaces:
40     ///   * IDXGIDisplayControl
41     ///   * IDXGIOutputDuplication
42     ///   * IDXGISurface2
43     ///   * IDXGIResource1
44     ///   * IDXGIDevice2
45     ///   * IDXGISwapChain1
46     ///   * IDXGIFactory2
47     ///   * IDXGIAdapter2
48     ///   * IDXGIOutput1
49     Dxgi1_2,
50 
51     /// Capable of the following interfaces:
52     ///   * IDXGIDevice3
53     ///   * IDXGISwapChain2
54     ///   * IDXGIOutput2
55     ///   * IDXGIDecodeSwapChain
56     ///   * IDXGIFactoryMedia
57     ///   * IDXGISwapChainMedia
58     ///   * IDXGIOutput3
59     Dxgi1_3,
60 
61     /// Capable of the following interfaces:
62     ///   * IDXGISwapChain3
63     ///   * IDXGIOutput4
64     ///   * IDXGIFactory4
65     ///   * IDXGIAdapter3
66     Dxgi1_4,
67 
68     /// Capable of the following interfaces:
69     ///   * IDXGIOutput5
70     ///   * IDXGISwapChain4
71     ///   * IDXGIDevice4
72     ///   * IDXGIFactory5
73     Dxgi1_5,
74 }
75 
76 type DxgiFun =
77     unsafe extern "system" fn(REFIID, *mut *mut winapi::ctypes::c_void) -> winerror::HRESULT;
78 
create_dxgi_factory1( func: &DxgiFun, guid: &GUID, ) -> Result<ComPtr<dxgi::IDXGIFactory>, winerror::HRESULT>79 fn create_dxgi_factory1(
80     func: &DxgiFun,
81     guid: &GUID,
82 ) -> Result<ComPtr<dxgi::IDXGIFactory>, winerror::HRESULT> {
83     let mut factory: *mut IUnknown = ptr::null_mut();
84 
85     let hr = unsafe { func(guid, &mut factory as *mut *mut _ as *mut *mut _) };
86 
87     if winerror::SUCCEEDED(hr) {
88         Ok(unsafe { ComPtr::from_raw(factory as *mut _) })
89     } else {
90         Err(hr)
91     }
92 }
93 
get_dxgi_factory( ) -> Result<(libloading::Library, ComPtr<dxgi::IDXGIFactory>, DxgiVersion), winerror::HRESULT>94 pub(crate) fn get_dxgi_factory(
95 ) -> Result<(libloading::Library, ComPtr<dxgi::IDXGIFactory>, DxgiVersion), winerror::HRESULT> {
96     // The returned Com-pointer is only safe to use for the lifetime of the Library.
97     let library = libloading::Library::new("dxgi.dll").map_err(|_| -1)?;
98     let func: libloading::Symbol<DxgiFun> =
99         unsafe { library.get(b"CreateDXGIFactory1") }.map_err(|_| -1)?;
100 
101     // TODO: do we even need `create_dxgi_factory2`?
102     if let Ok(factory) = create_dxgi_factory1(&func, &dxgi1_5::IDXGIFactory5::uuidof()) {
103         return Ok((library, factory, DxgiVersion::Dxgi1_5));
104     }
105 
106     if let Ok(factory) = create_dxgi_factory1(&func, &dxgi1_4::IDXGIFactory4::uuidof()) {
107         return Ok((library, factory, DxgiVersion::Dxgi1_4));
108     }
109 
110     if let Ok(factory) = create_dxgi_factory1(&func, &dxgi1_3::IDXGIFactory3::uuidof()) {
111         return Ok((library, factory, DxgiVersion::Dxgi1_3));
112     }
113 
114     if let Ok(factory) = create_dxgi_factory1(&func, &dxgi1_2::IDXGIFactory2::uuidof()) {
115         return Ok((library, factory, DxgiVersion::Dxgi1_2));
116     }
117 
118     if let Ok(factory) = create_dxgi_factory1(&func, &dxgi::IDXGIFactory1::uuidof()) {
119         return Ok((library, factory, DxgiVersion::Dxgi1_0));
120     }
121 
122     // TODO: any reason why above would fail and this wouldnt?
123     match create_dxgi_factory1(&func, &dxgi::IDXGIFactory::uuidof()) {
124         Ok(factory) => Ok((library, factory, DxgiVersion::Dxgi1_0)),
125         Err(hr) => Err(hr),
126     }
127 }
128 
enum_adapters1( idx: u32, factory: *mut dxgi::IDXGIFactory, ) -> Result<ComPtr<dxgi::IDXGIAdapter>, winerror::HRESULT>129 fn enum_adapters1(
130     idx: u32,
131     factory: *mut dxgi::IDXGIFactory,
132 ) -> Result<ComPtr<dxgi::IDXGIAdapter>, winerror::HRESULT> {
133     let mut adapter: *mut dxgi::IDXGIAdapter = ptr::null_mut();
134 
135     let hr = unsafe {
136         (*(factory as *mut dxgi::IDXGIFactory1))
137             .EnumAdapters1(idx, &mut adapter as *mut *mut _ as *mut *mut _)
138     };
139 
140     if winerror::SUCCEEDED(hr) {
141         Ok(unsafe { ComPtr::from_raw(adapter) })
142     } else {
143         Err(hr)
144     }
145 }
146 
get_adapter_desc(adapter: *mut dxgi::IDXGIAdapter, version: DxgiVersion) -> AdapterInfo147 fn get_adapter_desc(adapter: *mut dxgi::IDXGIAdapter, version: DxgiVersion) -> AdapterInfo {
148     match version {
149         DxgiVersion::Dxgi1_0 => {
150             let mut desc: dxgi::DXGI_ADAPTER_DESC1 = unsafe { mem::zeroed() };
151             unsafe {
152                 (*(adapter as *mut dxgi::IDXGIAdapter1)).GetDesc1(&mut desc);
153             }
154 
155             let device_name = {
156                 let len = desc.Description.iter().take_while(|&&c| c != 0).count();
157                 let name = <OsString as OsStringExt>::from_wide(&desc.Description[.. len]);
158                 name.to_string_lossy().into_owned()
159             };
160 
161             AdapterInfo {
162                 name: device_name,
163                 vendor: desc.VendorId as usize,
164                 device: desc.DeviceId as usize,
165                 device_type: if (desc.Flags & dxgi::DXGI_ADAPTER_FLAG_SOFTWARE) != 0 {
166                     DeviceType::VirtualGpu
167                 } else {
168                     DeviceType::DiscreteGpu
169                 },
170             }
171         }
172         DxgiVersion::Dxgi1_2
173         | DxgiVersion::Dxgi1_3
174         | DxgiVersion::Dxgi1_4
175         | DxgiVersion::Dxgi1_5 => {
176             let mut desc: dxgi1_2::DXGI_ADAPTER_DESC2 = unsafe { mem::zeroed() };
177             unsafe {
178                 (*(adapter as *mut dxgi1_2::IDXGIAdapter2)).GetDesc2(&mut desc);
179             }
180 
181             let device_name = {
182                 let len = desc.Description.iter().take_while(|&&c| c != 0).count();
183                 let name = <OsString as OsStringExt>::from_wide(&desc.Description[.. len]);
184                 name.to_string_lossy().into_owned()
185             };
186 
187             AdapterInfo {
188                 name: device_name,
189                 vendor: desc.VendorId as usize,
190                 device: desc.DeviceId as usize,
191                 device_type: if (desc.Flags & dxgi::DXGI_ADAPTER_FLAG_SOFTWARE) != 0 {
192                     DeviceType::VirtualGpu
193                 } else {
194                     DeviceType::DiscreteGpu
195                 },
196             }
197         }
198     }
199 }
200 
get_adapter( idx: u32, factory: *mut dxgi::IDXGIFactory, version: DxgiVersion, ) -> Result<(ComPtr<dxgi::IDXGIAdapter>, AdapterInfo), winerror::HRESULT>201 pub(crate) fn get_adapter(
202     idx: u32,
203     factory: *mut dxgi::IDXGIFactory,
204     version: DxgiVersion,
205 ) -> Result<(ComPtr<dxgi::IDXGIAdapter>, AdapterInfo), winerror::HRESULT> {
206     let adapter = match version {
207         DxgiVersion::Dxgi1_0
208         | DxgiVersion::Dxgi1_2
209         | DxgiVersion::Dxgi1_3
210         | DxgiVersion::Dxgi1_4
211         | DxgiVersion::Dxgi1_5 => enum_adapters1(idx, factory)?,
212     };
213 
214     let desc = get_adapter_desc(adapter.as_raw(), version);
215 
216     Ok((adapter, desc))
217 }
218