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 
     22 #import <Cocoa/Cocoa.h>
     23 #import <Foundation/Foundation.h>
     24 #import <IOKit/IOKitLib.h>
     25 
     26 namespace gpu {
     27 
     28 namespace {
     29 
     30 const UInt32 kVendorIDIntel = 0x8086;
     31 const UInt32 kVendorIDNVidia = 0x10de;
     32 const UInt32 kVendorIDAMD = 0x1002;
     33 
     34 // Return 0 if we couldn't find the property.
     35 // The property values we use should not be 0, so it's OK to use 0 as failure.
     36 UInt32 GetEntryProperty(io_registry_entry_t entry, CFStringRef property_name) {
     37   base::ScopedCFTypeRef<CFDataRef> data_ref(
     38       static_cast<CFDataRef>(IORegistryEntrySearchCFProperty(
     39           entry,
     40           kIOServicePlane,
     41           property_name,
     42           kCFAllocatorDefault,
     43           kIORegistryIterateRecursively | kIORegistryIterateParents)));
     44   if (!data_ref)
     45     return 0;
     46 
     47   UInt32 value = 0;
     48   const UInt32* value_pointer =
     49       reinterpret_cast<const UInt32*>(CFDataGetBytePtr(data_ref));
     50   if (value_pointer != NULL)
     51     value = *value_pointer;
     52   return value;
     53 }
     54 
     55 // Find the info of the current GPU.
     56 GPUInfo::GPUDevice GetActiveGPU() {
     57   GPUInfo::GPUDevice gpu;
     58   io_registry_entry_t dsp_port = CGDisplayIOServicePort(kCGDirectMainDisplay);
     59   gpu.vendor_id = GetEntryProperty(dsp_port, CFSTR("vendor-id"));
     60   gpu.device_id = GetEntryProperty(dsp_port, CFSTR("device-id"));
     61   return gpu;
     62 }
     63 
     64 // Scan IO registry for PCI video cards.
     65 CollectInfoResult CollectPCIVideoCardInfo(GPUInfo* gpu_info) {
     66   DCHECK(gpu_info);
     67   GPUInfo::GPUDevice active_gpu = GetActiveGPU();
     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         if (gpu.vendor_id == active_gpu.vendor_id &&
     89             gpu.device_id == active_gpu.device_id) {
     90           gpu.active = true;
     91         }
     92         gpu_list.push_back(gpu);
     93       }
     94     }
     95     IOObjectRelease(entry_iterator);
     96   }
     97 
     98   switch (gpu_list.size()) {
     99     case 0:
    100       return kCollectInfoNonFatalFailure;
    101     case 1:
    102       gpu_info->gpu = gpu_list[0];
    103       break;
    104     case 2:
    105       {
    106         int integrated = -1;
    107         int discrete = -1;
    108         if (gpu_list[0].vendor_id == kVendorIDIntel)
    109           integrated = 0;
    110         else if (gpu_list[1].vendor_id == kVendorIDIntel)
    111           integrated = 1;
    112         if (integrated >= 0) {
    113           switch (gpu_list[1 - integrated].vendor_id) {
    114             case kVendorIDAMD:
    115               gpu_info->amd_switchable = true;
    116               discrete = 1 - integrated;
    117               break;
    118             case kVendorIDNVidia:
    119               gpu_info->optimus = true;
    120               discrete = 1 - integrated;
    121               break;
    122             default:
    123               break;
    124           }
    125         }
    126         if (integrated >= 0 && discrete >= 0) {
    127           // We always put discrete GPU as primary for blacklisting purpose.
    128           gpu_info->gpu = gpu_list[discrete];
    129           gpu_info->secondary_gpus.push_back(gpu_list[integrated]);
    130           break;
    131         }
    132         // If it's not optimus or amd_switchable, we put the current GPU as
    133         // primary.  Fall through to default.
    134       }
    135     default:
    136       {
    137         size_t current = gpu_list.size();
    138         for (size_t i = 0; i < gpu_list.size(); ++i) {
    139           if (gpu_list[i].active) {
    140             current = i;
    141             break;
    142           }
    143         }
    144         if (current == gpu_list.size()) {
    145           // If we fail to identify the current GPU, select any one as primary.
    146           current = 0;
    147         }
    148         for (size_t i = 0; i < gpu_list.size(); ++i) {
    149           if (i == current)
    150             gpu_info->gpu = gpu_list[i];
    151           else
    152             gpu_info->secondary_gpus.push_back(gpu_list[i]);
    153         }
    154       }
    155       break;
    156   }
    157   if (gpu_info->gpu.vendor_id == 0 || gpu_info->gpu.device_id == 0)
    158     return kCollectInfoNonFatalFailure;
    159   return kCollectInfoSuccess;
    160 }
    161 
    162 }  // namespace anonymous
    163 
    164 CollectInfoResult CollectContextGraphicsInfo(GPUInfo* gpu_info) {
    165   DCHECK(gpu_info);
    166 
    167   TRACE_EVENT0("gpu", "gpu_info_collector::CollectGraphicsInfo");
    168 
    169   gpu_info->can_lose_context =
    170       (gfx::GetGLImplementation() == gfx::kGLImplementationEGLGLES2);
    171   CollectInfoResult result = CollectGraphicsInfoGL(gpu_info);
    172   gpu_info->context_info_state = result;
    173   return result;
    174 }
    175 
    176 CollectInfoResult CollectGpuID(uint32* vendor_id, uint32* device_id) {
    177   DCHECK(vendor_id && device_id);
    178 
    179   GPUInfo::GPUDevice gpu = GetActiveGPU();
    180   *vendor_id = gpu.vendor_id;
    181   *device_id = gpu.device_id;
    182 
    183   if (*vendor_id != 0 && *device_id != 0)
    184     return kCollectInfoSuccess;
    185   return kCollectInfoNonFatalFailure;
    186 }
    187 
    188 CollectInfoResult CollectBasicGraphicsInfo(GPUInfo* gpu_info) {
    189   DCHECK(gpu_info);
    190 
    191   int32 model_major = 0, model_minor = 0;
    192   base::mac::ParseModelIdentifier(base::mac::GetModelIdentifier(),
    193                                   &gpu_info->machine_model_name,
    194                                   &model_major, &model_minor);
    195   gpu_info->machine_model_version =
    196       base::IntToString(model_major) + "." + base::IntToString(model_minor);
    197 
    198   CollectInfoResult result = CollectPCIVideoCardInfo(gpu_info);
    199   gpu_info->basic_info_state = result;
    200   return result;
    201 }
    202 
    203 CollectInfoResult CollectDriverInfoGL(GPUInfo* gpu_info) {
    204   DCHECK(gpu_info);
    205 
    206   // Extract the OpenGL driver version string from the GL_VERSION string.
    207   // Mac OpenGL drivers have the driver version
    208   // at the end of the gl version string preceded by a dash.
    209   // Use some jiggery-pokery to turn that utf8 string into a std::wstring.
    210   size_t pos = gpu_info->gl_version.find_last_of('-');
    211   if (pos == std::string::npos)
    212     return kCollectInfoNonFatalFailure;
    213   gpu_info->driver_version = gpu_info->gl_version.substr(pos + 1);
    214   return kCollectInfoSuccess;
    215 }
    216 
    217 void MergeGPUInfo(GPUInfo* basic_gpu_info,
    218                   const GPUInfo& context_gpu_info) {
    219   MergeGPUInfoGL(basic_gpu_info, context_gpu_info);
    220 }
    221 
    222 }  // namespace gpu
    223