summaryrefslogtreecommitdiff
path: root/vcl
diff options
context:
space:
mode:
authorMarkus Mohrhard <markus.mohrhard@collabora.co.uk>2014-11-20 09:52:03 +0100
committerMarkus Mohrhard <markus.mohrhard@collabora.co.uk>2014-11-20 13:50:51 +0100
commita55f740045d6d30e1ef5522889d2dc359ca5784a (patch)
treecf05fa9cd35419dc488e96f23c67d4eb308f71e0 /vcl
parentdcdc8df2e6d740dd75ff6e28727ca757e4882f86 (diff)
include the unx part for getting OpenGL driver & device information
desktop/unx/source/glxtest.cxx is taken directly from the Mozilla project. THe whole concept is taken from Mozilla and is based on starting an early process that creates an OpenGL context. This prevents crashing drivers to crash Libreoffice. We read the information from the pipe as soon as we create the first vcl Window. In that place we then decide if the device/driver combination is blacklisted. Change-Id: I2624d4ce06d503281a4459cf3174f57cf1f7b733
Diffstat (limited to 'vcl')
-rw-r--r--vcl/Library_vcl.mk5
-rw-r--r--vcl/inc/opengl/DeviceInfo.hxx23
-rw-r--r--vcl/inc/opengl/x11/X11DeviceInfo.hxx51
-rw-r--r--vcl/opengl/DeviceInfo.cxx16
-rw-r--r--vcl/opengl/x11/X11DeviceInfo.cxx326
-rw-r--r--vcl/source/opengl/OpenGLHelper.cxx26
6 files changed, 446 insertions, 1 deletions
diff --git a/vcl/Library_vcl.mk b/vcl/Library_vcl.mk
index 81f774a98644..932ca4cd5026 100644
--- a/vcl/Library_vcl.mk
+++ b/vcl/Library_vcl.mk
@@ -122,6 +122,7 @@ $(eval $(call gb_Library_use_externals,vcl,\
))
$(eval $(call gb_Library_add_exception_objects,vcl,\
+ vcl/opengl/DeviceInfo \
vcl/opengl/gdiimpl \
vcl/opengl/salbmp \
vcl/opengl/scale \
@@ -705,6 +706,10 @@ $(eval $(call gb_Library_add_libs,vcl,\
-lGLU \
-lX11 \
))
+
+$(eval $(call gb_Library_add_exception_objects,vcl,\
+ vcl/opengl/x11/X11DeviceInfo \
+))
endif
ifeq ($(OS),SOLARIS)
diff --git a/vcl/inc/opengl/DeviceInfo.hxx b/vcl/inc/opengl/DeviceInfo.hxx
new file mode 100644
index 000000000000..402a3d02e8d7
--- /dev/null
+++ b/vcl/inc/opengl/DeviceInfo.hxx
@@ -0,0 +1,23 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef INCLUDED_VCL_INC_OPENGL_DEVICEINFO_HXX
+#define INCLUDED_VCL_INC_OPENGL_DEVICEINFO_HXX
+
+class OpenGLDeviceInfo
+{
+public:
+ virtual ~OpenGLDeviceInfo();
+
+ virtual bool isDeviceBlocked() = 0;
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/inc/opengl/x11/X11DeviceInfo.hxx b/vcl/inc/opengl/x11/X11DeviceInfo.hxx
new file mode 100644
index 000000000000..5e41b6b45c9a
--- /dev/null
+++ b/vcl/inc/opengl/x11/X11DeviceInfo.hxx
@@ -0,0 +1,51 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef INCLUDED_VCL_INC_OPENGL_X11_X11DEVICEINFO_HXX
+#define INCLUDED_VCL_INC_OPENGL_X11_X11DEVICEINFO_HXX
+
+#include "opengl/DeviceInfo.hxx"
+
+#include <rtl/string.hxx>
+
+class X11OpenGLDeviceInfo : public OpenGLDeviceInfo
+{
+private:
+ bool mbIsMesa;
+ bool mbIsNVIDIA;
+ bool mbIsFGLRX;
+ bool mbIsNouveau;
+ bool mbIsIntel;
+ bool mbIsOldSwrast;
+ bool mbIsLlvmpipe;
+ bool mbHasTextureFromPixmap;
+
+ OString maVendor;
+ OString maRenderer;
+ OString maVersion;
+ OString maOS;
+ OString maOSRelease;
+
+ size_t mnGLMajorVersion;
+ size_t mnMajorVersion;
+ size_t mnMinorVersion;
+ size_t mnRevisionVersion;
+
+ void GetData();
+
+public:
+ X11OpenGLDeviceInfo();
+ virtual ~X11OpenGLDeviceInfo();
+
+ virtual bool isDeviceBlocked();
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/opengl/DeviceInfo.cxx b/vcl/opengl/DeviceInfo.cxx
new file mode 100644
index 000000000000..135e0e7cf04e
--- /dev/null
+++ b/vcl/opengl/DeviceInfo.cxx
@@ -0,0 +1,16 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include "opengl/DeviceInfo.hxx"
+
+OpenGLDeviceInfo::~OpenGLDeviceInfo()
+{
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/opengl/x11/X11DeviceInfo.cxx b/vcl/opengl/x11/X11DeviceInfo.cxx
new file mode 100644
index 000000000000..24fd279943a6
--- /dev/null
+++ b/vcl/opengl/x11/X11DeviceInfo.cxx
@@ -0,0 +1,326 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include "opengl/x11/X11DeviceInfo.hxx"
+
+#include <vcl/opengl/glxtest.hxx>
+#include <rtl/ustring.hxx>
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <errno.h>
+#include <sys/utsname.h>
+
+namespace glx {
+
+static int glxtest_pipe = 0;
+
+static pid_t glxtest_pid = 0;
+
+}
+
+pid_t* getGlxPid()
+{
+ return &glx::glxtest_pid;
+}
+
+int* getGlxPipe()
+{
+ return &glx::glxtest_pipe;
+}
+
+namespace {
+
+const char*
+strspnp_wrapper(const char* aDelims, const char* aStr)
+{
+ const char* d;
+ do {
+ for (d = aDelims; *d != '\0'; ++d) {
+ if (*aStr == *d) {
+ ++aStr;
+ break;
+ }
+ }
+ } while (*d);
+
+ return aStr;
+}
+
+char* strtok_wrapper(const char* aDelims, char** aStr)
+{
+ if (!*aStr) {
+ return nullptr;
+ }
+
+ char* ret = (char*)strspnp_wrapper(aDelims, *aStr);
+
+ if (!*ret) {
+ *aStr = ret;
+ return nullptr;
+ }
+
+ char* i = ret;
+ do {
+ for (const char* d = aDelims; *d != '\0'; ++d) {
+ if (*i == *d) {
+ *i = '\0';
+ *aStr = ++i;
+ return ret;
+ }
+ }
+ ++i;
+ } while (*i);
+
+ *aStr = nullptr;
+ return ret;
+}
+
+uint64_t version(uint32_t major, uint32_t minor, uint32_t revision = 0)
+{
+ return (uint64_t(major) << 32) + (uint64_t(minor) << 16) + uint64_t(revision);
+}
+
+}
+
+X11OpenGLDeviceInfo::X11OpenGLDeviceInfo():
+ mbIsMesa(false),
+ mbIsNVIDIA(false),
+ mbIsFGLRX(false),
+ mbIsNouveau(false),
+ mbIsIntel(false),
+ mbIsOldSwrast(false),
+ mbIsLlvmpipe(false),
+ mbHasTextureFromPixmap(false),
+ mnGLMajorVersion(0),
+ mnMajorVersion(0),
+ mnMinorVersion(0),
+ mnRevisionVersion(0)
+{
+ GetData();
+}
+
+X11OpenGLDeviceInfo::~X11OpenGLDeviceInfo()
+{
+}
+
+void X11OpenGLDeviceInfo::GetData()
+{
+ if (!glx::glxtest_pipe)
+ return;
+
+ // to understand this function, see bug 639842. We retrieve the OpenGL driver information in a
+ // separate process to protect against bad drivers.
+
+ enum { buf_size = 1024 };
+ char buf[buf_size];
+ ssize_t bytesread = read(glx::glxtest_pipe,
+ &buf,
+ buf_size-1); // -1 because we'll append a zero
+ close(glx::glxtest_pipe);
+ glx::glxtest_pipe = 0;
+
+ // bytesread < 0 would mean that the above read() call failed.
+ // This should never happen. If it did, the outcome would be to blacklist anyway.
+ if (bytesread < 0)
+ bytesread = 0;
+
+ // let buf be a zero-terminated string
+ buf[bytesread] = 0;
+
+ // Wait for the glxtest process to finish. This serves 2 purposes:
+ // * avoid having a zombie glxtest process laying around
+ // * get the glxtest process status info.
+ int glxtest_status = 0;
+ bool wait_for_glxtest_process = true;
+ bool waiting_for_glxtest_process_failed = false;
+ int waitpid_errno = 0;
+ while(wait_for_glxtest_process) {
+ wait_for_glxtest_process = false;
+ if (waitpid(glx::glxtest_pid, &glxtest_status, 0) == -1) {
+ waitpid_errno = errno;
+ if (waitpid_errno == EINTR) {
+ wait_for_glxtest_process = true;
+ } else {
+ // Bug 718629
+ // ECHILD happens when the glxtest process got reaped got reaped after a PR_CreateProcess
+ // as per bug 227246. This shouldn't matter, as we still seem to get the data
+ // from the pipe, and if we didn't, the outcome would be to blacklist anyway.
+ waiting_for_glxtest_process_failed = (waitpid_errno != ECHILD);
+ }
+ }
+ }
+
+ bool exited_with_error_code = !waiting_for_glxtest_process_failed &&
+ WIFEXITED(glxtest_status) &&
+ WEXITSTATUS(glxtest_status) != EXIT_SUCCESS;
+ bool received_signal = !waiting_for_glxtest_process_failed &&
+ WIFSIGNALED(glxtest_status);
+
+ bool error = waiting_for_glxtest_process_failed || exited_with_error_code || received_signal;
+
+ OString textureFromPixmap;
+ OString *stringToFill = nullptr;
+ char *bufptr = buf;
+ if (!error) {
+ while(true) {
+ char *line = strtok_wrapper("\n", &bufptr);
+ if (!line)
+ break;
+ if (stringToFill) {
+ *stringToFill = OString(line);
+ stringToFill = nullptr;
+ }
+ else if(!strcmp(line, "VENDOR"))
+ stringToFill = &maVendor;
+ else if(!strcmp(line, "RENDERER"))
+ stringToFill = &maRenderer;
+ else if(!strcmp(line, "VERSION"))
+ stringToFill = &maVersion;
+ else if(!strcmp(line, "TFP"))
+ stringToFill = &textureFromPixmap;
+ }
+ }
+
+ if (!strcmp(textureFromPixmap.getStr(), "TRUE"))
+ mbHasTextureFromPixmap = true;
+
+ // only useful for Linux kernel version check for FGLRX driver.
+ // assumes X client == X server, which is sad.
+ struct utsname unameobj;
+ if (!uname(&unameobj))
+ {
+ maOS = OString(unameobj.sysname);
+ maOSRelease = OString(unameobj.release);
+ }
+
+ // determine the major OpenGL version. That's the first integer in the version string.
+ mnGLMajorVersion = strtol(maVersion.getStr(), 0, 10);
+
+ // determine driver type (vendor) and where in the version string
+ // the actual driver version numbers should be expected to be found (whereToReadVersionNumbers)
+ const char *whereToReadVersionNumbers = nullptr;
+ const char *Mesa_in_version_string = strstr(maVersion.getStr(), "Mesa");
+ if (Mesa_in_version_string) {
+ mbIsMesa = true;
+ // with Mesa, the version string contains "Mesa major.minor" and that's all the version information we get:
+ // there is no actual driver version info.
+ whereToReadVersionNumbers = Mesa_in_version_string + strlen("Mesa");
+ if (strcasestr(maVendor.getStr(), "nouveau"))
+ mbIsNouveau = true;
+ if (strcasestr(maRenderer.getStr(), "intel")) // yes, intel is in the renderer string
+ mbIsIntel = true;
+ if (strcasestr(maRenderer.getStr(), "llvmpipe"))
+ mbIsLlvmpipe = true;
+ if (strcasestr(maRenderer.getStr(), "software rasterizer"))
+ mbIsOldSwrast = true;
+ } else if (strstr(maVendor.getStr(), "NVIDIA Corporation")) {
+ mbIsNVIDIA = true;
+ // with the NVIDIA driver, the version string contains "NVIDIA major.minor"
+ // note that here the vendor and version strings behave differently, that's why we don't put this above
+ // alongside Mesa_in_version_string.
+ const char *NVIDIA_in_version_string = strstr(maVersion.getStr(), "NVIDIA");
+ if (NVIDIA_in_version_string)
+ whereToReadVersionNumbers = NVIDIA_in_version_string + strlen("NVIDIA");
+ } else if (strstr(maVendor.getStr(), "ATI Technologies Inc")) {
+ mbIsFGLRX = true;
+ // with the FGLRX driver, the version string only gives a OpenGL version :/ so let's return that.
+ // that can at least give a rough idea of how old the driver is.
+ whereToReadVersionNumbers = maVersion.getStr();
+ }
+
+ // read major.minor version numbers of the driver (not to be confused with the OpenGL version)
+ if (whereToReadVersionNumbers) {
+ // copy into writable buffer, for tokenization
+ strncpy(buf, whereToReadVersionNumbers, buf_size);
+ bufptr = buf;
+
+ // now try to read major.minor version numbers. In case of failure, gracefully exit: these numbers have
+ // been initialized as 0 anyways
+ char *token = strtok_wrapper(".", &bufptr);
+ if (token) {
+ mnMajorVersion = strtol(token, 0, 10);
+ token = strtok_wrapper(".", &bufptr);
+ if (token) {
+ mnMinorVersion = strtol(token, 0, 10);
+ token = strtok_wrapper(".", &bufptr);
+ if (token)
+ mnRevisionVersion = strtol(token, 0, 10);
+ }
+ }
+ }
+}
+
+bool X11OpenGLDeviceInfo::isDeviceBlocked()
+{
+ // don't even try to use OpenGL 1.x
+ if (mnGLMajorVersion == 1)
+ return true;
+
+ SAL_INFO("vcl.opengl", "Vendor: " << maVendor);
+ SAL_INFO("vcl.opengl", "Renderer: " << maRenderer);
+ SAL_INFO("vcl.opengl", "Version: " << maVersion);
+ SAL_INFO("vcl.opengl", "OS: " << maOS);
+ SAL_INFO("vcl.opengl", "OSRelease: " << maOSRelease);
+
+
+ if (mbIsMesa) {
+ if (mbIsNouveau && version(mnMajorVersion, mnMinorVersion) < version(8,0)) {
+ SAL_WARN("vcl.opengl", "blocked driver version: old nouveau driver (requires mesa 8.0+)");
+ return true;
+ }
+ else if (version(mnMajorVersion, mnMinorVersion, mnRevisionVersion) < version(7,10,3)) {
+ SAL_WARN("vcl.opengl", "blocked driver version: requires at least mesa 7.10.3");
+ return true;
+ }
+ else if (mbIsOldSwrast) {
+ SAL_WARN("vcl.opengl", "blocked driver version: software rasterizer");
+ return true;
+ }
+ else if (mbIsLlvmpipe && version(mnMajorVersion, mnMinorVersion) < version(9, 1)) {
+ // bug 791905, Mesa bug 57733, fixed in Mesa 9.1 according to
+ // https://bugs.freedesktop.org/show_bug.cgi?id=57733#c3
+ SAL_WARN("vcl.opengl", "blocked driver version: fdo#57733");
+ return true;
+ }
+
+ } else if (mbIsNVIDIA) {
+ if (version(mnMajorVersion, mnMinorVersion, mnRevisionVersion) < version(257,21)) {
+ SAL_WARN("vcl.opengl", "blocked driver version: nvidia requires at least 257.21");
+ return true;
+ }
+ } else if (mbIsFGLRX) {
+ // FGLRX does not report a driver version number, so we have the OpenGL version instead.
+ // by requiring OpenGL 3, we effectively require recent drivers.
+ if (version(mnMajorVersion, mnMinorVersion, mnRevisionVersion) < version(3, 0)) {
+ SAL_WARN("vcl.opengl", "blocked driver version: require at least OpenGL 3 for fglrx");
+ return true;
+ }
+ // Bug 724640: FGLRX + Linux 2.6.32 is a crashy combo
+ bool unknownOS = maOS.isEmpty() || maOSRelease.isEmpty();
+ OUString aOS = rtl::OStringToOUString(maOS, RTL_TEXTENCODING_UTF8);
+ OUString aOSRelease = rtl::OStringToOUString(maOSRelease, RTL_TEXTENCODING_UTF8);
+ bool badOS = aOS.indexOf("Linux", true) != -1 &&
+ maOSRelease.indexOf("2.6.32") != -1;
+ if (unknownOS || badOS) {
+ SAL_WARN("vcl.opengl", "blocked OS version with fglrx");
+ return true;
+ }
+ } else {
+ // like on windows, let's block unknown vendors. Think of virtual machines.
+ // Also, this case is hit whenever the GLXtest probe failed to get driver info or crashed.
+ SAL_WARN("vcl.opengl", "unknown vendor => blocked");
+ return true;
+ }
+
+ return false;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/opengl/OpenGLHelper.cxx b/vcl/source/opengl/OpenGLHelper.cxx
index 202ac2b095f6..df8b6d897bfe 100644
--- a/vcl/source/opengl/OpenGLHelper.cxx
+++ b/vcl/source/opengl/OpenGLHelper.cxx
@@ -22,6 +22,10 @@
#include <vector>
+#if defined UNX && !defined MACOSX && !defined IOS && !defined ANDROID
+#include "opengl/x11/X11DeviceInfo.hxx"
+#endif
+
namespace {
OUString getShaderFolder()
@@ -360,11 +364,31 @@ void OpenGLHelper::checkGLError(const char* pFile, size_t nLine)
}
}
+bool OpenGLHelper::isDeviceBlacklisted()
+{
+ static bool bSet = false;
+ static bool bBlacklisted = true; // assume the worst
+ if (!bSet)
+ {
+#if defined UNX && !defined MACOSX && !defined IOS && !defined ANDROID
+ X11OpenGLDeviceInfo aInfo;
+ bBlacklisted = aInfo.isDeviceBlocked();
+ SAL_INFO("vcl.opengl", "blacklisted: " << bBlacklisted);
+#else
+ bBlacklisted = false;
+#endif
+ bSet = true;
+ }
+
+ return bBlacklisted;
+}
+
bool OpenGLHelper::supportsVCLOpenGL()
{
static bool bDisableGL = !!getenv("SAL_DISABLEGL");
+ bool bBlacklisted = isDeviceBlacklisted();
- if (bDisableGL)
+ if (bDisableGL || bBlacklisted)
return false;
else
return true;