• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
4 
5 #include "gpu/config/gpu_info_collector.h"
6 
7 // This has to be included before windows.h.
8 #include "third_party/re2/re2/re2.h"
9 
10 #include <windows.h>
11 #include <d3d9.h>
12 #include <d3d11.h>
13 #include <dxgi.h>
14 #include <setupapi.h>
15 
16 #include "base/command_line.h"
17 #include "base/debug/trace_event.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/win/registry.h"
34 #include "base/win/scoped_com_initializer.h"
35 #include "base/win/scoped_comptr.h"
36 #include "base/win/windows_version.h"
37 #include "third_party/libxml/chromium/libxml_utils.h"
38 #include "ui/gl/gl_implementation.h"
39 #include "ui/gl/gl_surface_egl.h"
40 
41 namespace gpu {
42 
43 namespace {
44 
45 // This must be kept in sync with histograms.xml.
46 enum DisplayLinkInstallationStatus {
47   DISPLAY_LINK_NOT_INSTALLED,
48   DISPLAY_LINK_7_1_OR_EARLIER,
49   DISPLAY_LINK_7_2_OR_LATER,
50   DISPLAY_LINK_INSTALLATION_STATUS_MAX
51 };
52 
ReadXMLFloatValue(XmlReader * reader)53 float ReadXMLFloatValue(XmlReader* reader) {
54   std::string score_string;
55   if (!reader->ReadElementContent(&score_string))
56     return 0.0;
57 
58   double score;
59   if (!base::StringToDouble(score_string, &score))
60     return 0.0;
61 
62   return static_cast<float>(score);
63 }
64 
RetrieveGpuPerformanceStats()65 GpuPerformanceStats RetrieveGpuPerformanceStats() {
66   TRACE_EVENT0("gpu", "RetrieveGpuPerformanceStats");
67 
68   // If the user re-runs the assessment without restarting, the COM API
69   // returns WINSAT_ASSESSMENT_STATE_NOT_AVAILABLE. Because of that and
70   // http://crbug.com/124325, read the assessment result files directly.
71   GpuPerformanceStats stats;
72 
73   // Get path to WinSAT results files.
74   wchar_t winsat_results_path[MAX_PATH];
75   DWORD size = ExpandEnvironmentStrings(
76       L"%WinDir%\\Performance\\WinSAT\\DataStore\\",
77       winsat_results_path, MAX_PATH);
78   if (size == 0 || size > MAX_PATH) {
79     LOG(ERROR) << "The path to the WinSAT results is too long: "
80                << size << " chars.";
81     return stats;
82   }
83 
84   // Find most recent formal assessment results.
85   base::FileEnumerator file_enumerator(
86       base::FilePath(winsat_results_path),
87       false,  // not recursive
88       base::FileEnumerator::FILES,
89       FILE_PATH_LITERAL("* * Formal.Assessment (*).WinSAT.xml"));
90 
91   base::FilePath current_results;
92   for (base::FilePath results = file_enumerator.Next(); !results.empty();
93        results = file_enumerator.Next()) {
94     // The filenames start with the date and time as yyyy-mm-dd hh.mm.ss.xxx,
95     // so the greatest file lexicographically is also the most recent file.
96     if (base::FilePath::CompareLessIgnoreCase(current_results.value(),
97                                               results.value()))
98       current_results = results;
99   }
100 
101   std::string current_results_string = current_results.MaybeAsASCII();
102   if (current_results_string.empty())
103     return stats;
104 
105   // Get relevant scores from results file. XML schema at:
106   // http://msdn.microsoft.com/en-us/library/windows/desktop/aa969210.aspx
107   XmlReader reader;
108   if (!reader.LoadFile(current_results_string)) {
109     LOG(ERROR) << "Could not open WinSAT results file.";
110     return stats;
111   }
112   // Descend into <WinSAT> root element.
113   if (!reader.SkipToElement() || !reader.Read()) {
114     LOG(ERROR) << "Could not read WinSAT results file.";
115     return stats;
116   }
117 
118   // Search for <WinSPR> element containing the results.
119   do {
120     if (reader.NodeName() == "WinSPR")
121       break;
122   } while (reader.Next());
123   // Descend into <WinSPR> element.
124   if (!reader.Read()) {
125     LOG(ERROR) << "Could not find WinSPR element in results file.";
126     return stats;
127   }
128 
129   // Read scores.
130   for (int depth = reader.Depth(); reader.Depth() == depth; reader.Next()) {
131     std::string node_name = reader.NodeName();
132     if (node_name == "SystemScore")
133       stats.overall = ReadXMLFloatValue(&reader);
134     else if (node_name == "GraphicsScore")
135       stats.graphics = ReadXMLFloatValue(&reader);
136     else if (node_name == "GamingScore")
137       stats.gaming = ReadXMLFloatValue(&reader);
138   }
139 
140   if (stats.overall == 0.0)
141     LOG(ERROR) << "Could not read overall score from assessment results.";
142   if (stats.graphics == 0.0)
143     LOG(ERROR) << "Could not read graphics score from assessment results.";
144   if (stats.gaming == 0.0)
145     LOG(ERROR) << "Could not read gaming score from assessment results.";
146 
147   return stats;
148 }
149 
RetrieveGpuPerformanceStatsWithHistograms()150 GpuPerformanceStats RetrieveGpuPerformanceStatsWithHistograms() {
151   base::TimeTicks start_time = base::TimeTicks::Now();
152 
153   GpuPerformanceStats stats = RetrieveGpuPerformanceStats();
154 
155   UMA_HISTOGRAM_TIMES("GPU.WinSAT.ReadResultsFileTime",
156                       base::TimeTicks::Now() - start_time);
157   UMA_HISTOGRAM_CUSTOM_COUNTS("GPU.WinSAT.OverallScore2",
158                               stats.overall * 10, 10, 200, 50);
159   UMA_HISTOGRAM_CUSTOM_COUNTS("GPU.WinSAT.GraphicsScore2",
160                               stats.graphics * 10, 10, 200, 50);
161   UMA_HISTOGRAM_CUSTOM_COUNTS("GPU.WinSAT.GamingScore2",
162                               stats.gaming * 10, 10, 200, 50);
163   UMA_HISTOGRAM_BOOLEAN(
164       "GPU.WinSAT.HasResults",
165       stats.overall != 0.0 && stats.graphics != 0.0 && stats.gaming != 0.0);
166 
167   return stats;
168 }
169 
170 // Returns the display link driver version or an invalid version if it is
171 // not installed.
DisplayLinkVersion()172 Version DisplayLinkVersion() {
173   base::win::RegKey key;
174 
175   if (key.Open(HKEY_LOCAL_MACHINE, L"SOFTWARE", KEY_READ | KEY_WOW64_64KEY))
176     return Version();
177 
178   if (key.OpenKey(L"DisplayLink", KEY_READ | KEY_WOW64_64KEY))
179     return Version();
180 
181   if (key.OpenKey(L"Core", KEY_READ | KEY_WOW64_64KEY))
182     return Version();
183 
184   base::string16 version;
185   if (key.ReadValue(L"Version", &version))
186     return Version();
187 
188   return Version(base::UTF16ToASCII(version));
189 }
190 
191 // Returns whether Lenovo dCute is installed.
IsLenovoDCuteInstalled()192 bool IsLenovoDCuteInstalled() {
193   base::win::RegKey key;
194 
195   if (key.Open(HKEY_LOCAL_MACHINE, L"SOFTWARE", KEY_READ | KEY_WOW64_64KEY))
196     return false;
197 
198   if (key.OpenKey(L"Lenovo", KEY_READ | KEY_WOW64_64KEY))
199     return false;
200 
201   if (key.OpenKey(L"Lenovo dCute", KEY_READ | KEY_WOW64_64KEY))
202     return false;
203 
204   return true;
205 }
206 
207 // Determines whether D3D11 won't work, either because it is not supported on
208 // the machine or because it is known it is likely to crash.
D3D11ShouldWork(const GPUInfo & gpu_info)209 bool D3D11ShouldWork(const GPUInfo& gpu_info) {
210   // TODO(apatrick): This is a temporary change to see what impact disabling
211   // D3D11 stats collection has on Canary.
212 #if 1
213   return false;
214 #else
215   // Windows XP never supports D3D11. It seems to be less stable that D3D9 on
216   // Vista.
217   if (base::win::GetVersion() <= base::win::VERSION_VISTA)
218     return false;
219 
220   // http://crbug.com/175525.
221   if (gpu_info.display_link_version.IsValid())
222     return false;
223 
224   return true;
225 #endif
226 }
227 
228 // Collects information about the level of D3D11 support and records it in
229 // the UMA stats. Records no stats when D3D11 in not supported at all.
CollectD3D11SupportOnWorkerThread()230 void CollectD3D11SupportOnWorkerThread() {
231   TRACE_EVENT0("gpu", "CollectD3D11Support");
232 
233   typedef HRESULT (WINAPI *D3D11CreateDeviceFunc)(
234       IDXGIAdapter* adapter,
235       D3D_DRIVER_TYPE driver_type,
236       HMODULE software,
237       UINT flags,
238       const D3D_FEATURE_LEVEL* feature_levels,
239       UINT num_feature_levels,
240       UINT sdk_version,
241       ID3D11Device** device,
242       D3D_FEATURE_LEVEL* feature_level,
243       ID3D11DeviceContext** immediate_context);
244 
245   // This enumeration must be kept in sync with histograms.xml. Do not reorder
246   // the members; always add to the end.
247   enum FeatureLevel {
248     FEATURE_LEVEL_UNKNOWN,
249     FEATURE_LEVEL_NO_D3D11_DLL,
250     FEATURE_LEVEL_NO_CREATE_DEVICE_ENTRY_POINT,
251     FEATURE_LEVEL_DEVICE_CREATION_FAILED,
252     FEATURE_LEVEL_9_1,
253     FEATURE_LEVEL_9_2,
254     FEATURE_LEVEL_9_3,
255     FEATURE_LEVEL_10_0,
256     FEATURE_LEVEL_10_1,
257     FEATURE_LEVEL_11_0,
258     NUM_FEATURE_LEVELS
259   };
260 
261   FeatureLevel feature_level = FEATURE_LEVEL_UNKNOWN;
262   UINT bgra_support = 0;
263 
264   // This module is leaked in case it is hooked by third party software.
265   base::NativeLibrary d3d11_module = base::LoadNativeLibrary(
266       base::FilePath(L"d3d11.dll"),
267       NULL);
268 
269   if (!d3d11_module) {
270     feature_level = FEATURE_LEVEL_NO_D3D11_DLL;
271   } else {
272     D3D11CreateDeviceFunc create_func =
273         reinterpret_cast<D3D11CreateDeviceFunc>(
274             base::GetFunctionPointerFromNativeLibrary(d3d11_module,
275                                                       "D3D11CreateDevice"));
276     if (!create_func) {
277       feature_level = FEATURE_LEVEL_NO_CREATE_DEVICE_ENTRY_POINT;
278     } else {
279       static const D3D_FEATURE_LEVEL d3d_feature_levels[] = {
280         D3D_FEATURE_LEVEL_11_0,
281         D3D_FEATURE_LEVEL_10_1,
282         D3D_FEATURE_LEVEL_10_0,
283         D3D_FEATURE_LEVEL_9_3,
284         D3D_FEATURE_LEVEL_9_2,
285         D3D_FEATURE_LEVEL_9_1
286       };
287 
288       base::win::ScopedComPtr<ID3D11Device> device;
289       D3D_FEATURE_LEVEL d3d_feature_level;
290       base::win::ScopedComPtr<ID3D11DeviceContext> device_context;
291       HRESULT hr = create_func(NULL,
292                                D3D_DRIVER_TYPE_HARDWARE,
293                                NULL,
294                                0,
295                                d3d_feature_levels,
296                                arraysize(d3d_feature_levels),
297                                D3D11_SDK_VERSION,
298                                device.Receive(),
299                                &d3d_feature_level,
300                                device_context.Receive());
301       if (FAILED(hr)) {
302         feature_level = FEATURE_LEVEL_DEVICE_CREATION_FAILED;
303       } else {
304         switch (d3d_feature_level) {
305           case D3D_FEATURE_LEVEL_11_0:
306             feature_level = FEATURE_LEVEL_11_0;
307             break;
308           case D3D_FEATURE_LEVEL_10_1:
309             feature_level = FEATURE_LEVEL_10_1;
310             break;
311           case D3D_FEATURE_LEVEL_10_0:
312             feature_level = FEATURE_LEVEL_10_0;
313             break;
314           case D3D_FEATURE_LEVEL_9_3:
315             feature_level = FEATURE_LEVEL_9_3;
316             break;
317           case D3D_FEATURE_LEVEL_9_2:
318             feature_level = FEATURE_LEVEL_9_2;
319             break;
320           case D3D_FEATURE_LEVEL_9_1:
321             feature_level = FEATURE_LEVEL_9_1;
322             break;
323           default:
324             NOTREACHED();
325             break;
326         }
327 
328         hr = device->CheckFormatSupport(DXGI_FORMAT_B8G8R8A8_UNORM,
329                                         &bgra_support);
330         DCHECK(SUCCEEDED(hr));
331       }
332     }
333   }
334 
335   UMA_HISTOGRAM_ENUMERATION("GPU.D3D11_FeatureLevel",
336                             feature_level,
337                             NUM_FEATURE_LEVELS);
338 
339   // ANGLE requires at least feature level 10.0. Do not record any further
340   // stats if ANGLE would not work anyway.
341   if (feature_level < FEATURE_LEVEL_10_0)
342     return;
343 
344   UMA_HISTOGRAM_BOOLEAN(
345       "GPU.D3D11_B8G8R8A8_Texture2DSupport",
346       (bgra_support & D3D11_FORMAT_SUPPORT_TEXTURE2D) != 0);
347   UMA_HISTOGRAM_BOOLEAN(
348       "GPU.D3D11_B8G8R8A8_RenderTargetSupport",
349       (bgra_support & D3D11_FORMAT_SUPPORT_RENDER_TARGET) != 0);
350 }
351 
352 // Collects information about the level of D3D11 support and records it in
353 // the UMA stats. Records no stats when D3D11 in not supported at all.
CollectD3D11Support()354 void CollectD3D11Support() {
355   // D3D11 takes about 50ms to initialize so do this on a worker thread.
356   base::WorkerPool::PostTask(
357       FROM_HERE,
358       base::Bind(CollectD3D11SupportOnWorkerThread),
359       false);
360 }
361 }  // namespace anonymous
362 
363 #if defined(GOOGLE_CHROME_BUILD) && defined(OFFICIAL_BUILD)
364 // This function has a real implementation for official builds that can
365 // be found in src/third_party/amd.
366 void GetAMDVideocardInfo(GPUInfo* gpu_info);
367 #else
GetAMDVideocardInfo(GPUInfo * gpu_info)368 void GetAMDVideocardInfo(GPUInfo* gpu_info) {
369   DCHECK(gpu_info);
370   return;
371 }
372 #endif
373 
CollectDriverInfoD3D(const std::wstring & device_id,GPUInfo * gpu_info)374 CollectInfoResult CollectDriverInfoD3D(const std::wstring& device_id,
375                                        GPUInfo* gpu_info) {
376   TRACE_EVENT0("gpu", "CollectDriverInfoD3D");
377 
378   // create device info for the display device
379   HDEVINFO device_info = SetupDiGetClassDevsW(
380       NULL, device_id.c_str(), NULL,
381       DIGCF_PRESENT | DIGCF_PROFILE | DIGCF_ALLCLASSES);
382   if (device_info == INVALID_HANDLE_VALUE) {
383     LOG(ERROR) << "Creating device info failed";
384     return kCollectInfoNonFatalFailure;
385   }
386 
387   DWORD index = 0;
388   bool found = false;
389   SP_DEVINFO_DATA device_info_data;
390   device_info_data.cbSize = sizeof(device_info_data);
391   while (SetupDiEnumDeviceInfo(device_info, index++, &device_info_data)) {
392     WCHAR value[255];
393     if (SetupDiGetDeviceRegistryPropertyW(device_info,
394                                         &device_info_data,
395                                         SPDRP_DRIVER,
396                                         NULL,
397                                         reinterpret_cast<PBYTE>(value),
398                                         sizeof(value),
399                                         NULL)) {
400       HKEY key;
401       std::wstring driver_key = L"System\\CurrentControlSet\\Control\\Class\\";
402       driver_key += value;
403       LONG result = RegOpenKeyExW(
404           HKEY_LOCAL_MACHINE, driver_key.c_str(), 0, KEY_QUERY_VALUE, &key);
405       if (result == ERROR_SUCCESS) {
406         DWORD dwcb_data = sizeof(value);
407         std::string driver_version;
408         result = RegQueryValueExW(
409             key, L"DriverVersion", NULL, NULL,
410             reinterpret_cast<LPBYTE>(value), &dwcb_data);
411         if (result == ERROR_SUCCESS)
412           driver_version = base::UTF16ToASCII(std::wstring(value));
413 
414         std::string driver_date;
415         dwcb_data = sizeof(value);
416         result = RegQueryValueExW(
417             key, L"DriverDate", NULL, NULL,
418             reinterpret_cast<LPBYTE>(value), &dwcb_data);
419         if (result == ERROR_SUCCESS)
420           driver_date = base::UTF16ToASCII(std::wstring(value));
421 
422         std::string driver_vendor;
423         dwcb_data = sizeof(value);
424         result = RegQueryValueExW(
425             key, L"ProviderName", NULL, NULL,
426             reinterpret_cast<LPBYTE>(value), &dwcb_data);
427         if (result == ERROR_SUCCESS) {
428           driver_vendor = base::UTF16ToASCII(std::wstring(value));
429           if (driver_vendor == "Advanced Micro Devices, Inc." ||
430               driver_vendor == "ATI Technologies Inc.") {
431             // We are conservative and assume that in the absence of a clear
432             // signal the videocard is assumed to be switchable. Additionally,
433             // some switchable systems with Intel GPUs aren't correctly
434             // detected, so always count them.
435             GetAMDVideocardInfo(gpu_info);
436             if (!gpu_info->amd_switchable &&
437                 gpu_info->gpu.vendor_id == 0x8086) {
438               gpu_info->amd_switchable = true;
439               gpu_info->secondary_gpus.push_back(gpu_info->gpu);
440               gpu_info->gpu.vendor_id = 0x1002;
441               gpu_info->gpu.device_id = 0;  // Unknown discrete AMD GPU.
442             }
443           }
444         }
445 
446         gpu_info->driver_vendor = driver_vendor;
447         gpu_info->driver_version = driver_version;
448         gpu_info->driver_date = driver_date;
449         found = true;
450         RegCloseKey(key);
451         break;
452       }
453     }
454   }
455   SetupDiDestroyDeviceInfoList(device_info);
456   return found ? kCollectInfoSuccess : kCollectInfoNonFatalFailure;
457 }
458 
CollectContextGraphicsInfo(GPUInfo * gpu_info)459 CollectInfoResult CollectContextGraphicsInfo(GPUInfo* gpu_info) {
460   TRACE_EVENT0("gpu", "CollectGraphicsInfo");
461 
462   DCHECK(gpu_info);
463 
464   if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kUseGL)) {
465     std::string requested_implementation_name =
466         CommandLine::ForCurrentProcess()->GetSwitchValueASCII(switches::kUseGL);
467     if (requested_implementation_name == "swiftshader") {
468       gpu_info->software_rendering = true;
469       gpu_info->context_info_state = kCollectInfoNonFatalFailure;
470       return kCollectInfoNonFatalFailure;
471     }
472   }
473 
474   CollectInfoResult result = CollectGraphicsInfoGL(gpu_info);
475   if (result != kCollectInfoSuccess) {
476     gpu_info->context_info_state = result;
477     return result;
478   }
479 
480   // ANGLE's renderer strings are of the form:
481   // ANGLE (<adapter_identifier> Direct3D<version> vs_x_x ps_x_x)
482   std::string direct3d_version;
483   int vertex_shader_major_version = 0;
484   int vertex_shader_minor_version = 0;
485   int pixel_shader_major_version = 0;
486   int pixel_shader_minor_version = 0;
487   gpu_info->adapter_luid = 0;
488   if (RE2::FullMatch(gpu_info->gl_renderer,
489                      "ANGLE \\(.*\\)") &&
490       RE2::PartialMatch(gpu_info->gl_renderer,
491                         " Direct3D(\\w+)",
492                         &direct3d_version) &&
493       RE2::PartialMatch(gpu_info->gl_renderer,
494                         " vs_(\\d+)_(\\d+)",
495                         &vertex_shader_major_version,
496                         &vertex_shader_minor_version) &&
497       RE2::PartialMatch(gpu_info->gl_renderer,
498                         " ps_(\\d+)_(\\d+)",
499                         &pixel_shader_major_version,
500                         &pixel_shader_minor_version)) {
501     gpu_info->can_lose_context = direct3d_version == "9";
502     gpu_info->vertex_shader_version =
503         base::StringPrintf("%d.%d",
504                            vertex_shader_major_version,
505                            vertex_shader_minor_version);
506     gpu_info->pixel_shader_version =
507         base::StringPrintf("%d.%d",
508                            pixel_shader_major_version,
509                            pixel_shader_minor_version);
510 
511     // ANGLE's EGL vendor strings are of the form:
512     // Google, Inc. (adapter LUID: 0123456789ABCDEF)
513     // The LUID is optional and identifies the GPU adapter ANGLE is using.
514     const char* egl_vendor = eglQueryString(
515         gfx::GLSurfaceEGL::GetHardwareDisplay(),
516         EGL_VENDOR);
517     RE2::PartialMatch(egl_vendor,
518                       " \\(adapter LUID: ([0-9A-Fa-f]{16})\\)",
519                       RE2::Hex(&gpu_info->adapter_luid));
520 
521     // DirectX diagnostics are collected asynchronously because it takes a
522     // couple of seconds.
523   } else {
524     gpu_info->dx_diagnostics_info_state = kCollectInfoNonFatalFailure;
525   }
526 
527   gpu_info->context_info_state = kCollectInfoSuccess;
528   return kCollectInfoSuccess;
529 }
530 
CollectGpuID(uint32 * vendor_id,uint32 * device_id)531 CollectInfoResult CollectGpuID(uint32* vendor_id, uint32* device_id) {
532   DCHECK(vendor_id && device_id);
533   *vendor_id = 0;
534   *device_id = 0;
535 
536   // Taken from http://developer.nvidia.com/object/device_ids.html
537   DISPLAY_DEVICE dd;
538   dd.cb = sizeof(DISPLAY_DEVICE);
539   std::wstring id;
540   for (int i = 0; EnumDisplayDevices(NULL, i, &dd, 0); ++i) {
541     if (dd.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE) {
542       id = dd.DeviceID;
543       break;
544     }
545   }
546 
547   if (id.length() > 20) {
548     int vendor = 0, device = 0;
549     std::wstring vendor_string = id.substr(8, 4);
550     std::wstring device_string = id.substr(17, 4);
551     base::HexStringToInt(base::UTF16ToASCII(vendor_string), &vendor);
552     base::HexStringToInt(base::UTF16ToASCII(device_string), &device);
553     *vendor_id = vendor;
554     *device_id = device;
555     if (*vendor_id != 0 && *device_id != 0)
556       return kCollectInfoSuccess;
557   }
558   return kCollectInfoNonFatalFailure;
559 }
560 
CollectBasicGraphicsInfo(GPUInfo * gpu_info)561 CollectInfoResult CollectBasicGraphicsInfo(GPUInfo* gpu_info) {
562   TRACE_EVENT0("gpu", "CollectPreliminaryGraphicsInfo");
563 
564   DCHECK(gpu_info);
565 
566   gpu_info->performance_stats = RetrieveGpuPerformanceStatsWithHistograms();
567 
568   // nvd3d9wrap.dll is loaded into all processes when Optimus is enabled.
569   HMODULE nvd3d9wrap = GetModuleHandleW(L"nvd3d9wrap.dll");
570   gpu_info->optimus = nvd3d9wrap != NULL;
571 
572   gpu_info->lenovo_dcute = IsLenovoDCuteInstalled();
573 
574   gpu_info->display_link_version = DisplayLinkVersion();
575 
576   if (!gpu_info->display_link_version .IsValid()) {
577     UMA_HISTOGRAM_ENUMERATION("GPU.DisplayLinkInstallationStatus",
578                               DISPLAY_LINK_NOT_INSTALLED,
579                               DISPLAY_LINK_INSTALLATION_STATUS_MAX);
580   } else if (gpu_info->display_link_version.IsOlderThan("7.2")) {
581     UMA_HISTOGRAM_ENUMERATION("GPU.DisplayLinkInstallationStatus",
582                               DISPLAY_LINK_7_1_OR_EARLIER,
583                               DISPLAY_LINK_INSTALLATION_STATUS_MAX);
584   } else {
585     UMA_HISTOGRAM_ENUMERATION("GPU.DisplayLinkInstallationStatus",
586                               DISPLAY_LINK_7_2_OR_LATER,
587                               DISPLAY_LINK_INSTALLATION_STATUS_MAX);
588   }
589 
590   // Taken from http://developer.nvidia.com/object/device_ids.html
591   DISPLAY_DEVICE dd;
592   dd.cb = sizeof(DISPLAY_DEVICE);
593   std::wstring id;
594   for (int i = 0; EnumDisplayDevices(NULL, i, &dd, 0); ++i) {
595     if (dd.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE) {
596       id = dd.DeviceID;
597       break;
598     }
599   }
600 
601   if (id.length() <= 20) {
602     gpu_info->basic_info_state = kCollectInfoNonFatalFailure;
603     return kCollectInfoNonFatalFailure;
604   }
605 
606   int vendor_id = 0, device_id = 0;
607   base::string16 vendor_id_string = id.substr(8, 4);
608   base::string16 device_id_string = id.substr(17, 4);
609   base::HexStringToInt(base::UTF16ToASCII(vendor_id_string), &vendor_id);
610   base::HexStringToInt(base::UTF16ToASCII(device_id_string), &device_id);
611   gpu_info->gpu.vendor_id = vendor_id;
612   gpu_info->gpu.device_id = device_id;
613   // TODO(zmo): we only need to call CollectDriverInfoD3D() if we use ANGLE.
614   if (!CollectDriverInfoD3D(id, gpu_info)) {
615     gpu_info->basic_info_state = kCollectInfoNonFatalFailure;
616     return kCollectInfoNonFatalFailure;
617   }
618 
619   // Collect basic information about supported D3D11 features. Delay for 45
620   // seconds so as not to regress performance tests.
621   if (D3D11ShouldWork(*gpu_info)) {
622     // This is on a field trial so we can turn it off easily if it blows up
623     // again in stable channel.
624     scoped_refptr<base::FieldTrial> trial(
625         base::FieldTrialList::FactoryGetFieldTrial(
626             "D3D11Experiment", 100, "Disabled", 2015, 7, 8,
627             base::FieldTrial::SESSION_RANDOMIZED, NULL));
628     const int enabled_group =
629         trial->AppendGroup("Enabled", 0);
630 
631     if (trial->group() == enabled_group) {
632       base::MessageLoop::current()->PostDelayedTask(
633           FROM_HERE,
634           base::Bind(&CollectD3D11Support),
635           base::TimeDelta::FromSeconds(45));
636     }
637   }
638 
639   gpu_info->basic_info_state = kCollectInfoSuccess;
640   return kCollectInfoSuccess;
641 }
642 
CollectDriverInfoGL(GPUInfo * gpu_info)643 CollectInfoResult CollectDriverInfoGL(GPUInfo* gpu_info) {
644   TRACE_EVENT0("gpu", "CollectDriverInfoGL");
645 
646   if (!gpu_info->driver_version.empty())
647     return kCollectInfoSuccess;
648 
649   bool parsed = RE2::PartialMatch(
650       gpu_info->gl_version, "([\\d\\.]+)$", &gpu_info->driver_version);
651   return parsed ? kCollectInfoSuccess : kCollectInfoNonFatalFailure;
652 }
653 
MergeGPUInfo(GPUInfo * basic_gpu_info,const GPUInfo & context_gpu_info)654 void MergeGPUInfo(GPUInfo* basic_gpu_info,
655                   const GPUInfo& context_gpu_info) {
656   DCHECK(basic_gpu_info);
657 
658   if (context_gpu_info.software_rendering) {
659     basic_gpu_info->software_rendering = true;
660     return;
661   }
662 
663   MergeGPUInfoGL(basic_gpu_info, context_gpu_info);
664 
665   basic_gpu_info->dx_diagnostics = context_gpu_info.dx_diagnostics;
666 }
667 
668 }  // namespace gpu
669