Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / gpu / config / gpu_info_collector_win.cc
blobcc935ea9b87ead159d68cf58b5c66ae46943a305
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 float ReadXMLFloatValue(XmlReader* reader) {
55 std::string score_string;
56 if (!reader->ReadElementContent(&score_string))
57 return 0.0;
59 double score;
60 if (!base::StringToDouble(score_string, &score))
61 return 0.0;
63 return static_cast<float>(score);
66 // Returns the display link driver version or an invalid version if it is
67 // not installed.
68 Version DisplayLinkVersion() {
69 base::win::RegKey key;
71 if (key.Open(HKEY_LOCAL_MACHINE, L"SOFTWARE", KEY_READ | KEY_WOW64_64KEY))
72 return Version();
74 if (key.OpenKey(L"DisplayLink", KEY_READ | KEY_WOW64_64KEY))
75 return Version();
77 if (key.OpenKey(L"Core", KEY_READ | KEY_WOW64_64KEY))
78 return Version();
80 base::string16 version;
81 if (key.ReadValue(L"Version", &version))
82 return Version();
84 return Version(base::UTF16ToASCII(version));
87 // Returns whether Lenovo dCute is installed.
88 bool IsLenovoDCuteInstalled() {
89 base::win::RegKey key;
91 if (key.Open(HKEY_LOCAL_MACHINE, L"SOFTWARE", KEY_READ | KEY_WOW64_64KEY))
92 return false;
94 if (key.OpenKey(L"Lenovo", KEY_READ | KEY_WOW64_64KEY))
95 return false;
97 if (key.OpenKey(L"Lenovo dCute", KEY_READ | KEY_WOW64_64KEY))
98 return false;
100 return true;
103 void DeviceIDToVendorAndDevice(const std::wstring& id,
104 uint32* vendor_id,
105 uint32* device_id) {
106 *vendor_id = 0;
107 *device_id = 0;
108 if (id.length() < 21)
109 return;
110 base::string16 vendor_id_string = id.substr(8, 4);
111 base::string16 device_id_string = id.substr(17, 4);
112 int vendor = 0;
113 int device = 0;
114 base::HexStringToInt(base::UTF16ToASCII(vendor_id_string), &vendor);
115 base::HexStringToInt(base::UTF16ToASCII(device_id_string), &device);
116 *vendor_id = vendor;
117 *device_id = device;
120 } // namespace anonymous
122 #if defined(GOOGLE_CHROME_BUILD) && defined(OFFICIAL_BUILD)
123 // This function has a real implementation for official builds that can
124 // be found in src/third_party/amd.
125 void GetAMDVideocardInfo(GPUInfo* gpu_info);
126 #else
127 void GetAMDVideocardInfo(GPUInfo* gpu_info) {
128 DCHECK(gpu_info);
129 return;
131 #endif
133 CollectInfoResult CollectDriverInfoD3D(const std::wstring& device_id,
134 GPUInfo* gpu_info) {
135 TRACE_EVENT0("gpu", "CollectDriverInfoD3D");
137 // Display adapter class GUID from
138 // https://msdn.microsoft.com/en-us/library/windows/hardware/ff553426%28v=vs.85%29.aspx
139 GUID display_class = {0x4d36e968,
140 0xe325,
141 0x11ce,
142 {0xbf, 0xc1, 0x08, 0x00, 0x2b, 0xe1, 0x03, 0x18}};
144 // create device info for the display device
145 HDEVINFO device_info;
146 if (base::win::GetVersion() <= base::win::VERSION_XP) {
147 // Collection of information on all adapters is much slower on XP (almost
148 // 100ms), and not very useful (as it's not going to use the GPU anyway), so
149 // just collect information on the current device. http://crbug.com/456178
150 device_info =
151 SetupDiGetClassDevsW(NULL, device_id.c_str(), NULL,
152 DIGCF_PRESENT | DIGCF_PROFILE | DIGCF_ALLCLASSES);
153 } else {
154 device_info =
155 SetupDiGetClassDevsW(&display_class, NULL, NULL, DIGCF_PRESENT);
157 if (device_info == INVALID_HANDLE_VALUE) {
158 LOG(ERROR) << "Creating device info failed";
159 return kCollectInfoNonFatalFailure;
162 struct GPUDriver {
163 GPUInfo::GPUDevice device;
164 std::string driver_vendor;
165 std::string driver_version;
166 std::string driver_date;
169 std::vector<GPUDriver> drivers;
171 int primary_device = -1;
172 bool found_amd = false;
173 bool found_intel = false;
175 DWORD index = 0;
176 SP_DEVINFO_DATA device_info_data;
177 device_info_data.cbSize = sizeof(device_info_data);
178 while (SetupDiEnumDeviceInfo(device_info, index++, &device_info_data)) {
179 WCHAR value[255];
180 if (SetupDiGetDeviceRegistryPropertyW(device_info,
181 &device_info_data,
182 SPDRP_DRIVER,
183 NULL,
184 reinterpret_cast<PBYTE>(value),
185 sizeof(value),
186 NULL)) {
187 HKEY key;
188 std::wstring driver_key = L"System\\CurrentControlSet\\Control\\Class\\";
189 driver_key += value;
190 LONG result = RegOpenKeyExW(
191 HKEY_LOCAL_MACHINE, driver_key.c_str(), 0, KEY_QUERY_VALUE, &key);
192 if (result == ERROR_SUCCESS) {
193 DWORD dwcb_data = sizeof(value);
194 std::string driver_version;
195 result = RegQueryValueExW(
196 key, L"DriverVersion", NULL, NULL,
197 reinterpret_cast<LPBYTE>(value), &dwcb_data);
198 if (result == ERROR_SUCCESS)
199 driver_version = base::UTF16ToASCII(std::wstring(value));
201 std::string driver_date;
202 dwcb_data = sizeof(value);
203 result = RegQueryValueExW(
204 key, L"DriverDate", NULL, NULL,
205 reinterpret_cast<LPBYTE>(value), &dwcb_data);
206 if (result == ERROR_SUCCESS)
207 driver_date = base::UTF16ToASCII(std::wstring(value));
209 std::string driver_vendor;
210 dwcb_data = sizeof(value);
211 result = RegQueryValueExW(
212 key, L"ProviderName", NULL, NULL,
213 reinterpret_cast<LPBYTE>(value), &dwcb_data);
214 if (result == ERROR_SUCCESS)
215 driver_vendor = base::UTF16ToASCII(std::wstring(value));
217 wchar_t new_device_id[MAX_DEVICE_ID_LEN];
218 CONFIGRET status = CM_Get_Device_ID(
219 device_info_data.DevInst, new_device_id, MAX_DEVICE_ID_LEN, 0);
221 if (status == CR_SUCCESS) {
222 GPUDriver driver;
224 driver.driver_vendor = driver_vendor;
225 driver.driver_version = driver_version;
226 driver.driver_date = driver_date;
227 std::wstring id = new_device_id;
229 if (id.compare(0, device_id.size(), device_id) == 0)
230 primary_device = drivers.size();
232 uint32 vendor_id = 0, device_id = 0;
233 DeviceIDToVendorAndDevice(id, &vendor_id, &device_id);
234 driver.device.vendor_id = vendor_id;
235 driver.device.device_id = device_id;
236 drivers.push_back(driver);
238 if (vendor_id == 0x8086)
239 found_intel = true;
240 if (vendor_id == 0x1002)
241 found_amd = true;
244 RegCloseKey(key);
248 SetupDiDestroyDeviceInfoList(device_info);
249 bool found = false;
250 if (found_amd && found_intel) {
251 // AMD Switchable system found.
252 for (const auto& driver : drivers) {
253 if (driver.device.vendor_id == 0x8086) {
254 gpu_info->gpu = driver.device;
257 if (driver.device.vendor_id == 0x1002) {
258 gpu_info->driver_vendor = driver.driver_vendor;
259 gpu_info->driver_version = driver.driver_version;
260 gpu_info->driver_date = driver.driver_date;
263 GetAMDVideocardInfo(gpu_info);
265 if (!gpu_info->amd_switchable) {
266 // Some machines aren't properly detected as AMD switchable, but count
267 // them anyway.
268 gpu_info->amd_switchable = true;
269 for (const auto& driver : drivers) {
270 if (driver.device.vendor_id == 0x1002) {
271 gpu_info->gpu = driver.device;
272 } else {
273 gpu_info->secondary_gpus.push_back(driver.device);
277 found = true;
278 } else {
279 for (size_t i = 0; i < drivers.size(); ++i) {
280 const GPUDriver& driver = drivers[i];
281 if (static_cast<int>(i) == primary_device) {
282 found = true;
283 gpu_info->gpu = driver.device;
284 gpu_info->driver_vendor = driver.driver_vendor;
285 gpu_info->driver_version = driver.driver_version;
286 gpu_info->driver_date = driver.driver_date;
287 } else {
288 gpu_info->secondary_gpus.push_back(driver.device);
293 return found ? kCollectInfoSuccess : kCollectInfoNonFatalFailure;
296 CollectInfoResult CollectContextGraphicsInfo(GPUInfo* gpu_info) {
297 TRACE_EVENT0("gpu", "CollectGraphicsInfo");
299 DCHECK(gpu_info);
301 if (base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kUseGL)) {
302 std::string requested_implementation_name =
303 base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
304 switches::kUseGL);
305 if (requested_implementation_name == "swiftshader") {
306 gpu_info->software_rendering = true;
307 gpu_info->context_info_state = kCollectInfoNonFatalFailure;
308 return kCollectInfoNonFatalFailure;
312 CollectInfoResult result = CollectGraphicsInfoGL(gpu_info);
313 if (result != kCollectInfoSuccess) {
314 gpu_info->context_info_state = result;
315 return result;
318 // ANGLE's renderer strings are of the form:
319 // ANGLE (<adapter_identifier> Direct3D<version> vs_x_x ps_x_x)
320 std::string direct3d_version;
321 int vertex_shader_major_version = 0;
322 int vertex_shader_minor_version = 0;
323 int pixel_shader_major_version = 0;
324 int pixel_shader_minor_version = 0;
325 gpu_info->adapter_luid = 0;
326 if (RE2::FullMatch(gpu_info->gl_renderer,
327 "ANGLE \\(.*\\)") &&
328 RE2::PartialMatch(gpu_info->gl_renderer,
329 " Direct3D(\\w+)",
330 &direct3d_version) &&
331 RE2::PartialMatch(gpu_info->gl_renderer,
332 " vs_(\\d+)_(\\d+)",
333 &vertex_shader_major_version,
334 &vertex_shader_minor_version) &&
335 RE2::PartialMatch(gpu_info->gl_renderer,
336 " ps_(\\d+)_(\\d+)",
337 &pixel_shader_major_version,
338 &pixel_shader_minor_version)) {
339 gpu_info->can_lose_context = direct3d_version == "9";
340 gpu_info->vertex_shader_version =
341 base::StringPrintf("%d.%d",
342 vertex_shader_major_version,
343 vertex_shader_minor_version);
344 gpu_info->pixel_shader_version =
345 base::StringPrintf("%d.%d",
346 pixel_shader_major_version,
347 pixel_shader_minor_version);
349 // ANGLE's EGL vendor strings are of the form:
350 // Google, Inc. (adapter LUID: 0123456789ABCDEF)
351 // The LUID is optional and identifies the GPU adapter ANGLE is using.
352 const char* egl_vendor = eglQueryString(
353 gfx::GLSurfaceEGL::GetHardwareDisplay(),
354 EGL_VENDOR);
355 RE2::PartialMatch(egl_vendor,
356 " \\(adapter LUID: ([0-9A-Fa-f]{16})\\)",
357 RE2::Hex(&gpu_info->adapter_luid));
359 // DirectX diagnostics are collected asynchronously because it takes a
360 // couple of seconds.
361 } else {
362 gpu_info->dx_diagnostics_info_state = kCollectInfoNonFatalFailure;
365 gpu_info->context_info_state = kCollectInfoSuccess;
366 return kCollectInfoSuccess;
369 CollectInfoResult CollectGpuID(uint32* vendor_id, uint32* device_id) {
370 DCHECK(vendor_id && device_id);
371 *vendor_id = 0;
372 *device_id = 0;
374 // Taken from http://developer.nvidia.com/object/device_ids.html
375 DISPLAY_DEVICE dd;
376 dd.cb = sizeof(DISPLAY_DEVICE);
377 std::wstring id;
378 for (int i = 0; EnumDisplayDevices(NULL, i, &dd, 0); ++i) {
379 if (dd.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE) {
380 id = dd.DeviceID;
381 break;
385 if (id.length() > 20) {
386 DeviceIDToVendorAndDevice(id, vendor_id, device_id);
387 if (*vendor_id != 0 && *device_id != 0)
388 return kCollectInfoSuccess;
390 return kCollectInfoNonFatalFailure;
393 CollectInfoResult CollectBasicGraphicsInfo(GPUInfo* gpu_info) {
394 TRACE_EVENT0("gpu", "CollectPreliminaryGraphicsInfo");
396 DCHECK(gpu_info);
398 // nvd3d9wrap.dll is loaded into all processes when Optimus is enabled.
399 HMODULE nvd3d9wrap = GetModuleHandleW(L"nvd3d9wrap.dll");
400 gpu_info->optimus = nvd3d9wrap != NULL;
402 gpu_info->lenovo_dcute = IsLenovoDCuteInstalled();
404 gpu_info->display_link_version = DisplayLinkVersion();
406 if (!gpu_info->display_link_version .IsValid()) {
407 UMA_HISTOGRAM_ENUMERATION("GPU.DisplayLinkInstallationStatus",
408 DISPLAY_LINK_NOT_INSTALLED,
409 DISPLAY_LINK_INSTALLATION_STATUS_MAX);
410 } else if (gpu_info->display_link_version.IsOlderThan("7.2")) {
411 UMA_HISTOGRAM_ENUMERATION("GPU.DisplayLinkInstallationStatus",
412 DISPLAY_LINK_7_1_OR_EARLIER,
413 DISPLAY_LINK_INSTALLATION_STATUS_MAX);
414 } else {
415 UMA_HISTOGRAM_ENUMERATION("GPU.DisplayLinkInstallationStatus",
416 DISPLAY_LINK_7_2_OR_LATER,
417 DISPLAY_LINK_INSTALLATION_STATUS_MAX);
420 // Taken from http://developer.nvidia.com/object/device_ids.html
421 DISPLAY_DEVICE dd;
422 dd.cb = sizeof(DISPLAY_DEVICE);
423 std::wstring id;
424 for (int i = 0; EnumDisplayDevices(NULL, i, &dd, 0); ++i) {
425 if (dd.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE) {
426 id = dd.DeviceID;
427 break;
431 if (id.length() <= 20) {
432 gpu_info->basic_info_state = kCollectInfoNonFatalFailure;
433 return kCollectInfoNonFatalFailure;
436 DeviceIDToVendorAndDevice(id, &gpu_info->gpu.vendor_id,
437 &gpu_info->gpu.device_id);
438 // TODO(zmo): we only need to call CollectDriverInfoD3D() if we use ANGLE.
439 if (!CollectDriverInfoD3D(id, gpu_info)) {
440 gpu_info->basic_info_state = kCollectInfoNonFatalFailure;
441 return kCollectInfoNonFatalFailure;
444 gpu_info->basic_info_state = kCollectInfoSuccess;
445 return kCollectInfoSuccess;
448 CollectInfoResult CollectDriverInfoGL(GPUInfo* gpu_info) {
449 TRACE_EVENT0("gpu", "CollectDriverInfoGL");
451 if (!gpu_info->driver_version.empty())
452 return kCollectInfoSuccess;
454 bool parsed = RE2::PartialMatch(
455 gpu_info->gl_version, "([\\d\\.]+)$", &gpu_info->driver_version);
456 return parsed ? kCollectInfoSuccess : kCollectInfoNonFatalFailure;
459 void MergeGPUInfo(GPUInfo* basic_gpu_info,
460 const GPUInfo& context_gpu_info) {
461 DCHECK(basic_gpu_info);
463 if (context_gpu_info.software_rendering) {
464 basic_gpu_info->software_rendering = true;
465 return;
468 // Track D3D Shader Model (if available)
469 const std::string& shader_version =
470 context_gpu_info.vertex_shader_version;
472 // Only gather if this is the first time we're seeing
473 // a non-empty shader version string.
474 if (!shader_version.empty() &&
475 basic_gpu_info->vertex_shader_version.empty()) {
477 // Note: do not reorder, used by UMA_HISTOGRAM below
478 enum ShaderModel {
479 SHADER_MODEL_UNKNOWN,
480 SHADER_MODEL_2_0,
481 SHADER_MODEL_3_0,
482 SHADER_MODEL_4_0,
483 SHADER_MODEL_4_1,
484 SHADER_MODEL_5_0,
485 NUM_SHADER_MODELS
488 ShaderModel shader_model = SHADER_MODEL_UNKNOWN;
490 if (shader_version == "5.0") {
491 shader_model = SHADER_MODEL_5_0;
492 } else if (shader_version == "4.1") {
493 shader_model = SHADER_MODEL_4_1;
494 } else if (shader_version == "4.0") {
495 shader_model = SHADER_MODEL_4_0;
496 } else if (shader_version == "3.0") {
497 shader_model = SHADER_MODEL_3_0;
498 } else if (shader_version == "2.0") {
499 shader_model = SHADER_MODEL_2_0;
502 UMA_HISTOGRAM_ENUMERATION("GPU.D3DShaderModel",
503 shader_model,
504 NUM_SHADER_MODELS);
507 MergeGPUInfoGL(basic_gpu_info, context_gpu_info);
509 basic_gpu_info->dx_diagnostics_info_state =
510 context_gpu_info.dx_diagnostics_info_state;
511 basic_gpu_info->dx_diagnostics = context_gpu_info.dx_diagnostics;
514 } // namespace gpu