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