Version 6.4.0.0.beta1, tag libreoffice-6.4.0.0.beta1
[LibreOffice.git] / vcl / opengl / x11 / X11DeviceInfo.cxx
blob7f671952f0fe6b454f35285ed38637d307272b05
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>
11 #include <opengl/x11/glxtest.hxx>
13 #include <config_features.h>
15 #include <rtl/ustring.hxx>
16 #include <sal/log.hxx>
18 #include <unistd.h>
19 #include <sys/types.h>
20 #include <sys/wait.h>
21 #include <errno.h>
22 #include <sys/utsname.h>
24 #include <desktop/crashreport.hxx>
26 namespace glx {
28 static int glxtest_pipe = 0;
30 static pid_t glxtest_pid = 0;
34 pid_t* getGlxPid()
36 return &glx::glxtest_pid;
39 int* getGlxPipe()
41 return &glx::glxtest_pipe;
44 namespace {
46 const char*
47 strspnp_wrapper(const char* aDelims, const char* aStr)
49 const char* d;
52 for (d = aDelims; *d != '\0'; ++d)
54 if (*aStr == *d)
56 ++aStr;
57 break;
60 } while (*d);
62 return aStr;
65 char* strtok_wrapper(const char* aDelims, char** aStr)
67 if (!*aStr)
69 return nullptr;
72 char* ret = const_cast<char*>(strspnp_wrapper(aDelims, *aStr));
74 if (!*ret)
76 *aStr = ret;
77 return nullptr;
80 char* i = ret;
83 for (const char* d = aDelims; *d != '\0'; ++d)
85 if (*i == *d) {
86 *i = '\0';
87 *aStr = ++i;
88 return ret;
91 ++i;
92 } while (*i);
94 *aStr = nullptr;
95 return ret;
98 uint64_t version(uint32_t major, uint32_t minor, uint32_t revision = 0)
100 return (uint64_t(major) << 32) + (uint64_t(minor) << 16) + uint64_t(revision);
105 X11OpenGLDeviceInfo::X11OpenGLDeviceInfo():
106 mbIsMesa(false),
107 mbIsNVIDIA(false),
108 mbIsFGLRX(false),
109 mbIsNouveau(false),
110 mbIsIntel(false),
111 mbIsOldSwrast(false),
112 mbIsLlvmpipe(false),
113 mnGLMajorVersion(0),
114 mnMajorVersion(0),
115 mnMinorVersion(0),
116 mnRevisionVersion(0)
118 GetData();
121 void X11OpenGLDeviceInfo::GetData()
123 if (!glx::glxtest_pipe)
124 return;
126 // to understand this function, see bug moz#639842. We retrieve the OpenGL driver information in a
127 // separate process to protect against bad drivers.
128 enum { buf_size = 1024 };
129 char buf[buf_size];
130 ssize_t bytesread = read(glx::glxtest_pipe,
131 &buf,
132 buf_size-1); // -1 because we'll append a zero
133 close(glx::glxtest_pipe);
134 glx::glxtest_pipe = 0;
136 // bytesread < 0 would mean that the above read() call failed.
137 // This should never happen. If it did, the outcome would be to blacklist anyway.
138 if (bytesread < 0)
139 bytesread = 0;
141 // let buf be a zero-terminated string
142 buf[bytesread] = 0;
144 // Wait for the glxtest process to finish. This serves 2 purposes:
145 // * avoid having a zombie glxtest process laying around
146 // * get the glxtest process status info.
147 int glxtest_status = 0;
148 bool wait_for_glxtest_process = true;
149 bool waiting_for_glxtest_process_failed = false;
150 int waitpid_errno = 0;
151 while(wait_for_glxtest_process)
153 wait_for_glxtest_process = false;
154 if (waitpid(glx::glxtest_pid, &glxtest_status, 0) == -1)
156 waitpid_errno = errno;
157 if (waitpid_errno == EINTR)
159 wait_for_glxtest_process = true;
161 else
163 // Bug moz#718629
164 // ECHILD happens when the glxtest process got reaped got reaped after a PR_CreateProcess
165 // as per bug moz#227246. This shouldn't matter, as we still seem to get the data
166 // from the pipe, and if we didn't, the outcome would be to blacklist anyway.
167 waiting_for_glxtest_process_failed = (waitpid_errno != ECHILD);
172 bool exited_with_error_code = !waiting_for_glxtest_process_failed &&
173 WIFEXITED(glxtest_status) &&
174 WEXITSTATUS(glxtest_status) != EXIT_SUCCESS;
175 bool received_signal = !waiting_for_glxtest_process_failed &&
176 WIFSIGNALED(glxtest_status);
178 bool error = waiting_for_glxtest_process_failed || exited_with_error_code || received_signal;
180 OString textureFromPixmap;
181 OString *stringToFill = nullptr;
182 char *bufptr = buf;
183 if (!error)
185 while(true)
187 char *line = strtok_wrapper("\n", &bufptr);
188 if (!line)
189 break;
190 if (stringToFill) {
191 *stringToFill = OString(line);
192 stringToFill = nullptr;
194 else if(!strcmp(line, "VENDOR"))
195 stringToFill = &maVendor;
196 else if(!strcmp(line, "RENDERER"))
197 stringToFill = &maRenderer;
198 else if(!strcmp(line, "VERSION"))
199 stringToFill = &maVersion;
200 else if(!strcmp(line, "TFP"))
201 stringToFill = &textureFromPixmap;
205 // only useful for Linux kernel version check for FGLRX driver.
206 // assumes X client == X server, which is sad.
207 struct utsname unameobj;
208 if (!uname(&unameobj))
210 maOS = OString(unameobj.sysname);
211 maOSRelease = OString(unameobj.release);
214 // determine the major OpenGL version. That's the first integer in the version string.
215 mnGLMajorVersion = strtol(maVersion.getStr(), nullptr, 10);
217 // determine driver type (vendor) and where in the version string
218 // the actual driver version numbers should be expected to be found (whereToReadVersionNumbers)
219 const char *whereToReadVersionNumbers = nullptr;
220 const char *Mesa_in_version_string = strstr(maVersion.getStr(), "Mesa");
221 if (Mesa_in_version_string)
223 mbIsMesa = true;
224 // with Mesa, the version string contains "Mesa major.minor" and that's all the version information we get:
225 // there is no actual driver version info.
226 whereToReadVersionNumbers = Mesa_in_version_string + strlen("Mesa");
227 if (strcasestr(maVendor.getStr(), "nouveau"))
228 mbIsNouveau = true;
229 if (strcasestr(maRenderer.getStr(), "intel")) // yes, intel is in the renderer string
230 mbIsIntel = true;
231 if (strcasestr(maRenderer.getStr(), "llvmpipe"))
232 mbIsLlvmpipe = true;
233 if (strcasestr(maRenderer.getStr(), "software rasterizer"))
234 mbIsOldSwrast = true;
236 else if (strstr(maVendor.getStr(), "NVIDIA Corporation"))
238 mbIsNVIDIA = true;
239 // with the NVIDIA driver, the version string contains "NVIDIA major.minor"
240 // note that here the vendor and version strings behave differently, that's why we don't put this above
241 // alongside Mesa_in_version_string.
242 const char *NVIDIA_in_version_string = strstr(maVersion.getStr(), "NVIDIA");
243 if (NVIDIA_in_version_string)
244 whereToReadVersionNumbers = NVIDIA_in_version_string + strlen("NVIDIA");
246 else if (strstr(maVendor.getStr(), "ATI Technologies Inc"))
248 mbIsFGLRX = true;
249 // with the FGLRX driver, the version string only gives an OpenGL version: so let's return that.
250 // that can at least give a rough idea of how old the driver is.
251 whereToReadVersionNumbers = maVersion.getStr();
254 // read major.minor version numbers of the driver (not to be confused with the OpenGL version)
255 if (whereToReadVersionNumbers)
257 // copy into writable buffer, for tokenization
258 strncpy(buf, whereToReadVersionNumbers, buf_size-1);
259 buf[buf_size-1] = 0;
260 bufptr = buf;
262 // now try to read major.minor version numbers. In case of failure, gracefully exit: these numbers have
263 // been initialized as 0 anyways
264 char *token = strtok_wrapper(".", &bufptr);
265 if (token)
267 mnMajorVersion = strtol(token, nullptr, 10);
268 token = strtok_wrapper(".", &bufptr);
269 if (token)
271 mnMinorVersion = strtol(token, nullptr, 10);
272 token = strtok_wrapper(".", &bufptr);
273 if (token)
274 mnRevisionVersion = strtol(token, nullptr, 10);
280 bool X11OpenGLDeviceInfo::isDeviceBlocked()
282 // don't even try to use OpenGL 1.x
283 if (mnGLMajorVersion == 1)
284 return true;
286 CrashReporter::addKeyValue("AdapterVendorId", OStringToOUString(maVendor, RTL_TEXTENCODING_UTF8), CrashReporter::AddItem);
287 CrashReporter::addKeyValue("AdapterDeviceId", OStringToOUString(maRenderer, RTL_TEXTENCODING_UTF8), CrashReporter::Write);
289 SAL_INFO("vcl.opengl", "Vendor: " << maVendor);
290 SAL_INFO("vcl.opengl", "Renderer: " << maRenderer);
291 SAL_INFO("vcl.opengl", "Version: " << maVersion);
292 SAL_INFO("vcl.opengl", "OS: " << maOS);
293 SAL_INFO("vcl.opengl", "OSRelease: " << maOSRelease);
295 if (mbIsMesa)
297 if (mbIsNouveau && version(mnMajorVersion, mnMinorVersion) < version(8,0))
299 SAL_WARN("vcl.opengl", "blocked driver version: old nouveau driver (requires mesa 8.0+)");
300 return true;
302 else if (version(mnMajorVersion, mnMinorVersion, mnRevisionVersion) < version(7,10,3))
304 SAL_WARN("vcl.opengl", "blocked driver version: requires at least mesa 7.10.3");
305 return true;
307 else if (mbIsIntel && version(mnMajorVersion, mnMinorVersion, mnRevisionVersion) == version(9,0,2))
309 SAL_WARN("vcl.opengl", "blocked driver version: my broken intel driver Mesa 9.0.2");
310 return true;
312 else if (mbIsOldSwrast)
314 SAL_WARN("vcl.opengl", "blocked driver version: software rasterizer");
315 return true;
317 else if (mbIsLlvmpipe && version(mnMajorVersion, mnMinorVersion) < version(9, 1))
319 // bug moz#791905, Mesa bug 57733, fixed in Mesa 9.1 according to
320 // https://bugs.freedesktop.org/show_bug.cgi?id=57733#c3
321 SAL_WARN("vcl.opengl", "blocked driver version: fdo#57733");
322 return true;
325 else if (mbIsNVIDIA)
327 if (version(mnMajorVersion, mnMinorVersion, mnRevisionVersion) < version(257,21))
329 SAL_WARN("vcl.opengl", "blocked driver version: nvidia requires at least 257.21");
330 return true;
333 else if (mbIsFGLRX)
335 // FGLRX does not report a driver version number, so we have the OpenGL version instead.
336 // by requiring OpenGL 3, we effectively require recent drivers.
337 if (version(mnMajorVersion, mnMinorVersion, mnRevisionVersion) < version(3, 0))
339 SAL_WARN("vcl.opengl", "blocked driver version: require at least OpenGL 3 for fglrx");
340 return true;
342 // Bug moz#724640: FGLRX + Linux 2.6.32 is a crashy combo
343 bool unknownOS = maOS.isEmpty() || maOSRelease.isEmpty();
344 bool badOS = maOS.indexOf("Linux") != -1 &&
345 maOSRelease.indexOf("2.6.32") != -1;
346 if (unknownOS || badOS)
348 SAL_WARN("vcl.opengl", "blocked OS version with fglrx");
349 return true;
352 else
354 // like on windows, let's block unknown vendors. Think of virtual machines.
355 // Also, this case is hit whenever the GLXtest probe failed to get driver info or crashed.
356 SAL_WARN("vcl.opengl", "unknown vendor => blocked");
357 return true;
360 return false;
363 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */