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