summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--desktop/Library_sofficeapp.mk15
-rw-r--r--desktop/source/app/sofficemain.cxx12
-rw-r--r--desktop/unx/source/glxtest.cxx281
-rw-r--r--include/vcl/opengl/OpenGLHelper.hxx4
-rw-r--r--include/vcl/opengl/glxtest.hxx21
-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
11 files changed, 779 insertions, 1 deletions
diff --git a/desktop/Library_sofficeapp.mk b/desktop/Library_sofficeapp.mk
index be86dd10e663..f322a6ca74b6 100644
--- a/desktop/Library_sofficeapp.mk
+++ b/desktop/Library_sofficeapp.mk
@@ -94,6 +94,21 @@ $(eval $(call gb_Library_add_exception_objects,sofficeapp,\
desktop/source/migration/migration \
))
+ifeq ($(OS),LINUX)
+$(eval $(call gb_Library_add_exception_objects,sofficeapp,\
+ desktop/unx/source/glxtest \
+))
+
+$(eval $(call gb_Library_add_libs,sofficeapp,\
+ -lm \
+ -ldl \
+ -lpthread \
+ -lGL \
+ -lGLU \
+ -lX11 \
+))
+endif
+
# liblibreoffice bits
$(eval $(call gb_Library_add_exception_objects,sofficeapp,\
desktop/source/lib/init \
diff --git a/desktop/source/app/sofficemain.cxx b/desktop/source/app/sofficemain.cxx
index dff16e88a7d9..606ba4fdcc22 100644
--- a/desktop/source/app/sofficemain.cxx
+++ b/desktop/source/app/sofficemain.cxx
@@ -53,8 +53,20 @@
#include <touch/touch.h>
#endif
+#if defined( UNX ) && !defined MACOSX && !defined IOS && !defined ANDROID
+bool fire_glxtest_process();
+#endif
+
extern "C" int DESKTOP_DLLPUBLIC soffice_main()
{
+#if defined( UNX ) && !defined MACOSX && !defined IOS && !defined ANDROID
+ /* Run test for OpenGL support in own process to avoid crash with broken
+ * OpenGL drivers. Start process as early as possible.
+ */
+ bool bSuccess = fire_glxtest_process();
+ SAL_WARN_IF(!bSuccess, "desktop.opengl", "problems with glxtest");
+#endif
+
#if defined ANDROID
try {
rtl::Bootstrap::setIniFilename("file:///assets/program/lofficerc");
diff --git a/desktop/unx/source/glxtest.cxx b/desktop/unx/source/glxtest.cxx
new file mode 100644
index 000000000000..d8d9c2059e84
--- /dev/null
+++ b/desktop/unx/source/glxtest.cxx
@@ -0,0 +1,281 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* 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/. */
+
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// Explanation: See bug 639842. Safely getting GL driver info on X11 is hard, because the only way to do
+// that is to create a GL context and call glGetString(), but with bad drivers,
+// just creating a GL context may crash.
+//
+// This file implements the idea to do that in a separate process.
+//
+// The only non-static function here is fire_glxtest_process(). It creates a pipe, publishes its 'read' end as the
+// mozilla::widget::glxtest_pipe global variable, forks, and runs that GLX probe in the child process,
+// which runs the glxtest() static function. This creates a X connection, a GLX context, calls glGetString, and writes that
+// to the 'write' end of the pipe.
+
+#include <cstdio>
+#include <cstdlib>
+#include <unistd.h>
+#include <dlfcn.h>
+#include <fcntl.h>
+#include "stdint.h"
+#include <string.h>
+
+#include <vcl/opengl/glxtest.hxx>
+
+#ifdef __SUNPRO_CC
+#include <stdio.h>
+#endif
+
+#include "X11/Xlib.h"
+#include "X11/Xutil.h"
+
+// stuff from glx.h
+typedef struct __GLXcontextRec *GLXContext;
+typedef XID GLXPixmap;
+typedef XID GLXDrawable;
+/* GLX 1.3 and later */
+typedef struct __GLXFBConfigRec *GLXFBConfig;
+typedef XID GLXFBConfigID;
+typedef XID GLXContextID;
+typedef XID GLXWindow;
+typedef XID GLXPbuffer;
+#define GLX_RGBA 4
+#define GLX_RED_SIZE 8
+#define GLX_GREEN_SIZE 9
+#define GLX_BLUE_SIZE 10
+
+// stuff from gl.h
+typedef uint8_t GLubyte;
+typedef uint32_t GLenum;
+#define GL_VENDOR 0x1F00
+#define GL_RENDERER 0x1F01
+#define GL_VERSION 0x1F02
+
+// the write end of the pipe, which we're going to write to
+static int write_end_of_the_pipe = -1;
+
+// C++ standard collides with C standard in that it doesn't allow casting void* to function pointer types.
+// So the work-around is to convert first to size_t.
+// http://www.trilithium.com/johan/2004/12/problem-with-dlsym/
+template<typename func_ptr_type>
+static func_ptr_type cast(void *ptr)
+{
+ return reinterpret_cast<func_ptr_type>(
+ reinterpret_cast<size_t>(ptr)
+ );
+}
+
+static void fatal_error(const char *str)
+{
+ write(write_end_of_the_pipe, str, strlen(str));
+ write(write_end_of_the_pipe, "\n", 1);
+ _exit(EXIT_FAILURE);
+}
+
+static int
+x_error_handler(Display *, XErrorEvent *ev)
+{
+ enum { bufsize = 1024 };
+ char buf[bufsize];
+ int length = snprintf(buf, bufsize,
+ "X error occurred in GLX probe, error_code=%d, request_code=%d, minor_code=%d\n",
+ ev->error_code,
+ ev->request_code,
+ ev->minor_code);
+ write(write_end_of_the_pipe, buf, length);
+ _exit(EXIT_FAILURE);
+ return 0;
+}
+
+
+// glxtest is declared inside extern "C" so that the name is not mangled.
+// The name is used in build/valgrind/x86_64-redhat-linux-gnu.sup to suppress
+// memory leak errors because we run it inside a short lived fork and we don't
+// care about leaking memory
+extern "C" {
+
+void glxtest()
+{
+ // we want to redirect to /dev/null stdout, stderr, and while we're at it,
+ // any PR logging file descriptors. To that effect, we redirect all positive
+ // file descriptors up to what open() returns here. In particular, 1 is stdout and 2 is stderr.
+ int fd = open("/dev/null", O_WRONLY);
+ for (int i = 1; i < fd; i++)
+ dup2(fd, i);
+ close(fd);
+
+ if (getenv("MOZ_AVOID_OPENGL_ALTOGETHER"))
+ fatal_error("The MOZ_AVOID_OPENGL_ALTOGETHER environment variable is defined");
+
+ ///// Open libGL and load needed symbols /////
+#ifdef __OpenBSD__
+ #define LIBGL_FILENAME "libGL.so"
+#else
+ #define LIBGL_FILENAME "libGL.so.1"
+#endif
+ void *libgl = dlopen(LIBGL_FILENAME, RTLD_LAZY);
+ if (!libgl)
+ fatal_error("Unable to load " LIBGL_FILENAME);
+
+ typedef void* (* PFNGLXGETPROCADDRESS) (const char *);
+ PFNGLXGETPROCADDRESS glXGetProcAddress = cast<PFNGLXGETPROCADDRESS>(dlsym(libgl, "glXGetProcAddress"));
+
+ if (!glXGetProcAddress)
+ fatal_error("Unable to find glXGetProcAddress in " LIBGL_FILENAME);
+
+ typedef GLXFBConfig* (* PFNGLXQUERYEXTENSION) (Display *, int *, int *);
+ PFNGLXQUERYEXTENSION glXQueryExtension = cast<PFNGLXQUERYEXTENSION>(glXGetProcAddress("glXQueryExtension"));
+
+ typedef GLXFBConfig* (* PFNGLXQUERYVERSION) (Display *, int *, int *);
+ PFNGLXQUERYVERSION glXQueryVersion = cast<PFNGLXQUERYVERSION>(dlsym(libgl, "glXQueryVersion"));
+
+ typedef XVisualInfo* (* PFNGLXCHOOSEVISUAL) (Display *, int, int *);
+ PFNGLXCHOOSEVISUAL glXChooseVisual = cast<PFNGLXCHOOSEVISUAL>(glXGetProcAddress("glXChooseVisual"));
+
+ typedef GLXContext (* PFNGLXCREATECONTEXT) (Display *, XVisualInfo *, GLXContext, Bool);
+ PFNGLXCREATECONTEXT glXCreateContext = cast<PFNGLXCREATECONTEXT>(glXGetProcAddress("glXCreateContext"));
+
+ typedef Bool (* PFNGLXMAKECURRENT) (Display*, GLXDrawable, GLXContext);
+ PFNGLXMAKECURRENT glXMakeCurrent = cast<PFNGLXMAKECURRENT>(glXGetProcAddress("glXMakeCurrent"));
+
+ typedef void (* PFNGLXDESTROYCONTEXT) (Display*, GLXContext);
+ PFNGLXDESTROYCONTEXT glXDestroyContext = cast<PFNGLXDESTROYCONTEXT>(glXGetProcAddress("glXDestroyContext"));
+
+ typedef GLubyte* (* PFNGLGETSTRING) (GLenum);
+ PFNGLGETSTRING glGetString = cast<PFNGLGETSTRING>(glXGetProcAddress("glGetString"));
+
+ if (!glXQueryExtension ||
+ !glXQueryVersion ||
+ !glXChooseVisual ||
+ !glXCreateContext ||
+ !glXMakeCurrent ||
+ !glXDestroyContext ||
+ !glGetString)
+ {
+ fatal_error("glXGetProcAddress couldn't find required functions");
+ }
+ ///// Open a connection to the X server /////
+ Display *dpy = XOpenDisplay(nullptr);
+ if (!dpy)
+ fatal_error("Unable to open a connection to the X server");
+
+ ///// Check that the GLX extension is present /////
+ if (!glXQueryExtension(dpy, nullptr, nullptr))
+ fatal_error("GLX extension missing");
+
+ XSetErrorHandler(x_error_handler);
+
+ ///// Get a visual /////
+ int attribs[] = {
+ GLX_RGBA,
+ GLX_RED_SIZE, 1,
+ GLX_GREEN_SIZE, 1,
+ GLX_BLUE_SIZE, 1,
+ None };
+ XVisualInfo *vInfo = glXChooseVisual(dpy, DefaultScreen(dpy), attribs);
+ if (!vInfo)
+ fatal_error("No visuals found");
+
+ // using a X11 Window instead of a GLXPixmap does not crash
+ // fglrx in indirect rendering. bug 680644
+ Window window;
+ XSetWindowAttributes swa;
+ swa.colormap = XCreateColormap(dpy, RootWindow(dpy, vInfo->screen),
+ vInfo->visual, AllocNone);
+
+ swa.border_pixel = 0;
+ window = XCreateWindow(dpy, RootWindow(dpy, vInfo->screen),
+ 0, 0, 16, 16,
+ 0, vInfo->depth, InputOutput, vInfo->visual,
+ CWBorderPixel | CWColormap, &swa);
+
+ ///// Get a GL context and make it current //////
+ GLXContext context = glXCreateContext(dpy, vInfo, nullptr, True);
+ glXMakeCurrent(dpy, window, context);
+
+ ///// Look for this symbol to determine texture_from_pixmap support /////
+ void* glXBindTexImageEXT = glXGetProcAddress("glXBindTexImageEXT");
+
+ ///// Get GL vendor/renderer/versions strings /////
+ enum { bufsize = 1024 };
+ char buf[bufsize];
+ const GLubyte *vendorString = glGetString(GL_VENDOR);
+ const GLubyte *rendererString = glGetString(GL_RENDERER);
+ const GLubyte *versionString = glGetString(GL_VERSION);
+
+ if (!vendorString || !rendererString || !versionString)
+ fatal_error("glGetString returned null");
+
+ int length = snprintf(buf, bufsize,
+ "VENDOR\n%s\nRENDERER\n%s\nVERSION\n%s\nTFP\n%s\n",
+ vendorString,
+ rendererString,
+ versionString,
+ glXBindTexImageEXT ? "TRUE" : "FALSE");
+ if (length >= bufsize)
+ fatal_error("GL strings length too large for buffer size");
+
+ ///// Clean up. Indeed, the parent process might fail to kill us (e.g. if it doesn't need to check GL info)
+ ///// so we might be staying alive for longer than expected, so it's important to consume as little memory as
+ ///// possible. Also we want to check that we're able to do that too without generating X errors.
+ glXMakeCurrent(dpy, None, nullptr); // must release the GL context before destroying it
+ glXDestroyContext(dpy, context);
+ XDestroyWindow(dpy, window);
+ XFreeColormap(dpy, swa.colormap);
+
+#ifdef NS_FREE_PERMANENT_DATA // conditionally defined in nscore.h, don't forget to #include it above
+ XCloseDisplay(dpy);
+#else
+ // This XSync call wanted to be instead:
+ // XCloseDisplay(dpy);
+ // but this can cause 1-minute stalls on certain setups using Nouveau, see bug 973192
+ XSync(dpy, False);
+#endif
+
+ dlclose(libgl);
+
+ ///// Finally write data to the pipe
+ write(write_end_of_the_pipe, buf, length);
+}
+
+}
+
+/** \returns true in the child glxtest process, false in the parent process */
+bool fire_glxtest_process()
+{
+ int pfd[2];
+ if (pipe(pfd) == -1) {
+ perror("pipe");
+ return false;
+ }
+ pid_t pid = fork();
+ if (pid < 0) {
+ perror("fork");
+ close(pfd[0]);
+ close(pfd[1]);
+ return false;
+ }
+ // The child exits early to avoid running the full shutdown sequence and avoid conflicting with threads
+ // we have already spawned (like the profiler).
+ if (pid == 0) {
+ close(pfd[0]);
+ write_end_of_the_pipe = pfd[1];
+ glxtest();
+ close(pfd[1]);
+ _exit(0);
+ }
+
+ close(pfd[1]);
+ int* glxtest_pipe = getGlxPipe();
+ *glxtest_pipe = pfd[0];
+ pid_t* glxtest_pid = getGlxPid();
+ *glxtest_pid = pid;
+ return true;
+}
diff --git a/include/vcl/opengl/OpenGLHelper.hxx b/include/vcl/opengl/OpenGLHelper.hxx
index ee5a9f8d3a28..243051519dac 100644
--- a/include/vcl/opengl/OpenGLHelper.hxx
+++ b/include/vcl/opengl/OpenGLHelper.hxx
@@ -54,6 +54,10 @@ public:
static void checkGLError(const char* aFile, size_t nLine);
/**
+ * checks if the device/driver pair is on our OpenGL blacklist
+ */
+ static bool isDeviceBlacklisted();
+ /**
* checks if the system supports all features that are necessary for the OpenGL VCL support
*/
static bool supportsVCLOpenGL();
diff --git a/include/vcl/opengl/glxtest.hxx b/include/vcl/opengl/glxtest.hxx
new file mode 100644
index 000000000000..0889cdd403b3
--- /dev/null
+++ b/include/vcl/opengl/glxtest.hxx
@@ -0,0 +1,21 @@
+/* -*- 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_OPENGL_GLXTEST_HXX
+#define INCLUDED_VCL_OPENGL_GLXTEST_HXX
+
+#include <vcl/dllapi.h>
+
+VCL_DLLPUBLIC int* getGlxPipe();
+
+VCL_DLLPUBLIC pid_t* getGlxPid();
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
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;