- add sources.
[platform/framework/web/crosswalk.git] / src / gpu / config / gpu_info_collector_mac.mm
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   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 }  // namespace gpu