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 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 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 bool 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