blob: ade4f33eb5913ce2c1fcdaf232efea681ba3c0d6 [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]48fe9f32010-12-21 16:54:10167} // namespace anonymous
168
[email protected]1bf14bc2010-07-09 16:10:43169namespace gpu_info_collector {
170
[email protected]a80f5ece2011-10-20 23:56:55171bool CollectGraphicsInfo(content::GPUInfo* gpu_info) {
[email protected]7004d7eb2011-01-21 00:27:53172 DCHECK(gpu_info);
[email protected]41579ae2010-11-15 22:31:26173
[email protected]a6057292011-07-28 21:26:56174 if (CommandLine::ForCurrentProcess()->HasSwitch(
175 switches::kGpuNoContextLost)) {
176 gpu_info->can_lose_context = false;
177 } else {
178 // TODO(zmo): need to consider the case where we are running on top
179 // of desktop GL and GL_ARB_robustness extension is available.
180 gpu_info->can_lose_context =
181 (gfx::GetGLImplementation() == gfx::kGLImplementationEGLGLES2);
182 }
183
[email protected]6cc8ece62011-03-14 20:05:47184 gpu_info->finalized = true;
[email protected]29ad10c2011-11-23 19:48:06185 bool rt = CollectGraphicsInfoGL(gpu_info);
[email protected]079c4ad2011-02-19 00:05:57186
[email protected]3225aeb2011-03-22 21:34:59187 if (gpu_info->vendor_id == 0x1002) { // ATI
188 std::string ati_driver_version = CollectDriverVersionATI();
189 if (ati_driver_version != "") {
190 gpu_info->driver_vendor = "ATI / AMD";
191 gpu_info->driver_version = ati_driver_version;
192 }
193 }
[email protected]079c4ad2011-02-19 00:05:57194
195 return rt;
196}
197
[email protected]29ad10c2011-11-23 19:48:06198bool CollectPreliminaryGraphicsInfo(content::GPUInfo* gpu_info) {
199 DCHECK(gpu_info);
200
201 return CollectVideoCardInfo(gpu_info);
202}
203
[email protected]a80f5ece2011-10-20 23:56:55204bool CollectVideoCardInfo(content::GPUInfo* gpu_info) {
[email protected]7004d7eb2011-01-21 00:27:53205 DCHECK(gpu_info);
[email protected]41579ae2010-11-15 22:31:26206
[email protected]67f88572011-03-01 21:29:27207 if (IsPciSupported() == false) {
[email protected]7ace0ef4e2011-03-17 21:23:05208 VLOG(1) << "PCI bus scanning is not supported";
[email protected]67f88572011-03-01 21:29:27209 return false;
210 }
211
[email protected]48fe9f32010-12-21 16:54:10212 // TODO(zmo): be more flexible about library name.
213 PciInterface* interface = InitializeLibPci("libpci.so.3");
[email protected]7004d7eb2011-01-21 00:27:53214 if (interface == NULL)
[email protected]c5790ae2011-03-01 16:51:49215 interface = InitializeLibPci("libpci.so");
216 if (interface == NULL) {
[email protected]7ace0ef4e2011-03-17 21:23:05217 VLOG(1) << "Failed to locate libpci";
[email protected]48fe9f32010-12-21 16:54:10218 return false;
[email protected]c5790ae2011-03-01 16:51:49219 }
[email protected]48fe9f32010-12-21 16:54:10220
221 PciAccess* access = (interface->pci_alloc)();
222 DCHECK(access != NULL);
223 (interface->pci_init)(access);
224 (interface->pci_scan_bus)(access);
225 std::vector<PciDevice*> gpu_list;
226 PciDevice* gpu_active = NULL;
227 for (PciDevice* device = access->device_list;
228 device != NULL; device = device->next) {
229 (interface->pci_fill_info)(device, 33); // Fill the IDs and class fields.
[email protected]7004d7eb2011-01-21 00:27:53230 // TODO(zmo): there might be other classes that qualify as display devices.
[email protected]48fe9f32010-12-21 16:54:10231 if (device->device_class == 0x0300) { // Device class is DISPLAY_VGA.
[email protected]c1e70642011-04-08 00:47:22232 if (gpu_info->vendor_id == 0 || gpu_info->vendor_id == device->vendor_id)
233 gpu_list.push_back(device);
[email protected]48fe9f32010-12-21 16:54:10234 }
235 }
236 if (gpu_list.size() == 1) {
237 gpu_active = gpu_list[0];
238 } else {
239 // If more than one graphics card are identified, find the one that matches
240 // gl VENDOR and RENDERER info.
[email protected]a61508e52011-03-08 17:59:42241 std::string gl_vendor_string = gpu_info->gl_vendor;
242 std::string gl_renderer_string = gpu_info->gl_renderer;
[email protected]48fe9f32010-12-21 16:54:10243 const int buffer_size = 255;
244 scoped_array<char> buffer(new char[buffer_size]);
245 std::vector<PciDevice*> candidates;
246 for (size_t i = 0; i < gpu_list.size(); ++i) {
247 PciDevice* gpu = gpu_list[i];
248 // The current implementation of pci_lookup_name returns the same pointer
249 // as the passed in upon success, and a different one (NULL or a pointer
250 // to an error message) upon failure.
251 if ((interface->pci_lookup_name)(access,
252 buffer.get(),
253 buffer_size,
254 1,
255 gpu->vendor_id) != buffer.get())
256 continue;
257 std::string vendor_string = buffer.get();
258 const bool kCaseSensitive = false;
259 if (!StartsWithASCII(gl_vendor_string, vendor_string, kCaseSensitive))
260 continue;
261 if ((interface->pci_lookup_name)(access,
262 buffer.get(),
263 buffer_size,
264 2,
265 gpu->vendor_id,
266 gpu->device_id) != buffer.get())
267 continue;
268 std::string device_string = buffer.get();
269 size_t begin = device_string.find_first_of('[');
270 size_t end = device_string.find_last_of(']');
271 if (begin != std::string::npos && end != std::string::npos &&
272 begin < end) {
273 device_string = device_string.substr(begin + 1, end - begin - 1);
274 }
275 if (StartsWithASCII(gl_renderer_string, device_string, kCaseSensitive)) {
276 gpu_active = gpu;
277 break;
278 }
279 // If a device's vendor matches gl VENDOR string, we want to consider the
280 // possibility that libpci may not return the exact same name as gl
281 // RENDERER string.
282 candidates.push_back(gpu);
283 }
284 if (gpu_active == NULL && candidates.size() == 1)
285 gpu_active = candidates[0];
286 }
[email protected]a61508e52011-03-08 17:59:42287 if (gpu_active != NULL) {
288 gpu_info->vendor_id = gpu_active->vendor_id;
289 gpu_info->device_id = gpu_active->device_id;
290 }
[email protected]48fe9f32010-12-21 16:54:10291 (interface->pci_cleanup)(access);
[email protected]7004d7eb2011-01-21 00:27:53292 FinalizeLibPci(&interface);
[email protected]48fe9f32010-12-21 16:54:10293 return (gpu_active != NULL);
[email protected]1bf14bc2010-07-09 16:10:43294}
295
[email protected]a80f5ece2011-10-20 23:56:55296bool CollectDriverInfoGL(content::GPUInfo* gpu_info) {
[email protected]7004d7eb2011-01-21 00:27:53297 DCHECK(gpu_info);
298
[email protected]a61508e52011-03-08 17:59:42299 std::string gl_version_string = gpu_info->gl_version_string;
[email protected]aa2284d2011-08-04 18:36:03300 if (StartsWithASCII(gl_version_string, "OpenGL ES", true))
301 gl_version_string = gl_version_string.substr(10);
[email protected]7004d7eb2011-01-21 00:27:53302 std::vector<std::string> pieces;
303 base::SplitStringAlongWhitespace(gl_version_string, &pieces);
304 // In linux, the gl version string might be in the format of
305 // GLVersion DriverVendor DriverVersion
306 if (pieces.size() < 3)
307 return false;
308
309 std::string driver_version = pieces[2];
310 size_t pos = driver_version.find_first_not_of("0123456789.");
311 if (pos == 0)
312 return false;
313 if (pos != std::string::npos)
314 driver_version = driver_version.substr(0, pos);
315
[email protected]a61508e52011-03-08 17:59:42316 gpu_info->driver_vendor = pieces[1];
317 gpu_info->driver_version = driver_version;
[email protected]7004d7eb2011-01-21 00:27:53318 return true;
319}
320
[email protected]1bf14bc2010-07-09 16:10:43321} // namespace gpu_info_collector