OLD | NEW |
| (Empty) |
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 "content/gpu/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.h" | |
16 #include "base/string_util.h" | |
17 #include "base/strings/string_piece.h" | |
18 #include "base/strings/string_split.h" | |
19 #include "base/strings/string_tokenizer.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 { | |
30 | |
31 // This checks if a system supports PCI bus. | |
32 // We check the existence of /sys/bus/pci or /sys/bug/pci_express. | |
33 bool IsPciSupported() { | |
34 const base::FilePath pci_path("/sys/bus/pci/"); | |
35 const base::FilePath pcie_path("/sys/bus/pci_express/"); | |
36 return (file_util::PathExists(pci_path) || | |
37 file_util::PathExists(pcie_path)); | |
38 } | |
39 | |
40 // Scan /etc/ati/amdpcsdb.default for "ReleaseVersion". | |
41 // Return empty string on failing. | |
42 std::string CollectDriverVersionATI() { | |
43 const base::FilePath::CharType kATIFileName[] = | |
44 FILE_PATH_LITERAL("/etc/ati/amdpcsdb.default"); | |
45 base::FilePath ati_file_path(kATIFileName); | |
46 if (!file_util::PathExists(ati_file_path)) | |
47 return std::string(); | |
48 std::string contents; | |
49 if (!file_util::ReadFileToString(ati_file_path, &contents)) | |
50 return std::string(); | |
51 base::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 } | |
65 return std::string(); | |
66 } | |
67 | |
68 // Use NVCtrl extention to query NV driver version. | |
69 // Return empty string on failing. | |
70 std::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)) { | |
78 LOG(INFO) << "NVCtrl extension does not exist."; | |
79 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(); | |
94 } | |
95 | |
96 const uint32 kVendorIDIntel = 0x8086; | |
97 const uint32 kVendorIDNVidia = 0x10de; | |
98 const uint32 kVendorIDAMD = 0x1002; | |
99 | |
100 bool CollectPCIVideoCardInfo(content::GPUInfo* gpu_info) { | |
101 DCHECK(gpu_info); | |
102 | |
103 if (IsPciSupported() == false) { | |
104 VLOG(1) << "PCI bus scanning is not supported"; | |
105 return false; | |
106 } | |
107 | |
108 // TODO(zmo): be more flexible about library name. | |
109 LibPciLoader libpci_loader; | |
110 if (!libpci_loader.Load("libpci.so.3") && | |
111 !libpci_loader.Load("libpci.so")) { | |
112 VLOG(1) << "Failed to locate libpci"; | |
113 return false; | |
114 } | |
115 | |
116 pci_access* access = (libpci_loader.pci_alloc)(); | |
117 DCHECK(access != NULL); | |
118 (libpci_loader.pci_init)(access); | |
119 (libpci_loader.pci_scan_bus)(access); | |
120 bool primary_gpu_identified = false; | |
121 for (pci_dev* device = access->devices; | |
122 device != NULL; device = device->next) { | |
123 // Fill the IDs and class fields. | |
124 (libpci_loader.pci_fill_info)(device, 33); | |
125 // TODO(zmo): there might be other classes that qualify as display devices. | |
126 if (device->device_class != 0x0300) // Device class is DISPLAY_VGA. | |
127 continue; | |
128 | |
129 content::GPUInfo::GPUDevice gpu; | |
130 gpu.vendor_id = device->vendor_id; | |
131 gpu.device_id = device->device_id; | |
132 | |
133 if (!primary_gpu_identified) { | |
134 primary_gpu_identified = true; | |
135 gpu_info->gpu = gpu; | |
136 } else { | |
137 // TODO(zmo): if there are multiple GPUs, we assume the non Intel | |
138 // one is primary. Revisit this logic because we actually don't know | |
139 // which GPU we are using at this point. | |
140 if (gpu_info->gpu.vendor_id == kVendorIDIntel && | |
141 gpu.vendor_id != kVendorIDIntel) { | |
142 gpu_info->secondary_gpus.push_back(gpu_info->gpu); | |
143 gpu_info->gpu = gpu; | |
144 } else { | |
145 gpu_info->secondary_gpus.push_back(gpu); | |
146 } | |
147 } | |
148 } | |
149 | |
150 // Detect Optimus or AMD Switchable GPU. | |
151 if (gpu_info->secondary_gpus.size() == 1 && | |
152 gpu_info->secondary_gpus[0].vendor_id == kVendorIDIntel) { | |
153 if (gpu_info->gpu.vendor_id == kVendorIDNVidia) | |
154 gpu_info->optimus = true; | |
155 if (gpu_info->gpu.vendor_id == kVendorIDAMD) | |
156 gpu_info->amd_switchable = true; | |
157 } | |
158 | |
159 (libpci_loader.pci_cleanup)(access); | |
160 return (primary_gpu_identified); | |
161 } | |
162 | |
163 } // namespace anonymous | |
164 | |
165 namespace gpu_info_collector { | |
166 | |
167 bool CollectContextGraphicsInfo(content::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 content::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(content::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(content::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(content::GPUInfo* basic_gpu_info, | |
270 const content::GPUInfo& context_gpu_info) { | |
271 MergeGPUInfoGL(basic_gpu_info, context_gpu_info); | |
272 } | |
273 | |
274 } // namespace gpu_info_collector | |
OLD | NEW |