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 <X11/Xlib.h> 8 #include <vector> 9 10 #include "base/command_line.h" 11 #include "base/debug/trace_event.h" 12 #include "base/file_util.h" 13 #include "base/logging.h" 14 #include "base/memory/scoped_ptr.h" 15 #include "base/message_loop/message_loop.h" 16 #include "base/strings/string_piece.h" 17 #include "base/strings/string_split.h" 18 #include "base/strings/string_tokenizer.h" 19 #include "base/strings/string_util.h" 20 #include "library_loaders/libpci.h" 21 #include "third_party/libXNVCtrl/NVCtrl.h" 22 #include "third_party/libXNVCtrl/NVCtrlLib.h" 23 #include "ui/gl/gl_bindings.h" 24 #include "ui/gl/gl_context.h" 25 #include "ui/gl/gl_implementation.h" 26 #include "ui/gl/gl_surface.h" 27 #include "ui/gl/gl_switches.h" 28 29 namespace gpu { 30 31 namespace { 32 33 // This checks if a system supports PCI bus. 34 // We check the existence of /sys/bus/pci or /sys/bug/pci_express. 35 bool IsPciSupported() { 36 const base::FilePath pci_path("/sys/bus/pci/"); 37 const base::FilePath pcie_path("/sys/bus/pci_express/"); 38 return (base::PathExists(pci_path) || 39 base::PathExists(pcie_path)); 40 } 41 42 // Scan /etc/ati/amdpcsdb.default for "ReleaseVersion". 43 // Return empty string on failing. 44 std::string CollectDriverVersionATI() { 45 const base::FilePath::CharType kATIFileName[] = 46 FILE_PATH_LITERAL("/etc/ati/amdpcsdb.default"); 47 base::FilePath ati_file_path(kATIFileName); 48 if (!base::PathExists(ati_file_path)) 49 return std::string(); 50 std::string contents; 51 if (!base::ReadFileToString(ati_file_path, &contents)) 52 return std::string(); 53 base::StringTokenizer t(contents, "\r\n"); 54 while (t.GetNext()) { 55 std::string line = t.token(); 56 if (StartsWithASCII(line, "ReleaseVersion=", true)) { 57 size_t begin = line.find_first_of("0123456789"); 58 if (begin != std::string::npos) { 59 size_t end = line.find_first_not_of("0123456789.", begin); 60 if (end == std::string::npos) 61 return line.substr(begin); 62 else 63 return line.substr(begin, end - begin); 64 } 65 } 66 } 67 return std::string(); 68 } 69 70 // Use NVCtrl extention to query NV driver version. 71 // Return empty string on failing. 72 std::string CollectDriverVersionNVidia() { 73 Display* display = base::MessagePumpForUI::GetDefaultXDisplay(); 74 if (!display) { 75 LOG(ERROR) << "XOpenDisplay failed."; 76 return std::string(); 77 } 78 int event_base = 0, error_base = 0; 79 if (!XNVCTRLQueryExtension(display, &event_base, &error_base)) { 80 VLOG(1) << "NVCtrl extension does not exist."; 81 return std::string(); 82 } 83 int screen_count = ScreenCount(display); 84 for (int screen = 0; screen < screen_count; ++screen) { 85 char* buffer = NULL; 86 if (XNVCTRLIsNvScreen(display, screen) && 87 XNVCTRLQueryStringAttribute(display, screen, 0, 88 NV_CTRL_STRING_NVIDIA_DRIVER_VERSION, 89 &buffer)) { 90 std::string driver_version(buffer); 91 XFree(buffer); 92 return driver_version; 93 } 94 } 95 return std::string(); 96 } 97 98 const uint32 kVendorIDIntel = 0x8086; 99 const uint32 kVendorIDNVidia = 0x10de; 100 const uint32 kVendorIDAMD = 0x1002; 101 102 bool CollectPCIVideoCardInfo(GPUInfo* gpu_info) { 103 DCHECK(gpu_info); 104 105 if (IsPciSupported() == false) { 106 VLOG(1) << "PCI bus scanning is not supported"; 107 return false; 108 } 109 110 // TODO(zmo): be more flexible about library name. 111 LibPciLoader libpci_loader; 112 if (!libpci_loader.Load("libpci.so.3") && 113 !libpci_loader.Load("libpci.so")) { 114 VLOG(1) << "Failed to locate libpci"; 115 return false; 116 } 117 118 pci_access* access = (libpci_loader.pci_alloc)(); 119 DCHECK(access != NULL); 120 (libpci_loader.pci_init)(access); 121 (libpci_loader.pci_scan_bus)(access); 122 bool primary_gpu_identified = false; 123 for (pci_dev* device = access->devices; 124 device != NULL; device = device->next) { 125 // Fill the IDs and class fields. 126 (libpci_loader.pci_fill_info)(device, 33); 127 // TODO(zmo): there might be other classes that qualify as display devices. 128 if (device->device_class != 0x0300) // Device class is DISPLAY_VGA. 129 continue; 130 131 GPUInfo::GPUDevice gpu; 132 gpu.vendor_id = device->vendor_id; 133 gpu.device_id = device->device_id; 134 135 if (!primary_gpu_identified) { 136 primary_gpu_identified = true; 137 gpu_info->gpu = gpu; 138 } else { 139 // TODO(zmo): if there are multiple GPUs, we assume the non Intel 140 // one is primary. Revisit this logic because we actually don't know 141 // which GPU we are using at this point. 142 if (gpu_info->gpu.vendor_id == kVendorIDIntel && 143 gpu.vendor_id != kVendorIDIntel) { 144 gpu_info->secondary_gpus.push_back(gpu_info->gpu); 145 gpu_info->gpu = gpu; 146 } else { 147 gpu_info->secondary_gpus.push_back(gpu); 148 } 149 } 150 } 151 152 // Detect Optimus or AMD Switchable GPU. 153 if (gpu_info->secondary_gpus.size() == 1 && 154 gpu_info->secondary_gpus[0].vendor_id == kVendorIDIntel) { 155 if (gpu_info->gpu.vendor_id == kVendorIDNVidia) 156 gpu_info->optimus = true; 157 if (gpu_info->gpu.vendor_id == kVendorIDAMD) 158 gpu_info->amd_switchable = true; 159 } 160 161 (libpci_loader.pci_cleanup)(access); 162 return (primary_gpu_identified); 163 } 164 165 // Find the first GPU in |secondary_gpus| list that matches |active_vendor| 166 // and switch it to primary gpu. 167 // Return false if we cannot find a match. 168 bool FindAndSetActiveGPU(GPUInfo* gpu_info, uint32 active_vendor) { 169 DCHECK(gpu_info); 170 GPUInfo::GPUDevice* device = NULL; 171 for (size_t ii = 0; ii < gpu_info->secondary_gpus.size(); ++ii) { 172 if (gpu_info->secondary_gpus[ii].vendor_id == active_vendor) { 173 device = &(gpu_info->secondary_gpus[ii]); 174 break; 175 } 176 } 177 if (device == NULL) 178 return false; 179 GPUInfo::GPUDevice temp = gpu_info->gpu; 180 gpu_info->gpu = *device; 181 *device = temp; 182 return true; 183 } 184 185 } // namespace anonymous 186 187 bool CollectContextGraphicsInfo(GPUInfo* gpu_info) { 188 DCHECK(gpu_info); 189 190 TRACE_EVENT0("gpu", "gpu_info_collector::CollectGraphicsInfo"); 191 192 if (CommandLine::ForCurrentProcess()->HasSwitch( 193 switches::kGpuNoContextLost)) { 194 gpu_info->can_lose_context = false; 195 } else { 196 #if defined(OS_CHROMEOS) 197 gpu_info->can_lose_context = false; 198 #else 199 // TODO(zmo): need to consider the case where we are running on top 200 // of desktop GL and GL_ARB_robustness extension is available. 201 gpu_info->can_lose_context = 202 (gfx::GetGLImplementation() == gfx::kGLImplementationEGLGLES2); 203 #endif 204 } 205 206 gpu_info->finalized = true; 207 bool rt = CollectGraphicsInfoGL(gpu_info); 208 209 return rt; 210 } 211 212 GpuIDResult CollectGpuID(uint32* vendor_id, uint32* device_id) { 213 DCHECK(vendor_id && device_id); 214 *vendor_id = 0; 215 *device_id = 0; 216 217 GPUInfo gpu_info; 218 if (CollectPCIVideoCardInfo(&gpu_info)) { 219 *vendor_id = gpu_info.gpu.vendor_id; 220 *device_id = gpu_info.gpu.device_id; 221 return kGpuIDSuccess; 222 } 223 return kGpuIDFailure; 224 } 225 226 bool CollectBasicGraphicsInfo(GPUInfo* gpu_info) { 227 DCHECK(gpu_info); 228 229 bool rt = CollectPCIVideoCardInfo(gpu_info); 230 231 std::string driver_version; 232 switch (gpu_info->gpu.vendor_id) { 233 case kVendorIDAMD: 234 driver_version = CollectDriverVersionATI(); 235 if (!driver_version.empty()) { 236 gpu_info->driver_vendor = "ATI / AMD"; 237 gpu_info->driver_version = driver_version; 238 } 239 break; 240 case kVendorIDNVidia: 241 driver_version = CollectDriverVersionNVidia(); 242 if (!driver_version.empty()) { 243 gpu_info->driver_vendor = "NVIDIA"; 244 gpu_info->driver_version = driver_version; 245 } 246 break; 247 case kVendorIDIntel: 248 // In dual-GPU cases, sometimes PCI scan only gives us the 249 // integrated GPU (i.e., the Intel one). 250 driver_version = CollectDriverVersionNVidia(); 251 if (!driver_version.empty()) { 252 gpu_info->driver_vendor = "NVIDIA"; 253 gpu_info->driver_version = driver_version; 254 // Machines with more than two GPUs are not handled. 255 if (gpu_info->secondary_gpus.size() <= 1) 256 gpu_info->optimus = true; 257 } 258 break; 259 } 260 261 return rt; 262 } 263 264 bool CollectDriverInfoGL(GPUInfo* gpu_info) { 265 DCHECK(gpu_info); 266 267 std::string gl_version_string = gpu_info->gl_version_string; 268 if (StartsWithASCII(gl_version_string, "OpenGL ES", true)) 269 gl_version_string = gl_version_string.substr(10); 270 std::vector<std::string> pieces; 271 base::SplitStringAlongWhitespace(gl_version_string, &pieces); 272 // In linux, the gl version string might be in the format of 273 // GLVersion DriverVendor DriverVersion 274 if (pieces.size() < 3) 275 return false; 276 277 std::string driver_version = pieces[2]; 278 size_t pos = driver_version.find_first_not_of("0123456789."); 279 if (pos == 0) 280 return false; 281 if (pos != std::string::npos) 282 driver_version = driver_version.substr(0, pos); 283 284 gpu_info->driver_vendor = pieces[1]; 285 gpu_info->driver_version = driver_version; 286 return true; 287 } 288 289 void MergeGPUInfo(GPUInfo* basic_gpu_info, 290 const GPUInfo& context_gpu_info) { 291 MergeGPUInfoGL(basic_gpu_info, context_gpu_info); 292 } 293 294 bool DetermineActiveGPU(GPUInfo* gpu_info) { 295 DCHECK(gpu_info); 296 if (gpu_info->secondary_gpus.size() == 0) 297 return true; 298 if (gpu_info->gl_vendor.empty()) 299 return false; 300 uint32 active_vendor = 0; 301 // For now we only handle Intel/NVIDIA/AMD. 302 if (gpu_info->gl_vendor.find("Intel") != std::string::npos) { 303 if (gpu_info->gpu.vendor_id == kVendorIDIntel) 304 return true; 305 active_vendor = kVendorIDIntel; 306 } 307 if (gpu_info->gl_vendor.find("NVIDIA") != std::string::npos) { 308 if (gpu_info->gpu.vendor_id == kVendorIDNVidia) 309 return true; 310 active_vendor = kVendorIDNVidia; 311 } 312 if (gpu_info->gl_vendor.find("ATI") != std::string::npos || 313 gpu_info->gl_vendor.find("AMD") != std::string::npos) { 314 if (gpu_info->gpu.vendor_id == kVendorIDAMD) 315 return true; 316 active_vendor = kVendorIDAMD; 317 } 318 if (active_vendor == 0) 319 return false; 320 return FindAndSetActiveGPU(gpu_info, active_vendor); 321 } 322 323 } // namespace gpu 324