1 // Copyright 2014 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_linux.h" 6 7 #include <vector> 8 9 #include "base/command_line.h" 10 #include "base/debug/trace_event.h" 11 #include "base/files/file_util.h" 12 #include "base/logging.h" 13 #include "base/memory/scoped_ptr.h" 14 #include "base/message_loop/message_loop.h" 15 #include "base/strings/string_piece.h" 16 #include "base/strings/string_split.h" 17 #include "base/strings/string_tokenizer.h" 18 #include "base/strings/string_util.h" 19 #include "gpu/config/gpu_info_collector.h" 20 #include "library_loaders/libpci.h" 21 #include "ui/gl/gl_bindings.h" 22 #include "ui/gl/gl_context.h" 23 #include "ui/gl/gl_implementation.h" 24 #include "ui/gl/gl_surface.h" 25 #include "ui/gl/gl_switches.h" 26 27 namespace gpu { 28 29 namespace { 30 31 // This checks if a system supports PCI bus. 32 // We check the existence of /sys/bus/pci or /sys/bug/pci_express. 33 bool IsPciSupported() { 34 const base::FilePath pci_path("/sys/bus/pci/"); 35 const base::FilePath pcie_path("/sys/bus/pci_express/"); 36 return (base::PathExists(pci_path) || 37 base::PathExists(pcie_path)); 38 } 39 40 // Scan /etc/ati/amdpcsdb.default for "ReleaseVersion". 41 // Return empty string on failing. 42 std::string CollectDriverVersionATI() { 43 const base::FilePath::CharType kATIFileName[] = 44 FILE_PATH_LITERAL("/etc/ati/amdpcsdb.default"); 45 base::FilePath ati_file_path(kATIFileName); 46 if (!base::PathExists(ati_file_path)) 47 return std::string(); 48 std::string contents; 49 if (!base::ReadFileToString(ati_file_path, &contents)) 50 return std::string(); 51 base::StringTokenizer t(contents, "\r\n"); 52 while (t.GetNext()) { 53 std::string line = t.token(); 54 if (StartsWithASCII(line, "ReleaseVersion=", true)) { 55 size_t begin = line.find_first_of("0123456789"); 56 if (begin != std::string::npos) { 57 size_t end = line.find_first_not_of("0123456789.", begin); 58 if (end == std::string::npos) 59 return line.substr(begin); 60 else 61 return line.substr(begin, end - begin); 62 } 63 } 64 } 65 return std::string(); 66 } 67 68 const uint32 kVendorIDIntel = 0x8086; 69 const uint32 kVendorIDNVidia = 0x10de; 70 const uint32 kVendorIDAMD = 0x1002; 71 72 CollectInfoResult CollectPCIVideoCardInfo(GPUInfo* gpu_info) { 73 DCHECK(gpu_info); 74 75 if (IsPciSupported() == false) { 76 VLOG(1) << "PCI bus scanning is not supported"; 77 return kCollectInfoNonFatalFailure; 78 } 79 80 // TODO(zmo): be more flexible about library name. 81 LibPciLoader libpci_loader; 82 if (!libpci_loader.Load("libpci.so.3") && 83 !libpci_loader.Load("libpci.so")) { 84 VLOG(1) << "Failed to locate libpci"; 85 return kCollectInfoNonFatalFailure; 86 } 87 88 pci_access* access = (libpci_loader.pci_alloc)(); 89 DCHECK(access != NULL); 90 (libpci_loader.pci_init)(access); 91 (libpci_loader.pci_scan_bus)(access); 92 bool primary_gpu_identified = false; 93 for (pci_dev* device = access->devices; 94 device != NULL; device = device->next) { 95 // Fill the IDs and class fields. 96 (libpci_loader.pci_fill_info)(device, 33); 97 bool is_gpu = false; 98 switch (device->device_class) { 99 case PCI_CLASS_DISPLAY_VGA: 100 case PCI_CLASS_DISPLAY_XGA: 101 case PCI_CLASS_DISPLAY_3D: 102 is_gpu = true; 103 break; 104 case PCI_CLASS_DISPLAY_OTHER: 105 default: 106 break; 107 } 108 if (!is_gpu) 109 continue; 110 if (device->vendor_id == 0 || device->device_id == 0) 111 continue; 112 113 GPUInfo::GPUDevice gpu; 114 gpu.vendor_id = device->vendor_id; 115 gpu.device_id = device->device_id; 116 117 if (!primary_gpu_identified) { 118 primary_gpu_identified = true; 119 gpu_info->gpu = gpu; 120 } else { 121 // TODO(zmo): if there are multiple GPUs, we assume the non Intel 122 // one is primary. Revisit this logic because we actually don't know 123 // which GPU we are using at this point. 124 if (gpu_info->gpu.vendor_id == kVendorIDIntel && 125 gpu.vendor_id != kVendorIDIntel) { 126 gpu_info->secondary_gpus.push_back(gpu_info->gpu); 127 gpu_info->gpu = gpu; 128 } else { 129 gpu_info->secondary_gpus.push_back(gpu); 130 } 131 } 132 } 133 134 // Detect Optimus or AMD Switchable GPU. 135 if (gpu_info->secondary_gpus.size() == 1 && 136 gpu_info->secondary_gpus[0].vendor_id == kVendorIDIntel) { 137 if (gpu_info->gpu.vendor_id == kVendorIDNVidia) 138 gpu_info->optimus = true; 139 if (gpu_info->gpu.vendor_id == kVendorIDAMD) 140 gpu_info->amd_switchable = true; 141 } 142 143 (libpci_loader.pci_cleanup)(access); 144 if (!primary_gpu_identified) 145 return kCollectInfoNonFatalFailure; 146 return kCollectInfoSuccess; 147 } 148 149 } // namespace anonymous 150 151 CollectInfoResult CollectContextGraphicsInfo(GPUInfo* gpu_info) { 152 DCHECK(gpu_info); 153 154 TRACE_EVENT0("gpu", "gpu_info_collector::CollectGraphicsInfo"); 155 156 if (CommandLine::ForCurrentProcess()->HasSwitch( 157 switches::kGpuNoContextLost)) { 158 gpu_info->can_lose_context = false; 159 } else { 160 #if defined(OS_CHROMEOS) 161 gpu_info->can_lose_context = false; 162 #else 163 // TODO(zmo): need to consider the case where we are running on top 164 // of desktop GL and GL_ARB_robustness extension is available. 165 gpu_info->can_lose_context = 166 (gfx::GetGLImplementation() == gfx::kGLImplementationEGLGLES2); 167 #endif 168 } 169 170 CollectInfoResult result = CollectGraphicsInfoGL(gpu_info); 171 gpu_info->context_info_state = result; 172 return result; 173 } 174 175 CollectInfoResult CollectGpuID(uint32* vendor_id, uint32* device_id) { 176 DCHECK(vendor_id && device_id); 177 *vendor_id = 0; 178 *device_id = 0; 179 180 GPUInfo gpu_info; 181 CollectInfoResult result = CollectPCIVideoCardInfo(&gpu_info); 182 if (result == kCollectInfoSuccess) { 183 *vendor_id = gpu_info.gpu.vendor_id; 184 *device_id = gpu_info.gpu.device_id; 185 } 186 return result; 187 } 188 189 CollectInfoResult CollectBasicGraphicsInfo(GPUInfo* gpu_info) { 190 DCHECK(gpu_info); 191 192 CollectInfoResult result = CollectPCIVideoCardInfo(gpu_info); 193 194 std::string driver_version; 195 switch (gpu_info->gpu.vendor_id) { 196 case kVendorIDAMD: 197 driver_version = CollectDriverVersionATI(); 198 if (!driver_version.empty()) { 199 gpu_info->driver_vendor = "ATI / AMD"; 200 gpu_info->driver_version = driver_version; 201 } 202 break; 203 case kVendorIDNVidia: 204 driver_version = CollectDriverVersionNVidia(); 205 if (!driver_version.empty()) { 206 gpu_info->driver_vendor = "NVIDIA"; 207 gpu_info->driver_version = driver_version; 208 } 209 break; 210 case kVendorIDIntel: 211 // In dual-GPU cases, sometimes PCI scan only gives us the 212 // integrated GPU (i.e., the Intel one). 213 if (gpu_info->secondary_gpus.size() == 0) { 214 driver_version = CollectDriverVersionNVidia(); 215 if (!driver_version.empty()) { 216 gpu_info->driver_vendor = "NVIDIA"; 217 gpu_info->driver_version = driver_version; 218 gpu_info->optimus = true; 219 // Put Intel to the secondary GPU list. 220 gpu_info->secondary_gpus.push_back(gpu_info->gpu); 221 // Put NVIDIA as the primary GPU. 222 gpu_info->gpu.vendor_id = kVendorIDNVidia; 223 gpu_info->gpu.device_id = 0; // Unknown Device. 224 } 225 } 226 break; 227 } 228 229 gpu_info->basic_info_state = result; 230 return result; 231 } 232 233 CollectInfoResult CollectDriverInfoGL(GPUInfo* gpu_info) { 234 DCHECK(gpu_info); 235 236 std::string gl_version = gpu_info->gl_version; 237 if (StartsWithASCII(gl_version, "OpenGL ES", true)) 238 gl_version = gl_version.substr(10); 239 std::vector<std::string> pieces; 240 base::SplitStringAlongWhitespace(gl_version, &pieces); 241 // In linux, the gl version string might be in the format of 242 // GLVersion DriverVendor DriverVersion 243 if (pieces.size() < 3) 244 return kCollectInfoNonFatalFailure; 245 246 std::string driver_version = pieces[2]; 247 size_t pos = driver_version.find_first_not_of("0123456789."); 248 if (pos == 0) 249 return kCollectInfoNonFatalFailure; 250 if (pos != std::string::npos) 251 driver_version = driver_version.substr(0, pos); 252 253 gpu_info->driver_vendor = pieces[1]; 254 gpu_info->driver_version = driver_version; 255 return kCollectInfoSuccess; 256 } 257 258 void MergeGPUInfo(GPUInfo* basic_gpu_info, 259 const GPUInfo& context_gpu_info) { 260 MergeGPUInfoGL(basic_gpu_info, context_gpu_info); 261 } 262 263 } // namespace gpu 264