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 // Returns the display link driver version or an invalid version if it is
56 Version
DisplayLinkVersion() {
57 base::win::RegKey key
;
59 if (key
.Open(HKEY_LOCAL_MACHINE
, L
"SOFTWARE", KEY_READ
| KEY_WOW64_64KEY
))
62 if (key
.OpenKey(L
"DisplayLink", KEY_READ
| KEY_WOW64_64KEY
))
65 if (key
.OpenKey(L
"Core", KEY_READ
| KEY_WOW64_64KEY
))
68 base::string16 version
;
69 if (key
.ReadValue(L
"Version", &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
))
82 if (key
.OpenKey(L
"Lenovo", KEY_READ
| KEY_WOW64_64KEY
))
85 if (key
.OpenKey(L
"Lenovo dCute", KEY_READ
| KEY_WOW64_64KEY
))
91 void DeviceIDToVendorAndDevice(const std::wstring
& id
,
98 base::string16 vendor_id_string
= id
.substr(8, 4);
99 base::string16 device_id_string
= id
.substr(17, 4);
102 base::HexStringToInt(base::UTF16ToASCII(vendor_id_string
), &vendor
);
103 base::HexStringToInt(base::UTF16ToASCII(device_id_string
), &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
);
115 void GetAMDVideocardInfo(GPUInfo
* gpu_info
) {
121 CollectInfoResult
CollectDriverInfoD3D(const std::wstring
& device_id
,
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,
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
139 SetupDiGetClassDevsW(NULL
, device_id
.c_str(), NULL
,
140 DIGCF_PRESENT
| DIGCF_PROFILE
| DIGCF_ALLCLASSES
);
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
;
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;
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
)) {
168 if (SetupDiGetDeviceRegistryPropertyW(device_info
,
172 reinterpret_cast<PBYTE
>(value
),
176 std::wstring driver_key
= L
"System\\CurrentControlSet\\Control\\Class\\";
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
) {
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)
228 if (vendor_id
== 0x1002)
236 SetupDiDestroyDeviceInfoList(device_info
);
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
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
;
261 gpu_info
->secondary_gpus
.push_back(driver
.device
);
267 for (size_t i
= 0; i
< drivers
.size(); ++i
) {
268 const GPUDriver
& driver
= drivers
[i
];
269 if (static_cast<int>(i
) == primary_device
) {
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
;
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");
289 if (base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kUseGL
)) {
290 std::string requested_implementation_name
=
291 base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
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
;
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
,
316 RE2::PartialMatch(gpu_info
->gl_renderer
,
318 &direct3d_version
) &&
319 RE2::PartialMatch(gpu_info
->gl_renderer
,
321 &vertex_shader_major_version
,
322 &vertex_shader_minor_version
) &&
323 RE2::PartialMatch(gpu_info
->gl_renderer
,
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(),
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.
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
);
362 // Taken from http://developer.nvidia.com/object/device_ids.html
364 dd
.cb
= sizeof(DISPLAY_DEVICE
);
366 for (int i
= 0; EnumDisplayDevices(NULL
, i
, &dd
, 0); ++i
) {
367 if (dd
.StateFlags
& DISPLAY_DEVICE_PRIMARY_DEVICE
) {
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");
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
);
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
410 dd
.cb
= sizeof(DISPLAY_DEVICE
);
412 for (int i
= 0; EnumDisplayDevices(NULL
, i
, &dd
, 0); ++i
) {
413 if (dd
.StateFlags
& DISPLAY_DEVICE_PRIMARY_DEVICE
) {
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;
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
467 SHADER_MODEL_UNKNOWN
,
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",
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
;