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 // C system before C++ system.
8 #include <stddef.h>
9 #include <stdint.h>
10 
11 // This has to be included before windows.h.
12 #include "third_party/re2/src/re2/re2.h"
13 
14 #include <windows.h>
15 
16 #include <d3d11.h>
17 #include <d3d11_3.h>
18 #include <d3d12.h>
19 #include <dxgi.h>
20 #include <wrl/client.h>
21 
22 #include "base/file_version_info_win.h"
23 #include "base/files/file_path.h"
24 #include "base/files/file_util.h"
25 #include "base/logging.h"
26 #include "base/metrics/histogram_functions.h"
27 #include "base/metrics/histogram_macros.h"
28 #include "base/numerics/safe_conversions.h"
29 #include "base/scoped_native_library.h"
30 #include "base/strings/stringprintf.h"
31 #include "base/trace_event/trace_event.h"
32 #include "base/win/scoped_com_initializer.h"
33 #include "base/win/windows_version.h"
34 #include "build/branding_buildflags.h"
35 #include "gpu/config/gpu_util.h"
36 #include "third_party/vulkan/include/vulkan/vulkan.h"
37 #include "ui/gl/direct_composition_surface_win.h"
38 
39 namespace gpu {
40 
41 namespace {
42 
43 // These values are persisted to logs. Entries should not be renumbered and
44 // numeric values should never be reused.
45 // This should match enum D3D12FeatureLevel in
46 // \tools\metrics\histograms\enums.xml
47 enum class D3D12FeatureLevel {
48   kD3DFeatureLevelUnknown = 0,
49   kD3DFeatureLevel_12_0 = 1,
50   kD3DFeatureLevel_12_1 = 2,
51   kD3DFeatureLevel_11_0 = 3,
52   kD3DFeatureLevel_11_1 = 4,
53   kMaxValue = kD3DFeatureLevel_11_1,
54 };
55 
ConvertToHistogramFeatureLevel(uint32_t d3d_feature_level)56 inline D3D12FeatureLevel ConvertToHistogramFeatureLevel(
57     uint32_t d3d_feature_level) {
58   switch (d3d_feature_level) {
59     case 0:
60       return D3D12FeatureLevel::kD3DFeatureLevelUnknown;
61     case D3D_FEATURE_LEVEL_12_0:
62       return D3D12FeatureLevel::kD3DFeatureLevel_12_0;
63     case D3D_FEATURE_LEVEL_12_1:
64       return D3D12FeatureLevel::kD3DFeatureLevel_12_1;
65     case D3D_FEATURE_LEVEL_11_0:
66       return D3D12FeatureLevel::kD3DFeatureLevel_11_0;
67     case D3D_FEATURE_LEVEL_11_1:
68       return D3D12FeatureLevel::kD3DFeatureLevel_11_1;
69     default:
70       NOTREACHED();
71       return D3D12FeatureLevel::kD3DFeatureLevelUnknown;
72   }
73 }
74 
FlagsToOverlaySupport(UINT flags)75 OverlaySupport FlagsToOverlaySupport(UINT flags) {
76   if (flags & DXGI_OVERLAY_SUPPORT_FLAG_SCALING)
77     return OverlaySupport::kScaling;
78   if (flags & DXGI_OVERLAY_SUPPORT_FLAG_DIRECT)
79     return OverlaySupport::kDirect;
80   return OverlaySupport::kNone;
81 }
82 
83 }  // namespace
84 
85 #if BUILDFLAG(GOOGLE_CHROME_BRANDING) && defined(OFFICIAL_BUILD)
86 // This function has a real implementation for official builds that can
87 // be found in src/third_party/amd.
88 bool GetAMDSwitchableInfo(bool* is_switchable,
89                           uint32_t* active_vendor_id,
90                           uint32_t* active_device_id);
91 #else
GetAMDSwitchableInfo(bool * is_switchable,uint32_t * active_vendor_id,uint32_t * active_device_id)92 bool GetAMDSwitchableInfo(bool* is_switchable,
93                           uint32_t* active_vendor_id,
94                           uint32_t* active_device_id) {
95   return false;
96 }
97 #endif
98 
99 // This has to be called after a context is created, active GPU is identified,
100 // and GPU driver bug workarounds are computed again. Otherwise the workaround
101 // |disable_direct_composition| may not be correctly applied.
102 // Also, this has to be called after falling back to SwiftShader decision is
103 // finalized because this function depends on GL is ANGLE's GLES or not.
CollectHardwareOverlayInfo(OverlayInfo * overlay_info)104 void CollectHardwareOverlayInfo(OverlayInfo* overlay_info) {
105   if (gl::GetGLImplementation() == gl::kGLImplementationEGLANGLE) {
106     overlay_info->direct_composition =
107         gl::DirectCompositionSurfaceWin::IsDirectCompositionSupported();
108     overlay_info->supports_overlays =
109         gl::DirectCompositionSurfaceWin::AreOverlaysSupported();
110     overlay_info->nv12_overlay_support = FlagsToOverlaySupport(
111         gl::DirectCompositionSurfaceWin::GetOverlaySupportFlags(
112             DXGI_FORMAT_NV12));
113     overlay_info->yuy2_overlay_support = FlagsToOverlaySupport(
114         gl::DirectCompositionSurfaceWin::GetOverlaySupportFlags(
115             DXGI_FORMAT_YUY2));
116   }
117 }
118 
CollectDriverInfoD3D(GPUInfo * gpu_info)119 bool CollectDriverInfoD3D(GPUInfo* gpu_info) {
120   TRACE_EVENT0("gpu", "CollectDriverInfoD3D");
121 
122   Microsoft::WRL::ComPtr<IDXGIFactory> dxgi_factory;
123   HRESULT hr = ::CreateDXGIFactory(IID_PPV_ARGS(&dxgi_factory));
124   if (FAILED(hr))
125     return false;
126 
127   bool found_amd = false;
128   bool found_intel = false;
129   bool found_nvidia = false;
130 
131   UINT i;
132   Microsoft::WRL::ComPtr<IDXGIAdapter> dxgi_adapter;
133   for (i = 0; SUCCEEDED(dxgi_factory->EnumAdapters(i, &dxgi_adapter)); i++) {
134     DXGI_ADAPTER_DESC desc;
135     dxgi_adapter->GetDesc(&desc);
136 
137     GPUInfo::GPUDevice device;
138     device.vendor_id = desc.VendorId;
139     device.device_id = desc.DeviceId;
140     device.sub_sys_id = desc.SubSysId;
141     device.revision = desc.Revision;
142 
143     LARGE_INTEGER umd_version;
144     hr = dxgi_adapter->CheckInterfaceSupport(__uuidof(IDXGIDevice),
145                                              &umd_version);
146     if (SUCCEEDED(hr)) {
147       device.driver_version = base::StringPrintf(
148           "%d.%d.%d.%d", HIWORD(umd_version.HighPart),
149           LOWORD(umd_version.HighPart), HIWORD(umd_version.LowPart),
150           LOWORD(umd_version.LowPart));
151     } else {
152       DLOG(ERROR) << "Unable to retrieve the umd version of adapter: "
153                   << desc.Description << " HR: " << std::hex << hr;
154     }
155     switch (device.vendor_id) {
156       case 0x8086:
157         found_intel = true;
158         break;
159       case 0x1002:
160         found_amd = true;
161         break;
162       case 0x10de:
163         found_nvidia = true;
164         break;
165       default:
166         break;
167     }
168 
169     if (i == 0) {
170       gpu_info->gpu = device;
171     } else {
172       gpu_info->secondary_gpus.push_back(device);
173     }
174   }
175 
176   if (found_intel && base::win::GetVersion() < base::win::Version::WIN10) {
177     // Since Windows 10 (and Windows 8.1 on some systems), switchable graphics
178     // platforms are managed by Windows and each adapter is accessible as
179     // separate devices.
180     // See https://msdn.microsoft.com/en-us/windows/dn265501(v=vs.80)
181     if (found_amd) {
182       bool is_amd_switchable = false;
183       uint32_t active_vendor = 0, active_device = 0;
184       GetAMDSwitchableInfo(&is_amd_switchable, &active_vendor, &active_device);
185       gpu_info->amd_switchable = is_amd_switchable;
186     } else if (found_nvidia) {
187       // nvd3d9wrap.dll is loaded into all processes when Optimus is enabled.
188       HMODULE nvd3d9wrap = GetModuleHandleW(L"nvd3d9wrap.dll");
189       gpu_info->optimus = nvd3d9wrap != nullptr;
190     }
191   }
192 
193   return i > 0;
194 }
195 
196 // DirectX 12 are included with Windows 10 and Server 2016.
GetGpuSupportedD3D12Version(Dx12VulkanVersionInfo * info)197 void GetGpuSupportedD3D12Version(Dx12VulkanVersionInfo* info) {
198   TRACE_EVENT0("gpu", "GetGpuSupportedD3D12Version");
199   info->supports_dx12 = false;
200   info->d3d12_feature_level = 0;
201 
202   base::ScopedNativeLibrary d3d12_library(
203       base::FilePath(FILE_PATH_LITERAL("d3d12.dll")));
204   if (!d3d12_library.is_valid())
205     return;
206 
207   // The order of feature levels to attempt to create in D3D CreateDevice
208   const D3D_FEATURE_LEVEL feature_levels[] = {
209       D3D_FEATURE_LEVEL_12_1, D3D_FEATURE_LEVEL_12_0, D3D_FEATURE_LEVEL_11_1,
210       D3D_FEATURE_LEVEL_11_0};
211 
212   PFN_D3D12_CREATE_DEVICE D3D12CreateDevice =
213       reinterpret_cast<PFN_D3D12_CREATE_DEVICE>(
214           d3d12_library.GetFunctionPointer("D3D12CreateDevice"));
215   if (D3D12CreateDevice) {
216     // For the default adapter only. (*pAdapter == nullptr)
217     // Check to see if the adapter supports Direct3D 12, but don't create the
218     // actual device yet. (**ppDevice == nullptr)
219     for (auto level : feature_levels) {
220       if (SUCCEEDED(D3D12CreateDevice(nullptr, level, _uuidof(ID3D12Device),
221                                       nullptr))) {
222         info->d3d12_feature_level = level;
223         info->supports_dx12 = (level >= D3D_FEATURE_LEVEL_12_0) ? true : false;
224         break;
225       }
226     }
227   }
228 }
229 
BadAMDVulkanDriverVersion()230 bool BadAMDVulkanDriverVersion() {
231   // Both 32-bit and 64-bit dll are broken. If 64-bit doesn't exist,
232   // 32-bit dll will be used to detect the AMD Vulkan driver.
233   const base::FilePath kAmdDriver64(FILE_PATH_LITERAL("amdvlk64.dll"));
234   const base::FilePath kAmdDriver32(FILE_PATH_LITERAL("amdvlk32.dll"));
235   std::unique_ptr<FileVersionInfoWin> file_version_info =
236       FileVersionInfoWin::CreateFileVersionInfoWin(kAmdDriver64);
237   if (!file_version_info) {
238     file_version_info =
239         FileVersionInfoWin::CreateFileVersionInfoWin(kAmdDriver32);
240     if (!file_version_info)
241       return false;
242   }
243 
244   // From the Canary crash logs, the broken amdvlk64.dll versions
245   // are 1.0.39.0, 1.0.51.0 and 1.0.54.0. In the manual test, version
246   // 9.2.10.1 dated 12/6/2017 works and version 1.0.54.0 dated 11/2/1017
247   // crashes. All version numbers small than 1.0.54.0 will be marked as
248   // broken.
249   const base::Version kBadAMDVulkanDriverVersion("1.0.54.0");
250   return file_version_info->GetFileVersion() <= kBadAMDVulkanDriverVersion;
251 }
252 
BadVulkanDllVersion()253 bool BadVulkanDllVersion() {
254   std::unique_ptr<FileVersionInfoWin> file_version_info =
255       FileVersionInfoWin::CreateFileVersionInfoWin(
256           base::FilePath(FILE_PATH_LITERAL("vulkan-1.dll")));
257   if (!file_version_info)
258     return false;
259 
260   // From the logs, most vulkan-1.dll crashs are from the following versions.
261   // As of 7/23/2018.
262   // 0.0.0.0 -  # of crashes: 6556
263   // 1.0.26.0 - # of crashes: 5890
264   // 1.0.33.0 - # of crashes: 12271
265   // 1.0.42.0 - # of crashes: 35749
266   // 1.0.42.1 - # of crashes: 68214
267   // 1.0.51.0 - # of crashes: 5152
268   // The GPU could be from any vendor, but only some certain models would crash.
269   // For those that don't crash, they usually return failures upon GPU vulkan
270   // support querying even though the GPU drivers can support it.
271   base::Version fv = file_version_info->GetFileVersion();
272   const char* const kBadVulkanDllVersion[] = {
273       "0.0.0.0", "1.0.26.0", "1.0.33.0", "1.0.42.0", "1.0.42.1", "1.0.51.0"};
274   for (const char* bad_version : kBadVulkanDllVersion) {
275     if (fv == base::Version(bad_version))
276       return true;
277   }
278   return false;
279 }
280 
InitVulkan(base::NativeLibrary * vulkan_library,PFN_vkGetInstanceProcAddr * vkGetInstanceProcAddr,PFN_vkCreateInstance * vkCreateInstance)281 bool InitVulkan(base::NativeLibrary* vulkan_library,
282                 PFN_vkGetInstanceProcAddr* vkGetInstanceProcAddr,
283                 PFN_vkCreateInstance* vkCreateInstance) {
284   *vulkan_library =
285       base::LoadNativeLibrary(base::FilePath(L"vulkan-1.dll"), nullptr);
286 
287   if (!(*vulkan_library)) {
288     return false;
289   }
290 
291   *vkGetInstanceProcAddr = reinterpret_cast<PFN_vkGetInstanceProcAddr>(
292       GetProcAddress(*vulkan_library, "vkGetInstanceProcAddr"));
293 
294   if (*vkGetInstanceProcAddr) {
295     *vkCreateInstance = reinterpret_cast<PFN_vkCreateInstance>(
296         (*vkGetInstanceProcAddr)(nullptr, "vkCreateInstance"));
297     if (*vkCreateInstance) {
298       return true;
299     }
300   }
301 
302   // From the crash reports, unloading the library here might cause a crash in
303   // the Vulkan loader or in the Vulkan driver. To work around it, don't
304   // explicitly unload the DLL. Instead, GPU process shutdown will unload all
305   // loaded DLLs.
306   // base::UnloadNativeLibrary(*vulkan_library);
307   return false;
308 }
309 
InitVulkanInstanceProc(const VkInstance & vk_instance,const PFN_vkGetInstanceProcAddr & vkGetInstanceProcAddr,PFN_vkEnumeratePhysicalDevices * vkEnumeratePhysicalDevices,PFN_vkEnumerateDeviceExtensionProperties * vkEnumerateDeviceExtensionProperties)310 bool InitVulkanInstanceProc(
311     const VkInstance& vk_instance,
312     const PFN_vkGetInstanceProcAddr& vkGetInstanceProcAddr,
313     PFN_vkEnumeratePhysicalDevices* vkEnumeratePhysicalDevices,
314     PFN_vkEnumerateDeviceExtensionProperties*
315         vkEnumerateDeviceExtensionProperties) {
316 
317   *vkEnumeratePhysicalDevices =
318       reinterpret_cast<PFN_vkEnumeratePhysicalDevices>(
319           vkGetInstanceProcAddr(vk_instance, "vkEnumeratePhysicalDevices"));
320 
321   *vkEnumerateDeviceExtensionProperties =
322       reinterpret_cast<PFN_vkEnumerateDeviceExtensionProperties>(
323           vkGetInstanceProcAddr(vk_instance,
324                                 "vkEnumerateDeviceExtensionProperties"));
325 
326   if ((*vkEnumeratePhysicalDevices) &&
327       (*vkEnumerateDeviceExtensionProperties)) {
328     return true;
329   }
330   return false;
331 }
332 
GetGpuSupportedVulkanVersionAndExtensions(Dx12VulkanVersionInfo * info,const std::vector<const char * > & requested_vulkan_extensions,std::vector<bool> * extension_support)333 void GetGpuSupportedVulkanVersionAndExtensions(
334     Dx12VulkanVersionInfo* info,
335     const std::vector<const char*>& requested_vulkan_extensions,
336     std::vector<bool>* extension_support) {
337   TRACE_EVENT0("gpu", "GetGpuSupportedVulkanVersionAndExtensions");
338 
339   base::NativeLibrary vulkan_library;
340   PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr;
341   PFN_vkCreateInstance vkCreateInstance;
342   PFN_vkEnumeratePhysicalDevices vkEnumeratePhysicalDevices;
343   PFN_vkEnumerateDeviceExtensionProperties vkEnumerateDeviceExtensionProperties;
344   VkInstance vk_instance = VK_NULL_HANDLE;
345   uint32_t physical_device_count = 0;
346   info->supports_vulkan = false;
347   info->vulkan_version = 0;
348 
349   // Skip if the system has an older AMD Vulkan driver amdvlk64.dll or
350   // amdvlk32.dll which crashes when vkCreateInstance() is called. This bug has
351   // been fixed in the latest AMD driver.
352   if (BadAMDVulkanDriverVersion()) {
353     return;
354   }
355 
356   // Some early versions of vulkan-1.dll might crash
357   if (BadVulkanDllVersion()) {
358     return;
359   }
360 
361   if (!InitVulkan(&vulkan_library, &vkGetInstanceProcAddr, &vkCreateInstance)) {
362     return;
363   }
364 
365   VkApplicationInfo app_info = {};
366   app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
367 
368   VkInstanceCreateInfo create_info = {};
369   create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
370   create_info.pApplicationInfo = &app_info;
371 
372   // Get the Vulkan API version supported in the GPU driver
373   for (int minor_version = 2; minor_version >= 0; --minor_version) {
374     app_info.apiVersion = VK_MAKE_VERSION(1, minor_version, 0);
375     VkResult result = vkCreateInstance(&create_info, nullptr, &vk_instance);
376     if (result == VK_SUCCESS && vk_instance &&
377         InitVulkanInstanceProc(vk_instance, vkGetInstanceProcAddr,
378                                &vkEnumeratePhysicalDevices,
379                                &vkEnumerateDeviceExtensionProperties)) {
380       result = vkEnumeratePhysicalDevices(vk_instance, &physical_device_count,
381                                           nullptr);
382       if (result == VK_SUCCESS && physical_device_count > 0) {
383         info->supports_vulkan = true;
384         info->vulkan_version = app_info.apiVersion;
385         break;
386       } else {
387         // Skip destroy here. GPU process shutdown will unload all loaded DLLs.
388         // vkDestroyInstance(vk_instance, nullptr);
389         vk_instance = VK_NULL_HANDLE;
390       }
391     }
392   }
393 
394   // Check whether the requested_vulkan_extensions are supported
395   if (info->supports_vulkan) {
396     std::vector<VkPhysicalDevice> physical_devices(physical_device_count);
397     vkEnumeratePhysicalDevices(vk_instance, &physical_device_count,
398                                physical_devices.data());
399 
400     // physical_devices[0]: Only query the default device for now
401     uint32_t property_count;
402     vkEnumerateDeviceExtensionProperties(physical_devices[0], nullptr,
403                                          &property_count, nullptr);
404 
405     std::vector<VkExtensionProperties> extension_properties(property_count);
406     if (property_count > 0) {
407       vkEnumerateDeviceExtensionProperties(physical_devices[0], nullptr,
408                                            &property_count,
409                                            extension_properties.data());
410     }
411 
412     for (size_t i = 0; i < requested_vulkan_extensions.size(); ++i) {
413       for (size_t p = 0; p < property_count; ++p) {
414         if (strcmp(requested_vulkan_extensions[i],
415                    extension_properties[p].extensionName) == 0) {
416           (*extension_support)[i] = true;
417           break;
418         }
419       }
420     }
421   }
422 
423   // From the crash reports, calling the following two functions might cause a
424   // crash in the Vulkan loader or in the Vulkan driver. To work around it,
425   // don't explicitly unload the DLL. Instead, GPU process shutdown will unload
426   // all loaded DLLs.
427   // if (vk_instance) {
428   //   vkDestroyInstance(vk_instance, nullptr);
429   // }
430   // base::UnloadNativeLibrary(vulkan_library);
431 }
432 
RecordGpuSupportedRuntimeVersionHistograms(Dx12VulkanVersionInfo * info)433 void RecordGpuSupportedRuntimeVersionHistograms(Dx12VulkanVersionInfo* info) {
434   // D3D
435   GetGpuSupportedD3D12Version(info);
436   UMA_HISTOGRAM_BOOLEAN("GPU.SupportsDX12", info->supports_dx12);
437   UMA_HISTOGRAM_ENUMERATION(
438       "GPU.D3D12FeatureLevel",
439       ConvertToHistogramFeatureLevel(info->d3d12_feature_level));
440 
441   // Vulkan
442   const std::vector<const char*> vulkan_extensions = {
443       "VK_KHR_external_memory_win32", "VK_KHR_external_semaphore_win32",
444       "VK_KHR_win32_keyed_mutex"};
445   std::vector<bool> extension_support(vulkan_extensions.size(), false);
446   GetGpuSupportedVulkanVersionAndExtensions(info, vulkan_extensions,
447                                             &extension_support);
448 
449   UMA_HISTOGRAM_BOOLEAN("GPU.SupportsVulkan", info->supports_vulkan);
450   UMA_HISTOGRAM_ENUMERATION(
451       "GPU.VulkanVersion",
452       ConvertToHistogramVulkanVersion(info->vulkan_version));
453 
454   for (size_t i = 0; i < vulkan_extensions.size(); ++i) {
455     std::string name = "GPU.VulkanExtSupport.";
456     name.append(vulkan_extensions[i]);
457     base::UmaHistogramBoolean(name, extension_support[i]);
458   }
459 }
460 
CollectD3D11FeatureInfo(D3D_FEATURE_LEVEL * d3d11_feature_level,bool * has_discrete_gpu)461 bool CollectD3D11FeatureInfo(D3D_FEATURE_LEVEL* d3d11_feature_level,
462                              bool* has_discrete_gpu) {
463   Microsoft::WRL::ComPtr<IDXGIFactory> dxgi_factory;
464   if (FAILED(::CreateDXGIFactory(IID_PPV_ARGS(&dxgi_factory))))
465     return false;
466 
467   base::ScopedNativeLibrary d3d11_library(
468       base::FilePath(FILE_PATH_LITERAL("d3d11.dll")));
469   if (!d3d11_library.is_valid())
470     return false;
471   PFN_D3D11_CREATE_DEVICE D3D11CreateDevice =
472       reinterpret_cast<PFN_D3D11_CREATE_DEVICE>(
473           d3d11_library.GetFunctionPointer("D3D11CreateDevice"));
474   if (!D3D11CreateDevice)
475     return false;
476 
477   // The order of feature levels to attempt to create in D3D CreateDevice
478   const D3D_FEATURE_LEVEL kFeatureLevels[] = {
479       D3D_FEATURE_LEVEL_12_1, D3D_FEATURE_LEVEL_12_0, D3D_FEATURE_LEVEL_11_1,
480       D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_1, D3D_FEATURE_LEVEL_10_0,
481       D3D_FEATURE_LEVEL_9_3,  D3D_FEATURE_LEVEL_9_2,  D3D_FEATURE_LEVEL_9_1};
482 
483   bool detected_discrete_gpu = false;
484   D3D_FEATURE_LEVEL max_level = D3D_FEATURE_LEVEL_1_0_CORE;
485   Microsoft::WRL::ComPtr<IDXGIAdapter> dxgi_adapter;
486   for (UINT ii = 0; SUCCEEDED(dxgi_factory->EnumAdapters(ii, &dxgi_adapter));
487        ++ii) {
488     DXGI_ADAPTER_DESC desc;
489     if (SUCCEEDED(dxgi_adapter->GetDesc(&desc)) && desc.VendorId == 0x1414) {
490       // Bypass Microsoft software renderer.
491       continue;
492     }
493     Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device;
494     D3D_FEATURE_LEVEL returned_feature_level = D3D_FEATURE_LEVEL_1_0_CORE;
495     if (FAILED(D3D11CreateDevice(dxgi_adapter.Get(), D3D_DRIVER_TYPE_UNKNOWN,
496                                  /*Software=*/0,
497                                  /*Flags=*/0, kFeatureLevels,
498                                  _countof(kFeatureLevels), D3D11_SDK_VERSION,
499                                  &d3d11_device, &returned_feature_level,
500                                  /*ppImmediateContext=*/nullptr))) {
501       continue;
502     }
503     if (returned_feature_level > max_level)
504       max_level = returned_feature_level;
505     Microsoft::WRL::ComPtr<ID3D11Device3> d3d11_device_3;
506     if (FAILED(d3d11_device.As(&d3d11_device_3)))
507       continue;
508     D3D11_FEATURE_DATA_D3D11_OPTIONS2 data = {};
509     if (FAILED(d3d11_device_3->CheckFeatureSupport(D3D11_FEATURE_D3D11_OPTIONS2,
510                                                    &data, sizeof(data)))) {
511       continue;
512     }
513     if (!data.UnifiedMemoryArchitecture)
514       detected_discrete_gpu = true;
515   }
516 
517   if (max_level > D3D_FEATURE_LEVEL_1_0_CORE) {
518     *d3d11_feature_level = max_level;
519     *has_discrete_gpu = detected_discrete_gpu;
520     return true;
521   }
522   return false;
523 }
524 
CollectContextGraphicsInfo(GPUInfo * gpu_info)525 bool CollectContextGraphicsInfo(GPUInfo* gpu_info) {
526   TRACE_EVENT0("gpu", "CollectGraphicsInfo");
527 
528   DCHECK(gpu_info);
529 
530   if (!CollectGraphicsInfoGL(gpu_info))
531     return false;
532 
533   // ANGLE's renderer strings are of the form:
534   // ANGLE (<adapter_identifier> Direct3D<version> vs_x_x ps_x_x)
535   std::string direct3d_version;
536   int vertex_shader_major_version = 0;
537   int vertex_shader_minor_version = 0;
538   int pixel_shader_major_version = 0;
539   int pixel_shader_minor_version = 0;
540   if (RE2::FullMatch(gpu_info->gl_renderer,
541                      "ANGLE \\(.*\\)") &&
542       RE2::PartialMatch(gpu_info->gl_renderer,
543                         " Direct3D(\\w+)",
544                         &direct3d_version) &&
545       RE2::PartialMatch(gpu_info->gl_renderer,
546                         " vs_(\\d+)_(\\d+)",
547                         &vertex_shader_major_version,
548                         &vertex_shader_minor_version) &&
549       RE2::PartialMatch(gpu_info->gl_renderer,
550                         " ps_(\\d+)_(\\d+)",
551                         &pixel_shader_major_version,
552                         &pixel_shader_minor_version)) {
553     gpu_info->vertex_shader_version =
554         base::StringPrintf("%d.%d",
555                            vertex_shader_major_version,
556                            vertex_shader_minor_version);
557     gpu_info->pixel_shader_version =
558         base::StringPrintf("%d.%d",
559                            pixel_shader_major_version,
560                            pixel_shader_minor_version);
561 
562     DCHECK(!gpu_info->vertex_shader_version.empty());
563     // Note: do not reorder, used by UMA_HISTOGRAM below
564     enum ShaderModel {
565       SHADER_MODEL_UNKNOWN,
566       SHADER_MODEL_2_0,
567       SHADER_MODEL_3_0,
568       SHADER_MODEL_4_0,
569       SHADER_MODEL_4_1,
570       SHADER_MODEL_5_0,
571       NUM_SHADER_MODELS
572     };
573     ShaderModel shader_model = SHADER_MODEL_UNKNOWN;
574     if (gpu_info->vertex_shader_version == "5.0") {
575       shader_model = SHADER_MODEL_5_0;
576     } else if (gpu_info->vertex_shader_version == "4.1") {
577       shader_model = SHADER_MODEL_4_1;
578     } else if (gpu_info->vertex_shader_version == "4.0") {
579       shader_model = SHADER_MODEL_4_0;
580     } else if (gpu_info->vertex_shader_version == "3.0") {
581       shader_model = SHADER_MODEL_3_0;
582     } else if (gpu_info->vertex_shader_version == "2.0") {
583       shader_model = SHADER_MODEL_2_0;
584     }
585     UMA_HISTOGRAM_ENUMERATION("GPU.D3DShaderModel", shader_model,
586                               NUM_SHADER_MODELS);
587 
588     // DirectX diagnostics are collected asynchronously because it takes a
589     // couple of seconds.
590   }
591   return true;
592 }
593 
CollectBasicGraphicsInfo(GPUInfo * gpu_info)594 bool CollectBasicGraphicsInfo(GPUInfo* gpu_info) {
595   TRACE_EVENT0("gpu", "CollectPreliminaryGraphicsInfo");
596   DCHECK(gpu_info);
597   // TODO(zmo): we only need to call CollectDriverInfoD3D() if we use ANGLE.
598   return CollectDriverInfoD3D(gpu_info);
599 }
600 
601 }  // namespace gpu
602