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.
5 #include "gpu/config/gpu_info_collector.h"
9 #include "base/logging.h"
10 #include "base/mac/mac_util.h"
11 #include "base/mac/scoped_cftyperef.h"
12 #include "base/memory/scoped_ptr.h"
13 #include "base/strings/string_number_conversions.h"
14 #include "base/strings/string_piece.h"
15 #include "base/strings/string_util.h"
16 #include "base/strings/sys_string_conversions.h"
17 #include "base/trace_event/trace_event.h"
18 #include "ui/gl/gl_bindings.h"
19 #include "ui/gl/gl_context.h"
20 #include "ui/gl/gl_implementation.h"
22 #import <Cocoa/Cocoa.h>
23 #import <Foundation/Foundation.h>
24 #import <IOKit/IOKitLib.h>
30 const UInt32 kVendorIDIntel = 0x8086;
31 const UInt32 kVendorIDNVidia = 0x10de;
32 const UInt32 kVendorIDAMD = 0x1002;
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(
43 kIORegistryIterateRecursively | kIORegistryIterateParents)));
48 const UInt32* value_pointer =
49 reinterpret_cast<const UInt32*>(CFDataGetBytePtr(data_ref));
50 if (value_pointer != NULL)
51 value = *value_pointer;
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"));
64 // Scan IO registry for PCI video cards.
65 CollectInfoResult CollectPCIVideoCardInfo(GPUInfo* gpu_info) {
67 GPUInfo::GPUDevice active_gpu = GetActiveGPU();
69 // Collect all GPUs' info.
70 // match_dictionary will be consumed by IOServiceGetMatchingServices, no need
72 CFMutableDictionaryRef match_dictionary = IOServiceMatching("IOPCIDevice");
73 io_iterator_t entry_iterator;
74 std::vector<GPUInfo::GPUDevice> gpu_list;
75 if (IOServiceGetMatchingServices(kIOMasterPortDefault,
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
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) {
92 gpu_list.push_back(gpu);
95 IOObjectRelease(entry_iterator);
98 switch (gpu_list.size()) {
100 return kCollectInfoNonFatalFailure;
102 gpu_info->gpu = gpu_list[0];
108 if (gpu_list[0].vendor_id == kVendorIDIntel)
110 else if (gpu_list[1].vendor_id == kVendorIDIntel)
112 if (integrated >= 0) {
113 switch (gpu_list[1 - integrated].vendor_id) {
115 gpu_info->amd_switchable = true;
116 discrete = 1 - integrated;
118 case kVendorIDNVidia:
119 gpu_info->optimus = true;
120 discrete = 1 - integrated;
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]);
132 // If it's not optimus or amd_switchable, we put the current GPU as
133 // primary. Fall through to default.
137 size_t current = gpu_list.size();
138 for (size_t i = 0; i < gpu_list.size(); ++i) {
139 if (gpu_list[i].active) {
144 if (current == gpu_list.size()) {
145 // If we fail to identify the current GPU, select any one as primary.
148 for (size_t i = 0; i < gpu_list.size(); ++i) {
150 gpu_info->gpu = gpu_list[i];
152 gpu_info->secondary_gpus.push_back(gpu_list[i]);
157 if (gpu_info->gpu.vendor_id == 0 || gpu_info->gpu.device_id == 0)
158 return kCollectInfoNonFatalFailure;
159 return kCollectInfoSuccess;
162 } // namespace anonymous
164 CollectInfoResult CollectContextGraphicsInfo(GPUInfo* gpu_info) {
167 TRACE_EVENT0("gpu", "gpu_info_collector::CollectGraphicsInfo");
169 gpu_info->can_lose_context =
170 (gfx::GetGLImplementation() == gfx::kGLImplementationEGLGLES2);
171 CollectInfoResult result = CollectGraphicsInfoGL(gpu_info);
172 gpu_info->context_info_state = result;
176 CollectInfoResult CollectGpuID(uint32* vendor_id, uint32* device_id) {
177 DCHECK(vendor_id && device_id);
179 GPUInfo::GPUDevice gpu = GetActiveGPU();
180 *vendor_id = gpu.vendor_id;
181 *device_id = gpu.device_id;
183 if (*vendor_id != 0 && *device_id != 0)
184 return kCollectInfoSuccess;
185 return kCollectInfoNonFatalFailure;
188 CollectInfoResult CollectBasicGraphicsInfo(GPUInfo* gpu_info) {
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);
198 CollectInfoResult result = CollectPCIVideoCardInfo(gpu_info);
199 gpu_info->basic_info_state = result;
203 CollectInfoResult CollectDriverInfoGL(GPUInfo* gpu_info) {
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;
217 void MergeGPUInfo(GPUInfo* basic_gpu_info,
218 const GPUInfo& context_gpu_info) {
219 MergeGPUInfoGL(basic_gpu_info, context_gpu_info);