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 (!file_util::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 LOG(INFO) << "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 } // namespace anonymous 166 167 bool CollectContextGraphicsInfo(GPUInfo* gpu_info) { 168 DCHECK(gpu_info); 169 170 TRACE_EVENT0("gpu", "gpu_info_collector::CollectGraphicsInfo"); 171 172 if (CommandLine::ForCurrentProcess()->HasSwitch( 173 switches::kGpuNoContextLost)) { 174 gpu_info->can_lose_context = false; 175 } else { 176 #if defined(OS_CHROMEOS) 177 gpu_info->can_lose_context = false; 178 #else 179 // TODO(zmo): need to consider the case where we are running on top 180 // of desktop GL and GL_ARB_robustness extension is available. 181 gpu_info->can_lose_context = 182 (gfx::GetGLImplementation() == gfx::kGLImplementationEGLGLES2); 183 #endif 184 } 185 186 gpu_info->finalized = true; 187 bool rt = CollectGraphicsInfoGL(gpu_info); 188 189 return rt; 190 } 191 192 GpuIDResult CollectGpuID(uint32* vendor_id, uint32* device_id) { 193 DCHECK(vendor_id && device_id); 194 *vendor_id = 0; 195 *device_id = 0; 196 197 GPUInfo gpu_info; 198 if (CollectPCIVideoCardInfo(&gpu_info)) { 199 *vendor_id = gpu_info.gpu.vendor_id; 200 *device_id = gpu_info.gpu.device_id; 201 return kGpuIDSuccess; 202 } 203 return kGpuIDFailure; 204 } 205 206 bool CollectBasicGraphicsInfo(GPUInfo* gpu_info) { 207 DCHECK(gpu_info); 208 209 bool rt = CollectPCIVideoCardInfo(gpu_info); 210 211 std::string driver_version; 212 switch (gpu_info->gpu.vendor_id) { 213 case kVendorIDAMD: 214 driver_version = CollectDriverVersionATI(); 215 if (!driver_version.empty()) { 216 gpu_info->driver_vendor = "ATI / AMD"; 217 gpu_info->driver_version = driver_version; 218 } 219 break; 220 case kVendorIDNVidia: 221 driver_version = CollectDriverVersionNVidia(); 222 if (!driver_version.empty()) { 223 gpu_info->driver_vendor = "NVIDIA"; 224 gpu_info->driver_version = driver_version; 225 } 226 break; 227 case kVendorIDIntel: 228 // In dual-GPU cases, sometimes PCI scan only gives us the 229 // integrated GPU (i.e., the Intel one). 230 driver_version = CollectDriverVersionNVidia(); 231 if (!driver_version.empty()) { 232 gpu_info->driver_vendor = "NVIDIA"; 233 gpu_info->driver_version = driver_version; 234 // Machines with more than two GPUs are not handled. 235 if (gpu_info->secondary_gpus.size() <= 1) 236 gpu_info->optimus = true; 237 } 238 break; 239 } 240 241 return rt; 242 } 243 244 bool CollectDriverInfoGL(GPUInfo* gpu_info) { 245 DCHECK(gpu_info); 246 247 std::string gl_version_string = gpu_info->gl_version_string; 248 if (StartsWithASCII(gl_version_string, "OpenGL ES", true)) 249 gl_version_string = gl_version_string.substr(10); 250 std::vector<std::string> pieces; 251 base::SplitStringAlongWhitespace(gl_version_string, &pieces); 252 // In linux, the gl version string might be in the format of 253 // GLVersion DriverVendor DriverVersion 254 if (pieces.size() < 3) 255 return false; 256 257 std::string driver_version = pieces[2]; 258 size_t pos = driver_version.find_first_not_of("0123456789."); 259 if (pos == 0) 260 return false; 261 if (pos != std::string::npos) 262 driver_version = driver_version.substr(0, pos); 263 264 gpu_info->driver_vendor = pieces[1]; 265 gpu_info->driver_version = driver_version; 266 return true; 267 } 268 269 void MergeGPUInfo(GPUInfo* basic_gpu_info, 270 const GPUInfo& context_gpu_info) { 271 MergeGPUInfoGL(basic_gpu_info, context_gpu_info); 272 } 273 274 } // namespace gpu 275