Commit 430b6f8f authored by Benjamin Otte's avatar Benjamin Otte
Browse files

gdk: Add GdkDisplay::init_gl vfunc

The vfunc is called to initialize GL and it returns a "base" context
that GDK then uses as the context all others are shared with. So the GL
context share tree now looks like:

+ context from init_gl
  - context1
  - context2
  ...

So this is a flat tree now, the complexity is gone.

The only caveat is that backends now need to create a GL context when
initializing GL so some refactoring was needed.

Two new functions have been added:

* gdk_display_prepare_gl()
  This is public API and can be used to ensure that GL has been
  initialized or if not, retrieve an error to display (or debug-print).
* gdk_display_get_gl_context()
  This is a private function to retrieve the base context from
  init_gl(). It replaces gdk_surface_get_shared_data_context().
parent ba3e80cb
......@@ -80,6 +80,13 @@ enum {
typedef struct _GdkDisplayPrivate GdkDisplayPrivate;
struct _GdkDisplayPrivate {
/* The base context that all other contexts inherit from.
* This context is never exposed to public API and is
* allowed to have a %NULL surface.
*/
GdkGLContext *gl_context;
GError *gl_error;
guint rgba : 1;
guint composited : 1;
guint input_shapes : 1;
......@@ -129,10 +136,14 @@ gdk_display_real_make_default (GdkDisplay *display)
{
}
static void
gdk_display_real_opened (GdkDisplay *display)
static GdkGLContext *
gdk_display_default_init_gl (GdkDisplay *display,
GError **error)
{
_gdk_display_manager_add_display (gdk_display_manager_get (), display);
g_set_error_literal (error, GDK_GL_ERROR, GDK_GL_ERROR_NOT_AVAILABLE,
_("The current backend does not support OpenGL"));
return NULL;
}
static GdkSeat *
......@@ -144,6 +155,12 @@ gdk_display_real_get_default_seat (GdkDisplay *display)
return display->seats->data;
}
static void
gdk_display_real_opened (GdkDisplay *display)
{
_gdk_display_manager_add_display (gdk_display_manager_get (), display);
}
static void
gdk_display_class_init (GdkDisplayClass *class)
{
......@@ -153,10 +170,11 @@ gdk_display_class_init (GdkDisplayClass *class)
object_class->dispose = gdk_display_dispose;
object_class->get_property = gdk_display_get_property;
class->get_app_launch_context = gdk_display_real_get_app_launch_context;
class->opened = gdk_display_real_opened;
class->make_default = gdk_display_real_make_default;
class->get_app_launch_context = gdk_display_real_get_app_launch_context;
class->init_gl = gdk_display_default_init_gl;
class->get_default_seat = gdk_display_real_get_default_seat;
class->opened = gdk_display_real_opened;
/**
* GdkDisplay:composited: (attributes org.gtk.Property.get=gdk_display_is_composited)
......@@ -327,11 +345,15 @@ static void
gdk_display_dispose (GObject *object)
{
GdkDisplay *display = GDK_DISPLAY (object);
GdkDisplayPrivate *priv = gdk_display_get_instance_private (display);
_gdk_display_manager_remove_display (gdk_display_manager_get (), display);
g_queue_clear (&display->queued_events);
g_clear_object (&priv->gl_context);
g_clear_error (&priv->gl_error);
G_OBJECT_CLASS (gdk_display_parent_class)->dispose (object);
}
......@@ -1173,6 +1195,109 @@ gdk_display_get_keymap (GdkDisplay *display)
return GDK_DISPLAY_GET_CLASS (display)->get_keymap (display);
}
static void
gdk_display_init_gl (GdkDisplay *self)
{
GdkDisplayPrivate *priv = gdk_display_get_instance_private (self);
GdkGLContext *context;
if (GDK_DISPLAY_DEBUG_CHECK (self, GL_DISABLE))
{
g_set_error_literal (&priv->gl_error, GDK_GL_ERROR,
GDK_GL_ERROR_NOT_AVAILABLE,
_("GL support disabled via GDK_DEBUG"));
return;
}
context = GDK_DISPLAY_GET_CLASS (self)->init_gl (self, &priv->gl_error);
if (context == NULL)
return;
if (!gdk_gl_context_realize (context, &priv->gl_error))
{
g_object_unref (context);
return;
}
/* Only assign after realize, so GdkGLContext::realize() can use
* gdk_display_get_gl_context() == NULL to differentiate between
* the display's context and any other context.
*/
priv->gl_context = context;
}
/**
* gdk_display_prepare_gl:
* @self: a `GdkDisplay`
* @error: return location for a `GError`
*
* Checks that OpenGL is available for @self and ensures that it is
* properly initialized.
* When this fails, an @error will be set describing the error and this
* function returns %FALSE.
*
* Note that even if this function succeeds, creating a `GdkGLContext`
* may still fail.
*
* This function is idempotent. Calling it multiple times will just
* return the same value or error.
*
* You never need to call this function, GDK will call it automatically
* as needed. But you can use it as a check when setting up code that
* might make use of OpenGL.
*
* Returns: %TRUE if the display supports OpenGL
*
* Since: 4.4
**/
gboolean
gdk_display_prepare_gl (GdkDisplay *self,
GError **error)
{
GdkDisplayPrivate *priv = gdk_display_get_instance_private (self);
g_return_val_if_fail (GDK_IS_DISPLAY (self), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
for (;;)
{
if (priv->gl_context)
return TRUE;
if (priv->gl_error != NULL)
{
if (error)
*error = g_error_copy (priv->gl_error);
return FALSE;
}
gdk_display_init_gl (self);
/* try again */
}
}
/*< private >
* gdk_display_get_gl_context:
* @self: the `GdkDisplay`
*
* Gets the GL context returned from [vfunc@Gdk.Display.init_gl]
* previously.
*
* If that function has not been called yet or did fail, %NULL is
* returned.
* Call [method@Gdk.Display.prepare_gl] to avoid this.
*
* Returns: The `GdkGLContext`
*/
GdkGLContext *
gdk_display_get_gl_context (GdkDisplay *self)
{
GdkDisplayPrivate *priv = gdk_display_get_instance_private (self);
return priv->gl_context;
}
/*< private >
* gdk_display_make_gl_context_current:
* @display: a `GdkDisplay`
......
......@@ -68,6 +68,10 @@ gboolean gdk_display_is_rgba (GdkDisplay *display);
GDK_AVAILABLE_IN_ALL
gboolean gdk_display_supports_input_shapes (GdkDisplay *display);
GDK_AVAILABLE_IN_4_4
gboolean gdk_display_prepare_gl (GdkDisplay *self,
GError **error);
GDK_AVAILABLE_IN_ALL
GdkDisplay *gdk_display_get_default (void);
......
......@@ -140,6 +140,8 @@ struct _GdkDisplayClass
GdkKeymap * (*get_keymap) (GdkDisplay *display);
GdkGLContext * (*init_gl) (GdkDisplay *display,
GError **error);
gboolean (*make_gl_context_current) (GdkDisplay *display,
GdkGLContext *context);
......@@ -206,6 +208,7 @@ GdkSurface * gdk_display_create_surface (GdkDisplay *display
int width,
int height);
GdkGLContext * gdk_display_get_gl_context (GdkDisplay *display);
gboolean gdk_display_make_gl_context_current (GdkDisplay *display,
GdkGLContext *context);
......
......@@ -1065,59 +1065,14 @@ gdk_surface_get_mapped (GdkSurface *surface)
return GDK_SURFACE_IS_MAPPED (surface);
}
GdkGLContext *
gdk_surface_get_shared_data_gl_context (GdkSurface *surface)
{
static int in_shared_data_creation;
GdkDisplay *display;
GdkGLContext *context;
if (in_shared_data_creation)
return NULL;
in_shared_data_creation = 1;
display = gdk_surface_get_display (surface);
context = (GdkGLContext *)g_object_get_data (G_OBJECT (display), "gdk-gl-shared-data-context");
if (context == NULL)
{
GError *error = NULL;
context = GDK_SURFACE_GET_CLASS (surface)->create_gl_context (surface, FALSE, NULL, &error);
if (context == NULL)
{
g_warning ("Failed to create shared context: %s", error->message);
g_clear_error (&error);
}
gdk_gl_context_realize (context, &error);
if (context == NULL)
{
g_warning ("Failed to realize shared context: %s", error->message);
g_clear_error (&error);
}
g_object_set_data (G_OBJECT (display), "gdk-gl-shared-data-context", context);
}
in_shared_data_creation = 0;
return context;
}
GdkGLContext *
gdk_surface_get_paint_gl_context (GdkSurface *surface,
GError **error)
GError **error)
{
GError *internal_error = NULL;
if (GDK_DISPLAY_DEBUG_CHECK (surface->display, GL_DISABLE))
{
g_set_error_literal (error, GDK_GL_ERROR,
GDK_GL_ERROR_NOT_AVAILABLE,
_("GL support disabled via GDK_DEBUG"));
return NULL;
}
if (!gdk_display_prepare_gl (surface->display, error))
return NULL;
if (surface->gl_paint_context == NULL)
{
......
......@@ -266,7 +266,6 @@ void _gdk_surface_update_size (GdkSurface *surface);
GdkGLContext * gdk_surface_get_paint_gl_context (GdkSurface *surface,
GError **error);
GdkGLContext * gdk_surface_get_shared_data_gl_context (GdkSurface *surface);
gboolean gdk_surface_handle_event (GdkEvent *event);
GdkSeat * gdk_surface_get_seat_from_event (GdkSurface *surface,
......
......@@ -637,6 +637,13 @@ gdk_macos_display_load_clipboard (GdkMacosDisplay *self)
GDK_DISPLAY (self)->clipboard = _gdk_macos_clipboard_new (self);
}
static GdkGLContext *
gdk_macos_display_init_gl (GdkDisplay *display,
GError **error)
{
return _gdk_macos_gl_context_new (display, NULL, FALSE, NULL, error);
}
static gboolean
gdk_macos_display_make_gl_context_current (GdkDisplay *display,
GdkGLContext *gl_context)
......@@ -695,6 +702,7 @@ gdk_macos_display_class_init (GdkMacosDisplayClass *klass)
display_class->get_name = gdk_macos_display_get_name;
display_class->get_setting = gdk_macos_display_get_setting;
display_class->has_pending = gdk_macos_display_has_pending;
display_class->init_gl = gdk_macos_display_init_gl;
display_class->make_gl_context_current = gdk_macos_display_make_gl_context_current;
display_class->notify_startup_complete = gdk_macos_display_notify_startup_complete;
display_class->queue_events = gdk_macos_display_queue_events;
......
......@@ -19,12 +19,14 @@
#ifndef __GDK_MACOS_GL_CONTEXT_PRIVATE_H__
#define __GDK_MACOS_GL_CONTEXT_PRIVATE_H__
#include "gdkmacosglcontext.h"
#include "gdkglcontextprivate.h"
#include "gdkdisplayprivate.h"
#include "gdksurface.h"
#include "gdkinternals.h"
#include "gdkmacosglcontext.h"
#include "gdkmacosdisplay.h"
#include "gdkmacossurface.h"
#import <OpenGL/OpenGL.h>
......@@ -55,7 +57,8 @@ struct _GdkMacosGLContextClass
GdkGLContextClass parent_class;
};
GdkGLContext *_gdk_macos_gl_context_new (GdkMacosSurface *surface,
GdkGLContext *_gdk_macos_gl_context_new (GdkMacosDisplay *display,
GdkMacosSurface *surface,
gboolean attached,
GdkGLContext *share,
GError **error);
......
......@@ -170,12 +170,12 @@ gdk_macos_gl_context_real_realize (GdkGLContext *context,
{
GdkMacosGLContext *self = (GdkMacosGLContext *)context;
GdkSurface *surface;
GdkDisplay *display;
NSOpenGLContext *shared_gl_context = nil;
NSOpenGLContext *gl_context;
NSOpenGLPixelFormat *pixelFormat;
CGLContextObj cgl_context;
GdkGLContext *shared;
GdkGLContext *shared_data;
NSOpenGLContext *existing;
GLint sync_to_framerate = 1;
GLint validate = 0;
......@@ -192,21 +192,16 @@ gdk_macos_gl_context_real_realize (GdkGLContext *context,
gdk_gl_context_get_required_version (context, &major, &minor);
surface = gdk_draw_context_get_surface (GDK_DRAW_CONTEXT (context));
shared = gdk_gl_context_get_shared_context (context);
shared_data = gdk_surface_get_shared_data_gl_context (surface);
display = gdk_gl_context_get_display (context);
shared = gdk_display_get_gl_context (display);
if (shared != NULL)
{
if (!(shared_gl_context = get_ns_open_gl_context (GDK_MACOS_GL_CONTEXT (shared), error)))
return FALSE;
}
else if (shared_data != NULL)
{
if (!(shared_gl_context = get_ns_open_gl_context (GDK_MACOS_GL_CONTEXT (shared_data), error)))
return FALSE;
}
GDK_DISPLAY_NOTE (gdk_draw_context_get_display (GDK_DRAW_CONTEXT (context)),
GDK_DISPLAY_NOTE (display,
OPENGL,
g_message ("Creating NSOpenGLContext (version %d.%d)",
major, minor));
......@@ -253,7 +248,7 @@ gdk_macos_gl_context_real_realize (GdkGLContext *context,
GLint renderer_id = 0;
[gl_context getValues:&renderer_id forParameter:NSOpenGLContextParameterCurrentRendererID];
GDK_DISPLAY_NOTE (gdk_draw_context_get_display (GDK_DRAW_CONTEXT (context)),
GDK_DISPLAY_NOTE (display,
OPENGL,
g_message ("Created NSOpenGLContext[%p] using %s",
gl_context,
......@@ -479,7 +474,8 @@ gdk_macos_gl_context_init (GdkMacosGLContext *self)
}
GdkGLContext *
_gdk_macos_gl_context_new (GdkMacosSurface *surface,
_gdk_macos_gl_context_new (GdkMacosDisplay *display,
GdkMacosSurface *surface,
gboolean attached,
GdkGLContext *share,
GError **error)
......@@ -490,6 +486,7 @@ _gdk_macos_gl_context_new (GdkMacosSurface *surface,
g_return_val_if_fail (!share || GDK_IS_MACOS_GL_CONTEXT (share), NULL);
context = g_object_new (GDK_TYPE_MACOS_GL_CONTEXT,
"display", display,
"surface", surface,
"shared-context", share,
NULL);
......
......@@ -369,7 +369,8 @@ gdk_macos_surface_create_gl_context (GdkSurface *surface,
g_assert (GDK_IS_MACOS_SURFACE (self));
g_assert (!share || GDK_IS_GL_CONTEXT (share));
return _gdk_macos_gl_context_new (self, attached, share, error);
return _gdk_macos_gl_context_new ((GdkMacosDisplay *) gdk_surface_get_display (surface),
self, attached, share, error);
}
static void
......
......@@ -969,6 +969,7 @@ gdk_wayland_display_class_init (GdkWaylandDisplayClass *class)
display_class->create_surface = _gdk_wayland_display_create_surface;
display_class->get_keymap = _gdk_wayland_display_get_keymap;
display_class->init_gl = gdk_wayland_display_init_gl;
display_class->make_gl_context_current = gdk_wayland_display_make_gl_context_current;
display_class->get_monitors = gdk_wayland_display_get_monitors;
......
......@@ -55,8 +55,7 @@ gdk_wayland_gl_context_realize (GdkGLContext *context,
{
GdkWaylandGLContext *context_wayland = GDK_WAYLAND_GL_CONTEXT (context);
GdkDisplay *display = gdk_gl_context_get_display (context);
GdkGLContext *share = gdk_gl_context_get_shared_context (context);
GdkGLContext *shared_data_context = gdk_surface_get_shared_data_gl_context (gdk_gl_context_get_surface (context));
GdkGLContext *share = gdk_display_get_gl_context (display);
GdkWaylandDisplay *display_wayland = GDK_WAYLAND_DISPLAY (display);
EGLContext ctx;
EGLint context_attribs[N_EGL_ATTRS];
......@@ -124,8 +123,7 @@ gdk_wayland_gl_context_realize (GdkGLContext *context,
ctx = eglCreateContext (display_wayland->egl_display,
display_wayland->egl_config,
share != NULL ? GDK_WAYLAND_GL_CONTEXT (share)->egl_context
: shared_data_context != NULL ? GDK_WAYLAND_GL_CONTEXT (shared_data_context)->egl_context
: EGL_NO_CONTEXT,
: EGL_NO_CONTEXT,
context_attribs);
/* If context creation failed without the ES bit, let's try again with it */
......@@ -151,8 +149,7 @@ gdk_wayland_gl_context_realize (GdkGLContext *context,
ctx = eglCreateContext (display_wayland->egl_display,
display_wayland->egl_config,
share != NULL ? GDK_WAYLAND_GL_CONTEXT (share)->egl_context
: shared_data_context != NULL ? GDK_WAYLAND_GL_CONTEXT (shared_data_context)->egl_context
: EGL_NO_CONTEXT,
: EGL_NO_CONTEXT,
context_attribs);
}
......@@ -181,8 +178,7 @@ gdk_wayland_gl_context_realize (GdkGLContext *context,
ctx = eglCreateContext (display_wayland->egl_display,
display_wayland->egl_config,
share != NULL ? GDK_WAYLAND_GL_CONTEXT (share)->egl_context
: shared_data_context != NULL ? GDK_WAYLAND_GL_CONTEXT (shared_data_context)->egl_context
: EGL_NO_CONTEXT,
: EGL_NO_CONTEXT,
context_attribs);
}
......@@ -346,7 +342,7 @@ gdk_wayland_display_get_egl_display (GdkDisplay *display)
g_return_val_if_fail (GDK_IS_WAYLAND_DISPLAY (display), NULL);
if (!gdk_wayland_display_init_gl (display, NULL))
if (!gdk_display_prepare_gl (display, NULL))
return NULL;
display_wayland = GDK_WAYLAND_DISPLAY (display);
......@@ -428,7 +424,7 @@ get_eglconfig (EGLDisplay dpy)
#undef MAX_EGL_ATTRS
gboolean
GdkGLContext *
gdk_wayland_display_init_gl (GdkDisplay *display,
GError **error)
{
......@@ -436,16 +432,13 @@ gdk_wayland_display_init_gl (GdkDisplay *display,
EGLint major, minor;
EGLDisplay dpy;
if (display_wayland->egl_display)
return TRUE;
dpy = get_egl_display (display_wayland);
if (dpy == NULL)
{
g_set_error_literal (error, GDK_GL_ERROR,
GDK_GL_ERROR_NOT_AVAILABLE,
_("Failed to create EGL display"));
return FALSE;
return NULL;
}
if (!eglInitialize (dpy, &major, &minor))
......@@ -453,7 +446,7 @@ gdk_wayland_display_init_gl (GdkDisplay *display,
g_set_error_literal (error, GDK_GL_ERROR,
GDK_GL_ERROR_NOT_AVAILABLE,
_("Could not initialize EGL display"));
return FALSE;
return NULL;
}
if (!eglBindAPI (EGL_OPENGL_API))
......@@ -462,7 +455,7 @@ gdk_wayland_display_init_gl (GdkDisplay *display,
g_set_error_literal (error, GDK_GL_ERROR,
GDK_GL_ERROR_NOT_AVAILABLE,
_("No GL implementation is available"));
return FALSE;
return NULL;
}
if (!epoxy_has_egl_extension (dpy, "EGL_KHR_create_context"))
......@@ -471,7 +464,7 @@ gdk_wayland_display_init_gl (GdkDisplay *display,
g_set_error_literal (error, GDK_GL_ERROR,
GDK_GL_ERROR_UNSUPPORTED_PROFILE,
_("Core GL is not available on EGL implementation"));
return FALSE;
return NULL;
}
display_wayland->egl_config = get_eglconfig (dpy);
......@@ -481,7 +474,7 @@ gdk_wayland_display_init_gl (GdkDisplay *display,
g_set_error_literal (error, GDK_GL_ERROR,
GDK_GL_ERROR_UNSUPPORTED_FORMAT,
_("No available configurations for the given pixel format"));
return FALSE;
return NULL;
}
display_wayland->egl_display = dpy;
......@@ -511,21 +504,19 @@ gdk_wayland_display_init_gl (GdkDisplay *display,
eglQueryString (dpy, EGL_CLIENT_APIS),
eglQueryString (dpy, EGL_EXTENSIONS)));
return TRUE;
return g_object_new (GDK_TYPE_WAYLAND_GL_CONTEXT,
"display", display,
NULL);
}
GdkGLContext *
gdk_wayland_surface_create_gl_context (GdkSurface *surface,
gdk_wayland_surface_create_gl_context (GdkSurface *surface,
gboolean attached,
GdkGLContext *share,
GError **error)
{
GdkDisplay *display = gdk_surface_get_display (surface);
GdkWaylandGLContext *context;
if (!gdk_wayland_display_init_gl (display, error))
return NULL;
context = g_object_new (GDK_TYPE_WAYLAND_GL_CONTEXT,
"surface", surface,
"shared-context", share,
......
......@@ -44,9 +44,9 @@ struct _GdkWaylandGLContextClass
GdkGLContextClass parent_class;
};
gboolean gdk_wayland_display_init_gl (GdkDisplay *display,
GdkGLContext * gdk_wayland_display_init_gl (GdkDisplay *display,
GError **error);
GdkGLContext * gdk_wayland_surface_create_gl_context (GdkSurface *surface,
GdkGLContext * gdk_wayland_surface_create_gl_context (GdkSurface *surface,
gboolean attach,
GdkGLContext *share,
GError **error);
......
......@@ -906,8 +906,6 @@ static gboolean
gdk_win32_gl_context_realize (GdkGLContext *context,
GError **error)
{
GdkGLContext *share = gdk_gl_context_get_shared_context (context);
GdkGLContext *shared_data_context;
GdkWin32GLContext *context_win32 = GDK_WIN32_GL_CONTEXT (context);
gboolean debug_bit, compat_bit, legacy_bit;
......@@ -922,11 +920,11 @@ gdk_win32_gl_context_realize (GdkGLContext *context,
GdkWin32Surface *impl = GDK_WIN32_SURFACE (surface);
GdkDisplay *display = gdk_surface_get_display (surface);
GdkWin32Display *win32_display = GDK_WIN32_DISPLAY (display);
GdkGLContext *share = gdk_display_get_gl_context (display);
gdk_gl_context_get_required_version (context, &major, &minor);
debug_bit = gdk_gl_context_get_debug_enabled (context);
compat_bit = gdk_gl_context_get_forward_compatible (context);
shared_data_context = gdk_surface_get_shared_data_gl_context (surface);
/*
* A legacy context cannot be shared with core profile ones, so this means we
......@@ -976,7 +974,7 @@ gdk_win32_gl_context_realize (GdkGLContext *context,
legacy_bit ? "yes" : "no"));
hglrc = _create_gl_context (context_win32->gl_hdc,
share ? share : shared_data_context,
share,
flags,
major,
minor,
......@@ -1018,7 +1016,7 @@ gdk_win32_gl_context_realize (GdkGLContext *context,
ctx = _create_egl_context (win32_display->egl_disp,
context_win32->egl_config,
share ? share : shared_data_context,
share,
flags,
major,
minor,
......
......@@ -1335,40 +1335,6 @@ set_sm_client_id (GdkDisplay *display,
gdk_x11_get_xatom_by_name_for_display (display, "SM_CLIENT_ID"));
}
static void
gdk_x11_display_query_default_visual (GdkX11Display *self,
Visual **out_visual,
int *out_depth)
{
XVisualInfo template, *visinfo;
int n_visuals;
Display *dpy;
dpy = gdk_x11_display_get_xdisplay (GDK_DISPLAY (self));
template.screen = self->screen->screen_num;
template.depth = 32;
template.red_mask = 0xff0000;
template.green_mask = 0x00ff00;
template.blue_mask = 0x0000ff;