Kaydet (Commit) a55f7400 authored tarafından Markus Mohrhard's avatar Markus Mohrhard

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
üst dcdc8df2
...@@ -94,6 +94,21 @@ $(eval $(call gb_Library_add_exception_objects,sofficeapp,\ ...@@ -94,6 +94,21 @@ $(eval $(call gb_Library_add_exception_objects,sofficeapp,\
desktop/source/migration/migration \ 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 # liblibreoffice bits
$(eval $(call gb_Library_add_exception_objects,sofficeapp,\ $(eval $(call gb_Library_add_exception_objects,sofficeapp,\
desktop/source/lib/init \ desktop/source/lib/init \
......
...@@ -53,8 +53,20 @@ ...@@ -53,8 +53,20 @@
#include <touch/touch.h> #include <touch/touch.h>
#endif #endif
#if defined( UNX ) && !defined MACOSX && !defined IOS && !defined ANDROID
bool fire_glxtest_process();
#endif
extern "C" int DESKTOP_DLLPUBLIC soffice_main() 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 #if defined ANDROID
try { try {
rtl::Bootstrap::setIniFilename("file:///assets/program/lofficerc"); rtl::Bootstrap::setIniFilename("file:///assets/program/lofficerc");
......
/* -*- 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;
}
...@@ -53,6 +53,10 @@ public: ...@@ -53,6 +53,10 @@ public:
static void checkGLError(const char* aFile, size_t nLine); 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 * checks if the system supports all features that are necessary for the OpenGL VCL support
*/ */
......
/* -*- 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: */
...@@ -122,6 +122,7 @@ $(eval $(call gb_Library_use_externals,vcl,\ ...@@ -122,6 +122,7 @@ $(eval $(call gb_Library_use_externals,vcl,\
)) ))
$(eval $(call gb_Library_add_exception_objects,vcl,\ $(eval $(call gb_Library_add_exception_objects,vcl,\
vcl/opengl/DeviceInfo \
vcl/opengl/gdiimpl \ vcl/opengl/gdiimpl \
vcl/opengl/salbmp \ vcl/opengl/salbmp \
vcl/opengl/scale \ vcl/opengl/scale \
...@@ -705,6 +706,10 @@ $(eval $(call gb_Library_add_libs,vcl,\ ...@@ -705,6 +706,10 @@ $(eval $(call gb_Library_add_libs,vcl,\
-lGLU \ -lGLU \
-lX11 \ -lX11 \
)) ))
$(eval $(call gb_Library_add_exception_objects,vcl,\
vcl/opengl/x11/X11DeviceInfo \
))
endif endif
ifeq ($(OS),SOLARIS) ifeq ($(OS),SOLARIS)
......
/* -*- 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: */
/* -*- 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: */
/* -*- 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: */
This diff is collapsed.
...@@ -22,6 +22,10 @@ ...@@ -22,6 +22,10 @@
#include <vector> #include <vector>
#if defined UNX && !defined MACOSX && !defined IOS && !defined ANDROID
#include "opengl/x11/X11DeviceInfo.hxx"
#endif
namespace { namespace {
OUString getShaderFolder() OUString getShaderFolder()
...@@ -360,11 +364,31 @@ void OpenGLHelper::checkGLError(const char* pFile, size_t nLine) ...@@ -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() bool OpenGLHelper::supportsVCLOpenGL()
{ {
static bool bDisableGL = !!getenv("SAL_DISABLEGL"); static bool bDisableGL = !!getenv("SAL_DISABLEGL");
bool bBlacklisted = isDeviceBlacklisted();
if (bDisableGL) if (bDisableGL || bBlacklisted)
return false; return false;
else else
return true; return true;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment