From 236d08c3c59a33e1ec4004af0aa4f6095402809a Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Thu, 9 Oct 2014 16:09:05 +0200 Subject: [PATCH] gl: Make all user GdkGLContexts not attached to any window We make user facing gl contexts not attached to a surface if possible, or attached to dummy surfaces. This means nothing can accidentally read/write to the toplevel back buffer. --- gdk/gdkglcontext.c | 16 ++-- gdk/gdkwindow.c | 7 +- gdk/gdkwindowimpl.h | 1 + gdk/wayland/gdkdisplay-wayland.h | 1 + gdk/wayland/gdkglcontext-wayland.c | 19 ++++- gdk/wayland/gdkglcontext-wayland.h | 2 + gdk/wayland/gdkprivate-wayland.h | 2 + gdk/wayland/gdkwindow-wayland.c | 41 +++++++++ gdk/x11/gdkglcontext-x11.c | 129 ++++++++++++++++++----------- gdk/x11/gdkglcontext-x11.h | 3 + 10 files changed, 162 insertions(+), 59 deletions(-) diff --git a/gdk/gdkglcontext.c b/gdk/gdkglcontext.c index 58eaa0abb4..9a5f457ef5 100644 --- a/gdk/gdkglcontext.c +++ b/gdk/gdkglcontext.c @@ -27,15 +27,15 @@ * OpenGL drawing context. * * #GdkGLContexts are created for a #GdkWindow using gdk_window_create_gl_context(), and - * the context will be tied to the native window backing that window, matching the - * GdkVisual of the window. + * the context will match the GdkVisual of the window. * - * A #GdkGLContexts normal framebuffer draws directly on to the back buffer of the native - * window backing the #GdkWindow, so its not allowed to draw directly to that, as the - * gdk repaint system is in full control of that. Instead you can create render buffers - * or textures and use gdk_cairo_draw_from_gl() in the draw function of your widget - * to draw them. Then Gdk will handle the integration of your rendering with that of - * other widgets. + * A #GdkGLContexts is not tied to any particulare normal + * framebuffer. For instance, it cannot draw to the #GdkWindow back + * buffer. The gdk repaint system is in full control of the painting + * to that. Instead you can create render buffers or textures and use + * gdk_cairo_draw_from_gl() in the draw function of your widget to + * draw them. Then Gdk will handle the integration of your rendering + * with that of other widgets. * * Support for #GdkGLContext is platform specific, context creation can fail, returning * a %NULL context. diff --git a/gdk/gdkwindow.c b/gdk/gdkwindow.c index a73725487b..21eddf013e 100644 --- a/gdk/gdkwindow.c +++ b/gdk/gdkwindow.c @@ -2725,6 +2725,7 @@ gdk_window_get_paint_gl_context (GdkWindow *window, GError **error) if (window->impl_window->gl_paint_context == NULL) window->impl_window->gl_paint_context = GDK_WINDOW_IMPL_GET_CLASS (window->impl)->create_gl_context (window, + TRUE, GDK_GL_PROFILE_DEFAULT, NULL, error); @@ -2738,8 +2739,9 @@ gdk_window_get_paint_gl_context (GdkWindow *window, GError **error) * @profile: the GL profile the context should target * @error: return location for an error * - * Creates a new #GdkGLContext for the given window, matching the - * framebuffer format to the visual of the #GdkWindow. + * Creates a new #GdkGLContext matching the + * framebuffer format to the visual of the #GdkWindow. The context + * is disconnected from any particular window or surface. * * If the creation of the #GdkGLContext failed, @error will be set. * @@ -2763,6 +2765,7 @@ gdk_window_create_gl_context (GdkWindow *window, return NULL; return GDK_WINDOW_IMPL_GET_CLASS (window->impl)->create_gl_context (window, + FALSE, profile, paint_context, error); diff --git a/gdk/gdkwindowimpl.h b/gdk/gdkwindowimpl.h index f6e9a638b7..2c1bc1e07a 100644 --- a/gdk/gdkwindowimpl.h +++ b/gdk/gdkwindowimpl.h @@ -290,6 +290,7 @@ struct _GdkWindowImplClass gboolean (* show_window_menu) (GdkWindow *window, GdkEvent *event); GdkGLContext *(*create_gl_context) (GdkWindow *window, + gboolean attached, GdkGLProfile profile, GdkGLContext *share, GError **error); diff --git a/gdk/wayland/gdkdisplay-wayland.h b/gdk/wayland/gdkdisplay-wayland.h index daa8baf08d..3bad519a12 100644 --- a/gdk/wayland/gdkdisplay-wayland.h +++ b/gdk/wayland/gdkdisplay-wayland.h @@ -89,6 +89,7 @@ struct _GdkWaylandDisplay guint have_egl_khr_create_context : 1; guint have_egl_buffer_age : 1; guint have_egl_swap_buffers_with_damage : 1; + guint have_egl_surfaceless_context : 1; }; struct _GdkWaylandDisplayClass diff --git a/gdk/wayland/gdkglcontext-wayland.c b/gdk/wayland/gdkglcontext-wayland.c index 5ddcdcd546..b6f1e9974d 100644 --- a/gdk/wayland/gdkglcontext-wayland.c +++ b/gdk/wayland/gdkglcontext-wayland.c @@ -76,7 +76,8 @@ gdk_wayland_window_invalidate_for_new_frame (GdkWindow *window, egl_surface = gdk_wayland_window_get_egl_surface (window->impl_window, context_wayland->egl_config); - if (display_wayland->have_egl_buffer_age) + if (display_wayland->have_egl_buffer_age && + gdk_gl_context_make_current (window->gl_paint_context)) eglQuerySurface (display_wayland->egl_display, egl_surface, EGL_BUFFER_AGE_EXT, &buffer_age); @@ -203,6 +204,9 @@ gdk_wayland_display_init_gl (GdkDisplay *display) display_wayland->have_egl_swap_buffers_with_damage = epoxy_has_egl_extension (dpy, "EGL_EXT_swap_buffers_with_damage"); + display_wayland->have_egl_surfaceless_context = + epoxy_has_egl_extension (dpy, "EGL_KHR_surfaceless_context"); + GDK_NOTE (OPENGL, g_print ("EGL API version %d.%d found\n" " - Vendor: %s\n" @@ -297,6 +301,7 @@ find_eglconfig_for_window (GdkWindow *window, GdkGLContext * gdk_wayland_window_create_gl_context (GdkWindow *window, + gboolean attached, GdkGLProfile profile, GdkGLContext *share, GError **error) @@ -359,6 +364,7 @@ gdk_wayland_window_create_gl_context (GdkWindow *window, context->egl_config = config; context->egl_context = ctx; + context->is_attached = attached; return GDK_GL_CONTEXT (context); } @@ -404,7 +410,16 @@ gdk_wayland_display_make_gl_context_current (GdkDisplay *display, context_wayland = GDK_WAYLAND_GL_CONTEXT (context); window = gdk_gl_context_get_window (context); - egl_surface = gdk_wayland_window_get_egl_surface (window->impl_window, context_wayland->egl_config); + if (context_wayland->is_attached) + egl_surface = gdk_wayland_window_get_egl_surface (window->impl_window, context_wayland->egl_config); + else + { + if (display_wayland->have_egl_surfaceless_context) + egl_surface = EGL_NO_SURFACE; + else + egl_surface = gdk_wayland_window_get_dummy_egl_surface (window->impl_window, + context_wayland->egl_config); + } if (!eglMakeCurrent(display_wayland->egl_display, egl_surface, egl_surface, context_wayland->egl_context)) diff --git a/gdk/wayland/gdkglcontext-wayland.h b/gdk/wayland/gdkglcontext-wayland.h index 475a28bef6..3d4fe37017 100644 --- a/gdk/wayland/gdkglcontext-wayland.h +++ b/gdk/wayland/gdkglcontext-wayland.h @@ -39,6 +39,7 @@ struct _GdkWaylandGLContext EGLContext egl_context; EGLConfig egl_config; + gboolean is_attached; }; struct _GdkWaylandGLContextClass @@ -48,6 +49,7 @@ struct _GdkWaylandGLContextClass gboolean gdk_wayland_display_init_gl (GdkDisplay *display); GdkGLContext * gdk_wayland_window_create_gl_context (GdkWindow *window, + gboolean attach, GdkGLProfile profile, GdkGLContext *share, GError **error); diff --git a/gdk/wayland/gdkprivate-wayland.h b/gdk/wayland/gdkprivate-wayland.h index b72b3594fb..4f6f15b378 100644 --- a/gdk/wayland/gdkprivate-wayland.h +++ b/gdk/wayland/gdkprivate-wayland.h @@ -239,5 +239,7 @@ void gdk_wayland_selection_unset_data_source (GdkAtom selection); EGLSurface gdk_wayland_window_get_egl_surface (GdkWindow *window, EGLConfig config); +EGLSurface gdk_wayland_window_get_dummy_egl_surface (GdkWindow *window, + EGLConfig config); #endif /* __GDK_PRIVATE_WAYLAND_H__ */ diff --git a/gdk/wayland/gdkwindow-wayland.c b/gdk/wayland/gdkwindow-wayland.c index 90d75efbab..828cd57358 100644 --- a/gdk/wayland/gdkwindow-wayland.c +++ b/gdk/wayland/gdkwindow-wayland.c @@ -101,6 +101,9 @@ struct _GdkWindowImplWayland struct wl_egl_window *egl_window; EGLSurface egl_surface; + struct wl_egl_window *dummy_egl_window; + EGLSurface dummy_egl_surface; + unsigned int mapped : 1; unsigned int use_custom_surface : 1; unsigned int pending_commit : 1; @@ -1171,6 +1174,18 @@ gdk_wayland_window_hide_surface (GdkWindow *window) if (impl->surface) { + if (impl->dummy_egl_surface) + { + eglDestroySurface(display_wayland->egl_display, impl->dummy_egl_surface); + impl->dummy_egl_surface = NULL; + } + + if (impl->dummy_egl_window) + { + wl_egl_window_destroy (impl->dummy_egl_window); + impl->dummy_egl_window = NULL; + } + if (impl->egl_surface) { eglDestroySurface(display_wayland->egl_display, impl->egl_surface); @@ -2238,6 +2253,32 @@ gdk_wayland_window_get_egl_surface (GdkWindow *window, return impl->egl_surface; } +EGLSurface +gdk_wayland_window_get_dummy_egl_surface (GdkWindow *window, + EGLConfig config) +{ + GdkWaylandDisplay *display_wayland = GDK_WAYLAND_DISPLAY (gdk_window_get_display (window)); + GdkWindowImplWayland *impl; + struct wl_egl_window *egl_window; + + g_return_val_if_fail (GDK_IS_WAYLAND_WINDOW (window), NULL); + + impl = GDK_WINDOW_IMPL_WAYLAND (window->impl); + + if (impl->dummy_egl_surface == NULL) + { + impl->dummy_egl_window = + wl_egl_window_create (impl->surface, 1, 1); + + impl->dummy_egl_surface = + eglCreateWindowSurface (display_wayland->egl_display, + config, impl->dummy_egl_window, NULL); + } + + return impl->dummy_egl_surface; +} + + /** * gdk_wayland_window_set_use_custom_surface: * @window: (type GdkWaylandWindow): a #GdkWindow diff --git a/gdk/x11/gdkglcontext-x11.c b/gdk/x11/gdkglcontext-x11.c index 70ff6a3d21..b4e4e57b40 100644 --- a/gdk/x11/gdkglcontext-x11.c +++ b/gdk/x11/gdkglcontext-x11.c @@ -41,10 +41,12 @@ G_DEFINE_TYPE (GdkX11GLContext, gdk_x11_gl_context, GDK_TYPE_GL_CONTEXT) typedef struct { - GLXDrawable drawable; - GdkDisplay *display; - GdkWindow *window; + + GLXDrawable glx_drawable; + + Window dummy_xwin; + GLXWindow dummy_glx; guint32 last_frame_counter; } DrawableInfo; @@ -53,11 +55,20 @@ static void drawable_info_free (gpointer data_) { DrawableInfo *data = data_; + Display *dpy; gdk_x11_display_error_trap_push (data->display); - if (data->drawable) - glXDestroyWindow (gdk_x11_display_get_xdisplay (data->display), data->drawable); + dpy = gdk_x11_display_get_xdisplay (data->display); + + if (data->glx_drawable) + glXDestroyWindow (dpy, data->glx_drawable); + + if (data->dummy_glx) + glXDestroyWindow (dpy, data->dummy_glx); + + if (data->dummy_xwin) + XDestroyWindow (dpy, data->dummy_xwin); gdk_x11_display_error_trap_pop_ignored (data->display); @@ -144,9 +155,10 @@ gdk_x11_window_invalidate_for_new_frame (GdkWindow *window, buffer_age = 0; - if (display_x11->has_glx_buffer_age) + if (display_x11->has_glx_buffer_age && + gdk_gl_context_make_current (window->gl_paint_context)) glXQueryDrawable(dpy, context_x11->drawable, - GLX_BACK_BUFFER_AGE_EXT, &buffer_age); + GLX_BACK_BUFFER_AGE_EXT, &buffer_age); invalidate_all = FALSE; if (buffer_age == 0 || buffer_age >= 4) @@ -500,14 +512,13 @@ gdk_x11_display_init_gl (GdkDisplay *display) #define MAX_GLX_ATTRS 30 static gboolean -find_fbconfig_for_window (GdkWindow *window, +find_fbconfig_for_visual (GdkDisplay *display, + GdkVisual *visual, GLXFBConfig *fb_config_out, XVisualInfo **visinfo_out, GError **error) { static int attrs[MAX_GLX_ATTRS]; - GdkVisual *visual = gdk_window_get_visual (window); - GdkDisplay *display = gdk_window_get_display (window); Display *dpy = gdk_x11_display_get_xdisplay (display); GLXFBConfig *configs; int n_configs, i; @@ -650,12 +661,14 @@ create_gl_context (GdkDisplay *display, GdkGLContext * gdk_x11_window_create_gl_context (GdkWindow *window, - GdkGLProfile profile, - GdkGLContext *share, - GError **error) + gboolean attached, + GdkGLProfile profile, + GdkGLContext *share, + GError **error) { - GdkDisplay *display = gdk_window_get_display (window); + GdkDisplay *display; GdkX11GLContext *context; + GdkVisual *visual; GdkVisual *gdk_visual; GLXFBConfig config; GLXContext glx_context; @@ -665,6 +678,8 @@ gdk_x11_window_create_gl_context (GdkWindow *window, Display *dpy; DrawableInfo *info; + display = gdk_window_get_display (window); + if (!gdk_x11_display_init_gl (display)) { g_set_error_literal (error, GDK_GL_ERROR, @@ -684,7 +699,9 @@ gdk_x11_window_create_gl_context (GdkWindow *window, return NULL; } - if (!find_fbconfig_for_window (window, &config, &xvisinfo, error)) + visual = gdk_window_get_visual (window); + + if (!find_fbconfig_for_visual (display, visual, &config, &xvisinfo, error)) return NULL; dpy = gdk_x11_display_get_xdisplay (display); @@ -712,31 +729,53 @@ gdk_x11_window_create_gl_context (GdkWindow *window, is_direct = glXIsDirect (dpy, glx_context); - gdk_x11_display_error_trap_push (display); - - if (GDK_X11_DISPLAY (display)->glx_version >= 13) + info = get_glx_drawable_info (window->impl_window); + if (info == NULL) { - info = get_glx_drawable_info (window->impl_window); + XSetWindowAttributes attrs; + unsigned long mask; - if (info == NULL) - { - info = g_slice_new (DrawableInfo); - info->window = window->impl_window; - info->display = display; - info->drawable = glXCreateWindow (dpy, - config, - gdk_x11_window_get_xid (window->impl_window), - NULL); - info->last_frame_counter = 0; + gdk_x11_display_error_trap_push (display); - set_glx_drawable_info (window->impl_window, info); - } + info = g_slice_new0 (DrawableInfo); + info->display = display; + info->last_frame_counter = 0; - drawable = info->drawable; - } - else - { - drawable = gdk_x11_window_get_xid (window); + attrs.override_redirect = True; + attrs.colormap = XCreateColormap (dpy, DefaultRootWindow (dpy), xvisinfo->visual, AllocNone); + attrs.border_pixel = 0; + mask = CWOverrideRedirect | CWColormap | CWBorderPixel; + info->dummy_xwin = XCreateWindow (dpy, DefaultRootWindow (dpy), + -100, -100, 1, 1, + 0, + xvisinfo->depth, + CopyFromParent, + xvisinfo->visual, + mask, + &attrs); + XMapWindow(dpy, info->dummy_xwin); + + if (GDK_X11_DISPLAY (display)->glx_version >= 13) + { + info->glx_drawable = glXCreateWindow (dpy, config, + gdk_x11_window_get_xid (window->impl_window), + NULL); + info->dummy_glx = glXCreateWindow (dpy, config, info->dummy_xwin, NULL); + } + + if (gdk_x11_display_error_trap_pop (display)) + { + g_set_error_literal (error, GDK_GL_ERROR, + GDK_GL_ERROR_NOT_AVAILABLE, + _("Unable to create a GL context")); + + drawable_info_free (info); + glXDestroyContext (dpy, glx_context); + + return NULL; + } + + set_glx_drawable_info (window->impl_window, info); } gdk_visual = gdk_x11_screen_lookup_visual (gdk_display_get_default_screen (display), @@ -744,16 +783,10 @@ gdk_x11_window_create_gl_context (GdkWindow *window, XFree (xvisinfo); - if (gdk_x11_display_error_trap_pop (display)) - { - g_set_error_literal (error, GDK_GL_ERROR, - GDK_GL_ERROR_NOT_AVAILABLE, - _("Unable to create a GL context")); - - glXDestroyContext (dpy, glx_context); - - return NULL; - } + if (attached) + drawable = info->glx_drawable ? info->glx_drawable : gdk_x11_window_get_xid (window->impl_window); + else + drawable = info->dummy_glx ? info->dummy_glx : info->dummy_xwin; GDK_NOTE (OPENGL, g_print ("Created GLX context[%p], %s\n", @@ -765,9 +798,11 @@ gdk_x11_window_create_gl_context (GdkWindow *window, "visual", gdk_visual, NULL); + context->profile = profile; context->glx_config = config; context->glx_context = glx_context; context->drawable = drawable; + context->is_attached = attached; context->is_direct = is_direct; return GDK_GL_CONTEXT (context); @@ -832,7 +867,7 @@ gdk_x11_display_make_gl_context_current (GdkDisplay *display, glXMakeContextCurrent (dpy, context_x11->drawable, context_x11->drawable, context_x11->glx_context); - if (GDK_X11_DISPLAY (display)->has_glx_swap_interval) + if (context_x11->is_attached && GDK_X11_DISPLAY (display)->has_glx_swap_interval) { if (context_x11->do_frame_sync) glXSwapIntervalSGI (1); diff --git a/gdk/x11/gdkglcontext-x11.h b/gdk/x11/gdkglcontext-x11.h index 9f7566ca5b..091783e143 100644 --- a/gdk/x11/gdkglcontext-x11.h +++ b/gdk/x11/gdkglcontext-x11.h @@ -40,10 +40,12 @@ struct _GdkX11GLContext { GdkGLContext parent_instance; + GdkGLProfile profile; GLXContext glx_context; GLXFBConfig glx_config; GLXDrawable drawable; + guint is_attached : 1; guint is_direct : 1; guint do_frame_sync : 1; @@ -56,6 +58,7 @@ struct _GdkX11GLContextClass gboolean gdk_x11_display_init_gl (GdkDisplay *display); GdkGLContext * gdk_x11_window_create_gl_context (GdkWindow *window, + gboolean attached, GdkGLProfile profile, GdkGLContext *share, GError **error);