blob: d849cab7f25b6873d3cf512a9037aed056856fdf [file] [log] [blame]
[email protected]b4097b12012-03-29 03:28:301// Copyright (c) 2012 The Chromium Authors. All rights reserved.
[email protected]1bf14bc2010-07-09 16:10:432// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
[email protected]623c0bd2011-03-12 01:00:415#include "content/gpu/gpu_info_collector.h"
[email protected]1bf14bc2010-07-09 16:10:436
[email protected]ba8a61c2012-06-21 22:31:067#include <X11/Xlib.h>
[email protected]48fe9f32010-12-21 16:54:108#include <vector>
9
[email protected]a6057292011-07-28 21:26:5610#include "base/command_line.h"
[email protected]d13f35d2012-05-18 02:28:1511#include "base/debug/trace_event.h"
[email protected]67f88572011-03-01 21:29:2712#include "base/file_util.h"
[email protected]48fe9f32010-12-21 16:54:1013#include "base/logging.h"
[email protected]3b63f8f42011-03-28 01:54:1514#include "base/memory/scoped_ptr.h"
[email protected]ba8a61c2012-06-21 22:31:0615#include "base/message_loop.h"
[email protected]7004d7eb2011-01-21 00:27:5316#include "base/string_piece.h"
17#include "base/string_split.h"
[email protected]3225aeb2011-03-22 21:34:5918#include "base/string_tokenizer.h"
[email protected]48fe9f32010-12-21 16:54:1019#include "base/string_util.h"
[email protected]dfbff862012-11-28 19:08:1420#include "library_loaders/libpci.h"
[email protected]ba8a61c2012-06-21 22:31:0621#include "third_party/libXNVCtrl/NVCtrl.h"
22#include "third_party/libXNVCtrl/NVCtrlLib.h"
[email protected]c9e2cbbb2012-05-12 21:17:2723#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"
[email protected]48fe9f32010-12-21 16:54:1028
29namespace {
30
[email protected]67f88572011-03-01 21:29:2731// This checks if a system supports PCI bus.
32// We check the existence of /sys/bus/pci or /sys/bug/pci_express.
33bool IsPciSupported() {
34 const FilePath pci_path("/sys/bus/pci/");
35 const FilePath pcie_path("/sys/bus/pci_express/");
36 return (file_util::PathExists(pci_path) ||
37 file_util::PathExists(pcie_path));
38}
39
[email protected]3225aeb2011-03-22 21:34:5940// Scan /etc/ati/amdpcsdb.default for "ReleaseVersion".
[email protected]ba8a61c2012-06-21 22:31:0641// Return empty string on failing.
[email protected]3225aeb2011-03-22 21:34:5942std::string CollectDriverVersionATI() {
43 const FilePath::CharType kATIFileName[] =
44 FILE_PATH_LITERAL("/etc/ati/amdpcsdb.default");
45 FilePath ati_file_path(kATIFileName);
46 if (!file_util::PathExists(ati_file_path))
[email protected]ba8a61c2012-06-21 22:31:0647 return std::string();
[email protected]3225aeb2011-03-22 21:34:5948 std::string contents;
49 if (!file_util::ReadFileToString(ati_file_path, &contents))
[email protected]ba8a61c2012-06-21 22:31:0650 return std::string();
[email protected]3225aeb2011-03-22 21:34:5951 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 }
[email protected]ba8a61c2012-06-21 22:31:0665 return std::string();
66}
67
68// Use NVCtrl extention to query NV driver version.
69// Return empty string on failing.
70std::string CollectDriverVersionNVidia() {
71 Display* display = base::MessagePumpForUI::GetDefaultXDisplay();
72 if (!display) {
73 LOG(ERROR) << "XOpenDisplay failed.";
74 return std::string();
75 }
76 int event_base = 0, error_base = 0;
77 if (!XNVCTRLQueryExtension(display, &event_base, &error_base)) {
[email protected]2e1f57c2012-09-05 03:56:3378 LOG(INFO) << "NVCtrl extension does not exist.";
[email protected]ba8a61c2012-06-21 22:31:0679 return std::string();
80 }
81 int screen_count = ScreenCount(display);
82 for (int screen = 0; screen < screen_count; ++screen) {
83 char* buffer = NULL;
84 if (XNVCTRLIsNvScreen(display, screen) &&
85 XNVCTRLQueryStringAttribute(display, screen, 0,
86 NV_CTRL_STRING_NVIDIA_DRIVER_VERSION,
87 &buffer)) {
88 std::string driver_version(buffer);
89 XFree(buffer);
90 return driver_version;
91 }
92 }
93 return std::string();
[email protected]3225aeb2011-03-22 21:34:5994}
95
[email protected]a094e2c2012-05-10 23:02:4296const uint32 kVendorIDIntel = 0x8086;
97const uint32 kVendorIDNVidia = 0x10de;
98const uint32 kVendorIDAMD = 0x1002;
99
[email protected]48fe9f32010-12-21 16:54:10100} // namespace anonymous
101
[email protected]1bf14bc2010-07-09 16:10:43102namespace gpu_info_collector {
103
[email protected]a80f5ece2011-10-20 23:56:55104bool CollectGraphicsInfo(content::GPUInfo* gpu_info) {
[email protected]7004d7eb2011-01-21 00:27:53105 DCHECK(gpu_info);
[email protected]21de1e42012-11-14 23:38:40106 *gpu_info = content::GPUInfo();
[email protected]41579ae2010-11-15 22:31:26107
[email protected]d13f35d2012-05-18 02:28:15108 TRACE_EVENT0("gpu", "gpu_info_collector::CollectGraphicsInfo");
109
[email protected]a6057292011-07-28 21:26:56110 if (CommandLine::ForCurrentProcess()->HasSwitch(
111 switches::kGpuNoContextLost)) {
112 gpu_info->can_lose_context = false;
113 } else {
[email protected]6df3b33e2012-11-01 19:33:41114#if defined(OS_CHROMEOS)
115 gpu_info->can_lose_context = false;
116#else
[email protected]a6057292011-07-28 21:26:56117 // TODO(zmo): need to consider the case where we are running on top
118 // of desktop GL and GL_ARB_robustness extension is available.
119 gpu_info->can_lose_context =
120 (gfx::GetGLImplementation() == gfx::kGLImplementationEGLGLES2);
[email protected]6df3b33e2012-11-01 19:33:41121#endif
[email protected]a6057292011-07-28 21:26:56122 }
123
[email protected]6cc8ece62011-03-14 20:05:47124 gpu_info->finalized = true;
[email protected]29ad10c2011-11-23 19:48:06125 bool rt = CollectGraphicsInfoGL(gpu_info);
[email protected]079c4ad2011-02-19 00:05:57126
[email protected]079c4ad2011-02-19 00:05:57127 return rt;
128}
129
[email protected]29ad10c2011-11-23 19:48:06130bool CollectPreliminaryGraphicsInfo(content::GPUInfo* gpu_info) {
131 DCHECK(gpu_info);
132
[email protected]b4097b12012-03-29 03:28:30133 bool rt = CollectVideoCardInfo(gpu_info);
134
[email protected]ba8a61c2012-06-21 22:31:06135 std::string driver_version;
136 switch (gpu_info->gpu.vendor_id) {
137 case kVendorIDAMD:
138 driver_version = CollectDriverVersionATI();
139 if (!driver_version.empty()) {
140 gpu_info->driver_vendor = "ATI / AMD";
141 gpu_info->driver_version = driver_version;
142 }
143 break;
144 case kVendorIDNVidia:
145 driver_version = CollectDriverVersionNVidia();
146 if (!driver_version.empty()) {
147 gpu_info->driver_vendor = "NVIDIA";
148 gpu_info->driver_version = driver_version;
149 }
150 break;
[email protected]e98b98432012-08-22 23:03:13151 case kVendorIDIntel:
152 // In dual-GPU cases, sometimes PCI scan only gives us the
153 // integrated GPU (i.e., the Intel one).
154 driver_version = CollectDriverVersionNVidia();
155 if (!driver_version.empty()) {
156 gpu_info->driver_vendor = "NVIDIA";
157 gpu_info->driver_version = driver_version;
158 // Machines with more than two GPUs are not handled.
159 if (gpu_info->secondary_gpus.size() <= 1)
160 gpu_info->optimus = true;
161 }
162 break;
[email protected]b4097b12012-03-29 03:28:30163 }
164
165 return rt;
[email protected]29ad10c2011-11-23 19:48:06166}
167
[email protected]a80f5ece2011-10-20 23:56:55168bool CollectVideoCardInfo(content::GPUInfo* gpu_info) {
[email protected]7004d7eb2011-01-21 00:27:53169 DCHECK(gpu_info);
[email protected]41579ae2010-11-15 22:31:26170
[email protected]67f88572011-03-01 21:29:27171 if (IsPciSupported() == false) {
[email protected]7ace0ef4e2011-03-17 21:23:05172 VLOG(1) << "PCI bus scanning is not supported";
[email protected]67f88572011-03-01 21:29:27173 return false;
174 }
175
[email protected]48fe9f32010-12-21 16:54:10176 // TODO(zmo): be more flexible about library name.
[email protected]dfbff862012-11-28 19:08:14177 LibPciLoader libpci_loader;
178 if (!libpci_loader.Load("libpci.so.3") &&
179 !libpci_loader.Load("libpci.so")) {
[email protected]7ace0ef4e2011-03-17 21:23:05180 VLOG(1) << "Failed to locate libpci";
[email protected]48fe9f32010-12-21 16:54:10181 return false;
[email protected]c5790ae2011-03-01 16:51:49182 }
[email protected]48fe9f32010-12-21 16:54:10183
[email protected]dfbff862012-11-28 19:08:14184 pci_access* access = (libpci_loader.pci_alloc)();
[email protected]48fe9f32010-12-21 16:54:10185 DCHECK(access != NULL);
[email protected]dfbff862012-11-28 19:08:14186 (libpci_loader.pci_init)(access);
187 (libpci_loader.pci_scan_bus)(access);
[email protected]a094e2c2012-05-10 23:02:42188 bool primary_gpu_identified = false;
[email protected]9c87f012012-11-01 20:08:46189 for (pci_dev* device = access->devices;
[email protected]48fe9f32010-12-21 16:54:10190 device != NULL; device = device->next) {
[email protected]dfbff862012-11-28 19:08:14191 // Fill the IDs and class fields.
192 (libpci_loader.pci_fill_info)(device, 33);
[email protected]7004d7eb2011-01-21 00:27:53193 // TODO(zmo): there might be other classes that qualify as display devices.
[email protected]a094e2c2012-05-10 23:02:42194 if (device->device_class != 0x0300) // Device class is DISPLAY_VGA.
195 continue;
196
197 content::GPUInfo::GPUDevice gpu;
198 gpu.vendor_id = device->vendor_id;
199 gpu.device_id = device->device_id;
200
[email protected]48fe9f32010-12-21 16:54:10201 const int buffer_size = 255;
202 scoped_array<char> buffer(new char[buffer_size]);
[email protected]a094e2c2012-05-10 23:02:42203 // The current implementation of pci_lookup_name returns the same pointer
204 // as the passed in upon success, and a different one (NULL or a pointer
205 // to an error message) upon failure.
[email protected]dfbff862012-11-28 19:08:14206 if ((libpci_loader.pci_lookup_name)(access,
207 buffer.get(),
208 buffer_size,
209 1,
210 device->vendor_id) == buffer.get()) {
[email protected]a094e2c2012-05-10 23:02:42211 gpu.vendor_string = buffer.get();
212 }
[email protected]dfbff862012-11-28 19:08:14213 if ((libpci_loader.pci_lookup_name)(access,
214 buffer.get(),
215 buffer_size,
216 2,
217 device->vendor_id,
218 device->device_id) == buffer.get()) {
[email protected]48fe9f32010-12-21 16:54:10219 std::string device_string = buffer.get();
220 size_t begin = device_string.find_first_of('[');
221 size_t end = device_string.find_last_of(']');
222 if (begin != std::string::npos && end != std::string::npos &&
223 begin < end) {
224 device_string = device_string.substr(begin + 1, end - begin - 1);
225 }
[email protected]a094e2c2012-05-10 23:02:42226 gpu.device_string = device_string;
[email protected]48fe9f32010-12-21 16:54:10227 }
[email protected]a094e2c2012-05-10 23:02:42228
229 if (!primary_gpu_identified) {
230 primary_gpu_identified = true;
231 gpu_info->gpu = gpu;
232 } else {
233 // TODO(zmo): if there are multiple GPUs, we assume the non Intel
234 // one is primary. Revisit this logic because we actually don't know
235 // which GPU we are using at this point.
236 if (gpu_info->gpu.vendor_id == kVendorIDIntel &&
237 gpu.vendor_id != kVendorIDIntel) {
238 gpu_info->secondary_gpus.push_back(gpu_info->gpu);
239 gpu_info->gpu = gpu;
240 } else {
241 gpu_info->secondary_gpus.push_back(gpu);
242 }
243 }
[email protected]48fe9f32010-12-21 16:54:10244 }
[email protected]a094e2c2012-05-10 23:02:42245
246 // Detect Optimus or AMD Switchable GPU.
247 if (gpu_info->secondary_gpus.size() == 1 &&
248 gpu_info->secondary_gpus[0].vendor_id == kVendorIDIntel) {
249 if (gpu_info->gpu.vendor_id == kVendorIDNVidia)
250 gpu_info->optimus = true;
251 if (gpu_info->gpu.vendor_id == kVendorIDAMD)
252 gpu_info->amd_switchable = true;
[email protected]a61508e52011-03-08 17:59:42253 }
[email protected]a094e2c2012-05-10 23:02:42254
[email protected]dfbff862012-11-28 19:08:14255 (libpci_loader.pci_cleanup)(access);
[email protected]a094e2c2012-05-10 23:02:42256 return (primary_gpu_identified);
[email protected]1bf14bc2010-07-09 16:10:43257}
258
[email protected]a80f5ece2011-10-20 23:56:55259bool CollectDriverInfoGL(content::GPUInfo* gpu_info) {
[email protected]7004d7eb2011-01-21 00:27:53260 DCHECK(gpu_info);
261
[email protected]a61508e52011-03-08 17:59:42262 std::string gl_version_string = gpu_info->gl_version_string;
[email protected]aa2284d2011-08-04 18:36:03263 if (StartsWithASCII(gl_version_string, "OpenGL ES", true))
264 gl_version_string = gl_version_string.substr(10);
[email protected]7004d7eb2011-01-21 00:27:53265 std::vector<std::string> pieces;
266 base::SplitStringAlongWhitespace(gl_version_string, &pieces);
267 // In linux, the gl version string might be in the format of
268 // GLVersion DriverVendor DriverVersion
269 if (pieces.size() < 3)
270 return false;
271
272 std::string driver_version = pieces[2];
273 size_t pos = driver_version.find_first_not_of("0123456789.");
274 if (pos == 0)
275 return false;
276 if (pos != std::string::npos)
277 driver_version = driver_version.substr(0, pos);
278
[email protected]a61508e52011-03-08 17:59:42279 gpu_info->driver_vendor = pieces[1];
280 gpu_info->driver_version = driver_version;
[email protected]7004d7eb2011-01-21 00:27:53281 return true;
282}
283
[email protected]1bf14bc2010-07-09 16:10:43284} // namespace gpu_info_collector