Upstreaming browser/ui/uikit_ui_util from iOS.
[chromium-blink-merge.git] / gpu / config / gpu_info_collector_win.cc
blobf51764da823fb50542f6e6ccdc03fbbca6bce0e3
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "gpu/config/gpu_info_collector.h"
7 // This has to be included before windows.h.
8 #include "third_party/re2/re2/re2.h"
10 #include <windows.h>
11 #include <cfgmgr32.h>
12 #include <d3d9.h>
13 #include <d3d11.h>
14 #include <dxgi.h>
15 #include <setupapi.h>
17 #include "base/command_line.h"
18 #include "base/files/file_enumerator.h"
19 #include "base/files/file_path.h"
20 #include "base/files/file_util.h"
21 #include "base/logging.h"
22 #include "base/message_loop/message_loop.h"
23 #include "base/metrics/field_trial.h"
24 #include "base/metrics/histogram.h"
25 #include "base/scoped_native_library.h"
26 #include "base/strings/string16.h"
27 #include "base/strings/string_number_conversions.h"
28 #include "base/strings/string_util.h"
29 #include "base/strings/stringprintf.h"
30 #include "base/strings/utf_string_conversions.h"
31 #include "base/threading/thread.h"
32 #include "base/threading/worker_pool.h"
33 #include "base/trace_event/trace_event.h"
34 #include "base/win/registry.h"
35 #include "base/win/scoped_com_initializer.h"
36 #include "base/win/scoped_comptr.h"
37 #include "base/win/windows_version.h"
38 #include "third_party/libxml/chromium/libxml_utils.h"
39 #include "ui/gl/gl_implementation.h"
40 #include "ui/gl/gl_surface_egl.h"
42 namespace gpu {
44 namespace {
46 // This must be kept in sync with histograms.xml.
47 enum DisplayLinkInstallationStatus {
48 DISPLAY_LINK_NOT_INSTALLED,
49 DISPLAY_LINK_7_1_OR_EARLIER,
50 DISPLAY_LINK_7_2_OR_LATER,
51 DISPLAY_LINK_INSTALLATION_STATUS_MAX
54 // Returns the display link driver version or an invalid version if it is
55 // not installed.
56 Version DisplayLinkVersion() {
57 base::win::RegKey key;
59 if (key.Open(HKEY_LOCAL_MACHINE, L"SOFTWARE", KEY_READ | KEY_WOW64_64KEY))
60 return Version();
62 if (key.OpenKey(L"DisplayLink", KEY_READ | KEY_WOW64_64KEY))
63 return Version();
65 if (key.OpenKey(L"Core", KEY_READ | KEY_WOW64_64KEY))
66 return Version();
68 base::string16 version;
69 if (key.ReadValue(L"Version", &version))
70 return Version();
72 return Version(base::UTF16ToASCII(version));
75 // Returns whether Lenovo dCute is installed.
76 bool IsLenovoDCuteInstalled() {
77 base::win::RegKey key;
79 if (key.Open(HKEY_LOCAL_MACHINE, L"SOFTWARE", KEY_READ | KEY_WOW64_64KEY))
80 return false;
82 if (key.OpenKey(L"Lenovo", KEY_READ | KEY_WOW64_64KEY))
83 return false;
85 if (key.OpenKey(L"Lenovo dCute", KEY_READ | KEY_WOW64_64KEY))
86 return false;
88 return true;
91 void DeviceIDToVendorAndDevice(const std::wstring& id,
92 uint32* vendor_id,
93 uint32* device_id) {
94 *vendor_id = 0;
95 *device_id = 0;
96 if (id.length() < 21)
97 return;
98 base::string16 vendor_id_string = id.substr(8, 4);
99 base::string16 device_id_string = id.substr(17, 4);
100 int vendor = 0;
101 int device = 0;
102 base::HexStringToInt(base::UTF16ToASCII(vendor_id_string), &vendor);
103 base::HexStringToInt(base::UTF16ToASCII(device_id_string), &device);
104 *vendor_id = vendor;
105 *device_id = device;
108 } // namespace anonymous
110 #if defined(GOOGLE_CHROME_BUILD) && defined(OFFICIAL_BUILD)
111 // This function has a real implementation for official builds that can
112 // be found in src/third_party/amd.
113 void GetAMDVideocardInfo(GPUInfo* gpu_info);
114 #else
115 void GetAMDVideocardInfo(GPUInfo* gpu_info) {
116 DCHECK(gpu_info);
117 return;
119 #endif
121 CollectInfoResult CollectDriverInfoD3D(const std::wstring& device_id,
122 GPUInfo* gpu_info) {
123 TRACE_EVENT0("gpu", "CollectDriverInfoD3D");
125 // Display adapter class GUID from
126 // https://msdn.microsoft.com/en-us/library/windows/hardware/ff553426%28v=vs.85%29.aspx
127 GUID display_class = {0x4d36e968,
128 0xe325,
129 0x11ce,
130 {0xbf, 0xc1, 0x08, 0x00, 0x2b, 0xe1, 0x03, 0x18}};
132 // create device info for the display device
133 HDEVINFO device_info;
134 if (base::win::GetVersion() <= base::win::VERSION_XP) {
135 // Collection of information on all adapters is much slower on XP (almost
136 // 100ms), and not very useful (as it's not going to use the GPU anyway), so
137 // just collect information on the current device. http://crbug.com/456178
138 device_info =
139 SetupDiGetClassDevsW(NULL, device_id.c_str(), NULL,
140 DIGCF_PRESENT | DIGCF_PROFILE | DIGCF_ALLCLASSES);
141 } else {
142 device_info =
143 SetupDiGetClassDevsW(&display_class, NULL, NULL, DIGCF_PRESENT);
145 if (device_info == INVALID_HANDLE_VALUE) {
146 LOG(ERROR) << "Creating device info failed";
147 return kCollectInfoNonFatalFailure;
150 struct GPUDriver {
151 GPUInfo::GPUDevice device;
152 std::string driver_vendor;
153 std::string driver_version;
154 std::string driver_date;
157 std::vector<GPUDriver> drivers;
159 int primary_device = -1;
160 bool found_amd = false;
161 bool found_intel = false;
163 DWORD index = 0;
164 SP_DEVINFO_DATA device_info_data;
165 device_info_data.cbSize = sizeof(device_info_data);
166 while (SetupDiEnumDeviceInfo(device_info, index++, &device_info_data)) {
167 WCHAR value[255];
168 if (SetupDiGetDeviceRegistryPropertyW(device_info,
169 &device_info_data,
170 SPDRP_DRIVER,
171 NULL,
172 reinterpret_cast<PBYTE>(value),
173 sizeof(value),
174 NULL)) {
175 HKEY key;
176 std::wstring driver_key = L"System\\CurrentControlSet\\Control\\Class\\";
177 driver_key += value;
178 LONG result = RegOpenKeyExW(
179 HKEY_LOCAL_MACHINE, driver_key.c_str(), 0, KEY_QUERY_VALUE, &key);
180 if (result == ERROR_SUCCESS) {
181 DWORD dwcb_data = sizeof(value);
182 std::string driver_version;
183 result = RegQueryValueExW(
184 key, L"DriverVersion", NULL, NULL,
185 reinterpret_cast<LPBYTE>(value), &dwcb_data);
186 if (result == ERROR_SUCCESS)
187 driver_version = base::UTF16ToASCII(std::wstring(value));
189 std::string driver_date;
190 dwcb_data = sizeof(value);
191 result = RegQueryValueExW(
192 key, L"DriverDate", NULL, NULL,
193 reinterpret_cast<LPBYTE>(value), &dwcb_data);
194 if (result == ERROR_SUCCESS)
195 driver_date = base::UTF16ToASCII(std::wstring(value));
197 std::string driver_vendor;
198 dwcb_data = sizeof(value);
199 result = RegQueryValueExW(
200 key, L"ProviderName", NULL, NULL,
201 reinterpret_cast<LPBYTE>(value), &dwcb_data);
202 if (result == ERROR_SUCCESS)
203 driver_vendor = base::UTF16ToASCII(std::wstring(value));
205 wchar_t new_device_id[MAX_DEVICE_ID_LEN];
206 CONFIGRET status = CM_Get_Device_ID(
207 device_info_data.DevInst, new_device_id, MAX_DEVICE_ID_LEN, 0);
209 if (status == CR_SUCCESS) {
210 GPUDriver driver;
212 driver.driver_vendor = driver_vendor;
213 driver.driver_version = driver_version;
214 driver.driver_date = driver_date;
215 std::wstring id = new_device_id;
217 if (id.compare(0, device_id.size(), device_id) == 0)
218 primary_device = drivers.size();
220 uint32 vendor_id = 0, device_id = 0;
221 DeviceIDToVendorAndDevice(id, &vendor_id, &device_id);
222 driver.device.vendor_id = vendor_id;
223 driver.device.device_id = device_id;
224 drivers.push_back(driver);
226 if (vendor_id == 0x8086)
227 found_intel = true;
228 if (vendor_id == 0x1002)
229 found_amd = true;
232 RegCloseKey(key);
236 SetupDiDestroyDeviceInfoList(device_info);
237 bool found = false;
238 if (found_amd && found_intel) {
239 // AMD Switchable system found.
240 for (const auto& driver : drivers) {
241 if (driver.device.vendor_id == 0x8086) {
242 gpu_info->gpu = driver.device;
245 if (driver.device.vendor_id == 0x1002) {
246 gpu_info->driver_vendor = driver.driver_vendor;
247 gpu_info->driver_version = driver.driver_version;
248 gpu_info->driver_date = driver.driver_date;
251 GetAMDVideocardInfo(gpu_info);
253 if (!gpu_info->amd_switchable) {
254 // Some machines aren't properly detected as AMD switchable, but count
255 // them anyway.
256 gpu_info->amd_switchable = true;
257 for (const auto& driver : drivers) {
258 if (driver.device.vendor_id == 0x1002) {
259 gpu_info->gpu = driver.device;
260 } else {
261 gpu_info->secondary_gpus.push_back(driver.device);
265 found = true;
266 } else {
267 for (size_t i = 0; i < drivers.size(); ++i) {
268 const GPUDriver& driver = drivers[i];
269 if (static_cast<int>(i) == primary_device) {
270 found = true;
271 gpu_info->gpu = driver.device;
272 gpu_info->driver_vendor = driver.driver_vendor;
273 gpu_info->driver_version = driver.driver_version;
274 gpu_info->driver_date = driver.driver_date;
275 } else {
276 gpu_info->secondary_gpus.push_back(driver.device);
281 return found ? kCollectInfoSuccess : kCollectInfoNonFatalFailure;
284 CollectInfoResult CollectContextGraphicsInfo(GPUInfo* gpu_info) {
285 TRACE_EVENT0("gpu", "CollectGraphicsInfo");
287 DCHECK(gpu_info);
289 if (base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kUseGL)) {
290 std::string requested_implementation_name =
291 base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
292 switches::kUseGL);
293 if (requested_implementation_name == "swiftshader") {
294 gpu_info->software_rendering = true;
295 gpu_info->context_info_state = kCollectInfoNonFatalFailure;
296 return kCollectInfoNonFatalFailure;
300 CollectInfoResult result = CollectGraphicsInfoGL(gpu_info);
301 if (result != kCollectInfoSuccess) {
302 gpu_info->context_info_state = result;
303 return result;
306 // ANGLE's renderer strings are of the form:
307 // ANGLE (<adapter_identifier> Direct3D<version> vs_x_x ps_x_x)
308 std::string direct3d_version;
309 int vertex_shader_major_version = 0;
310 int vertex_shader_minor_version = 0;
311 int pixel_shader_major_version = 0;
312 int pixel_shader_minor_version = 0;
313 gpu_info->adapter_luid = 0;
314 if (RE2::FullMatch(gpu_info->gl_renderer,
315 "ANGLE \\(.*\\)") &&
316 RE2::PartialMatch(gpu_info->gl_renderer,
317 " Direct3D(\\w+)",
318 &direct3d_version) &&
319 RE2::PartialMatch(gpu_info->gl_renderer,
320 " vs_(\\d+)_(\\d+)",
321 &vertex_shader_major_version,
322 &vertex_shader_minor_version) &&
323 RE2::PartialMatch(gpu_info->gl_renderer,
324 " ps_(\\d+)_(\\d+)",
325 &pixel_shader_major_version,
326 &pixel_shader_minor_version)) {
327 gpu_info->can_lose_context = direct3d_version == "9";
328 gpu_info->vertex_shader_version =
329 base::StringPrintf("%d.%d",
330 vertex_shader_major_version,
331 vertex_shader_minor_version);
332 gpu_info->pixel_shader_version =
333 base::StringPrintf("%d.%d",
334 pixel_shader_major_version,
335 pixel_shader_minor_version);
337 // ANGLE's EGL vendor strings are of the form:
338 // Google, Inc. (adapter LUID: 0123456789ABCDEF)
339 // The LUID is optional and identifies the GPU adapter ANGLE is using.
340 const char* egl_vendor = eglQueryString(
341 gfx::GLSurfaceEGL::GetHardwareDisplay(),
342 EGL_VENDOR);
343 RE2::PartialMatch(egl_vendor,
344 " \\(adapter LUID: ([0-9A-Fa-f]{16})\\)",
345 RE2::Hex(&gpu_info->adapter_luid));
347 // DirectX diagnostics are collected asynchronously because it takes a
348 // couple of seconds.
349 } else {
350 gpu_info->dx_diagnostics_info_state = kCollectInfoNonFatalFailure;
353 gpu_info->context_info_state = kCollectInfoSuccess;
354 return kCollectInfoSuccess;
357 CollectInfoResult CollectGpuID(uint32* vendor_id, uint32* device_id) {
358 DCHECK(vendor_id && device_id);
359 *vendor_id = 0;
360 *device_id = 0;
362 // Taken from http://developer.nvidia.com/object/device_ids.html
363 DISPLAY_DEVICE dd;
364 dd.cb = sizeof(DISPLAY_DEVICE);
365 std::wstring id;
366 for (int i = 0; EnumDisplayDevices(NULL, i, &dd, 0); ++i) {
367 if (dd.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE) {
368 id = dd.DeviceID;
369 break;
373 if (id.length() > 20) {
374 DeviceIDToVendorAndDevice(id, vendor_id, device_id);
375 if (*vendor_id != 0 && *device_id != 0)
376 return kCollectInfoSuccess;
378 return kCollectInfoNonFatalFailure;
381 CollectInfoResult CollectBasicGraphicsInfo(GPUInfo* gpu_info) {
382 TRACE_EVENT0("gpu", "CollectPreliminaryGraphicsInfo");
384 DCHECK(gpu_info);
386 // nvd3d9wrap.dll is loaded into all processes when Optimus is enabled.
387 HMODULE nvd3d9wrap = GetModuleHandleW(L"nvd3d9wrap.dll");
388 gpu_info->optimus = nvd3d9wrap != NULL;
390 gpu_info->lenovo_dcute = IsLenovoDCuteInstalled();
392 gpu_info->display_link_version = DisplayLinkVersion();
394 if (!gpu_info->display_link_version .IsValid()) {
395 UMA_HISTOGRAM_ENUMERATION("GPU.DisplayLinkInstallationStatus",
396 DISPLAY_LINK_NOT_INSTALLED,
397 DISPLAY_LINK_INSTALLATION_STATUS_MAX);
398 } else if (gpu_info->display_link_version.IsOlderThan("7.2")) {
399 UMA_HISTOGRAM_ENUMERATION("GPU.DisplayLinkInstallationStatus",
400 DISPLAY_LINK_7_1_OR_EARLIER,
401 DISPLAY_LINK_INSTALLATION_STATUS_MAX);
402 } else {
403 UMA_HISTOGRAM_ENUMERATION("GPU.DisplayLinkInstallationStatus",
404 DISPLAY_LINK_7_2_OR_LATER,
405 DISPLAY_LINK_INSTALLATION_STATUS_MAX);
408 // Taken from http://developer.nvidia.com/object/device_ids.html
409 DISPLAY_DEVICE dd;
410 dd.cb = sizeof(DISPLAY_DEVICE);
411 std::wstring id;
412 for (int i = 0; EnumDisplayDevices(NULL, i, &dd, 0); ++i) {
413 if (dd.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE) {
414 id = dd.DeviceID;
415 break;
419 if (id.length() <= 20) {
420 gpu_info->basic_info_state = kCollectInfoNonFatalFailure;
421 return kCollectInfoNonFatalFailure;
424 DeviceIDToVendorAndDevice(id, &gpu_info->gpu.vendor_id,
425 &gpu_info->gpu.device_id);
426 // TODO(zmo): we only need to call CollectDriverInfoD3D() if we use ANGLE.
427 if (!CollectDriverInfoD3D(id, gpu_info)) {
428 gpu_info->basic_info_state = kCollectInfoNonFatalFailure;
429 return kCollectInfoNonFatalFailure;
432 gpu_info->basic_info_state = kCollectInfoSuccess;
433 return kCollectInfoSuccess;
436 CollectInfoResult CollectDriverInfoGL(GPUInfo* gpu_info) {
437 TRACE_EVENT0("gpu", "CollectDriverInfoGL");
439 if (!gpu_info->driver_version.empty())
440 return kCollectInfoSuccess;
442 bool parsed = RE2::PartialMatch(
443 gpu_info->gl_version, "([\\d\\.]+)$", &gpu_info->driver_version);
444 return parsed ? kCollectInfoSuccess : kCollectInfoNonFatalFailure;
447 void MergeGPUInfo(GPUInfo* basic_gpu_info,
448 const GPUInfo& context_gpu_info) {
449 DCHECK(basic_gpu_info);
451 if (context_gpu_info.software_rendering) {
452 basic_gpu_info->software_rendering = true;
453 return;
456 // Track D3D Shader Model (if available)
457 const std::string& shader_version =
458 context_gpu_info.vertex_shader_version;
460 // Only gather if this is the first time we're seeing
461 // a non-empty shader version string.
462 if (!shader_version.empty() &&
463 basic_gpu_info->vertex_shader_version.empty()) {
465 // Note: do not reorder, used by UMA_HISTOGRAM below
466 enum ShaderModel {
467 SHADER_MODEL_UNKNOWN,
468 SHADER_MODEL_2_0,
469 SHADER_MODEL_3_0,
470 SHADER_MODEL_4_0,
471 SHADER_MODEL_4_1,
472 SHADER_MODEL_5_0,
473 NUM_SHADER_MODELS
476 ShaderModel shader_model = SHADER_MODEL_UNKNOWN;
478 if (shader_version == "5.0") {
479 shader_model = SHADER_MODEL_5_0;
480 } else if (shader_version == "4.1") {
481 shader_model = SHADER_MODEL_4_1;
482 } else if (shader_version == "4.0") {
483 shader_model = SHADER_MODEL_4_0;
484 } else if (shader_version == "3.0") {
485 shader_model = SHADER_MODEL_3_0;
486 } else if (shader_version == "2.0") {
487 shader_model = SHADER_MODEL_2_0;
490 UMA_HISTOGRAM_ENUMERATION("GPU.D3DShaderModel",
491 shader_model,
492 NUM_SHADER_MODELS);
495 MergeGPUInfoGL(basic_gpu_info, context_gpu_info);
497 basic_gpu_info->dx_diagnostics_info_state =
498 context_gpu_info.dx_diagnostics_info_state;
499 basic_gpu_info->dx_diagnostics = context_gpu_info.dx_diagnostics;
502 } // namespace gpu