• 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#include "ui/gl/gl_interface.h"
22
23#import <Cocoa/Cocoa.h>
24#import <Foundation/Foundation.h>
25#import <IOKit/IOKitLib.h>
26
27namespace gpu {
28
29namespace {
30
31const UInt32 kVendorIDIntel = 0x8086;
32const UInt32 kVendorIDNVidia = 0x10de;
33const 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.
37UInt32 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.
57GPUInfo::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.
66bool 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
161bool 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
172GpuIDResult 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
186bool 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
200bool 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
215void MergeGPUInfo(GPUInfo* basic_gpu_info,
216                  const GPUInfo& context_gpu_info) {
217  MergeGPUInfoGL(basic_gpu_info, context_gpu_info);
218}
219
220bool 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