• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
26namespace gpu {
27
28namespace {
29
30const UInt32 kVendorIDIntel = 0x8086;
31const UInt32 kVendorIDNVidia = 0x10de;
32const 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.
36UInt32 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.
56GPUInfo::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.
65CollectInfoResult 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
164CollectInfoResult 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
176CollectInfoResult 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
188CollectInfoResult 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
203CollectInfoResult 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
217void 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