Branch libreoffice-5-0-4
[LibreOffice.git] / vcl / opengl / x11 / X11DeviceInfo.cxx
blob74e84c607bd4bde7c3c9a473b6927242c44b6dfd
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 */
10 #include "opengl/x11/X11DeviceInfo.hxx"
12 #include <vcl/opengl/glxtest.hxx>
13 #include <rtl/ustring.hxx>
15 #include <unistd.h>
16 #include <sys/types.h>
17 #include <sys/wait.h>
18 #include <errno.h>
19 #include <sys/utsname.h>
21 namespace glx {
23 static int glxtest_pipe = 0;
25 static pid_t glxtest_pid = 0;
29 pid_t* getGlxPid()
31 return &glx::glxtest_pid;
34 int* getGlxPipe()
36 return &glx::glxtest_pipe;
39 namespace {
41 const char*
42 strspnp_wrapper(const char* aDelims, const char* aStr)
44 const char* d;
45 do {
46 for (d = aDelims; *d != '\0'; ++d) {
47 if (*aStr == *d) {
48 ++aStr;
49 break;
52 } while (*d);
54 return aStr;
57 char* strtok_wrapper(const char* aDelims, char** aStr)
59 if (!*aStr) {
60 return nullptr;
63 char* ret = const_cast<char*>(strspnp_wrapper(aDelims, *aStr));
65 if (!*ret) {
66 *aStr = ret;
67 return nullptr;
70 char* i = ret;
71 do {
72 for (const char* d = aDelims; *d != '\0'; ++d) {
73 if (*i == *d) {
74 *i = '\0';
75 *aStr = ++i;
76 return ret;
79 ++i;
80 } while (*i);
82 *aStr = nullptr;
83 return ret;
86 uint64_t version(uint32_t major, uint32_t minor, uint32_t revision = 0)
88 return (uint64_t(major) << 32) + (uint64_t(minor) << 16) + uint64_t(revision);
93 X11OpenGLDeviceInfo::X11OpenGLDeviceInfo():
94 mbIsMesa(false),
95 mbIsNVIDIA(false),
96 mbIsFGLRX(false),
97 mbIsNouveau(false),
98 mbIsIntel(false),
99 mbIsOldSwrast(false),
100 mbIsLlvmpipe(false),
101 mbHasTextureFromPixmap(false),
102 mnGLMajorVersion(0),
103 mnMajorVersion(0),
104 mnMinorVersion(0),
105 mnRevisionVersion(0)
107 GetData();
110 X11OpenGLDeviceInfo::~X11OpenGLDeviceInfo()
114 void X11OpenGLDeviceInfo::GetData()
116 if (!glx::glxtest_pipe)
117 return;
119 // to understand this function, see bug 639842. We retrieve the OpenGL driver information in a
120 // separate process to protect against bad drivers.
122 enum { buf_size = 1024 };
123 char buf[buf_size];
124 ssize_t bytesread = read(glx::glxtest_pipe,
125 &buf,
126 buf_size-1); // -1 because we'll append a zero
127 close(glx::glxtest_pipe);
128 glx::glxtest_pipe = 0;
130 // bytesread < 0 would mean that the above read() call failed.
131 // This should never happen. If it did, the outcome would be to blacklist anyway.
132 if (bytesread < 0)
133 bytesread = 0;
135 // let buf be a zero-terminated string
136 buf[bytesread] = 0;
138 // Wait for the glxtest process to finish. This serves 2 purposes:
139 // * avoid having a zombie glxtest process laying around
140 // * get the glxtest process status info.
141 int glxtest_status = 0;
142 bool wait_for_glxtest_process = true;
143 bool waiting_for_glxtest_process_failed = false;
144 int waitpid_errno = 0;
145 while(wait_for_glxtest_process) {
146 wait_for_glxtest_process = false;
147 if (waitpid(glx::glxtest_pid, &glxtest_status, 0) == -1) {
148 waitpid_errno = errno;
149 if (waitpid_errno == EINTR) {
150 wait_for_glxtest_process = true;
151 } else {
152 // Bug 718629
153 // ECHILD happens when the glxtest process got reaped got reaped after a PR_CreateProcess
154 // as per bug 227246. This shouldn't matter, as we still seem to get the data
155 // from the pipe, and if we didn't, the outcome would be to blacklist anyway.
156 waiting_for_glxtest_process_failed = (waitpid_errno != ECHILD);
161 bool exited_with_error_code = !waiting_for_glxtest_process_failed &&
162 WIFEXITED(glxtest_status) &&
163 WEXITSTATUS(glxtest_status) != EXIT_SUCCESS;
164 bool received_signal = !waiting_for_glxtest_process_failed &&
165 WIFSIGNALED(glxtest_status);
167 bool error = waiting_for_glxtest_process_failed || exited_with_error_code || received_signal;
169 OString textureFromPixmap;
170 OString *stringToFill = nullptr;
171 char *bufptr = buf;
172 if (!error) {
173 while(true) {
174 char *line = strtok_wrapper("\n", &bufptr);
175 if (!line)
176 break;
177 if (stringToFill) {
178 *stringToFill = OString(line);
179 stringToFill = nullptr;
181 else if(!strcmp(line, "VENDOR"))
182 stringToFill = &maVendor;
183 else if(!strcmp(line, "RENDERER"))
184 stringToFill = &maRenderer;
185 else if(!strcmp(line, "VERSION"))
186 stringToFill = &maVersion;
187 else if(!strcmp(line, "TFP"))
188 stringToFill = &textureFromPixmap;
192 if (!strcmp(textureFromPixmap.getStr(), "TRUE"))
193 mbHasTextureFromPixmap = true;
195 // only useful for Linux kernel version check for FGLRX driver.
196 // assumes X client == X server, which is sad.
197 struct utsname unameobj;
198 if (!uname(&unameobj))
200 maOS = OString(unameobj.sysname);
201 maOSRelease = OString(unameobj.release);
204 // determine the major OpenGL version. That's the first integer in the version string.
205 mnGLMajorVersion = strtol(maVersion.getStr(), 0, 10);
207 // determine driver type (vendor) and where in the version string
208 // the actual driver version numbers should be expected to be found (whereToReadVersionNumbers)
209 const char *whereToReadVersionNumbers = nullptr;
210 const char *Mesa_in_version_string = strstr(maVersion.getStr(), "Mesa");
211 if (Mesa_in_version_string) {
212 mbIsMesa = true;
213 // with Mesa, the version string contains "Mesa major.minor" and that's all the version information we get:
214 // there is no actual driver version info.
215 whereToReadVersionNumbers = Mesa_in_version_string + strlen("Mesa");
216 if (strcasestr(maVendor.getStr(), "nouveau"))
217 mbIsNouveau = true;
218 if (strcasestr(maRenderer.getStr(), "intel")) // yes, intel is in the renderer string
219 mbIsIntel = true;
220 if (strcasestr(maRenderer.getStr(), "llvmpipe"))
221 mbIsLlvmpipe = true;
222 if (strcasestr(maRenderer.getStr(), "software rasterizer"))
223 mbIsOldSwrast = true;
224 } else if (strstr(maVendor.getStr(), "NVIDIA Corporation")) {
225 mbIsNVIDIA = true;
226 // with the NVIDIA driver, the version string contains "NVIDIA major.minor"
227 // note that here the vendor and version strings behave differently, that's why we don't put this above
228 // alongside Mesa_in_version_string.
229 const char *NVIDIA_in_version_string = strstr(maVersion.getStr(), "NVIDIA");
230 if (NVIDIA_in_version_string)
231 whereToReadVersionNumbers = NVIDIA_in_version_string + strlen("NVIDIA");
232 } else if (strstr(maVendor.getStr(), "ATI Technologies Inc")) {
233 mbIsFGLRX = true;
234 // with the FGLRX driver, the version string only gives a OpenGL version :/ so let's return that.
235 // that can at least give a rough idea of how old the driver is.
236 whereToReadVersionNumbers = maVersion.getStr();
239 // read major.minor version numbers of the driver (not to be confused with the OpenGL version)
240 if (whereToReadVersionNumbers) {
241 // copy into writable buffer, for tokenization
242 strncpy(buf, whereToReadVersionNumbers, buf_size-1);
243 buf[buf_size-1] = 0;
244 bufptr = buf;
246 // now try to read major.minor version numbers. In case of failure, gracefully exit: these numbers have
247 // been initialized as 0 anyways
248 char *token = strtok_wrapper(".", &bufptr);
249 if (token) {
250 mnMajorVersion = strtol(token, 0, 10);
251 token = strtok_wrapper(".", &bufptr);
252 if (token) {
253 mnMinorVersion = strtol(token, 0, 10);
254 token = strtok_wrapper(".", &bufptr);
255 if (token)
256 mnRevisionVersion = strtol(token, 0, 10);
262 bool X11OpenGLDeviceInfo::isDeviceBlocked()
264 // don't even try to use OpenGL 1.x
265 if (mnGLMajorVersion == 1)
266 return true;
268 SAL_INFO("vcl.opengl", "Vendor: " << maVendor);
269 SAL_INFO("vcl.opengl", "Renderer: " << maRenderer);
270 SAL_INFO("vcl.opengl", "Version: " << maVersion);
271 SAL_INFO("vcl.opengl", "OS: " << maOS);
272 SAL_INFO("vcl.opengl", "OSRelease: " << maOSRelease);
274 if (mbIsMesa) {
275 if (mbIsNouveau && version(mnMajorVersion, mnMinorVersion) < version(8,0)) {
276 SAL_WARN("vcl.opengl", "blocked driver version: old nouveau driver (requires mesa 8.0+)");
277 return true;
279 else if (version(mnMajorVersion, mnMinorVersion, mnRevisionVersion) < version(7,10,3)) {
280 SAL_WARN("vcl.opengl", "blocked driver version: requires at least mesa 7.10.3");
281 return true;
283 else if (mbIsIntel && version(mnMajorVersion, mnMinorVersion, mnRevisionVersion) == version(9,0,2))
285 SAL_WARN("vcl.opengl", "blocked driver version: my broken intel driver Mesa 9.0.2");
286 return true;
288 else if (mbIsOldSwrast) {
289 SAL_WARN("vcl.opengl", "blocked driver version: software rasterizer");
290 return true;
292 else if (mbIsLlvmpipe && version(mnMajorVersion, mnMinorVersion) < version(9, 1)) {
293 // bug 791905, Mesa bug 57733, fixed in Mesa 9.1 according to
294 // https://bugs.freedesktop.org/show_bug.cgi?id=57733#c3
295 SAL_WARN("vcl.opengl", "blocked driver version: fdo#57733");
296 return true;
299 } else if (mbIsNVIDIA) {
300 if (version(mnMajorVersion, mnMinorVersion, mnRevisionVersion) < version(257,21)) {
301 SAL_WARN("vcl.opengl", "blocked driver version: nvidia requires at least 257.21");
302 return true;
304 } else if (mbIsFGLRX) {
305 // FGLRX does not report a driver version number, so we have the OpenGL version instead.
306 // by requiring OpenGL 3, we effectively require recent drivers.
307 if (version(mnMajorVersion, mnMinorVersion, mnRevisionVersion) < version(3, 0)) {
308 SAL_WARN("vcl.opengl", "blocked driver version: require at least OpenGL 3 for fglrx");
309 return true;
311 // Bug 724640: FGLRX + Linux 2.6.32 is a crashy combo
312 bool unknownOS = maOS.isEmpty() || maOSRelease.isEmpty();
313 bool badOS = maOS.indexOf("Linux") != -1 &&
314 maOSRelease.indexOf("2.6.32") != -1;
315 if (unknownOS || badOS) {
316 SAL_WARN("vcl.opengl", "blocked OS version with fglrx");
317 return true;
319 } else {
320 // like on windows, let's block unknown vendors. Think of virtual machines.
321 // Also, this case is hit whenever the GLXtest probe failed to get driver info or crashed.
322 SAL_WARN("vcl.opengl", "unknown vendor => blocked");
323 return true;
326 return false;
329 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */