Version 6.1.4.1, tag libreoffice-6.1.4.1
[LibreOffice.git] / vcl / opengl / x11 / X11DeviceInfo.cxx
blob5388f85c0c3dc5c94055f0b1e6cadcc44cc40ee3
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>
17 #include <unistd.h>
18 #include <sys/types.h>
19 #include <sys/wait.h>
20 #include <errno.h>
21 #include <sys/utsname.h>
23 #include <desktop/crashreport.hxx>
25 namespace glx {
27 static int glxtest_pipe = 0;
29 static pid_t glxtest_pid = 0;
33 pid_t* getGlxPid()
35 return &glx::glxtest_pid;
38 int* getGlxPipe()
40 return &glx::glxtest_pipe;
43 namespace {
45 const char*
46 strspnp_wrapper(const char* aDelims, const char* aStr)
48 const char* d;
51 for (d = aDelims; *d != '\0'; ++d)
53 if (*aStr == *d)
55 ++aStr;
56 break;
59 } while (*d);
61 return aStr;
64 char* strtok_wrapper(const char* aDelims, char** aStr)
66 if (!*aStr)
68 return nullptr;
71 char* ret = const_cast<char*>(strspnp_wrapper(aDelims, *aStr));
73 if (!*ret)
75 *aStr = ret;
76 return nullptr;
79 char* i = ret;
82 for (const char* d = aDelims; *d != '\0'; ++d)
84 if (*i == *d) {
85 *i = '\0';
86 *aStr = ++i;
87 return ret;
90 ++i;
91 } while (*i);
93 *aStr = nullptr;
94 return ret;
97 uint64_t version(uint32_t major, uint32_t minor, uint32_t revision = 0)
99 return (uint64_t(major) << 32) + (uint64_t(minor) << 16) + uint64_t(revision);
104 X11OpenGLDeviceInfo::X11OpenGLDeviceInfo():
105 mbIsMesa(false),
106 mbIsNVIDIA(false),
107 mbIsFGLRX(false),
108 mbIsNouveau(false),
109 mbIsIntel(false),
110 mbIsOldSwrast(false),
111 mbIsLlvmpipe(false),
112 mnGLMajorVersion(0),
113 mnMajorVersion(0),
114 mnMinorVersion(0),
115 mnRevisionVersion(0)
117 GetData();
120 void X11OpenGLDeviceInfo::GetData()
122 if (!glx::glxtest_pipe)
123 return;
125 // to understand this function, see bug moz#639842. We retrieve the OpenGL driver information in a
126 // separate process to protect against bad drivers.
127 enum { buf_size = 1024 };
128 char buf[buf_size];
129 ssize_t bytesread = read(glx::glxtest_pipe,
130 &buf,
131 buf_size-1); // -1 because we'll append a zero
132 close(glx::glxtest_pipe);
133 glx::glxtest_pipe = 0;
135 // bytesread < 0 would mean that the above read() call failed.
136 // This should never happen. If it did, the outcome would be to blacklist anyway.
137 if (bytesread < 0)
138 bytesread = 0;
140 // let buf be a zero-terminated string
141 buf[bytesread] = 0;
143 // Wait for the glxtest process to finish. This serves 2 purposes:
144 // * avoid having a zombie glxtest process laying around
145 // * get the glxtest process status info.
146 int glxtest_status = 0;
147 bool wait_for_glxtest_process = true;
148 bool waiting_for_glxtest_process_failed = false;
149 int waitpid_errno = 0;
150 while(wait_for_glxtest_process)
152 wait_for_glxtest_process = false;
153 if (waitpid(glx::glxtest_pid, &glxtest_status, 0) == -1)
155 waitpid_errno = errno;
156 if (waitpid_errno == EINTR)
158 wait_for_glxtest_process = true;
160 else
162 // Bug moz#718629
163 // ECHILD happens when the glxtest process got reaped got reaped after a PR_CreateProcess
164 // as per bug moz#227246. This shouldn't matter, as we still seem to get the data
165 // from the pipe, and if we didn't, the outcome would be to blacklist anyway.
166 waiting_for_glxtest_process_failed = (waitpid_errno != ECHILD);
171 bool exited_with_error_code = !waiting_for_glxtest_process_failed &&
172 WIFEXITED(glxtest_status) &&
173 WEXITSTATUS(glxtest_status) != EXIT_SUCCESS;
174 bool received_signal = !waiting_for_glxtest_process_failed &&
175 WIFSIGNALED(glxtest_status);
177 bool error = waiting_for_glxtest_process_failed || exited_with_error_code || received_signal;
179 OString textureFromPixmap;
180 OString *stringToFill = nullptr;
181 char *bufptr = buf;
182 if (!error)
184 while(true)
186 char *line = strtok_wrapper("\n", &bufptr);
187 if (!line)
188 break;
189 if (stringToFill) {
190 *stringToFill = OString(line);
191 stringToFill = nullptr;
193 else if(!strcmp(line, "VENDOR"))
194 stringToFill = &maVendor;
195 else if(!strcmp(line, "RENDERER"))
196 stringToFill = &maRenderer;
197 else if(!strcmp(line, "VERSION"))
198 stringToFill = &maVersion;
199 else if(!strcmp(line, "TFP"))
200 stringToFill = &textureFromPixmap;
204 // only useful for Linux kernel version check for FGLRX driver.
205 // assumes X client == X server, which is sad.
206 struct utsname unameobj;
207 if (!uname(&unameobj))
209 maOS = OString(unameobj.sysname);
210 maOSRelease = OString(unameobj.release);
213 // determine the major OpenGL version. That's the first integer in the version string.
214 mnGLMajorVersion = strtol(maVersion.getStr(), nullptr, 10);
216 // determine driver type (vendor) and where in the version string
217 // the actual driver version numbers should be expected to be found (whereToReadVersionNumbers)
218 const char *whereToReadVersionNumbers = nullptr;
219 const char *Mesa_in_version_string = strstr(maVersion.getStr(), "Mesa");
220 if (Mesa_in_version_string)
222 mbIsMesa = true;
223 // with Mesa, the version string contains "Mesa major.minor" and that's all the version information we get:
224 // there is no actual driver version info.
225 whereToReadVersionNumbers = Mesa_in_version_string + strlen("Mesa");
226 if (strcasestr(maVendor.getStr(), "nouveau"))
227 mbIsNouveau = true;
228 if (strcasestr(maRenderer.getStr(), "intel")) // yes, intel is in the renderer string
229 mbIsIntel = true;
230 if (strcasestr(maRenderer.getStr(), "llvmpipe"))
231 mbIsLlvmpipe = true;
232 if (strcasestr(maRenderer.getStr(), "software rasterizer"))
233 mbIsOldSwrast = true;
235 else if (strstr(maVendor.getStr(), "NVIDIA Corporation"))
237 mbIsNVIDIA = true;
238 // with the NVIDIA driver, the version string contains "NVIDIA major.minor"
239 // note that here the vendor and version strings behave differently, that's why we don't put this above
240 // alongside Mesa_in_version_string.
241 const char *NVIDIA_in_version_string = strstr(maVersion.getStr(), "NVIDIA");
242 if (NVIDIA_in_version_string)
243 whereToReadVersionNumbers = NVIDIA_in_version_string + strlen("NVIDIA");
245 else if (strstr(maVendor.getStr(), "ATI Technologies Inc"))
247 mbIsFGLRX = true;
248 // with the FGLRX driver, the version string only gives a OpenGL version :/ so let's return that.
249 // that can at least give a rough idea of how old the driver is.
250 whereToReadVersionNumbers = maVersion.getStr();
253 // read major.minor version numbers of the driver (not to be confused with the OpenGL version)
254 if (whereToReadVersionNumbers)
256 // copy into writable buffer, for tokenization
257 strncpy(buf, whereToReadVersionNumbers, buf_size-1);
258 buf[buf_size-1] = 0;
259 bufptr = buf;
261 // now try to read major.minor version numbers. In case of failure, gracefully exit: these numbers have
262 // been initialized as 0 anyways
263 char *token = strtok_wrapper(".", &bufptr);
264 if (token)
266 mnMajorVersion = strtol(token, nullptr, 10);
267 token = strtok_wrapper(".", &bufptr);
268 if (token)
270 mnMinorVersion = strtol(token, nullptr, 10);
271 token = strtok_wrapper(".", &bufptr);
272 if (token)
273 mnRevisionVersion = strtol(token, nullptr, 10);
279 bool X11OpenGLDeviceInfo::isDeviceBlocked()
281 // don't even try to use OpenGL 1.x
282 if (mnGLMajorVersion == 1)
283 return true;
285 CrashReporter::AddKeyValue("AdapterVendorId", rtl::OStringToOUString(maVendor, RTL_TEXTENCODING_UTF8));
286 CrashReporter::AddKeyValue("AdapterDeviceId", rtl::OStringToOUString(maRenderer, RTL_TEXTENCODING_UTF8));
288 SAL_INFO("vcl.opengl", "Vendor: " << maVendor);
289 SAL_INFO("vcl.opengl", "Renderer: " << maRenderer);
290 SAL_INFO("vcl.opengl", "Version: " << maVersion);
291 SAL_INFO("vcl.opengl", "OS: " << maOS);
292 SAL_INFO("vcl.opengl", "OSRelease: " << maOSRelease);
294 if (mbIsMesa)
296 if (mbIsNouveau && version(mnMajorVersion, mnMinorVersion) < version(8,0))
298 SAL_WARN("vcl.opengl", "blocked driver version: old nouveau driver (requires mesa 8.0+)");
299 return true;
301 else if (version(mnMajorVersion, mnMinorVersion, mnRevisionVersion) < version(7,10,3))
303 SAL_WARN("vcl.opengl", "blocked driver version: requires at least mesa 7.10.3");
304 return true;
306 else if (mbIsIntel && version(mnMajorVersion, mnMinorVersion, mnRevisionVersion) == version(9,0,2))
308 SAL_WARN("vcl.opengl", "blocked driver version: my broken intel driver Mesa 9.0.2");
309 return true;
311 else if (mbIsOldSwrast)
313 SAL_WARN("vcl.opengl", "blocked driver version: software rasterizer");
314 return true;
316 else if (mbIsLlvmpipe && version(mnMajorVersion, mnMinorVersion) < version(9, 1))
318 // bug moz#791905, Mesa bug 57733, fixed in Mesa 9.1 according to
319 // https://bugs.freedesktop.org/show_bug.cgi?id=57733#c3
320 SAL_WARN("vcl.opengl", "blocked driver version: fdo#57733");
321 return true;
324 else if (mbIsNVIDIA)
326 if (version(mnMajorVersion, mnMinorVersion, mnRevisionVersion) < version(257,21))
328 SAL_WARN("vcl.opengl", "blocked driver version: nvidia requires at least 257.21");
329 return true;
332 else if (mbIsFGLRX)
334 // FGLRX does not report a driver version number, so we have the OpenGL version instead.
335 // by requiring OpenGL 3, we effectively require recent drivers.
336 if (version(mnMajorVersion, mnMinorVersion, mnRevisionVersion) < version(3, 0))
338 SAL_WARN("vcl.opengl", "blocked driver version: require at least OpenGL 3 for fglrx");
339 return true;
341 // Bug moz#724640: FGLRX + Linux 2.6.32 is a crashy combo
342 bool unknownOS = maOS.isEmpty() || maOSRelease.isEmpty();
343 bool badOS = maOS.indexOf("Linux") != -1 &&
344 maOSRelease.indexOf("2.6.32") != -1;
345 if (unknownOS || badOS)
347 SAL_WARN("vcl.opengl", "blocked OS version with fglrx");
348 return true;
351 else
353 // like on windows, let's block unknown vendors. Think of virtual machines.
354 // Also, this case is hit whenever the GLXtest probe failed to get driver info or crashed.
355 SAL_WARN("vcl.opengl", "unknown vendor => blocked");
356 return true;
359 return false;
362 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */