summaryrefslogtreecommitdiff
path: root/vcl
diff options
context:
space:
mode:
authorMarkus Mohrhard <markus.mohrhard@googlemail.com>2014-11-21 12:45:51 +0100
committerMarkus Mohrhard <markus.mohrhard@googlemail.com>2014-11-21 21:38:09 +0100
commitbcd8f9e265faaca5905feb6bdd79e63161909368 (patch)
tree5f12138d1cd18781a648451f5b6bd97f97d695b7 /vcl
parent674c7abbd6b5e9014812d4f8839f62639fe9a7f4 (diff)
make glxtest available in salmain
Change-Id: Ic8bc3f2d5d96506590d35138089ead2eac984314
Diffstat (limited to 'vcl')
-rw-r--r--vcl/Executable_icontest.mk9
-rw-r--r--vcl/Executable_ui-previewer.mk15
-rw-r--r--vcl/Executable_vcldemo.mk15
-rw-r--r--vcl/Module_vcl.mk1
-rw-r--r--vcl/StaticLibrary_glxtest.mk45
-rw-r--r--vcl/source/salmain/salmain.cxx7
-rw-r--r--vcl/unx/glxtest.cxx280
7 files changed, 372 insertions, 0 deletions
diff --git a/vcl/Executable_icontest.mk b/vcl/Executable_icontest.mk
index d7962d846523..00dc9061585b 100644
--- a/vcl/Executable_icontest.mk
+++ b/vcl/Executable_icontest.mk
@@ -24,7 +24,16 @@ $(eval $(call gb_Executable_use_api,icontest,\
ifeq ($(OS),LINUX)
$(eval $(call gb_Executable_add_libs,icontest,\
+ -lm \
+ -ldl \
+ -lpthread \
-lGL \
+ -lGLU \
+ -lX11 \
+))
+
+$(eval $(call gb_Executable_use_static_libraries,icontest,\
+ glxtest \
))
else ifeq ($(OS),WNT)
diff --git a/vcl/Executable_ui-previewer.mk b/vcl/Executable_ui-previewer.mk
index 444ded1c3ba0..665f683ee199 100644
--- a/vcl/Executable_ui-previewer.mk
+++ b/vcl/Executable_ui-previewer.mk
@@ -34,4 +34,19 @@ $(eval $(call gb_Executable_add_exception_objects,ui-previewer,\
vcl/source/uipreviewer/previewer \
))
+ifeq ($(OS),LINUX)
+$(eval $(call gb_Executable_add_libs,ui-previewer,\
+ -lm \
+ -ldl \
+ -lpthread \
+ -lGL \
+ -lGLU \
+ -lX11 \
+))
+
+$(eval $(call gb_Executable_use_static_libraries,ui-previewer,\
+ glxtest \
+))
+endif
+
# vim: set noet sw=4 ts=4:
diff --git a/vcl/Executable_vcldemo.mk b/vcl/Executable_vcldemo.mk
index 721605f2c9fe..44f13b28c9c5 100644
--- a/vcl/Executable_vcldemo.mk
+++ b/vcl/Executable_vcldemo.mk
@@ -41,4 +41,19 @@ $(eval $(call gb_Executable_use_static_libraries,vcldemo,\
vclmain \
))
+ifeq ($(OS),LINUX)
+$(eval $(call gb_Executable_add_libs,vcldemo,\
+ -lm \
+ -ldl \
+ -lpthread \
+ -lGL \
+ -lGLU \
+ -lX11 \
+))
+
+$(eval $(call gb_Executable_use_static_libraries,vcldemo,\
+ glxtest \
+))
+endif
+
# vim: set noet sw=4 ts=4:
diff --git a/vcl/Module_vcl.mk b/vcl/Module_vcl.mk
index 5d1d2d2ad7fc..01db2e762794 100644
--- a/vcl/Module_vcl.mk
+++ b/vcl/Module_vcl.mk
@@ -47,6 +47,7 @@ $(eval $(call gb_Module_add_targets,vcl,\
Library_vclplug_gen \
Library_desktop_detector \
StaticLibrary_headless \
+ StaticLibrary_glxtest \
Package_fontunxppds \
Package_fontunxpsprint \
))
diff --git a/vcl/StaticLibrary_glxtest.mk b/vcl/StaticLibrary_glxtest.mk
new file mode 100644
index 000000000000..1e28775c8a1e
--- /dev/null
+++ b/vcl/StaticLibrary_glxtest.mk
@@ -0,0 +1,45 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# 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/.
+#
+# This file incorporates work covered by the following license notice:
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed
+# with this work for additional information regarding copyright
+# ownership. The ASF licenses this file to you under the Apache
+# License, Version 2.0 (the "License"); you may not use this file
+# except in compliance with the License. You may obtain a copy of
+# the License at http://www.apache.org/licenses/LICENSE-2.0 .
+#
+
+$(eval $(call gb_StaticLibrary_StaticLibrary,glxtest))
+
+$(eval $(call gb_StaticLibrary_set_include,glxtest,\
+ $$(INCLUDE) \
+ -I$(SRCDIR)/vcl/inc \
+))
+
+$(eval $(call gb_StaticLibrary_use_api,glxtest,\
+ offapi \
+ udkapi \
+))
+
+$(eval $(call gb_StaticLibrary_add_libs,glxtest,\
+ -lm \
+ -ldl \
+ -lpthread \
+ -lGL \
+ -lGLU \
+ -lX11 \
+))
+
+$(eval $(call gb_StaticLibrary_add_exception_objects,glxtest,\
+ vcl/unx/glxtest \
+))
+
+# vim: set noet sw=4 ts=4:
diff --git a/vcl/source/salmain/salmain.cxx b/vcl/source/salmain/salmain.cxx
index 7f0f90af0515..e7318e8424cd 100644
--- a/vcl/source/salmain/salmain.cxx
+++ b/vcl/source/salmain/salmain.cxx
@@ -28,7 +28,14 @@
#include "salinst.hxx"
+#if defined( UNX ) && !defined MACOSX && !defined IOS && !defined ANDROID
+#include <vcl/opengl/glxtest.hxx>
+#endif
+
SAL_IMPLEMENT_MAIN() {
+#if defined( UNX ) && !defined MACOSX && !defined IOS && !defined ANDROID
+ fire_glxtest_process();
+#endif
tools::extendApplicationEnvironment();
vclmain::createApplication();
return SVMain();
diff --git a/vcl/unx/glxtest.cxx b/vcl/unx/glxtest.cxx
new file mode 100644
index 000000000000..d1f959163ea9
--- /dev/null
+++ b/vcl/unx/glxtest.cxx
@@ -0,0 +1,280 @@
+/* -*- 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;
+}