From da0e17fbb19958d838a9ea6093a05384aa6fc207 Mon Sep 17 00:00:00 2001 From: rbuj Date: Wed, 17 Apr 2019 23:53:35 +0200 Subject: Add mate_session_check_accelerated helpers tools/mate-session-check-accelerated.{c,h} tools/mate-session-check-accelerated-gles-helper.c tools/mate-session-check-accelerated-gl-helper.c based on https://github.com/GNOME/gnome-session/commit/3aafcf0f0129e28b5c52f32284f1164a93867ea3 configure.ac tools/Makefile.am based on: based on https://github.com/GNOME/gnome-session/tree/eeefdc8e1a436d0c58cea756e8a3ea63e59145b5 --- .travis.yml | 5 + configure.ac | 3 + tools/Makefile.am | 45 ++ tools/mate-session-check-accelerated-common.h | 29 ++ tools/mate-session-check-accelerated-gl-helper.c | 515 +++++++++++++++++++++ tools/mate-session-check-accelerated-gles-helper.c | 201 ++++++++ tools/mate-session-check-accelerated.c | 312 +++++++++++++ 7 files changed, 1110 insertions(+) create mode 100644 tools/mate-session-check-accelerated-common.h create mode 100644 tools/mate-session-check-accelerated-gl-helper.c create mode 100644 tools/mate-session-check-accelerated-gles-helper.c create mode 100644 tools/mate-session-check-accelerated.c 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 + */ + +/* 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 + * + * Most of the code comes from desktop-effects [1], released under GPLv2+. + * desktop-effects was written by: + * Soren Sandmann + * + * [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 +#include +#include +#include +#include +#include + +#include + +#ifdef __FreeBSD__ +#include +#endif + +#include +#include +#include +#include +#include + +#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 + */ + +/* for strcasestr */ +#define _GNU_SOURCE + +#include + +#include +#include +#include +#include + +#ifdef GDK_WINDOWING_X11 +#define GL_GLEXT_PROTOTYPES + +#include +#include +#include +#include +#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 + */ + +#include +#include +#include + +#include +#include +#include +#include +#include + +#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; +} -- cgit v1.2.1