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"
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"
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
))
60 if (!base::StringToDouble(score_string
, &score
))
63 return static_cast<float>(score
);
66 // Returns the display link driver version or an invalid version if it is
68 Version
DisplayLinkVersion() {
69 base::win::RegKey key
;
71 if (key
.Open(HKEY_LOCAL_MACHINE
, L
"SOFTWARE", KEY_READ
| KEY_WOW64_64KEY
))
74 if (key
.OpenKey(L
"DisplayLink", KEY_READ
| KEY_WOW64_64KEY
))
77 if (key
.OpenKey(L
"Core", KEY_READ
| KEY_WOW64_64KEY
))
80 base::string16 version
;
81 if (key
.ReadValue(L
"Version", &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
))
94 if (key
.OpenKey(L
"Lenovo", KEY_READ
| KEY_WOW64_64KEY
))
97 if (key
.OpenKey(L
"Lenovo dCute", KEY_READ
| KEY_WOW64_64KEY
))
103 void DeviceIDToVendorAndDevice(const std::wstring
& id
,
108 if (id
.length() < 21)
110 base::string16 vendor_id_string
= id
.substr(8, 4);
111 base::string16 device_id_string
= id
.substr(17, 4);
114 base::HexStringToInt(base::UTF16ToASCII(vendor_id_string
), &vendor
);
115 base::HexStringToInt(base::UTF16ToASCII(device_id_string
), &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
);
127 void GetAMDVideocardInfo(GPUInfo
* gpu_info
) {
133 CollectInfoResult
CollectDriverInfoD3D(const std::wstring
& device_id
,
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,
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
151 SetupDiGetClassDevsW(NULL
, device_id
.c_str(), NULL
,
152 DIGCF_PRESENT
| DIGCF_PROFILE
| DIGCF_ALLCLASSES
);
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
;
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;
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
)) {
180 if (SetupDiGetDeviceRegistryPropertyW(device_info
,
184 reinterpret_cast<PBYTE
>(value
),
188 std::wstring driver_key
= L
"System\\CurrentControlSet\\Control\\Class\\";
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
) {
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)
240 if (vendor_id
== 0x1002)
248 SetupDiDestroyDeviceInfoList(device_info
);
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
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
;
273 gpu_info
->secondary_gpus
.push_back(driver
.device
);
279 for (size_t i
= 0; i
< drivers
.size(); ++i
) {
280 const GPUDriver
& driver
= drivers
[i
];
281 if (static_cast<int>(i
) == primary_device
) {
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
;
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");
301 if (base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kUseGL
)) {
302 std::string requested_implementation_name
=
303 base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
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
;
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
,
328 RE2::PartialMatch(gpu_info
->gl_renderer
,
330 &direct3d_version
) &&
331 RE2::PartialMatch(gpu_info
->gl_renderer
,
333 &vertex_shader_major_version
,
334 &vertex_shader_minor_version
) &&
335 RE2::PartialMatch(gpu_info
->gl_renderer
,
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(),
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.
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
);
374 // Taken from http://developer.nvidia.com/object/device_ids.html
376 dd
.cb
= sizeof(DISPLAY_DEVICE
);
378 for (int i
= 0; EnumDisplayDevices(NULL
, i
, &dd
, 0); ++i
) {
379 if (dd
.StateFlags
& DISPLAY_DEVICE_PRIMARY_DEVICE
) {
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");
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
);
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
422 dd
.cb
= sizeof(DISPLAY_DEVICE
);
424 for (int i
= 0; EnumDisplayDevices(NULL
, i
, &dd
, 0); ++i
) {
425 if (dd
.StateFlags
& DISPLAY_DEVICE_PRIMARY_DEVICE
) {
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;
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
479 SHADER_MODEL_UNKNOWN
,
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",
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
;