diff options
| -rw-r--r-- | .travis.yml | 5 | ||||
| -rw-r--r-- | configure.ac | 3 | ||||
| -rw-r--r-- | tools/Makefile.am | 45 | ||||
| -rw-r--r-- | tools/mate-session-check-accelerated-common.h | 29 | ||||
| -rw-r--r-- | tools/mate-session-check-accelerated-gl-helper.c | 515 | ||||
| -rw-r--r-- | tools/mate-session-check-accelerated-gles-helper.c | 201 | ||||
| -rw-r--r-- | tools/mate-session-check-accelerated.c | 312 | 
7 files changed, 1110 insertions, 0 deletions
| diff --git a/.travis.yml b/.travis.yml index c60f817..c932f0c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -58,6 +58,8 @@ requires:      - git      - intltool      - libdbus-glib-1-dev +    - libgl1-mesa-dev +    - libgles2-mesa-dev      - libglib2.0-dev      - libgtk-3-dev      - libice-dev @@ -86,6 +88,7 @@ requires:      - libXtst-devel      - make      - mate-common +    - mesa-libGLES-devel      - pangox-compat-devel      - redhat-rpm-config      - systemd-devel @@ -96,6 +99,8 @@ requires:      - git      - intltool      - libdbus-glib-1-dev +    - libgl1-mesa-dev +    - libgles2-mesa-dev      - libglib2.0-dev      - libgtk-3-dev      - libice-dev diff --git a/configure.ac b/configure.ac index a71617f..8806c72 100644 --- a/configure.ac +++ b/configure.ac @@ -61,6 +61,9 @@ PKG_CHECK_MODULES(X11, x11)  PKG_CHECK_MODULES(SM, sm)  PKG_CHECK_MODULES(ICE, ice)  PKG_CHECK_MODULES(XEXT, xext xau) +PKG_CHECK_MODULES(GTK3, gtk+-3.0 >= $GTK_REQUIRED) +PKG_CHECK_MODULES(GL_TEST, xcomposite gl glib-2.0 epoxy) +PKG_CHECK_MODULES(GLES_TEST, egl glesv2)  PKG_CHECK_MODULES(DBUS_GLIB, dbus-glib-1 >= $DBUS_GLIB_REQUIRED) diff --git a/tools/Makefile.am b/tools/Makefile.am index 5680214..01734e5 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -1,4 +1,8 @@  bin_PROGRAMS = mate-session-save mate-session-inhibit +libexec_PROGRAMS = \ +	mate-session-check-accelerated \ +	mate-session-check-accelerated-gl-helper \ +	mate-session-check-accelerated-gles-helper  AM_CPPFLAGS =					\  	$(MATE_SESSION_CFLAGS)			\ @@ -27,4 +31,45 @@ mate_session_inhibit_CPPFLAGS =			\  mate_session_inhibit_LDADD =			\  	$(MATE_SESSION_LIBS) +mate_session_check_accelerated_gles_helper_SOURCES =	\ +	mate-session-check-accelerated-common.h		\ +	mate-session-check-accelerated-gles-helper.c + +mate_session_check_accelerated_gles_helper_CPPFLAGS =	\ +	-DPKGDATADIR=\""$(pkgdatadir)"\"		\ +	$(GLES_TEST_CFLAGS)				\ +	$(GTK3_CFLAGS) + +mate_session_check_accelerated_gles_helper_LDADD =	\ +	$(GLES_TEST_LIBS)				\ +	$(GTK3_LIBS)					\ +	$(X11_LIBS) + +mate_session_check_accelerated_gl_helper_SOURCES =	\ +	mate-session-check-accelerated-common.h		\ +	mate-session-check-accelerated-gl-helper.c + +mate_session_check_accelerated_gl_helper_CPPFLAGS =	\ +	-DPKGDATADIR=\""$(pkgdatadir)"\"		\ +	$(GL_TEST_CFLAGS) + +mate_session_check_accelerated_gl_helper_LDADD = 	\ +	$(GL_TEST_LIBS)					\ +	$(X11_LIBS) + +mate_session_check_accelerated_SOURCES =       	\ +	mate-session-check-accelerated-common.h	\ +	mate-session-check-accelerated.c + +mate_session_check_accelerated_CPPFLAGS =	\ +	-DLIBEXECDIR=\""$(libexecdir)"\"	\ +	$(AM_CPPFLAGS)				\ +	$(GTK3_CFLAGS)				\ +	$(GL_TEST_CFLAGS) + +mate_session_check_accelerated_LDADD =	\ +	$(GTK3_LIBS)			\ +	$(X11_LIBS)			\ +	$(GL_TEST_LIBS) +  -include $(top_srcdir)/git.mk diff --git a/tools/mate-session-check-accelerated-common.h b/tools/mate-session-check-accelerated-common.h new file mode 100644 index 0000000..59631d9 --- /dev/null +++ b/tools/mate-session-check-accelerated-common.h @@ -0,0 +1,29 @@ +/* -*- mode:c; c-basic-offset: 8; indent-tabs-mode: nil; -*- */ +/* Tool to set the property _GNOME_SESSION_ACCELERATED on the root window */ +/* + * Copyright (C) 2013 SUSE LINUX Products GmbH, Nuernberg, Germany. + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA + * + * Author: + *   Frederic Crozat <[email protected]> + */ + +/* Exit value for helper */ +#define HELPER_ACCEL 0 +#define HELPER_NO_ACCEL 1 +#define HELPER_SOFTWARE_RENDERING 2 + diff --git a/tools/mate-session-check-accelerated-gl-helper.c b/tools/mate-session-check-accelerated-gl-helper.c new file mode 100644 index 0000000..0526336 --- /dev/null +++ b/tools/mate-session-check-accelerated-gl-helper.c @@ -0,0 +1,515 @@ +/* + * Copyright (C) 2010      Novell, Inc. + * Copyright (C) 2006-2009 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA + * + * Author: + *   Vincent Untz <[email protected]> + * + * Most of the code comes from desktop-effects [1], released under GPLv2+. + * desktop-effects was written by: + *   Soren Sandmann <[email protected]> + * + * [1] http://git.fedorahosted.org/git/?p=desktop-effects.git;a=blob_plain;f=desktop-effects.c;hb=HEAD + */ + +/* + * Here's the rationale behind this helper, quoting Owen, in his mail to the + * release team: + * (http://mail.gnome.org/archives/release-team/2010-June/msg00079.html) + * + * """ + * There are some limits to what we can do here automatically without + * knowing anything about the driver situation on the system. The basic + * problem is that there are all sorts of suck: + * + *  * No GL at all. This typically only happens if a system is + *    misconfigured. + * + *  * Only software GL. This one is easy to detect. We have code in + *    the Fedora desktop-effects tool, etc. + * + *  * GL that isn't featureful enough. (Tiny texture size limits, no + *    texture-from-pixmap, etc.) Possible to detect with more work, but + *    largely a fringe case. + * + *  * Buggy GL. This isn't possible to detect. Except for the case where + *    all GL programs crash. For that reason, we probably don't want + *    gnome-session to directly try and do any GL detection; better to + *    use a helper binary. + * + *  * Horribly slow hardware GL. We could theoretically develop some sort + *    of benchmark, but it's a tricky area. And how slow is too slow? + * """ + * + * Some other tools are doing similar checks: + *  - desktop-effects (Fedora Config Tool) [1] + *  - drak3d (Mandriva Config Tool) [2] + *  - compiz-manager (Compiz wrapper) [3] + * + * [1] http://git.fedorahosted.org/git/?p=desktop-effects.git;a=blob_plain;f=desktop-effects.c;hb=HEAD + * [2] http://svn.mandriva.com/cgi-bin/viewvc.cgi/soft/drak3d/trunk/lib/Xconfig/glx.pm?view=markup + * [3] http://git.compiz.org/fusion/misc/compiz-manager/tree/compiz-manager + */ + +/* for strcasestr */ +#define _GNU_SOURCE + +#include <ctype.h> +#include <locale.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <glib.h> + +#include <regex.h> + +#ifdef __FreeBSD__ +#include <kenv.h> +#endif + +#include <X11/Xlib.h> +#include <X11/Xatom.h> +#include <X11/extensions/Xcomposite.h> +#include <GL/gl.h> +#include <GL/glx.h> + +#include "mate-session-check-accelerated-common.h" + +#define SIZE_UNSET 0 +#define SIZE_ERROR -1 +static int max_texture_size = SIZE_UNSET; +static int max_renderbuffer_size = SIZE_UNSET; +static gboolean has_llvmpipe = FALSE; + +static inline void +_print_error (const char *str) +{ +        fprintf (stderr, "mate-session-is-accelerated: %s\n", str); +} + +#define CMDLINE_UNSET -1 +#define CMDLINE_NON_FALLBACK_FORCED 0 +#define CMDLINE_FALLBACK_FORCED 1 + +#if defined(__linux__) +static int +_parse_kcmdline (void) +{ +        int ret = CMDLINE_UNSET; +        GRegex *regex; +        GMatchInfo *match; +        char *contents; +        char *word; +        const char *arg; + +        if (!g_file_get_contents ("/proc/cmdline", &contents, NULL, NULL)) +                return ret; + +        regex = g_regex_new ("mate.fallback=(\\S+)", 0, G_REGEX_MATCH_NOTEMPTY, NULL); +        if (!g_regex_match (regex, contents, G_REGEX_MATCH_NOTEMPTY, &match)) +                goto out; + +        word = g_match_info_fetch (match, 0); +        g_debug ("Found command-line match '%s'", word); +        arg = word + strlen ("mate.fallback="); +        if (*arg != '0' && *arg != '1') +                fprintf (stderr, "mate-session-check-accelerated: Invalid value '%s' for mate.fallback passed in kernel command line.\n", arg); +        else +                ret = atoi (arg); +        g_free (word); + +out: +        g_match_info_free (match); +        g_regex_unref (regex); +        g_free (contents); + +        g_debug ("Command-line parsed to %d", ret); + +        return ret; +} +#elif defined(__FreeBSD__) +static int +_parse_kcmdline (void) +{ +        int ret = CMDLINE_UNSET; +        char value[KENV_MVALLEN]; + +        /* a compile time check to avoid unexpected stack overflow */ +        _Static_assert(KENV_MVALLEN < 1024 * 1024, "KENV_MVALLEN is too large"); + +        if (kenv (KENV_GET, "mate.fallback", value, KENV_MVALLEN) == -1) +                return ret; + +        if (*value != '0' && *value != '1') +                fprintf (stderr, "mate-session-is-accelerated: Invalid value '%s' for mate.fallback passed in kernel environment.\n", value); +        else +                ret = atoi (value); + +        g_debug ("Kernel environment parsed to %d", ret); + +        return ret; +} +#else +static int +_parse_kcmdline (void) +{ +        return CMDLINE_UNSET; +} +#endif + +static gboolean +_has_composite (Display *display) +{ +        int dummy1, dummy2; + +        return XCompositeQueryExtension (display, &dummy1, &dummy2); +} + +static gboolean +_is_comment (const char *line) +{ +        while (*line && isspace(*line)) +                line++; + +        if (*line == '#' || *line == '\0') +                return TRUE; +        return FALSE; +} + +static gboolean +_is_gl_renderer_blacklisted (const char *renderer) +{ +        FILE *blacklist; +        char *line = NULL; +        size_t line_len = 0; +        gboolean ret = TRUE; + +        blacklist = fopen(PKGDATADIR "/hardware-compatibility", "r"); +        if (blacklist == NULL) +                goto out; + +        while (getline (&line, &line_len, blacklist) != -1) { +                int whitelist = 0; +                const char *re_str; +                regex_t re; +                int status; + +                if (line == NULL) +                        break; + +                /* Drop trailing \n */ +                line[strlen(line) - 1] = '\0'; + +                if (_is_comment (line)) { +                        free (line); +                        line = NULL; +                        continue; +                } + +                if (line[0] == '+') +                        whitelist = 1; +                else if (line[0] == '-') +                        whitelist = 0; +                else { +                        _print_error ("Invalid syntax in this line for hardware compatibility:"); +                        _print_error (line); +                        free (line); +                        line = NULL; +                        continue; +                } + +                re_str = line + 1; + +                if (regcomp (&re, re_str, REG_EXTENDED|REG_ICASE|REG_NOSUB) != 0) { +                        _print_error ("Cannot use this regular expression for hardware compatibility:"); +                        _print_error (re_str); +                } else { +                        status = regexec (&re, renderer, 0, NULL, 0); +                        regfree(&re); + +                        if (status == 0) { +                                if (whitelist) +                                        ret = FALSE; +                                goto out; +                        } +                } + +                free (line); +                line = NULL; +        } + +        ret = FALSE; + +out: +        if (line != NULL) +                free (line); + +        if (blacklist != NULL) +                fclose (blacklist); + +        return ret; +} + +static char * +_get_hardware_gl (Display *display) +{ +        int screen; +        Window root; +        XVisualInfo *visual = NULL; +        GLXContext context = NULL; +        XSetWindowAttributes cwa = { 0 }; +        Window window = None; +        char *renderer = NULL; + +        int attrlist[] = { +                GLX_RGBA, +                GLX_RED_SIZE, 1, +                GLX_GREEN_SIZE, 1, +                GLX_BLUE_SIZE, 1, +                GLX_DOUBLEBUFFER, +                None +        }; + +        screen = DefaultScreen (display); +        root = RootWindow (display, screen); + +        visual = glXChooseVisual (display, screen, attrlist); +        if (!visual) +                goto out; + +        context = glXCreateContext (display, visual, NULL, True); +        if (!context) +                goto out; + +        cwa.colormap = XCreateColormap (display, root, +                                        visual->visual, AllocNone); +        cwa.background_pixel = 0; +        cwa.border_pixel = 0; +        window = XCreateWindow (display, root, +                                0, 0, 1, 1, 0, +                                visual->depth, InputOutput, visual->visual, +                                CWColormap | CWBackPixel | CWBorderPixel, +                                &cwa); + +        if (!glXMakeCurrent (display, window, context)) +                goto out; + +        renderer = g_strdup ((const char *) glGetString (GL_RENDERER)); +        if (_is_gl_renderer_blacklisted (renderer)) { +                g_clear_pointer (&renderer, g_free); +                goto out; +        } +        if (renderer && strcasestr (renderer, "llvmpipe")) +		has_llvmpipe = TRUE; + +        /* we need to get the max texture and renderbuffer sizes while we have +         * a context, but we'll check their values later */ + +        glGetIntegerv (GL_MAX_TEXTURE_SIZE, &max_texture_size); +        if (glGetError() != GL_NO_ERROR) +                max_texture_size = SIZE_ERROR; + +        glGetIntegerv (GL_MAX_RENDERBUFFER_SIZE_EXT, &max_renderbuffer_size); +        if (glGetError() != GL_NO_ERROR) +                max_renderbuffer_size = SIZE_ERROR; + +out: +        glXMakeCurrent (display, None, None); +        if (context) +                glXDestroyContext (display, context); +        if (window) +                XDestroyWindow (display, window); +        if (cwa.colormap) +                XFreeColormap (display, cwa.colormap); + +        return renderer; +} + +static gboolean +_has_extension (const char *extension_list, +                const char *extension) +{ +        char **extensions; +        guint i; +        gboolean ret; + +        g_return_val_if_fail (extension != NULL, TRUE); + +        /* Extension_list is one big string, containing extensions +         * separated by spaces. */ +        if (extension_list == NULL) +                return FALSE; + +        ret = FALSE; + +        extensions = g_strsplit (extension_list, " ", -1); +        if (extensions == NULL) +                return FALSE; + +        for (i = 0; extensions[i] != NULL; i++) { +                if (g_str_equal (extensions[i], extension)) { +                        ret = TRUE; +                        break; +                } +        } + +        g_strfreev (extensions); + +        return ret; +} + +static gboolean +_has_texture_from_pixmap (Display *display) +{ +        int screen; +        const char *server_extensions; +        const char *client_extensions; +        gboolean ret = FALSE; + +        screen = DefaultScreen (display); + +        server_extensions = glXQueryServerString (display, screen, +                                                  GLX_EXTENSIONS); +        if (!_has_extension (server_extensions, +                            "GLX_EXT_texture_from_pixmap")) +                goto out; + +        client_extensions = glXGetClientString (display, GLX_EXTENSIONS); +        if (!_has_extension (client_extensions, +                            "GLX_EXT_texture_from_pixmap")) +                goto out; + +        ret = TRUE; + +out: +        return ret; +} + +static void +_set_max_screen_size_property (Display *display, int screen, int size) +{ +        Atom max_screen_size_atom; + +        max_screen_size_atom = XInternAtom (display, "_GNOME_MAX_SCREEN_SIZE", +                                            False); + +        /* Will be read by gnome-settings-daemon and +         * gnome-control-center to avoid display configurations where 3D +         * is not available (and would break gnome-shell) */ +        XChangeProperty (display, RootWindow(display, screen), +                         max_screen_size_atom, +                         XA_CARDINAL, 32, PropModeReplace, +                         (unsigned char *)&size, 1); + +        XSync(display, False); +} + +static gboolean +_is_max_texture_size_big_enough (Display *display) +{ +        int screen, size; + +        screen = DefaultScreen (display); +        size = MIN(max_renderbuffer_size, max_texture_size); +        if (size < DisplayWidth (display, screen) || +            size < DisplayHeight (display, screen)) +                return FALSE; + +        _set_max_screen_size_property (display, screen, size); + +        return TRUE; +} + +static gboolean print_renderer = FALSE; + +static const GOptionEntry entries[] = { +        { "print-renderer", 'p', 0, G_OPTION_ARG_NONE, &print_renderer, "Print GL renderer name", NULL }, +        { NULL }, +}; + +int +main (int argc, char **argv) +{ +        int             kcmdline_parsed; +        Display        *display = NULL; +        int             ret = HELPER_NO_ACCEL; +        GOptionContext *context; +        GError         *error = NULL; +        char           *renderer = NULL; + +        setlocale (LC_ALL, ""); + +        context = g_option_context_new (NULL); +        g_option_context_add_main_entries (context, entries, NULL); + +        if (!g_option_context_parse (context, &argc, &argv, &error)) { +                g_error ("Can't parse command line: %s\n", error->message); +                g_error_free (error); +                goto out; +        } + +        kcmdline_parsed = _parse_kcmdline (); +        if (kcmdline_parsed > CMDLINE_UNSET) { +                if (kcmdline_parsed == CMDLINE_NON_FALLBACK_FORCED) { +                        _print_error ("Non-fallback mode forced by kernel command line."); +                        ret = HELPER_ACCEL; +                        goto out; +                } else if (kcmdline_parsed == CMDLINE_FALLBACK_FORCED) { +                        _print_error ("Fallback mode forced by kernel command line."); +                        goto out; +                } +        } + +        display = XOpenDisplay (NULL); +        if (!display) { +                _print_error ("No X display."); +                goto out; +        } + +        if (!_has_composite (display)) { +                _print_error ("No composite extension."); +                goto out; +        } + +        renderer = _get_hardware_gl (display); +        if (!renderer) { +                _print_error ("No hardware 3D support."); +                goto out; +        } + +        if (!_has_texture_from_pixmap (display)) { +                _print_error ("No GLX_EXT_texture_from_pixmap support."); +                goto out; +        } + +        if (!_is_max_texture_size_big_enough (display)) { +                _print_error ("GL_MAX_{TEXTURE,RENDERBUFFER}_SIZE is too small."); +                goto out; +        } + +        ret = has_llvmpipe ? HELPER_SOFTWARE_RENDERING : HELPER_ACCEL; + +        if (print_renderer) +                g_print ("%s", renderer); + +out: +        if (display) +                XCloseDisplay (display); +        g_free (renderer); + +        return ret; +} diff --git a/tools/mate-session-check-accelerated-gles-helper.c b/tools/mate-session-check-accelerated-gles-helper.c new file mode 100644 index 0000000..ff6a62e --- /dev/null +++ b/tools/mate-session-check-accelerated-gles-helper.c @@ -0,0 +1,201 @@ +/* -*- mode:c; c-basic-offset: 8; indent-tabs-mode: nil; -*- */ +/* + * + * Copyright (C) 2016 Endless Mobile, Inc + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Author: + *   Cosimo Cecchi <[email protected]> + */ + +/* for strcasestr */ +#define _GNU_SOURCE + +#include <config.h> + +#include <gtk/gtk.h> +#include <locale.h> +#include <stdlib.h> +#include <string.h> + +#ifdef GDK_WINDOWING_X11 +#define GL_GLEXT_PROTOTYPES + +#include <gdk/gdkx.h> +#include <GLES2/gl2.h> +#include <GLES2/gl2ext.h> +#include <EGL/egl.h> +#endif + +#include "mate-session-check-accelerated-common.h" + +#ifdef GDK_WINDOWING_X11 +static char * +get_gles_renderer (void) +{ +        /* Select GLESv2 config */ +        EGLint attribs[] = { +                EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, +                EGL_RED_SIZE, 1, +                EGL_GREEN_SIZE, 1, +                EGL_BLUE_SIZE, 1, +                EGL_NONE +        }; + +        EGLint ctx_attribs[] = { +                EGL_CONTEXT_CLIENT_VERSION, 2, +                EGL_NONE +        }; + +        gboolean egl_inited = FALSE; +        Display *display; +        Window win = None; +        EGLContext egl_ctx = NULL; +        EGLDisplay egl_dpy = NULL; +        EGLSurface egl_surf = NULL; +        char *renderer = NULL; + +        gdk_error_trap_push (); + +        display = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()); +        egl_dpy = eglGetDisplay (display); +        if (!egl_dpy) { +                g_warning ("eglGetDisplay() failed"); +                goto out; +        } + +        EGLint egl_major, egl_minor; +        if (!eglInitialize (egl_dpy, &egl_major, &egl_minor)) { +                g_warning ("eglInitialize() failed"); +                goto out; +        } + +        egl_inited = TRUE; + +        EGLint num_configs; +        EGLConfig config; +        if (!eglChooseConfig (egl_dpy, attribs, &config, 1, &num_configs)) { +                g_warning ("Failed to get EGL configuration"); +                goto out; +        } + +        EGLint vid; +        if (!eglGetConfigAttrib (egl_dpy, config, EGL_NATIVE_VISUAL_ID, &vid)) { +                g_warning ("Failed to get EGL visual"); +                goto out; +        } + +        /* The X window visual must match the EGL config */ +        XVisualInfo *vis_info, vis_template; +        int num_visuals; +        vis_template.visualid = vid; +        vis_info = XGetVisualInfo (display, VisualIDMask, &vis_template, &num_visuals); +        if (!vis_info) { +                g_warning ("Failed to get X visual"); +                goto out; +        } + +        XSetWindowAttributes attr; +        attr.colormap = XCreateColormap (display, DefaultRootWindow (display), +                                         vis_info->visual, AllocNone); +        win = XCreateWindow (display, DefaultRootWindow (display), +                             0, 0, /* x, y */ +                             1, 1, /* width, height */ +                             0,    /* border_width */ +                             vis_info->depth, InputOutput, +                             vis_info->visual, CWColormap, &attr); +        XFree (vis_info); + +        eglBindAPI (EGL_OPENGL_ES_API); + +        egl_ctx = eglCreateContext (egl_dpy, config, EGL_NO_CONTEXT, ctx_attribs); +        if (!egl_ctx) { +                g_warning ("Failed to create EGL context"); +                goto out; +        } + +        egl_surf = eglCreateWindowSurface (egl_dpy, config, win, NULL); +        if (!egl_surf) { +                g_warning ("Failed to create EGL surface"); +                goto out; +        } + +        if (!eglMakeCurrent (egl_dpy, egl_surf, egl_surf, egl_ctx)) { +                g_warning ("eglMakeCurrent() failed"); +                goto out; +        } + +        renderer = g_strdup ((const char *) glGetString (GL_RENDERER)); + + out: +        if (egl_ctx) +                eglDestroyContext (egl_dpy, egl_ctx); +        if (egl_surf) +                eglDestroySurface (egl_dpy, egl_surf); +        if (egl_inited) +                eglTerminate (egl_dpy); +        if (win != None) +                XDestroyWindow (display, win); + +        gdk_error_trap_pop_ignored (); +        return renderer; +} +#endif + +static gboolean print_renderer = FALSE; + +static const GOptionEntry entries[] = { +        { "print-renderer", 'p', 0, G_OPTION_ARG_NONE, &print_renderer, "Print EGL renderer name", NULL }, +        { NULL }, +}; + +int +main (int argc, +      char **argv) +{ +        char *renderer = NULL; +        GOptionContext *context; +        int ret = HELPER_NO_ACCEL; +        GError *error = NULL; + +        setlocale (LC_ALL, ""); + +        context = g_option_context_new (NULL); +        g_option_context_add_group (context, gtk_get_option_group (TRUE)); +        g_option_context_add_main_entries (context, entries, NULL); + +        if (!g_option_context_parse (context, &argc, &argv, &error)) { +                g_error ("Can't parse command line: %s\n", error->message); +                g_error_free (error); +                goto out; +        } + +#ifdef GDK_WINDOWING_X11 +        renderer = get_gles_renderer (); +#endif + +        if (renderer != NULL) { +                if (print_renderer) +                        g_print ("%s", renderer); +                if (strcasestr (renderer, "llvmpipe")) +                        ret = HELPER_SOFTWARE_RENDERING; +                else +                        ret = HELPER_ACCEL; +        } + +out: +        return ret; +} diff --git a/tools/mate-session-check-accelerated.c b/tools/mate-session-check-accelerated.c new file mode 100644 index 0000000..3c0d4bd --- /dev/null +++ b/tools/mate-session-check-accelerated.c @@ -0,0 +1,312 @@ +/* -*- mode:c; c-basic-offset: 8; indent-tabs-mode: nil; -*- */ +/* Tool to set the property _GNOME_SESSION_ACCELERATED on the root window */ +/* + * Copyright (C) 2011 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA + * + * Author: + *   Colin Walters <[email protected]> + */ + +#include <string.h> +#include <unistd.h> +#include <stdlib.h> + +#include <gtk/gtk.h> +#include <epoxy/gl.h> +#include <gdk/gdkx.h> +#include <X11/Xatom.h> +#include <sys/wait.h> + +#include "mate-session-check-accelerated-common.h" + +/* Wait up to this long for a running check to finish */ +#define PROPERTY_CHANGE_TIMEOUT 5000 + +/* Values used for the _GNOME_SESSION_ACCELERATED root window property */ +#define NO_ACCEL            0 +#define HAVE_ACCEL          1 +#define ACCEL_CHECK_RUNNING 2 + +static Atom is_accelerated_atom; +static Atom is_software_rendering_atom; +static Atom renderer_atom; +static gboolean property_changed; + +static gboolean +on_property_notify_timeout (gpointer data) +{ +        gtk_main_quit (); +        return FALSE; +} + +static GdkFilterReturn +property_notify_filter (GdkXEvent *xevent, +                        GdkEvent  *event, +                        gpointer   data) +{ +        XPropertyEvent *ev = xevent; + +        if (ev->type == PropertyNotify && ev->atom == is_accelerated_atom) { +                property_changed = TRUE; +                gtk_main_quit (); +        } + +        return GDK_FILTER_CONTINUE; +} + +static gboolean +wait_for_property_notify (void) +{ +        GdkDisplay *display; +        GdkScreen *screen; +        GdkWindow *root; +        Window rootwin; + +        property_changed = FALSE; + +        display = gdk_display_get_default (); +        screen = gdk_display_get_default_screen (display); +        root = gdk_screen_get_root_window (screen); +        rootwin = gdk_x11_window_get_xid (root); + +        XSelectInput (GDK_DISPLAY_XDISPLAY (display), rootwin, PropertyChangeMask); +        gdk_window_add_filter (root, property_notify_filter, NULL); +        g_timeout_add (PROPERTY_CHANGE_TIMEOUT, on_property_notify_timeout, NULL); + +        gtk_main (); + +        return property_changed; +} + +static char * +get_gtk_gles_renderer (void) +{ +        GtkWidget *win; +        GdkGLContext *context; +        char *renderer = NULL; + +        win = gtk_window_new (GTK_WINDOW_TOPLEVEL); +        gtk_widget_realize (win); +        context = gdk_window_create_gl_context (gtk_widget_get_window (win), NULL); +        if (!context) +                return NULL; +        gdk_gl_context_make_current (context); +        renderer = g_strdup ((char *) glGetString (GL_RENDERER)); +        gdk_gl_context_clear_current (); +        g_object_unref (context); + +        return renderer; +} + +static gboolean +is_discrete_gpu_check (void) +{ +	const char *dri_prime; + +	dri_prime = g_getenv ("DRI_PRIME"); +	if (!dri_prime) +		return FALSE; +	if (*dri_prime != '1') +		return FALSE; +	return TRUE; +} + +int +main (int argc, char **argv) +{ +        GdkDisplay *display = NULL; +        int estatus; +        char *gl_helper_argv[] = { LIBEXECDIR "/mate-session-check-accelerated-gl-helper", "--print-renderer", NULL }; +        char *gles_helper_argv[] = { LIBEXECDIR "/mate-session-check-accelerated-gles-helper", "--print-renderer", NULL }; +        char *renderer_string = NULL; +        char *gl_renderer_string = NULL, *gles_renderer_string = NULL; +        gboolean gl_software_rendering = FALSE, gles_software_rendering = FALSE; +        Window rootwin; +        glong is_accelerated, is_software_rendering; +        GError *gl_error = NULL, *gles_error = NULL; + +        gtk_init (NULL, NULL); + +        /* mate-session-check-accelerated gets run before X is started in the wayland +         * case, and it currently requires X. Until we have that working, just always +         * assume wayland will work. +         * Also make sure that we don't read cached information about the first GPU +         * when requesting information about the second. +         */ +        if (is_discrete_gpu_check () || g_strcmp0 (g_getenv ("XDG_SESSION_TYPE"), "x11") != 0) { +                renderer_string = get_gtk_gles_renderer (); +                if (renderer_string) { +                        g_print ("%s", renderer_string); +                        return 0; +                } +                return 1; +        } + +        display = gdk_display_get_default (); +        /* when running on X11 with a nested wayland GDK will default to wayland +         * so looking for X11 atoms will not work (and crash). +         */ +        if (!GDK_IS_X11_DISPLAY (display)) { +                g_printerr ("mate-session-check-accelerated: no X11 display found\n"); +                return 1; +        } + +        rootwin = gdk_x11_get_default_root_xwindow (); + +        is_accelerated_atom = gdk_x11_get_xatom_by_name_for_display (display, "_GNOME_SESSION_ACCELERATED"); +        is_software_rendering_atom = gdk_x11_get_xatom_by_name_for_display (display, "_GNOME_IS_SOFTWARE_RENDERING"); +        renderer_atom = gdk_x11_get_xatom_by_name_for_display (display, "_GNOME_SESSION_RENDERER"); + +        { +                Atom type; +                gint format; +                gulong nitems; +                gulong bytes_after; +                guchar *data; + + read: +                gdk_x11_display_error_trap_push (display); +                XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), rootwin, +                                    is_accelerated_atom, +                                    0, G_MAXLONG, False, XA_CARDINAL, &type, &format, &nitems, +                                    &bytes_after, &data); +                gdk_x11_display_error_trap_pop_ignored (display); + +                if (type == XA_CARDINAL) { +                        glong *is_accelerated_ptr = (glong*) data; + +                        if (*is_accelerated_ptr == ACCEL_CHECK_RUNNING) { +                                /* Test in progress, wait */ +                                if (wait_for_property_notify ()) +                                        goto read; +                                /* else fall through and do the check ourselves */ + +                        } else { +                                gdk_x11_display_error_trap_push (display); +                                XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), rootwin, +                                                    renderer_atom, +                                                    0, G_MAXLONG, False, XA_STRING, &type, &format, &nitems, +                                                    &bytes_after, &data); +                                gdk_x11_display_error_trap_pop_ignored (display); + +                                if (type == XA_STRING) { +                                        g_print ("%s", data); +                                } + +                                return (*is_accelerated_ptr == 0 ? 1 : 0); +                        } +                } +        } + +        /* We don't have the property or it's the wrong type. +         * Try to compute it now. +         */ + +        /* First indicate that a test is in progress */ +        is_accelerated = ACCEL_CHECK_RUNNING; +        is_software_rendering = FALSE; +        estatus = 1; + +        XChangeProperty (GDK_DISPLAY_XDISPLAY (display), +                         rootwin, +                         is_accelerated_atom, +                         XA_CARDINAL, 32, PropModeReplace, (guchar *) &is_accelerated, 1); + +        gdk_display_sync (display); + +        /* First, try the GL helper */ +        if (g_spawn_sync (NULL, (char **) gl_helper_argv, NULL, 0, +                           NULL, NULL, &gl_renderer_string, NULL, &estatus, &gl_error)) { +                is_accelerated = (WEXITSTATUS(estatus) == HELPER_ACCEL); +                gl_software_rendering = (WEXITSTATUS(estatus) == HELPER_SOFTWARE_RENDERING); +                if (is_accelerated) { +                        renderer_string = gl_renderer_string; +                        goto finish; +                } + +                g_printerr ("mate-session-check-accelerated: GL Helper exited with code %d\n", estatus); +        } + +        /* Then, try the GLES helper */ +        if (g_spawn_sync (NULL, (char **) gles_helper_argv, NULL, 0, +                           NULL, NULL, &gles_renderer_string, NULL, &estatus, &gles_error)) { +                is_accelerated = (WEXITSTATUS(estatus) == HELPER_ACCEL); +                gles_software_rendering = (WEXITSTATUS(estatus) == HELPER_SOFTWARE_RENDERING); +                if (is_accelerated) { +                        renderer_string = gles_renderer_string; +                        goto finish; +                } + +                g_printerr ("mate-session-check-accelerated: GLES Helper exited with code %d\n", estatus); +        } + +        /* If we got here, GL software rendering is our best bet */ +        if (gl_software_rendering || gles_software_rendering) { +                is_software_rendering = TRUE; +                is_accelerated = TRUE; + +                if (gl_software_rendering) +                        renderer_string = gl_renderer_string; +                else if (gles_software_rendering) +                        renderer_string = gles_renderer_string; + +                goto finish; +        } + +        /* Both helpers failed; print their error messages */ +        if (gl_error != NULL) { +                g_printerr ("mate-session-check-accelerated: Failed to run GL helper: %s\n", gl_error->message); +                g_clear_error (&gl_error); +        } + +        if (gles_error != NULL) { +                g_printerr ("mate-session-check-accelerated: Failed to run GLES helper: %s\n", gles_error->message); +                g_clear_error (&gles_error); +        } + + finish: +	if (is_accelerated) { +		XChangeProperty (GDK_DISPLAY_XDISPLAY (display), +				rootwin, +				is_accelerated_atom, +				XA_CARDINAL, 32, PropModeReplace, (guchar *) &is_accelerated, 1); +	} + +	if (is_software_rendering) { +		XChangeProperty (GDK_DISPLAY_XDISPLAY (display), +				rootwin, +				is_software_rendering_atom, +				XA_CARDINAL, 32, PropModeReplace, (guchar *) &is_software_rendering, 1); +	} + +        if (renderer_string != NULL) { +                XChangeProperty (GDK_DISPLAY_XDISPLAY (display), +				rootwin, +				renderer_atom, +				XA_STRING, 8, PropModeReplace, (guchar *) renderer_string, strlen (renderer_string)); + +                /* Print the renderer */ +                g_print ("%s", renderer_string); +        } + +        gdk_display_sync (display); + +        g_free (gl_renderer_string); +        g_free (gles_renderer_string); + +        return is_accelerated ? 0 : 1; +} | 
