blob: 0cf5b52605d4b2b90c5e7d5618b5ff8af3bd04b0 [file] [log] [blame]
[email protected]623c0bd2011-03-12 01:00:411// Copyright (c) 2011 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]48fe9f32010-12-21 16:54:107#include <dlfcn.h>
8#include <vector>
9
[email protected]a6057292011-07-28 21:26:5610#include "base/command_line.h"
[email protected]67f88572011-03-01 21:29:2711#include "base/file_util.h"
[email protected]48fe9f32010-12-21 16:54:1012#include "base/logging.h"
[email protected]3b63f8f42011-03-28 01:54:1513#include "base/memory/scoped_ptr.h"
[email protected]7004d7eb2011-01-21 00:27:5314#include "base/string_piece.h"
15#include "base/string_split.h"
[email protected]3225aeb2011-03-22 21:34:5916#include "base/string_tokenizer.h"
[email protected]48fe9f32010-12-21 16:54:1017#include "base/string_util.h"
[email protected]5ae0b282011-03-28 19:24:4918#include "ui/gfx/gl/gl_bindings.h"
19#include "ui/gfx/gl/gl_context.h"
20#include "ui/gfx/gl/gl_implementation.h"
[email protected]baa900062011-08-09 20:53:4021#include "ui/gfx/gl/gl_surface.h"
[email protected]a6057292011-07-28 21:26:5622#include "ui/gfx/gl/gl_switches.h"
[email protected]48fe9f32010-12-21 16:54:1023
24namespace {
25
26// PciDevice and PciAccess are defined to access libpci functions. Their
27// members match the corresponding structures defined by libpci in size up to
28// fields we may access. For those members we don't use, their names are
29// defined as "fieldX", etc., or, left out if they are declared after the
30// members we care about in libpci.
31
32struct PciDevice {
33 PciDevice* next;
34
35 uint16 field0;
36 uint8 field1;
37 uint8 field2;
38 uint8 field3;
39 int field4;
40
41 uint16 vendor_id;
42 uint16 device_id;
43 uint16 device_class;
44};
45
46struct PciAccess {
47 unsigned int field0;
48 int field1;
49 int field2;
50 char* field3;
51 int field4;
52 int field5;
53 unsigned int field6;
54 int field7;
55
56 void (*function0)();
57 void (*function1)();
58 void (*function2)();
59
60 PciDevice* device_list;
61};
62
63// Define function types.
64typedef PciAccess* (*FT_pci_alloc)();
65typedef void (*FT_pci_init)(PciAccess*);
66typedef void (*FT_pci_cleanup)(PciAccess*);
67typedef void (*FT_pci_scan_bus)(PciAccess*);
68typedef void (*FT_pci_scan_bus)(PciAccess*);
69typedef int (*FT_pci_fill_info)(PciDevice*, int);
70typedef char* (*FT_pci_lookup_name)(PciAccess*, char*, int, int, ...);
71
72// This includes dynamically linked library handle and functions pointers from
73// libpci.
74struct PciInterface {
75 void* lib_handle;
76
77 FT_pci_alloc pci_alloc;
78 FT_pci_init pci_init;
79 FT_pci_cleanup pci_cleanup;
80 FT_pci_scan_bus pci_scan_bus;
81 FT_pci_fill_info pci_fill_info;
82 FT_pci_lookup_name pci_lookup_name;
83};
84
[email protected]67f88572011-03-01 21:29:2785// This checks if a system supports PCI bus.
86// We check the existence of /sys/bus/pci or /sys/bug/pci_express.
87bool IsPciSupported() {
88 const FilePath pci_path("/sys/bus/pci/");
89 const FilePath pcie_path("/sys/bus/pci_express/");
90 return (file_util::PathExists(pci_path) ||
91 file_util::PathExists(pcie_path));
92}
93
[email protected]48fe9f32010-12-21 16:54:1094// This dynamically opens libpci and get function pointers we need. Return
95// NULL if library fails to open or any functions can not be located.
96// Returned interface (if not NULL) should be deleted in FinalizeLibPci.
97PciInterface* InitializeLibPci(const char* lib_name) {
98 void* handle = dlopen(lib_name, RTLD_LAZY);
99 if (handle == NULL) {
[email protected]7ace0ef4e2011-03-17 21:23:05100 VLOG(1) << "Failed to dlopen " << lib_name;
[email protected]48fe9f32010-12-21 16:54:10101 return NULL;
102 }
103 PciInterface* interface = new struct PciInterface;
104 interface->lib_handle = handle;
105 interface->pci_alloc = reinterpret_cast<FT_pci_alloc>(
106 dlsym(handle, "pci_alloc"));
107 interface->pci_init = reinterpret_cast<FT_pci_init>(
108 dlsym(handle, "pci_init"));
109 interface->pci_cleanup = reinterpret_cast<FT_pci_cleanup>(
110 dlsym(handle, "pci_cleanup"));
111 interface->pci_scan_bus = reinterpret_cast<FT_pci_scan_bus>(
112 dlsym(handle, "pci_scan_bus"));
113 interface->pci_fill_info = reinterpret_cast<FT_pci_fill_info>(
114 dlsym(handle, "pci_fill_info"));
115 interface->pci_lookup_name = reinterpret_cast<FT_pci_lookup_name>(
116 dlsym(handle, "pci_lookup_name"));
117 if (interface->pci_alloc == NULL ||
118 interface->pci_init == NULL ||
119 interface->pci_cleanup == NULL ||
120 interface->pci_scan_bus == NULL ||
121 interface->pci_fill_info == NULL ||
122 interface->pci_lookup_name == NULL) {
[email protected]7ace0ef4e2011-03-17 21:23:05123 VLOG(1) << "Missing required function(s) from " << lib_name;
[email protected]48fe9f32010-12-21 16:54:10124 dlclose(handle);
125 delete interface;
126 return NULL;
127 }
128 return interface;
129}
130
131// This close the dynamically opened libpci and delete the interface.
[email protected]7004d7eb2011-01-21 00:27:53132void FinalizeLibPci(PciInterface** interface) {
133 DCHECK(interface && *interface && (*interface)->lib_handle);
134 dlclose((*interface)->lib_handle);
135 delete (*interface);
136 *interface = NULL;
[email protected]48fe9f32010-12-21 16:54:10137}
138
[email protected]3225aeb2011-03-22 21:34:59139// Scan /etc/ati/amdpcsdb.default for "ReleaseVersion".
140// Return "" on failing.
141std::string CollectDriverVersionATI() {
142 const FilePath::CharType kATIFileName[] =
143 FILE_PATH_LITERAL("/etc/ati/amdpcsdb.default");
144 FilePath ati_file_path(kATIFileName);
145 if (!file_util::PathExists(ati_file_path))
146 return "";
147 std::string contents;
148 if (!file_util::ReadFileToString(ati_file_path, &contents))
149 return "";
150 StringTokenizer t(contents, "\r\n");
151 while (t.GetNext()) {
152 std::string line = t.token();
153 if (StartsWithASCII(line, "ReleaseVersion=", true)) {
154 size_t begin = line.find_first_of("0123456789");
155 if (begin != std::string::npos) {
156 size_t end = line.find_first_not_of("0123456789.", begin);
157 if (end == std::string::npos)
158 return line.substr(begin);
159 else
160 return line.substr(begin, end - begin);
161 }
162 }
163 }
164 return "";
165}
166
[email protected]c1e70642011-04-08 00:47:22167// Use glXGetClientString to get driver vendor.
168// Return "" on failing.
169std::string CollectDriverVendorGlx() {
170 // TODO(zmo): handle the EGL/GLES2 case.
171 if (gfx::GetGLImplementation() != gfx::kGLImplementationDesktopGL)
172 return "";
173 Display* display = XOpenDisplay(NULL);
174 if (display == NULL)
175 return "";
176 std::string vendor = glXGetClientString(display, GLX_VENDOR);
177 XCloseDisplay(display);
178 return vendor;
179}
180
181// Return 0 on unrecognized vendor.
182uint32 VendorStringToID(const std::string& vendor_string) {
183 if (StartsWithASCII(vendor_string, "NVIDIA", true))
184 return 0x10de;
185 if (StartsWithASCII(vendor_string, "ATI", true))
186 return 0x1002;
187 // TODO(zmo): find a way to identify Intel cards.
188 return 0;
189}
190
[email protected]48fe9f32010-12-21 16:54:10191} // namespace anonymous
192
[email protected]1bf14bc2010-07-09 16:10:43193namespace gpu_info_collector {
194
[email protected]4bce24e2010-09-07 20:45:01195bool CollectGraphicsInfo(GPUInfo* gpu_info) {
[email protected]7004d7eb2011-01-21 00:27:53196 DCHECK(gpu_info);
[email protected]41579ae2010-11-15 22:31:26197
[email protected]a6057292011-07-28 21:26:56198 if (CommandLine::ForCurrentProcess()->HasSwitch(
199 switches::kGpuNoContextLost)) {
200 gpu_info->can_lose_context = false;
201 } else {
202 // TODO(zmo): need to consider the case where we are running on top
203 // of desktop GL and GL_ARB_robustness extension is available.
204 gpu_info->can_lose_context =
205 (gfx::GetGLImplementation() == gfx::kGLImplementationEGLGLES2);
206 }
207
[email protected]6cc8ece62011-03-14 20:05:47208 gpu_info->finalized = true;
[email protected]7004d7eb2011-01-21 00:27:53209 return CollectGraphicsInfoGL(gpu_info);
210}
211
[email protected]079c4ad2011-02-19 00:05:57212bool CollectPreliminaryGraphicsInfo(GPUInfo* gpu_info) {
213 DCHECK(gpu_info);
214
[email protected]baa900062011-08-09 20:53:40215 if (!gfx::GLSurface::InitializeOneOff()) {
216 LOG(ERROR) << "gfx::GLContext::InitializeOneOff() failed";
217 return false;
218 }
219
[email protected]079c4ad2011-02-19 00:05:57220 bool rt = true;
221 if (!CollectVideoCardInfo(gpu_info))
222 rt = false;
223
[email protected]3225aeb2011-03-22 21:34:59224 if (gpu_info->vendor_id == 0x1002) { // ATI
225 std::string ati_driver_version = CollectDriverVersionATI();
226 if (ati_driver_version != "") {
227 gpu_info->driver_vendor = "ATI / AMD";
228 gpu_info->driver_version = ati_driver_version;
229 }
230 }
[email protected]079c4ad2011-02-19 00:05:57231
232 return rt;
233}
234
[email protected]7004d7eb2011-01-21 00:27:53235bool CollectVideoCardInfo(GPUInfo* gpu_info) {
236 DCHECK(gpu_info);
[email protected]41579ae2010-11-15 22:31:26237
[email protected]c1e70642011-04-08 00:47:22238 std::string driver_vendor = CollectDriverVendorGlx();
239 if (!driver_vendor.empty()) {
240 gpu_info->driver_vendor = driver_vendor;
241 uint32 vendor_id = VendorStringToID(driver_vendor);
242 if (vendor_id != 0)
243 gpu_info->vendor_id = vendor_id;
244 }
245
[email protected]67f88572011-03-01 21:29:27246 if (IsPciSupported() == false) {
[email protected]7ace0ef4e2011-03-17 21:23:05247 VLOG(1) << "PCI bus scanning is not supported";
[email protected]67f88572011-03-01 21:29:27248 return false;
249 }
250
[email protected]48fe9f32010-12-21 16:54:10251 // TODO(zmo): be more flexible about library name.
252 PciInterface* interface = InitializeLibPci("libpci.so.3");
[email protected]7004d7eb2011-01-21 00:27:53253 if (interface == NULL)
[email protected]c5790ae2011-03-01 16:51:49254 interface = InitializeLibPci("libpci.so");
255 if (interface == NULL) {
[email protected]7ace0ef4e2011-03-17 21:23:05256 VLOG(1) << "Failed to locate libpci";
[email protected]48fe9f32010-12-21 16:54:10257 return false;
[email protected]c5790ae2011-03-01 16:51:49258 }
[email protected]48fe9f32010-12-21 16:54:10259
260 PciAccess* access = (interface->pci_alloc)();
261 DCHECK(access != NULL);
262 (interface->pci_init)(access);
263 (interface->pci_scan_bus)(access);
264 std::vector<PciDevice*> gpu_list;
265 PciDevice* gpu_active = NULL;
266 for (PciDevice* device = access->device_list;
267 device != NULL; device = device->next) {
268 (interface->pci_fill_info)(device, 33); // Fill the IDs and class fields.
[email protected]7004d7eb2011-01-21 00:27:53269 // TODO(zmo): there might be other classes that qualify as display devices.
[email protected]48fe9f32010-12-21 16:54:10270 if (device->device_class == 0x0300) { // Device class is DISPLAY_VGA.
[email protected]c1e70642011-04-08 00:47:22271 if (gpu_info->vendor_id == 0 || gpu_info->vendor_id == device->vendor_id)
272 gpu_list.push_back(device);
[email protected]48fe9f32010-12-21 16:54:10273 }
274 }
275 if (gpu_list.size() == 1) {
276 gpu_active = gpu_list[0];
277 } else {
278 // If more than one graphics card are identified, find the one that matches
279 // gl VENDOR and RENDERER info.
[email protected]a61508e52011-03-08 17:59:42280 std::string gl_vendor_string = gpu_info->gl_vendor;
281 std::string gl_renderer_string = gpu_info->gl_renderer;
[email protected]48fe9f32010-12-21 16:54:10282 const int buffer_size = 255;
283 scoped_array<char> buffer(new char[buffer_size]);
284 std::vector<PciDevice*> candidates;
285 for (size_t i = 0; i < gpu_list.size(); ++i) {
286 PciDevice* gpu = gpu_list[i];
287 // The current implementation of pci_lookup_name returns the same pointer
288 // as the passed in upon success, and a different one (NULL or a pointer
289 // to an error message) upon failure.
290 if ((interface->pci_lookup_name)(access,
291 buffer.get(),
292 buffer_size,
293 1,
294 gpu->vendor_id) != buffer.get())
295 continue;
296 std::string vendor_string = buffer.get();
297 const bool kCaseSensitive = false;
298 if (!StartsWithASCII(gl_vendor_string, vendor_string, kCaseSensitive))
299 continue;
300 if ((interface->pci_lookup_name)(access,
301 buffer.get(),
302 buffer_size,
303 2,
304 gpu->vendor_id,
305 gpu->device_id) != buffer.get())
306 continue;
307 std::string device_string = buffer.get();
308 size_t begin = device_string.find_first_of('[');
309 size_t end = device_string.find_last_of(']');
310 if (begin != std::string::npos && end != std::string::npos &&
311 begin < end) {
312 device_string = device_string.substr(begin + 1, end - begin - 1);
313 }
314 if (StartsWithASCII(gl_renderer_string, device_string, kCaseSensitive)) {
315 gpu_active = gpu;
316 break;
317 }
318 // If a device's vendor matches gl VENDOR string, we want to consider the
319 // possibility that libpci may not return the exact same name as gl
320 // RENDERER string.
321 candidates.push_back(gpu);
322 }
323 if (gpu_active == NULL && candidates.size() == 1)
324 gpu_active = candidates[0];
325 }
[email protected]a61508e52011-03-08 17:59:42326 if (gpu_active != NULL) {
327 gpu_info->vendor_id = gpu_active->vendor_id;
328 gpu_info->device_id = gpu_active->device_id;
329 }
[email protected]48fe9f32010-12-21 16:54:10330 (interface->pci_cleanup)(access);
[email protected]7004d7eb2011-01-21 00:27:53331 FinalizeLibPci(&interface);
[email protected]48fe9f32010-12-21 16:54:10332 return (gpu_active != NULL);
[email protected]1bf14bc2010-07-09 16:10:43333}
334
[email protected]766653092011-03-01 16:45:23335bool CollectDriverInfoGL(GPUInfo* gpu_info) {
[email protected]7004d7eb2011-01-21 00:27:53336 DCHECK(gpu_info);
337
[email protected]a61508e52011-03-08 17:59:42338 std::string gl_version_string = gpu_info->gl_version_string;
[email protected]aa2284d2011-08-04 18:36:03339 if (StartsWithASCII(gl_version_string, "OpenGL ES", true))
340 gl_version_string = gl_version_string.substr(10);
[email protected]7004d7eb2011-01-21 00:27:53341 std::vector<std::string> pieces;
342 base::SplitStringAlongWhitespace(gl_version_string, &pieces);
343 // In linux, the gl version string might be in the format of
344 // GLVersion DriverVendor DriverVersion
345 if (pieces.size() < 3)
346 return false;
347
348 std::string driver_version = pieces[2];
349 size_t pos = driver_version.find_first_not_of("0123456789.");
350 if (pos == 0)
351 return false;
352 if (pos != std::string::npos)
353 driver_version = driver_version.substr(0, pos);
354
[email protected]a61508e52011-03-08 17:59:42355 gpu_info->driver_vendor = pieces[1];
356 gpu_info->driver_version = driver_version;
[email protected]7004d7eb2011-01-21 00:27:53357 return true;
358}
359
[email protected]1bf14bc2010-07-09 16:10:43360} // namespace gpu_info_collector