diff options
-rw-r--r-- | desktop/Library_sofficeapp.mk | 15 | ||||
-rw-r--r-- | desktop/source/app/sofficemain.cxx | 12 | ||||
-rw-r--r-- | desktop/unx/source/glxtest.cxx | 281 | ||||
-rw-r--r-- | include/vcl/opengl/OpenGLHelper.hxx | 4 | ||||
-rw-r--r-- | include/vcl/opengl/glxtest.hxx | 21 | ||||
-rw-r--r-- | vcl/Library_vcl.mk | 5 | ||||
-rw-r--r-- | vcl/inc/opengl/DeviceInfo.hxx | 23 | ||||
-rw-r--r-- | vcl/inc/opengl/x11/X11DeviceInfo.hxx | 51 | ||||
-rw-r--r-- | vcl/opengl/DeviceInfo.cxx | 16 | ||||
-rw-r--r-- | vcl/opengl/x11/X11DeviceInfo.cxx | 326 | ||||
-rw-r--r-- | vcl/source/opengl/OpenGLHelper.cxx | 26 |
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; |