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 #include <X11/Xlib.h>
      8 #include <vector>
      9 
     10 #include "base/command_line.h"
     11 #include "base/debug/trace_event.h"
     12 #include "base/file_util.h"
     13 #include "base/logging.h"
     14 #include "base/memory/scoped_ptr.h"
     15 #include "base/message_loop/message_loop.h"
     16 #include "base/strings/string_piece.h"
     17 #include "base/strings/string_split.h"
     18 #include "base/strings/string_tokenizer.h"
     19 #include "base/strings/string_util.h"
     20 #include "library_loaders/libpci.h"
     21 #include "third_party/libXNVCtrl/NVCtrl.h"
     22 #include "third_party/libXNVCtrl/NVCtrlLib.h"
     23 #include "ui/gl/gl_bindings.h"
     24 #include "ui/gl/gl_context.h"
     25 #include "ui/gl/gl_implementation.h"
     26 #include "ui/gl/gl_surface.h"
     27 #include "ui/gl/gl_switches.h"
     28 
     29 namespace gpu {
     30 
     31 namespace {
     32 
     33 // This checks if a system supports PCI bus.
     34 // We check the existence of /sys/bus/pci or /sys/bug/pci_express.
     35 bool IsPciSupported() {
     36   const base::FilePath pci_path("/sys/bus/pci/");
     37   const base::FilePath pcie_path("/sys/bus/pci_express/");
     38   return (base::PathExists(pci_path) ||
     39           base::PathExists(pcie_path));
     40 }
     41 
     42 // Scan /etc/ati/amdpcsdb.default for "ReleaseVersion".
     43 // Return empty string on failing.
     44 std::string CollectDriverVersionATI() {
     45   const base::FilePath::CharType kATIFileName[] =
     46       FILE_PATH_LITERAL("/etc/ati/amdpcsdb.default");
     47   base::FilePath ati_file_path(kATIFileName);
     48   if (!base::PathExists(ati_file_path))
     49     return std::string();
     50   std::string contents;
     51   if (!file_util::ReadFileToString(ati_file_path, &contents))
     52     return std::string();
     53   base::StringTokenizer t(contents, "\r\n");
     54   while (t.GetNext()) {
     55     std::string line = t.token();
     56     if (StartsWithASCII(line, "ReleaseVersion=", true)) {
     57       size_t begin = line.find_first_of("0123456789");
     58       if (begin != std::string::npos) {
     59         size_t end = line.find_first_not_of("0123456789.", begin);
     60         if (end == std::string::npos)
     61           return line.substr(begin);
     62         else
     63           return line.substr(begin, end - begin);
     64       }
     65     }
     66   }
     67   return std::string();
     68 }
     69 
     70 // Use NVCtrl extention to query NV driver version.
     71 // Return empty string on failing.
     72 std::string CollectDriverVersionNVidia() {
     73   Display* display = base::MessagePumpForUI::GetDefaultXDisplay();
     74   if (!display) {
     75     LOG(ERROR) << "XOpenDisplay failed.";
     76     return std::string();
     77   }
     78   int event_base = 0, error_base = 0;
     79   if (!XNVCTRLQueryExtension(display, &event_base, &error_base)) {
     80     LOG(INFO) << "NVCtrl extension does not exist.";
     81     return std::string();
     82   }
     83   int screen_count = ScreenCount(display);
     84   for (int screen = 0; screen < screen_count; ++screen) {
     85     char* buffer = NULL;
     86     if (XNVCTRLIsNvScreen(display, screen) &&
     87         XNVCTRLQueryStringAttribute(display, screen, 0,
     88                                     NV_CTRL_STRING_NVIDIA_DRIVER_VERSION,
     89                                     &buffer)) {
     90       std::string driver_version(buffer);
     91       XFree(buffer);
     92       return driver_version;
     93     }
     94   }
     95   return std::string();
     96 }
     97 
     98 const uint32 kVendorIDIntel = 0x8086;
     99 const uint32 kVendorIDNVidia = 0x10de;
    100 const uint32 kVendorIDAMD = 0x1002;
    101 
    102 bool CollectPCIVideoCardInfo(GPUInfo* gpu_info) {
    103   DCHECK(gpu_info);
    104 
    105   if (IsPciSupported() == false) {
    106     VLOG(1) << "PCI bus scanning is not supported";
    107     return false;
    108   }
    109 
    110   // TODO(zmo): be more flexible about library name.
    111   LibPciLoader libpci_loader;
    112   if (!libpci_loader.Load("libpci.so.3") &&
    113       !libpci_loader.Load("libpci.so")) {
    114     VLOG(1) << "Failed to locate libpci";
    115     return false;
    116   }
    117 
    118   pci_access* access = (libpci_loader.pci_alloc)();
    119   DCHECK(access != NULL);
    120   (libpci_loader.pci_init)(access);
    121   (libpci_loader.pci_scan_bus)(access);
    122   bool primary_gpu_identified = false;
    123   for (pci_dev* device = access->devices;
    124        device != NULL; device = device->next) {
    125     // Fill the IDs and class fields.
    126     (libpci_loader.pci_fill_info)(device, 33);
    127     // TODO(zmo): there might be other classes that qualify as display devices.
    128     if (device->device_class != 0x0300)  // Device class is DISPLAY_VGA.
    129       continue;
    130 
    131     GPUInfo::GPUDevice gpu;
    132     gpu.vendor_id = device->vendor_id;
    133     gpu.device_id = device->device_id;
    134 
    135     if (!primary_gpu_identified) {
    136       primary_gpu_identified = true;
    137       gpu_info->gpu = gpu;
    138     } else {
    139       // TODO(zmo): if there are multiple GPUs, we assume the non Intel
    140       // one is primary. Revisit this logic because we actually don't know
    141       // which GPU we are using at this point.
    142       if (gpu_info->gpu.vendor_id == kVendorIDIntel &&
    143           gpu.vendor_id != kVendorIDIntel) {
    144         gpu_info->secondary_gpus.push_back(gpu_info->gpu);
    145         gpu_info->gpu = gpu;
    146       } else {
    147         gpu_info->secondary_gpus.push_back(gpu);
    148       }
    149     }
    150   }
    151 
    152   // Detect Optimus or AMD Switchable GPU.
    153   if (gpu_info->secondary_gpus.size() == 1 &&
    154       gpu_info->secondary_gpus[0].vendor_id == kVendorIDIntel) {
    155     if (gpu_info->gpu.vendor_id == kVendorIDNVidia)
    156       gpu_info->optimus = true;
    157     if (gpu_info->gpu.vendor_id == kVendorIDAMD)
    158       gpu_info->amd_switchable = true;
    159   }
    160 
    161   (libpci_loader.pci_cleanup)(access);
    162   return (primary_gpu_identified);
    163 }
    164 
    165 }  // namespace anonymous
    166 
    167 bool CollectContextGraphicsInfo(GPUInfo* gpu_info) {
    168   DCHECK(gpu_info);
    169 
    170   TRACE_EVENT0("gpu", "gpu_info_collector::CollectGraphicsInfo");
    171 
    172   if (CommandLine::ForCurrentProcess()->HasSwitch(
    173           switches::kGpuNoContextLost)) {
    174     gpu_info->can_lose_context = false;
    175   } else {
    176 #if defined(OS_CHROMEOS)
    177     gpu_info->can_lose_context = false;
    178 #else
    179     // TODO(zmo): need to consider the case where we are running on top
    180     // of desktop GL and GL_ARB_robustness extension is available.
    181     gpu_info->can_lose_context =
    182         (gfx::GetGLImplementation() == gfx::kGLImplementationEGLGLES2);
    183 #endif
    184   }
    185 
    186   gpu_info->finalized = true;
    187   bool rt = CollectGraphicsInfoGL(gpu_info);
    188 
    189   return rt;
    190 }
    191 
    192 GpuIDResult CollectGpuID(uint32* vendor_id, uint32* device_id) {
    193   DCHECK(vendor_id && device_id);
    194   *vendor_id = 0;
    195   *device_id = 0;
    196 
    197   GPUInfo gpu_info;
    198   if (CollectPCIVideoCardInfo(&gpu_info)) {
    199     *vendor_id = gpu_info.gpu.vendor_id;
    200     *device_id = gpu_info.gpu.device_id;
    201     return kGpuIDSuccess;
    202   }
    203   return kGpuIDFailure;
    204 }
    205 
    206 bool CollectBasicGraphicsInfo(GPUInfo* gpu_info) {
    207   DCHECK(gpu_info);
    208 
    209   bool rt = CollectPCIVideoCardInfo(gpu_info);
    210 
    211   std::string driver_version;
    212   switch (gpu_info->gpu.vendor_id) {
    213     case kVendorIDAMD:
    214       driver_version = CollectDriverVersionATI();
    215       if (!driver_version.empty()) {
    216         gpu_info->driver_vendor = "ATI / AMD";
    217         gpu_info->driver_version = driver_version;
    218       }
    219       break;
    220     case kVendorIDNVidia:
    221       driver_version = CollectDriverVersionNVidia();
    222       if (!driver_version.empty()) {
    223         gpu_info->driver_vendor = "NVIDIA";
    224         gpu_info->driver_version = driver_version;
    225       }
    226       break;
    227     case kVendorIDIntel:
    228       // In dual-GPU cases, sometimes PCI scan only gives us the
    229       // integrated GPU (i.e., the Intel one).
    230       driver_version = CollectDriverVersionNVidia();
    231       if (!driver_version.empty()) {
    232         gpu_info->driver_vendor = "NVIDIA";
    233         gpu_info->driver_version = driver_version;
    234         // Machines with more than two GPUs are not handled.
    235         if (gpu_info->secondary_gpus.size() <= 1)
    236           gpu_info->optimus = true;
    237       }
    238       break;
    239   }
    240 
    241   return rt;
    242 }
    243 
    244 bool CollectDriverInfoGL(GPUInfo* gpu_info) {
    245   DCHECK(gpu_info);
    246 
    247   std::string gl_version_string = gpu_info->gl_version_string;
    248   if (StartsWithASCII(gl_version_string, "OpenGL ES", true))
    249     gl_version_string = gl_version_string.substr(10);
    250   std::vector<std::string> pieces;
    251   base::SplitStringAlongWhitespace(gl_version_string, &pieces);
    252   // In linux, the gl version string might be in the format of
    253   //   GLVersion DriverVendor DriverVersion
    254   if (pieces.size() < 3)
    255     return false;
    256 
    257   std::string driver_version = pieces[2];
    258   size_t pos = driver_version.find_first_not_of("0123456789.");
    259   if (pos == 0)
    260     return false;
    261   if (pos != std::string::npos)
    262     driver_version = driver_version.substr(0, pos);
    263 
    264   gpu_info->driver_vendor = pieces[1];
    265   gpu_info->driver_version = driver_version;
    266   return true;
    267 }
    268 
    269 void MergeGPUInfo(GPUInfo* basic_gpu_info,
    270                   const GPUInfo& context_gpu_info) {
    271   MergeGPUInfoGL(basic_gpu_info, context_gpu_info);
    272 }
    273 
    274 }  // namespace gpu
    275