1 //
2 // Copyright (c) 2013-2017 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6 
7 // SystemInfo_win.cpp: implementation of the Windows-specific parts of SystemInfo.h
8 
9 #include "gpu_info_util/SystemInfo_internal.h"
10 
11 #include "common/debug.h"
12 #include "common/string_utils.h"
13 
14 // Windows.h needs to be included first
15 #include <windows.h>
16 
17 #if defined(GPU_INFO_USE_SETUPAPI)
18 // Remove parts of commctrl.h that have compile errors
19 #define NOTOOLBAR
20 #define NOTOOLTIPS
21 #include <cfgmgr32.h>
22 #include <setupapi.h>
23 #elif defined(GPU_INFO_USE_DXGI)
24 #include <dxgi.h>
25 #include <d3d10.h>
26 #else
27 #error "SystemInfo_win needs at least GPU_INFO_USE_SETUPAPI or GPU_INFO_USE_DXGI defined"
28 #endif
29 
30 #include <array>
31 #include <sstream>
32 
33 namespace angle
34 {
35 
36 namespace
37 {
38 
39 // Returns the CM device ID of the primary GPU.
GetPrimaryDisplayDeviceId()40 std::string GetPrimaryDisplayDeviceId()
41 {
42     DISPLAY_DEVICEA displayDevice;
43     displayDevice.cb = sizeof(DISPLAY_DEVICEA);
44 
45     for (int i = 0; EnumDisplayDevicesA(nullptr, i, &displayDevice, 0); ++i)
46     {
47         if (displayDevice.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE)
48         {
49             return displayDevice.DeviceID;
50         }
51     }
52 
53     return "";
54 }
55 
56 #if defined(GPU_INFO_USE_SETUPAPI)
57 
GetRegistryStringValue(HKEY key,const char * valueName)58 std::string GetRegistryStringValue(HKEY key, const char *valueName)
59 {
60     std::array<char, 255> value;
61     DWORD valueSize = sizeof(value);
62     if (RegQueryValueExA(key, valueName, nullptr, nullptr, reinterpret_cast<LPBYTE>(value.data()),
63                          &valueSize) == ERROR_SUCCESS)
64     {
65         return value.data();
66     }
67     return "";
68 }
69 
70 // Gathers information about the devices from the registry. The reason why we aren't using
71 // a dedicated API such as DXGI is that we need information like the driver vendor and date.
72 // DXGI doesn't provide a way to know the device registry key from an IDXGIAdapter.
GetDevicesFromRegistry(std::vector<GPUDeviceInfo> * devices)73 bool GetDevicesFromRegistry(std::vector<GPUDeviceInfo> *devices)
74 {
75     // Display adapter class GUID from
76     // https://msdn.microsoft.com/en-us/library/windows/hardware/ff553426%28v=vs.85%29.aspx
77     GUID displayClass = {
78         0x4d36e968, 0xe325, 0x11ce, {0xbf, 0xc1, 0x08, 0x00, 0x2b, 0xe1, 0x03, 0x18}};
79 
80     HDEVINFO deviceInfo = SetupDiGetClassDevsW(&displayClass, nullptr, nullptr, DIGCF_PRESENT);
81 
82     if (deviceInfo == INVALID_HANDLE_VALUE)
83     {
84         return false;
85     }
86 
87     // This iterates over the devices of the "Display adapter" class
88     DWORD deviceIndex = 0;
89     SP_DEVINFO_DATA deviceData;
90     deviceData.cbSize = sizeof(deviceData);
91     while (SetupDiEnumDeviceInfo(deviceInfo, deviceIndex++, &deviceData))
92     {
93         // The device and vendor IDs can be gathered directly, but information about the driver
94         // requires some registry digging
95         char fullDeviceID[MAX_DEVICE_ID_LEN];
96         if (CM_Get_Device_IDA(deviceData.DevInst, fullDeviceID, MAX_DEVICE_ID_LEN, 0) != CR_SUCCESS)
97         {
98             continue;
99         }
100 
101         GPUDeviceInfo device;
102 
103         if (!CMDeviceIDToDeviceAndVendorID(fullDeviceID, &device.vendorId, &device.deviceId))
104         {
105             continue;
106         }
107 
108         // The driver key will end with something like {<displayClass>}/<4 digit number>.
109         std::array<WCHAR, 255> value;
110         if (!SetupDiGetDeviceRegistryPropertyW(deviceInfo, &deviceData, SPDRP_DRIVER, nullptr,
111                                                reinterpret_cast<PBYTE>(value.data()), sizeof(value),
112                                                nullptr))
113         {
114             continue;
115         }
116 
117         std::wstring driverKey = L"System\\CurrentControlSet\\Control\\Class\\";
118         driverKey += value.data();
119 
120         HKEY key;
121         if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, driverKey.c_str(), 0, KEY_QUERY_VALUE, &key) !=
122             ERROR_SUCCESS)
123         {
124             continue;
125         }
126 
127         device.driverVersion = GetRegistryStringValue(key, "DriverVersion");
128         device.driverDate    = GetRegistryStringValue(key, "DriverDate");
129         device.driverVendor  = GetRegistryStringValue(key, "ProviderName");
130 
131         RegCloseKey(key);
132 
133         devices->push_back(device);
134     }
135 
136     SetupDiDestroyDeviceInfoList(deviceInfo);
137 
138     return true;
139 }
140 
141 #elif defined(GPU_INFO_USE_DXGI)
142 
GetDevicesFromDXGI(std::vector<GPUDeviceInfo> * devices)143 bool GetDevicesFromDXGI(std::vector<GPUDeviceInfo> *devices)
144 {
145     IDXGIFactory *factory;
146     if (!SUCCEEDED(CreateDXGIFactory(__uuidof(IDXGIFactory), reinterpret_cast<void **>(&factory))))
147     {
148         return false;
149     }
150 
151     UINT i                = 0;
152     IDXGIAdapter *adapter = nullptr;
153     while (factory->EnumAdapters(i++, &adapter) != DXGI_ERROR_NOT_FOUND)
154     {
155         DXGI_ADAPTER_DESC desc;
156         adapter->GetDesc(&desc);
157 
158         LARGE_INTEGER umdVersion;
159         if (adapter->CheckInterfaceSupport(__uuidof(ID3D10Device), &umdVersion) ==
160             DXGI_ERROR_UNSUPPORTED)
161         {
162             adapter->Release();
163             continue;
164         }
165 
166         // The UMD driver version here is the same as in the registry except for the last number.
167         uint64_t intVersion = umdVersion.QuadPart;
168         std::ostringstream o;
169 
170         const uint64_t kMask = 0xFF;
171         o << ((intVersion >> 48) & kMask) << ".";
172         o << ((intVersion >> 32) & kMask) << ".";
173         o << ((intVersion >> 16) & kMask) << ".";
174         o << (intVersion & kMask);
175 
176         GPUDeviceInfo device;
177         device.vendorId      = desc.VendorId;
178         device.deviceId      = desc.DeviceId;
179         device.driverVersion = o.str();
180 
181         devices->push_back(device);
182 
183         adapter->Release();
184     }
185 
186     factory->Release();
187 
188     return true;
189 }
190 
191 #else
192 #error
193 #endif
194 
195 }  // anonymous namespace
196 
GetSystemInfo(SystemInfo * info)197 bool GetSystemInfo(SystemInfo *info)
198 {
199     // Get the CM device ID first so that it is returned even in error cases.
200     info->primaryDisplayDeviceId = GetPrimaryDisplayDeviceId();
201 
202 #if defined(GPU_INFO_USE_SETUPAPI)
203     if (!GetDevicesFromRegistry(&info->gpus))
204     {
205         return false;
206     }
207 #elif defined(GPU_INFO_USE_DXGI)
208     if (!GetDevicesFromDXGI(&info->gpus))
209     {
210         return false;
211     }
212 #else
213 #error
214 #endif
215 
216     if (info->gpus.size() == 0)
217     {
218         return false;
219     }
220 
221     FindPrimaryGPU(info);
222 
223     // Override the primary GPU index with what we gathered from EnumDisplayDevices
224     uint32_t primaryVendorId = 0;
225     uint32_t primaryDeviceId = 0;
226 
227     if (!CMDeviceIDToDeviceAndVendorID(info->primaryDisplayDeviceId, &primaryVendorId,
228                                        &primaryDeviceId))
229     {
230         return false;
231     }
232 
233     bool foundPrimary = false;
234     for (size_t i = 0; i < info->gpus.size(); ++i)
235     {
236         if (info->gpus[i].vendorId == primaryVendorId && info->gpus[i].deviceId == primaryDeviceId)
237         {
238             info->primaryGPUIndex = static_cast<int>(i);
239             foundPrimary          = true;
240         }
241     }
242     ASSERT(foundPrimary);
243 
244     // nvd3d9wrap.dll is loaded into all processes when Optimus is enabled.
245     HMODULE nvd3d9wrap = GetModuleHandleW(L"nvd3d9wrap.dll");
246     info->isOptimus    = nvd3d9wrap != nullptr;
247 
248     return true;
249 }
250 
251 }  // namespace angle
252