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 <vector>
      8 
      9 #include "base/debug/trace_event.h"
     10 #include "base/logging.h"
     11 #include "base/mac/mac_util.h"
     12 #include "base/mac/scoped_cftyperef.h"
     13 #include "base/memory/scoped_ptr.h"
     14 #include "base/strings/string_number_conversions.h"
     15 #include "base/strings/string_piece.h"
     16 #include "base/strings/string_util.h"
     17 #include "base/strings/sys_string_conversions.h"
     18 #include "ui/gl/gl_bindings.h"
     19 #include "ui/gl/gl_context.h"
     20 #include "ui/gl/gl_implementation.h"
     21 #include "ui/gl/gl_interface.h"
     22 
     23 #import <Cocoa/Cocoa.h>
     24 #import <Foundation/Foundation.h>
     25 #import <IOKit/IOKitLib.h>
     26 
     27 namespace gpu {
     28 
     29 namespace {
     30 
     31 const UInt32 kVendorIDIntel = 0x8086;
     32 const UInt32 kVendorIDNVidia = 0x10de;
     33 const UInt32 kVendorIDAMD = 0x1002;
     34 
     35 // Return 0 if we couldn't find the property.
     36 // The property values we use should not be 0, so it's OK to use 0 as failure.
     37 UInt32 GetEntryProperty(io_registry_entry_t entry, CFStringRef property_name) {
     38   base::ScopedCFTypeRef<CFDataRef> data_ref(
     39       static_cast<CFDataRef>(IORegistryEntrySearchCFProperty(
     40           entry,
     41           kIOServicePlane,
     42           property_name,
     43           kCFAllocatorDefault,
     44           kIORegistryIterateRecursively | kIORegistryIterateParents)));
     45   if (!data_ref)
     46     return 0;
     47 
     48   UInt32 value = 0;
     49   const UInt32* value_pointer =
     50       reinterpret_cast<const UInt32*>(CFDataGetBytePtr(data_ref));
     51   if (value_pointer != NULL)
     52     value = *value_pointer;
     53   return value;
     54 }
     55 
     56 // Find the info of the current GPU.
     57 GPUInfo::GPUDevice GetActiveGPU() {
     58   GPUInfo::GPUDevice gpu;
     59   io_registry_entry_t dsp_port = CGDisplayIOServicePort(kCGDirectMainDisplay);
     60   gpu.vendor_id = GetEntryProperty(dsp_port, CFSTR("vendor-id"));
     61   gpu.device_id = GetEntryProperty(dsp_port, CFSTR("device-id"));
     62   return gpu;
     63 }
     64 
     65 // Scan IO registry for PCI video cards.
     66 bool CollectPCIVideoCardInfo(GPUInfo* gpu_info) {
     67   DCHECK(gpu_info);
     68 
     69   // Collect all GPUs' info.
     70   // match_dictionary will be consumed by IOServiceGetMatchingServices, no need
     71   // to release it.
     72   CFMutableDictionaryRef match_dictionary = IOServiceMatching("IOPCIDevice");
     73   io_iterator_t entry_iterator;
     74   std::vector<GPUInfo::GPUDevice> gpu_list;
     75   if (IOServiceGetMatchingServices(kIOMasterPortDefault,
     76                                    match_dictionary,
     77                                    &entry_iterator) == kIOReturnSuccess) {
     78     io_registry_entry_t entry;
     79     while ((entry = IOIteratorNext(entry_iterator))) {
     80       GPUInfo::GPUDevice gpu;
     81       if (GetEntryProperty(entry, CFSTR("class-code")) != 0x30000) {
     82         // 0x30000 : DISPLAY_VGA
     83         continue;
     84       }
     85       gpu.vendor_id = GetEntryProperty(entry, CFSTR("vendor-id"));
     86       gpu.device_id = GetEntryProperty(entry, CFSTR("device-id"));
     87       if (gpu.vendor_id && gpu.device_id)
     88         gpu_list.push_back(gpu);
     89     }
     90     IOObjectRelease(entry_iterator);
     91   }
     92 
     93   switch (gpu_list.size()) {
     94     case 0:
     95       return false;
     96     case 1:
     97       gpu_info->gpu = gpu_list[0];
     98       break;
     99     case 2:
    100       {
    101         int integrated = -1;
    102         int discrete = -1;
    103         if (gpu_list[0].vendor_id == kVendorIDIntel)
    104           integrated = 0;
    105         else if (gpu_list[1].vendor_id == kVendorIDIntel)
    106           integrated = 1;
    107         if (integrated >= 0) {
    108           switch (gpu_list[1 - integrated].vendor_id) {
    109             case kVendorIDAMD:
    110               gpu_info->amd_switchable = true;
    111               discrete = 1 - integrated;
    112               break;
    113             case kVendorIDNVidia:
    114               gpu_info->optimus = true;
    115               discrete = 1 - integrated;
    116               break;
    117             default:
    118               break;
    119           }
    120         }
    121         if (integrated >= 0 && discrete >= 0) {
    122           // We always put discrete GPU as primary for blacklisting purpose.
    123           gpu_info->gpu = gpu_list[discrete];
    124           gpu_info->secondary_gpus.push_back(gpu_list[integrated]);
    125           break;
    126         }
    127         // If it's not optimus or amd_switchable, we put the current GPU as
    128         // primary.  Fall through to default.
    129       }
    130     default:
    131       {
    132         GPUInfo::GPUDevice active_gpu = GetActiveGPU();
    133         size_t current = gpu_list.size();
    134         if (active_gpu.vendor_id && active_gpu.device_id) {
    135           for (size_t i = 0; i < gpu_list.size(); ++i) {
    136             if (gpu_list[i].vendor_id == active_gpu.vendor_id &&
    137                 gpu_list[i].device_id == active_gpu.device_id) {
    138               current = i;
    139               break;
    140             }
    141           }
    142         }
    143         if (current == gpu_list.size()) {
    144           // If we fail to identify the current GPU, select any one as primary.
    145           current = 0;
    146         }
    147         for (size_t i = 0; i < gpu_list.size(); ++i) {
    148           if (i == current)
    149             gpu_info->gpu = gpu_list[i];
    150           else
    151             gpu_info->secondary_gpus.push_back(gpu_list[i]);
    152         }
    153       }
    154       break;
    155   }
    156   return (gpu_info->gpu.vendor_id && gpu_info->gpu.device_id);
    157 }
    158 
    159 }  // namespace anonymous
    160 
    161 bool CollectContextGraphicsInfo(GPUInfo* gpu_info) {
    162   DCHECK(gpu_info);
    163 
    164   TRACE_EVENT0("gpu", "gpu_info_collector::CollectGraphicsInfo");
    165 
    166   gpu_info->can_lose_context =
    167       (gfx::GetGLImplementation() == gfx::kGLImplementationEGLGLES2);
    168   gpu_info->finalized = true;
    169   return CollectGraphicsInfoGL(gpu_info);
    170 }
    171 
    172 GpuIDResult CollectGpuID(uint32* vendor_id, uint32* device_id) {
    173   DCHECK(vendor_id && device_id);
    174   *vendor_id = 0;
    175   *device_id = 0;
    176 
    177   GPUInfo gpu_info;
    178   if (CollectPCIVideoCardInfo(&gpu_info)) {
    179     *vendor_id = gpu_info.gpu.vendor_id;
    180     *device_id = gpu_info.gpu.device_id;
    181     return kGpuIDSuccess;
    182   }
    183   return kGpuIDFailure;
    184 }
    185 
    186 bool CollectBasicGraphicsInfo(GPUInfo* gpu_info) {
    187   DCHECK(gpu_info);
    188 
    189   std::string model_name;
    190   int32 model_major = 0, model_minor = 0;
    191   base::mac::ParseModelIdentifier(base::mac::GetModelIdentifier(),
    192                                   &model_name, &model_major, &model_minor);
    193   base::ReplaceChars(model_name, " ", "_", &gpu_info->machine_model);
    194   gpu_info->machine_model += " " + base::IntToString(model_major) +
    195                              "." + base::IntToString(model_minor);
    196 
    197   return CollectPCIVideoCardInfo(gpu_info);
    198 }
    199 
    200 bool CollectDriverInfoGL(GPUInfo* gpu_info) {
    201   DCHECK(gpu_info);
    202 
    203   // Extract the OpenGL driver version string from the GL_VERSION string.
    204   // Mac OpenGL drivers have the driver version
    205   // at the end of the gl version string preceded by a dash.
    206   // Use some jiggery-pokery to turn that utf8 string into a std::wstring.
    207   std::string gl_version_string = gpu_info->gl_version_string;
    208   size_t pos = gl_version_string.find_last_of('-');
    209   if (pos == std::string::npos)
    210     return false;
    211   gpu_info->driver_version = gl_version_string.substr(pos + 1);
    212   return true;
    213 }
    214 
    215 void MergeGPUInfo(GPUInfo* basic_gpu_info,
    216                   const GPUInfo& context_gpu_info) {
    217   MergeGPUInfoGL(basic_gpu_info, context_gpu_info);
    218 }
    219 
    220 bool DetermineActiveGPU(GPUInfo* gpu_info) {
    221   DCHECK(gpu_info);
    222   // On mac, during info collection, we've already detected the active gpu.
    223   return true;
    224 }
    225 
    226 }  // namespace gpu
    227