[mutter] cogl/glx: Move onscreen code to a separate file
- From: Marge Bot <marge-bot src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [mutter] cogl/glx: Move onscreen code to a separate file
- Date: Sat, 30 Jan 2021 09:39:48 +0000 (UTC)
commit a057432e3d889bf1e0a9daee7292f3b550f9ebc0
Author: Jonas Ådahl <jadahl gmail com>
Date: Sat Oct 17 17:58:35 2020 +0200
cogl/glx: Move onscreen code to a separate file
Mostly in order to untangle it from the rest, preparing turning it into
a GObject.
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1514>
cogl/cogl/meson.build | 2 +
cogl/cogl/winsys/cogl-onscreen-glx.c | 1129 ++++++++++++++++++++++++++++
cogl/cogl/winsys/cogl-onscreen-glx.h | 87 +++
cogl/cogl/winsys/cogl-winsys-glx-private.h | 13 +
cogl/cogl/winsys/cogl-winsys-glx.c | 1127 ++-------------------------
src/backends/x11/meta-renderer-x11.c | 9 +-
6 files changed, 1287 insertions(+), 1080 deletions(-)
---
diff --git a/cogl/cogl/meson.build b/cogl/cogl/meson.build
index d973a4cf7a..9bf5e04fb0 100644
--- a/cogl/cogl/meson.build
+++ b/cogl/cogl/meson.build
@@ -380,6 +380,8 @@ if have_glx
cogl_sources += [
'winsys/cogl-glx-display-private.h',
'winsys/cogl-glx-renderer-private.h',
+ 'winsys/cogl-onscreen-glx.c',
+ 'winsys/cogl-onscreen-glx.h',
'winsys/cogl-winsys-glx-feature-functions.h',
'winsys/cogl-winsys-glx-private.h',
'winsys/cogl-winsys-glx.c',
diff --git a/cogl/cogl/winsys/cogl-onscreen-glx.c b/cogl/cogl/winsys/cogl-onscreen-glx.c
new file mode 100644
index 0000000000..01d70af4f8
--- /dev/null
+++ b/cogl/cogl/winsys/cogl-onscreen-glx.c
@@ -0,0 +1,1129 @@
+/*
+ * Copyright (C) 2007,2008,2009,2010,2011,2013 Intel Corporation.
+ * Copyright (C) 2020 Red Hat
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "cogl-config.h"
+
+#include "winsys/cogl-onscreen-glx.h"
+
+#include <GL/glx.h>
+#include <sys/time.h>
+
+#include "cogl-context-private.h"
+#include "cogl-frame-info-private.h"
+#include "cogl-renderer-private.h"
+#include "cogl-xlib-renderer-private.h"
+#include "winsys/cogl-glx-display-private.h"
+#include "winsys/cogl-glx-renderer-private.h"
+#include "winsys/cogl-winsys-glx-private.h"
+
+typedef struct _CoglOnscreenGLX
+{
+ Window xwin;
+ int x, y;
+ CoglOutput *output;
+
+ GLXDrawable glxwin;
+ uint32_t last_swap_vsync_counter;
+ uint32_t pending_sync_notify;
+ uint32_t pending_complete_notify;
+ uint32_t pending_resize_notify;
+} CoglOnscreenGLX;
+
+#define COGL_ONSCREEN_X11_EVENT_MASK (StructureNotifyMask | ExposureMask)
+
+gboolean
+_cogl_winsys_onscreen_glx_init (CoglOnscreen *onscreen,
+ GError **error)
+{
+ CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen);
+ CoglContext *context = cogl_framebuffer_get_context (framebuffer);
+ CoglDisplay *display = context->display;
+ CoglGLXDisplay *glx_display = display->winsys;
+ CoglXlibRenderer *xlib_renderer =
+ _cogl_xlib_renderer_get_data (display->renderer);
+ CoglGLXRenderer *glx_renderer = display->renderer->winsys;
+ Window xwin;
+ CoglOnscreenGLX *glx_onscreen;
+ const CoglFramebufferConfig *config;
+ GLXFBConfig fbconfig;
+ GError *fbconfig_error = NULL;
+ CoglOnscreenGLX *winsys;
+
+ g_return_val_if_fail (glx_display->glx_context, FALSE);
+
+ config = cogl_framebuffer_get_config (framebuffer);
+ if (!cogl_display_glx_find_fbconfig (display, config,
+ &fbconfig,
+ &fbconfig_error))
+ {
+ g_set_error (error, COGL_WINSYS_ERROR,
+ COGL_WINSYS_ERROR_CREATE_CONTEXT,
+ "Unable to find suitable fbconfig for the GLX context: %s",
+ fbconfig_error->message);
+ g_error_free (fbconfig_error);
+ return FALSE;
+ }
+
+ /* Update the real number of samples_per_pixel now that we have
+ * found an fbconfig... */
+ if (config->samples_per_pixel)
+ {
+ int samples;
+ int status = glx_renderer->glXGetFBConfigAttrib (xlib_renderer->xdpy,
+ fbconfig,
+ GLX_SAMPLES,
+ &samples);
+ g_return_val_if_fail (status == Success, TRUE);
+ cogl_framebuffer_update_samples_per_pixel (framebuffer, samples);
+ }
+
+ /* FIXME: We need to explicitly Select for ConfigureNotify events.
+ * We need to document that for windows we create then toolkits
+ * must be careful not to clear event mask bits that we select.
+ */
+ {
+ int width;
+ int height;
+ CoglXlibTrapState state;
+ XVisualInfo *xvisinfo;
+ XSetWindowAttributes xattr;
+ unsigned long mask;
+ int xerror;
+
+ width = cogl_framebuffer_get_width (framebuffer);
+ height = cogl_framebuffer_get_height (framebuffer);
+
+ _cogl_xlib_renderer_trap_errors (display->renderer, &state);
+
+ xvisinfo = glx_renderer->glXGetVisualFromFBConfig (xlib_renderer->xdpy,
+ fbconfig);
+ if (xvisinfo == NULL)
+ {
+ g_set_error_literal (error, COGL_WINSYS_ERROR,
+ COGL_WINSYS_ERROR_CREATE_ONSCREEN,
+ "Unable to retrieve the X11 visual of context's "
+ "fbconfig");
+ return FALSE;
+ }
+
+ /* window attributes */
+ xattr.background_pixel = WhitePixel (xlib_renderer->xdpy,
+ DefaultScreen (xlib_renderer->xdpy));
+ xattr.border_pixel = 0;
+ /* XXX: is this an X resource that we are leaking‽... */
+ xattr.colormap = XCreateColormap (xlib_renderer->xdpy,
+ DefaultRootWindow (xlib_renderer->xdpy),
+ xvisinfo->visual,
+ AllocNone);
+ xattr.event_mask = COGL_ONSCREEN_X11_EVENT_MASK;
+
+ mask = CWBorderPixel | CWColormap | CWEventMask;
+
+ xwin = XCreateWindow (xlib_renderer->xdpy,
+ DefaultRootWindow (xlib_renderer->xdpy),
+ 0, 0,
+ width, height,
+ 0,
+ xvisinfo->depth,
+ InputOutput,
+ xvisinfo->visual,
+ mask, &xattr);
+
+ XFree (xvisinfo);
+
+ XSync (xlib_renderer->xdpy, False);
+ xerror = _cogl_xlib_renderer_untrap_errors (display->renderer, &state);
+ if (xerror)
+ {
+ char message[1000];
+ XGetErrorText (xlib_renderer->xdpy, xerror,
+ message, sizeof (message));
+ g_set_error (error, COGL_WINSYS_ERROR,
+ COGL_WINSYS_ERROR_CREATE_ONSCREEN,
+ "X error while creating Window for CoglOnscreen: %s",
+ message);
+ return FALSE;
+ }
+ }
+
+ winsys = g_slice_new0 (CoglOnscreenGLX);
+ cogl_onscreen_set_winsys (onscreen, winsys);
+ glx_onscreen = cogl_onscreen_get_winsys (onscreen);
+
+ glx_onscreen->xwin = xwin;
+
+ /* Try and create a GLXWindow to use with extensions dependent on
+ * GLX versions >= 1.3 that don't accept regular X Windows as GLX
+ * drawables. */
+ if (glx_renderer->glx_major == 1 && glx_renderer->glx_minor >= 3)
+ {
+ glx_onscreen->glxwin =
+ glx_renderer->glXCreateWindow (xlib_renderer->xdpy,
+ fbconfig,
+ glx_onscreen->xwin,
+ NULL);
+ }
+
+#ifdef GLX_INTEL_swap_event
+ if (_cogl_winsys_has_feature (COGL_WINSYS_FEATURE_SYNC_AND_COMPLETE_EVENT))
+ {
+ GLXDrawable drawable =
+ glx_onscreen->glxwin ? glx_onscreen->glxwin : glx_onscreen->xwin;
+
+ /* similarly to above, we unconditionally select this event
+ * because we rely on it to advance the master clock, and
+ * drive redraw/relayout, animations and event handling.
+ */
+ glx_renderer->glXSelectEvent (xlib_renderer->xdpy,
+ drawable,
+ GLX_BUFFER_SWAP_COMPLETE_INTEL_MASK);
+ }
+#endif /* GLX_INTEL_swap_event */
+
+ return TRUE;
+}
+
+void
+_cogl_winsys_onscreen_glx_deinit (CoglOnscreen *onscreen)
+{
+ CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen);
+ CoglContext *context = cogl_framebuffer_get_context (framebuffer);
+ CoglGLXDisplay *glx_display = context->display->winsys;
+ CoglXlibRenderer *xlib_renderer =
+ _cogl_xlib_renderer_get_data (context->display->renderer);
+ CoglGLXRenderer *glx_renderer = context->display->renderer->winsys;
+ CoglXlibTrapState old_state;
+ CoglOnscreenGLX *glx_onscreen = cogl_onscreen_get_winsys (onscreen);
+ GLXDrawable drawable;
+
+ /* If we never successfully allocated then there's nothing to do */
+ if (glx_onscreen == NULL)
+ return;
+
+ cogl_clear_object (&glx_onscreen->output);
+
+ _cogl_xlib_renderer_trap_errors (context->display->renderer, &old_state);
+
+ drawable =
+ glx_onscreen->glxwin == None ? glx_onscreen->xwin : glx_onscreen->glxwin;
+
+ /* Cogl always needs a valid context bound to something so if we are
+ * destroying the onscreen that is currently bound we'll switch back
+ * to the dummy drawable. Although the documentation for
+ * glXDestroyWindow states that a currently bound window won't
+ * actually be destroyed until it is unbound, it looks like this
+ * doesn't work if the X window itself is destroyed */
+ if (drawable == cogl_context_glx_get_current_drawable (context))
+ {
+ GLXDrawable dummy_drawable = (glx_display->dummy_glxwin == None ?
+ glx_display->dummy_xwin :
+ glx_display->dummy_glxwin);
+
+ glx_renderer->glXMakeContextCurrent (xlib_renderer->xdpy,
+ dummy_drawable,
+ dummy_drawable,
+ glx_display->glx_context);
+ cogl_context_glx_set_current_drawable (context, dummy_drawable);
+ }
+
+ if (glx_onscreen->glxwin != None)
+ {
+ glx_renderer->glXDestroyWindow (xlib_renderer->xdpy,
+ glx_onscreen->glxwin);
+ glx_onscreen->glxwin = None;
+ }
+
+ if (glx_onscreen->xwin != None)
+ {
+ XDestroyWindow (xlib_renderer->xdpy, glx_onscreen->xwin);
+ glx_onscreen->xwin = None;
+ }
+ else
+ {
+ glx_onscreen->xwin = None;
+ }
+
+ XSync (xlib_renderer->xdpy, False);
+
+ _cogl_xlib_renderer_untrap_errors (context->display->renderer, &old_state);
+
+ g_slice_free (CoglOnscreenGLX, cogl_onscreen_get_winsys (onscreen));
+ cogl_onscreen_set_winsys (onscreen, NULL);
+}
+
+void
+_cogl_winsys_onscreen_glx_bind (CoglOnscreen *onscreen)
+{
+ CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen);
+ CoglContext *context = cogl_framebuffer_get_context (framebuffer);
+ CoglGLXDisplay *glx_display = context->display->winsys;
+ CoglXlibRenderer *xlib_renderer =
+ _cogl_xlib_renderer_get_data (context->display->renderer);
+ CoglGLXRenderer *glx_renderer = context->display->renderer->winsys;
+ CoglOnscreenGLX *glx_onscreen = cogl_onscreen_get_winsys (onscreen);
+ CoglXlibTrapState old_state;
+ GLXDrawable drawable;
+
+ drawable =
+ glx_onscreen->glxwin ? glx_onscreen->glxwin : glx_onscreen->xwin;
+
+ if (cogl_context_glx_get_current_drawable (context) == drawable)
+ return;
+
+ _cogl_xlib_renderer_trap_errors (context->display->renderer, &old_state);
+
+ COGL_NOTE (WINSYS,
+ "MakeContextCurrent dpy: %p, window: 0x%x, context: %p",
+ xlib_renderer->xdpy,
+ (unsigned int) drawable,
+ glx_display->glx_context);
+
+ glx_renderer->glXMakeContextCurrent (xlib_renderer->xdpy,
+ drawable,
+ drawable,
+ glx_display->glx_context);
+
+ /* In case we are using GLX_SGI_swap_control for vblank syncing
+ * we need call glXSwapIntervalSGI here to make sure that it
+ * affects the current drawable.
+ *
+ * Note: we explicitly set to 0 when we aren't using the swap
+ * interval to synchronize since some drivers have a default
+ * swap interval of 1. Sadly some drivers even ignore requests
+ * to disable the swap interval.
+ *
+ * NB: glXSwapIntervalSGI applies to the context not the
+ * drawable which is why we can't just do this once when the
+ * framebuffer is allocated.
+ *
+ * FIXME: We should check for GLX_EXT_swap_control which allows
+ * per framebuffer swap intervals. GLX_MESA_swap_control also
+ * allows per-framebuffer swap intervals but the semantics tend
+ * to be more muddled since Mesa drivers tend to expose both the
+ * MESA and SGI extensions which should technically be mutually
+ * exclusive.
+ */
+ if (glx_renderer->glXSwapInterval)
+ glx_renderer->glXSwapInterval (1);
+
+ XSync (xlib_renderer->xdpy, False);
+
+ /* FIXME: We should be reporting a GError here */
+ if (_cogl_xlib_renderer_untrap_errors (context->display->renderer,
+ &old_state))
+ {
+ g_warning ("X Error received while making drawable 0x%08lX current",
+ drawable);
+ return;
+ }
+
+ cogl_context_glx_set_current_drawable (context, drawable);
+}
+
+static void
+_cogl_winsys_wait_for_gpu (CoglOnscreen *onscreen)
+{
+ CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen);
+ CoglContext *ctx = cogl_framebuffer_get_context (framebuffer);
+
+ ctx->glFinish ();
+}
+
+static int64_t
+get_monotonic_time_ns (void)
+{
+ struct timespec ts;
+
+ clock_gettime (CLOCK_MONOTONIC, &ts);
+ return ts.tv_sec * G_GINT64_CONSTANT (1000000000) + ts.tv_nsec;
+}
+
+static void
+ensure_ust_type (CoglRenderer *renderer,
+ GLXDrawable drawable)
+{
+ CoglGLXRenderer *glx_renderer = renderer->winsys;
+ CoglXlibRenderer *xlib_renderer = _cogl_xlib_renderer_get_data (renderer);
+ int64_t ust;
+ int64_t msc;
+ int64_t sbc;
+ struct timeval tv;
+ int64_t current_system_time;
+ int64_t current_monotonic_time;
+
+ if (glx_renderer->ust_type != COGL_GLX_UST_IS_UNKNOWN)
+ return;
+
+ glx_renderer->ust_type = COGL_GLX_UST_IS_OTHER;
+
+ if (glx_renderer->glXGetSyncValues == NULL)
+ goto out;
+
+ if (!glx_renderer->glXGetSyncValues (xlib_renderer->xdpy, drawable,
+ &ust, &msc, &sbc))
+ goto out;
+
+ /* This is the time source that existing (buggy) linux drm drivers
+ * use */
+ gettimeofday (&tv, NULL);
+ current_system_time = (tv.tv_sec * G_GINT64_CONSTANT (1000000)) + tv.tv_usec;
+
+ if (current_system_time > ust - 1000000 &&
+ current_system_time < ust + 1000000)
+ {
+ glx_renderer->ust_type = COGL_GLX_UST_IS_GETTIMEOFDAY;
+ goto out;
+ }
+
+ /* This is the time source that the newer (fixed) linux drm
+ * drivers use (Linux >= 3.8) */
+ current_monotonic_time = get_monotonic_time_ns () / 1000;
+
+ if (current_monotonic_time > ust - 1000000 &&
+ current_monotonic_time < ust + 1000000)
+ {
+ glx_renderer->ust_type = COGL_GLX_UST_IS_MONOTONIC_TIME;
+ goto out;
+ }
+
+ out:
+ COGL_NOTE (WINSYS, "Classified OML system time as: %s",
+ glx_renderer->ust_type == COGL_GLX_UST_IS_GETTIMEOFDAY ? "gettimeofday" :
+ (glx_renderer->ust_type == COGL_GLX_UST_IS_MONOTONIC_TIME ? "monotonic" :
+ "other"));
+ return;
+}
+
+static int64_t
+ust_to_nanoseconds (CoglRenderer *renderer,
+ GLXDrawable drawable,
+ int64_t ust)
+{
+ CoglGLXRenderer *glx_renderer = renderer->winsys;
+
+ ensure_ust_type (renderer, drawable);
+
+ switch (glx_renderer->ust_type)
+ {
+ case COGL_GLX_UST_IS_UNKNOWN:
+ g_assert_not_reached ();
+ break;
+ case COGL_GLX_UST_IS_GETTIMEOFDAY:
+ case COGL_GLX_UST_IS_MONOTONIC_TIME:
+ return 1000 * ust;
+ case COGL_GLX_UST_IS_OTHER:
+ /* In this case the scale of UST is undefined so we can't easily
+ * scale to nanoseconds.
+ *
+ * For example the driver may be reporting the rdtsc CPU counter
+ * as UST values and so the scale would need to be determined
+ * empirically.
+ *
+ * Potentially we could block for a known duration within
+ * ensure_ust_type() to measure the timescale of UST but for now
+ * we just ignore unknown time sources */
+ return 0;
+ }
+
+ return 0;
+}
+
+static void
+_cogl_winsys_wait_for_vblank (CoglOnscreen *onscreen)
+{
+ CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen);
+ CoglContext *ctx = cogl_framebuffer_get_context (framebuffer);
+ CoglGLXRenderer *glx_renderer;
+ CoglXlibRenderer *xlib_renderer;
+ CoglGLXDisplay *glx_display;
+
+ glx_renderer = ctx->display->renderer->winsys;
+ xlib_renderer = _cogl_xlib_renderer_get_data (ctx->display->renderer);
+ glx_display = ctx->display->winsys;
+
+ if (glx_display->can_vblank_wait)
+ {
+ CoglFrameInfo *info = cogl_onscreen_peek_tail_frame_info (onscreen);
+
+ if (glx_renderer->glXWaitForMsc)
+ {
+ CoglOnscreenGLX *glx_onscreen = cogl_onscreen_get_winsys (onscreen);
+ Drawable drawable = glx_onscreen->glxwin;
+ int64_t ust;
+ int64_t msc;
+ int64_t sbc;
+
+ glx_renderer->glXWaitForMsc (xlib_renderer->xdpy, drawable,
+ 0, 1, 0,
+ &ust, &msc, &sbc);
+ info->presentation_time = ust_to_nanoseconds (ctx->display->renderer,
+ drawable,
+ ust);
+ }
+ else
+ {
+ uint32_t current_count;
+
+ glx_renderer->glXGetVideoSync (¤t_count);
+ glx_renderer->glXWaitVideoSync (2,
+ (current_count + 1) % 2,
+ ¤t_count);
+
+ info->presentation_time = get_monotonic_time_ns ();
+ }
+ }
+}
+
+static uint32_t
+_cogl_winsys_get_vsync_counter (CoglContext *ctx)
+{
+ uint32_t video_sync_count;
+ CoglGLXRenderer *glx_renderer;
+
+ glx_renderer = ctx->display->renderer->winsys;
+
+ glx_renderer->glXGetVideoSync (&video_sync_count);
+
+ return video_sync_count;
+}
+
+#ifndef GLX_BACK_BUFFER_AGE_EXT
+#define GLX_BACK_BUFFER_AGE_EXT 0x20F4
+#endif
+
+int
+_cogl_winsys_onscreen_glx_get_buffer_age (CoglOnscreen *onscreen)
+{
+ CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen);
+ CoglContext *context = cogl_framebuffer_get_context (framebuffer);
+ CoglXlibRenderer *xlib_renderer = _cogl_xlib_renderer_get_data (context->display->renderer);
+ CoglGLXRenderer *glx_renderer = context->display->renderer->winsys;
+ CoglOnscreenGLX *glx_onscreen = cogl_onscreen_get_winsys (onscreen);
+ GLXDrawable drawable;
+ unsigned int age;
+
+ if (!_cogl_winsys_has_feature (COGL_WINSYS_FEATURE_BUFFER_AGE))
+ return 0;
+
+ drawable = glx_onscreen->glxwin ? glx_onscreen->glxwin : glx_onscreen->xwin;
+ glx_renderer->glXQueryDrawable (xlib_renderer->xdpy, drawable, GLX_BACK_BUFFER_AGE_EXT, &age);
+
+ return age;
+}
+
+static void
+set_frame_info_output (CoglOnscreen *onscreen,
+ CoglOutput *output)
+{
+ CoglFrameInfo *info = cogl_onscreen_peek_tail_frame_info (onscreen);
+
+ if (output)
+ {
+ float refresh_rate = cogl_output_get_refresh_rate (output);
+ if (refresh_rate != 0.0)
+ info->refresh_rate = refresh_rate;
+ }
+}
+
+static void
+cogl_onscreen_glx_flush_notification (CoglOnscreen *onscreen)
+{
+ CoglOnscreenGLX *glx_onscreen = cogl_onscreen_get_winsys (onscreen);
+
+ while (glx_onscreen->pending_sync_notify > 0 ||
+ glx_onscreen->pending_complete_notify > 0 ||
+ glx_onscreen->pending_resize_notify > 0)
+ {
+ if (glx_onscreen->pending_sync_notify > 0)
+ {
+ CoglFrameInfo *info;
+
+ info = cogl_onscreen_peek_head_frame_info (onscreen);
+ _cogl_onscreen_notify_frame_sync (onscreen, info);
+ glx_onscreen->pending_sync_notify--;
+ }
+
+ if (glx_onscreen->pending_complete_notify > 0)
+ {
+ CoglFrameInfo *info;
+
+ info = cogl_onscreen_pop_head_frame_info (onscreen);
+ _cogl_onscreen_notify_complete (onscreen, info);
+ cogl_object_unref (info);
+ glx_onscreen->pending_complete_notify--;
+ }
+
+ if (glx_onscreen->pending_resize_notify > 0)
+ {
+ _cogl_onscreen_notify_resize (onscreen);
+ glx_onscreen->pending_resize_notify--;
+ }
+ }
+}
+
+static void
+flush_pending_notifications_cb (void *data,
+ void *user_data)
+{
+ CoglFramebuffer *framebuffer = data;
+
+ if (COGL_IS_ONSCREEN (framebuffer))
+ {
+ CoglOnscreen *onscreen = COGL_ONSCREEN (framebuffer);
+
+ cogl_onscreen_glx_flush_notification (onscreen);
+ }
+}
+
+static void
+flush_pending_notifications_idle (void *user_data)
+{
+ CoglContext *context = user_data;
+ CoglRenderer *renderer = context->display->renderer;
+ CoglGLXRenderer *glx_renderer = renderer->winsys;
+
+ /* This needs to be disconnected before invoking the callbacks in
+ * case the callbacks cause it to be queued again */
+ _cogl_closure_disconnect (glx_renderer->flush_notifications_idle);
+ glx_renderer->flush_notifications_idle = NULL;
+
+ g_list_foreach (context->framebuffers,
+ flush_pending_notifications_cb,
+ NULL);
+}
+
+static void
+set_sync_pending (CoglOnscreen *onscreen)
+{
+ CoglOnscreenGLX *glx_onscreen = cogl_onscreen_get_winsys (onscreen);
+ CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen);
+ CoglContext *context = cogl_framebuffer_get_context (framebuffer);
+ CoglRenderer *renderer = context->display->renderer;
+ CoglGLXRenderer *glx_renderer = renderer->winsys;
+
+ /* We only want to dispatch sync events when the application calls
+ * cogl_context_dispatch so instead of immediately notifying we
+ * queue an idle callback */
+ if (!glx_renderer->flush_notifications_idle)
+ {
+ glx_renderer->flush_notifications_idle =
+ _cogl_poll_renderer_add_idle (renderer,
+ flush_pending_notifications_idle,
+ context,
+ NULL);
+ }
+
+ glx_onscreen->pending_sync_notify++;
+}
+
+static void
+set_complete_pending (CoglOnscreen *onscreen)
+{
+ CoglOnscreenGLX *glx_onscreen = cogl_onscreen_get_winsys (onscreen);
+ CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen);
+ CoglContext *context = cogl_framebuffer_get_context (framebuffer);
+ CoglRenderer *renderer = context->display->renderer;
+ CoglGLXRenderer *glx_renderer = renderer->winsys;
+
+ /* We only want to notify swap completion when the application calls
+ * cogl_context_dispatch so instead of immediately notifying we
+ * queue an idle callback */
+ if (!glx_renderer->flush_notifications_idle)
+ {
+ glx_renderer->flush_notifications_idle =
+ _cogl_poll_renderer_add_idle (renderer,
+ flush_pending_notifications_idle,
+ context,
+ NULL);
+ }
+
+ glx_onscreen->pending_complete_notify++;
+}
+
+void
+_cogl_winsys_onscreen_glx_swap_region (CoglOnscreen *onscreen,
+ const int *user_rectangles,
+ int n_rectangles,
+ CoglFrameInfo *info,
+ gpointer user_data)
+{
+ CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen);
+ CoglContext *context = cogl_framebuffer_get_context (framebuffer);
+ CoglXlibRenderer *xlib_renderer =
+ _cogl_xlib_renderer_get_data (context->display->renderer);
+ CoglGLXRenderer *glx_renderer = context->display->renderer->winsys;
+ CoglGLXDisplay *glx_display = context->display->winsys;
+ CoglOnscreenGLX *glx_onscreen = cogl_onscreen_get_winsys (onscreen);
+ uint32_t end_frame_vsync_counter = 0;
+ gboolean have_counter;
+ gboolean can_wait;
+ int x_min = 0, x_max = 0, y_min = 0, y_max = 0;
+
+ /*
+ * We assume that glXCopySubBuffer is synchronized which means it won't prevent multiple
+ * blits per retrace if they can all be performed in the blanking period. If that's the
+ * case then we still want to use the vblank sync menchanism but
+ * we only need it to throttle redraws.
+ */
+ gboolean blit_sub_buffer_is_synchronized =
+ _cogl_winsys_has_feature (COGL_WINSYS_FEATURE_SWAP_REGION_SYNCHRONIZED);
+
+ int framebuffer_width = cogl_framebuffer_get_width (framebuffer);
+ int framebuffer_height = cogl_framebuffer_get_height (framebuffer);
+ int *rectangles = g_alloca (sizeof (int) * n_rectangles * 4);
+ int i;
+
+ /* glXCopySubBuffer expects rectangles relative to the bottom left corner but
+ * we are given rectangles relative to the top left so we need to flip
+ * them... */
+ memcpy (rectangles, user_rectangles, sizeof (int) * n_rectangles * 4);
+ for (i = 0; i < n_rectangles; i++)
+ {
+ int *rect = &rectangles[4 * i];
+
+ if (i == 0)
+ {
+ x_min = rect[0];
+ x_max = rect[0] + rect[2];
+ y_min = rect[1];
+ y_max = rect[1] + rect[3];
+ }
+ else
+ {
+ x_min = MIN (x_min, rect[0]);
+ x_max = MAX (x_max, rect[0] + rect[2]);
+ y_min = MIN (y_min, rect[1]);
+ y_max = MAX (y_max, rect[1] + rect[3]);
+ }
+
+ rect[1] = framebuffer_height - rect[1] - rect[3];
+
+ }
+
+ _cogl_framebuffer_flush_state (framebuffer,
+ framebuffer,
+ COGL_FRAMEBUFFER_STATE_BIND);
+
+ have_counter = glx_display->have_vblank_counter;
+ can_wait = glx_display->can_vblank_wait;
+
+ /* We need to ensure that all the rendering is done, otherwise
+ * redraw operations that are slower than the framerate can
+ * queue up in the pipeline during a heavy animation, causing a
+ * larger and larger backlog of rendering visible as lag to the
+ * user.
+ *
+ * For an exaggerated example consider rendering at 60fps (so 16ms
+ * per frame) and you have a really slow frame that takes 160ms to
+ * render, even though painting the scene and issuing the commands
+ * to the GPU takes no time at all. If all we did was use the
+ * video_sync extension to throttle the painting done by the CPU
+ * then every 16ms we would have another frame queued up even though
+ * the GPU has only rendered one tenth of the current frame. By the
+ * time the GPU would get to the 2nd frame there would be 9 frames
+ * waiting to be rendered.
+ *
+ * The problem is that we don't currently have a good way to throttle
+ * the GPU, only the CPU so we have to resort to synchronizing the
+ * GPU with the CPU to throttle it.
+ *
+ * Note: since calling glFinish() and synchronizing the CPU with
+ * the GPU is far from ideal, we hope that this is only a short
+ * term solution.
+ * - One idea is to using sync objects to track render
+ * completion so we can throttle the backlog (ideally with an
+ * additional extension that lets us get notifications in our
+ * mainloop instead of having to busy wait for the
+ * completion.)
+ * - Another option is to support clipped redraws by reusing the
+ * contents of old back buffers such that we can flip instead
+ * of using a blit and then we can use GLX_INTEL_swap_events
+ * to throttle. For this though we would still probably want an
+ * additional extension so we can report the limited region of
+ * the window damage to X/compositors.
+ */
+ _cogl_winsys_wait_for_gpu (onscreen);
+
+ if (blit_sub_buffer_is_synchronized && have_counter && can_wait)
+ {
+ end_frame_vsync_counter = _cogl_winsys_get_vsync_counter (context);
+
+ /* If we have the GLX_SGI_video_sync extension then we can
+ * be a bit smarter about how we throttle blits by avoiding
+ * any waits if we can see that the video sync count has
+ * already progressed. */
+ if (glx_onscreen->last_swap_vsync_counter == end_frame_vsync_counter)
+ _cogl_winsys_wait_for_vblank (onscreen);
+ }
+ else if (can_wait)
+ _cogl_winsys_wait_for_vblank (onscreen);
+
+ if (glx_renderer->glXCopySubBuffer)
+ {
+ Display *xdpy = xlib_renderer->xdpy;
+ GLXDrawable drawable;
+
+ drawable =
+ glx_onscreen->glxwin ? glx_onscreen->glxwin : glx_onscreen->xwin;
+ int i;
+ for (i = 0; i < n_rectangles; i++)
+ {
+ int *rect = &rectangles[4 * i];
+ glx_renderer->glXCopySubBuffer (xdpy, drawable,
+ rect[0], rect[1], rect[2], rect[3]);
+ }
+ }
+ else if (context->glBlitFramebuffer)
+ {
+ int i;
+ /* XXX: checkout how this state interacts with the code to use
+ * glBlitFramebuffer in Neil's texture atlasing branch */
+
+ /* glBlitFramebuffer is affected by the scissor so we need to
+ * ensure we have flushed an empty clip stack to get rid of it.
+ * We also mark that the clip state is dirty so that it will be
+ * flushed to the correct state the next time something is
+ * drawn */
+ _cogl_clip_stack_flush (NULL, framebuffer);
+ context->current_draw_buffer_changes |= COGL_FRAMEBUFFER_STATE_CLIP;
+
+ context->glDrawBuffer (GL_FRONT);
+ for (i = 0; i < n_rectangles; i++)
+ {
+ int *rect = &rectangles[4 * i];
+ int x2 = rect[0] + rect[2];
+ int y2 = rect[1] + rect[3];
+ context->glBlitFramebuffer (rect[0], rect[1], x2, y2,
+ rect[0], rect[1], x2, y2,
+ GL_COLOR_BUFFER_BIT, GL_NEAREST);
+ }
+ context->glDrawBuffer (context->current_gl_draw_buffer);
+ }
+
+ /* NB: unlike glXSwapBuffers, glXCopySubBuffer and
+ * glBlitFramebuffer don't issue an implicit glFlush() so we
+ * have to flush ourselves if we want the request to complete in
+ * a finite amount of time since otherwise the driver can batch
+ * the command indefinitely. */
+ context->glFlush ();
+
+ /* NB: It's important we save the counter we read before acting on
+ * the swap request since if we are mixing and matching different
+ * swap methods between frames we don't want to read the timer e.g.
+ * after calling glFinish() some times and not for others.
+ *
+ * In other words; this way we consistently save the time at the end
+ * of the applications frame such that the counter isn't muddled by
+ * the varying costs of different swap methods.
+ */
+ if (have_counter)
+ glx_onscreen->last_swap_vsync_counter = end_frame_vsync_counter;
+
+ {
+ CoglOutput *output;
+
+ x_min = CLAMP (x_min, 0, framebuffer_width);
+ x_max = CLAMP (x_max, 0, framebuffer_width);
+ y_min = CLAMP (y_min, 0, framebuffer_width);
+ y_max = CLAMP (y_max, 0, framebuffer_height);
+
+ output =
+ _cogl_xlib_renderer_output_for_rectangle (context->display->renderer,
+ glx_onscreen->x + x_min,
+ glx_onscreen->y + y_min,
+ x_max - x_min,
+ y_max - y_min);
+
+ set_frame_info_output (onscreen, output);
+ }
+
+ /* XXX: we don't get SwapComplete events based on how we implement
+ * the _swap_region() API but if cogl-onscreen.c knows we are
+ * handling _SYNC and _COMPLETE events in the winsys then we need to
+ * send fake events in this case.
+ */
+ if (_cogl_winsys_has_feature (COGL_WINSYS_FEATURE_SYNC_AND_COMPLETE_EVENT))
+ {
+ set_sync_pending (onscreen);
+ set_complete_pending (onscreen);
+ }
+}
+
+void
+_cogl_winsys_onscreen_glx_swap_buffers_with_damage (CoglOnscreen *onscreen,
+ const int *rectangles,
+ int n_rectangles,
+ CoglFrameInfo *info,
+ gpointer user_data)
+{
+ CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen);
+ CoglContext *context = cogl_framebuffer_get_context (framebuffer);
+ CoglXlibRenderer *xlib_renderer =
+ _cogl_xlib_renderer_get_data (context->display->renderer);
+ CoglGLXRenderer *glx_renderer = context->display->renderer->winsys;
+ CoglGLXDisplay *glx_display = context->display->winsys;
+ CoglOnscreenGLX *glx_onscreen = cogl_onscreen_get_winsys (onscreen);
+ gboolean have_counter;
+ GLXDrawable drawable;
+
+ /* XXX: theoretically this shouldn't be necessary but at least with
+ * the Intel drivers we have see that if we don't call
+ * glXMakeContextCurrent for the drawable we are swapping then
+ * we get a BadDrawable error from the X server. */
+ _cogl_framebuffer_flush_state (framebuffer,
+ framebuffer,
+ COGL_FRAMEBUFFER_STATE_BIND);
+
+ drawable = glx_onscreen->glxwin ? glx_onscreen->glxwin : glx_onscreen->xwin;
+
+ have_counter = glx_display->have_vblank_counter;
+
+ if (!glx_renderer->glXSwapInterval)
+ {
+ gboolean can_wait = have_counter || glx_display->can_vblank_wait;
+
+ uint32_t end_frame_vsync_counter = 0;
+
+ /* If the swap_region API is also being used then we need to track
+ * the vsync counter for each swap request so we can manually
+ * throttle swap_region requests. */
+ if (have_counter)
+ end_frame_vsync_counter = _cogl_winsys_get_vsync_counter (context);
+
+ /* If we are going to wait for VBLANK manually, we not only
+ * need to flush out pending drawing to the GPU before we
+ * sleep, we need to wait for it to finish. Otherwise, we
+ * may end up with the situation:
+ *
+ * - We finish drawing - GPU drawing continues
+ * - We go to sleep - GPU drawing continues
+ * VBLANK - We call glXSwapBuffers - GPU drawing continues
+ * - GPU drawing continues
+ * - Swap buffers happens
+ *
+ * Producing a tear. Calling glFinish() first will cause us
+ * to properly wait for the next VBLANK before we swap. This
+ * obviously does not happen when we use _GLX_SWAP and let
+ * the driver do the right thing
+ */
+ _cogl_winsys_wait_for_gpu (onscreen);
+
+ if (have_counter && can_wait)
+ {
+ if (glx_onscreen->last_swap_vsync_counter ==
+ end_frame_vsync_counter)
+ _cogl_winsys_wait_for_vblank (onscreen);
+ }
+ else if (can_wait)
+ _cogl_winsys_wait_for_vblank (onscreen);
+ }
+
+ glx_renderer->glXSwapBuffers (xlib_renderer->xdpy, drawable);
+
+ if (have_counter)
+ glx_onscreen->last_swap_vsync_counter =
+ _cogl_winsys_get_vsync_counter (context);
+
+ set_frame_info_output (onscreen, glx_onscreen->output);
+}
+
+uint32_t
+_cogl_winsys_onscreen_glx_get_window_xid (CoglOnscreen *onscreen)
+{
+ CoglOnscreenGLX *glx_onscreen = cogl_onscreen_get_winsys (onscreen);
+
+ return glx_onscreen->xwin;
+}
+
+void
+_cogl_winsys_onscreen_glx_set_visibility (CoglOnscreen *onscreen,
+ gboolean visibility)
+{
+ CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen);
+ CoglContext *context = cogl_framebuffer_get_context (framebuffer);
+ CoglXlibRenderer *xlib_renderer =
+ _cogl_xlib_renderer_get_data (context->display->renderer);
+ CoglOnscreenGLX *glx_onscreen = cogl_onscreen_get_winsys (onscreen);
+
+ if (visibility)
+ XMapWindow (xlib_renderer->xdpy, glx_onscreen->xwin);
+ else
+ XUnmapWindow (xlib_renderer->xdpy, glx_onscreen->xwin);
+}
+
+void
+_cogl_winsys_onscreen_glx_set_resizable (CoglOnscreen *onscreen,
+ gboolean resizable)
+{
+ CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen);
+ CoglContext *context = cogl_framebuffer_get_context (framebuffer);
+ CoglXlibRenderer *xlib_renderer =
+ _cogl_xlib_renderer_get_data (context->display->renderer);
+ CoglOnscreenGLX *glx_onscreen = cogl_onscreen_get_winsys (onscreen);
+
+ XSizeHints *size_hints = XAllocSizeHints ();
+
+ if (resizable)
+ {
+ /* TODO: Add cogl_onscreen_request_minimum_size () */
+ size_hints->min_width = 1;
+ size_hints->min_height = 1;
+
+ size_hints->max_width = INT_MAX;
+ size_hints->max_height = INT_MAX;
+ }
+ else
+ {
+ int width = cogl_framebuffer_get_width (framebuffer);
+ int height = cogl_framebuffer_get_height (framebuffer);
+
+ size_hints->min_width = width;
+ size_hints->min_height = height;
+
+ size_hints->max_width = width;
+ size_hints->max_height = height;
+ }
+
+ XSetWMNormalHints (xlib_renderer->xdpy, glx_onscreen->xwin, size_hints);
+
+ XFree (size_hints);
+}
+
+void
+cogl_onscreen_glx_notify_swap_buffers (CoglOnscreen *onscreen,
+ GLXBufferSwapComplete *swap_event)
+{
+ CoglOnscreenGLX *glx_onscreen;
+
+ glx_onscreen = cogl_onscreen_get_winsys (onscreen);
+
+ /* We only want to notify that the swap is complete when the
+ application calls cogl_context_dispatch so instead of immediately
+ notifying we'll set a flag to remember to notify later */
+ set_sync_pending (onscreen);
+
+ if (swap_event->ust != 0)
+ {
+ CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen);
+ CoglContext *context = cogl_framebuffer_get_context (framebuffer);
+ CoglFrameInfo *info;
+
+ info = cogl_onscreen_peek_head_frame_info (onscreen);
+ info->presentation_time =
+ ust_to_nanoseconds (context->display->renderer,
+ glx_onscreen->glxwin,
+ swap_event->ust);
+ }
+
+ set_complete_pending (onscreen);
+}
+
+void
+cogl_onscreen_glx_update_output (CoglOnscreen *onscreen)
+{
+ CoglOnscreenGLX *glx_onscreen = cogl_onscreen_get_winsys (onscreen);
+ CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen);
+ CoglContext *context = cogl_framebuffer_get_context (framebuffer);
+ CoglDisplay *display = context->display;
+ CoglOutput *output;
+ int width, height;
+
+ width = cogl_framebuffer_get_width (framebuffer);
+ height = cogl_framebuffer_get_height (framebuffer);
+ output = _cogl_xlib_renderer_output_for_rectangle (display->renderer,
+ glx_onscreen->x,
+ glx_onscreen->y,
+ width, height);
+ if (glx_onscreen->output != output)
+ {
+ if (glx_onscreen->output)
+ cogl_object_unref (glx_onscreen->output);
+
+ glx_onscreen->output = output;
+
+ if (output)
+ cogl_object_ref (glx_onscreen->output);
+ }
+}
+
+void
+cogl_onscreen_glx_resize (CoglOnscreen *onscreen,
+ XConfigureEvent *configure_event)
+{
+ CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen);
+ CoglContext *context = cogl_framebuffer_get_context (framebuffer);
+ CoglRenderer *renderer = context->display->renderer;
+ CoglGLXRenderer *glx_renderer = renderer->winsys;
+ CoglOnscreenGLX *glx_onscreen;
+ int x, y;
+
+ glx_onscreen = cogl_onscreen_get_winsys (onscreen);
+
+ _cogl_framebuffer_winsys_update_size (framebuffer,
+ configure_event->width,
+ configure_event->height);
+
+ /* We only want to notify that a resize happened when the
+ * application calls cogl_context_dispatch so instead of immediately
+ * notifying we queue an idle callback */
+ if (!glx_renderer->flush_notifications_idle)
+ {
+ glx_renderer->flush_notifications_idle =
+ _cogl_poll_renderer_add_idle (renderer,
+ flush_pending_notifications_idle,
+ context,
+ NULL);
+ }
+
+ glx_onscreen->pending_resize_notify++;
+
+ if (configure_event->send_event)
+ {
+ x = configure_event->x;
+ y = configure_event->y;
+ }
+ else
+ {
+ Window child;
+ XTranslateCoordinates (configure_event->display,
+ configure_event->window,
+ DefaultRootWindow (configure_event->display),
+ 0, 0, &x, &y, &child);
+ }
+
+ glx_onscreen->x = x;
+ glx_onscreen->y = y;
+
+ cogl_onscreen_glx_update_output (onscreen);
+}
+
+gboolean
+cogl_onscreen_glx_is_for_window (CoglOnscreen *onscreen,
+ Window window)
+{
+ CoglOnscreenGLX *onscreen_glx = cogl_onscreen_get_winsys (onscreen);
+
+ return onscreen_glx->xwin == window;
+}
diff --git a/cogl/cogl/winsys/cogl-onscreen-glx.h b/cogl/cogl/winsys/cogl-onscreen-glx.h
new file mode 100644
index 0000000000..e218a6a964
--- /dev/null
+++ b/cogl/cogl/winsys/cogl-onscreen-glx.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2007,2008,2009,2010,2011,2013 Intel Corporation.
+ * Copyright (C) 2020 Red Hat
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef COGL_ONSCREEN_GLX_H
+#define COGL_ONSCREEN_GLX_H
+
+#include <GL/glx.h>
+#include <X11/Xlib.h>
+
+#include "cogl-onscreen.h"
+
+gboolean
+_cogl_winsys_onscreen_glx_init (CoglOnscreen *onscreen,
+ GError **error);
+
+void
+_cogl_winsys_onscreen_glx_deinit (CoglOnscreen *onscreen);
+
+void
+_cogl_winsys_onscreen_glx_bind (CoglOnscreen *onscreen);
+
+int
+_cogl_winsys_onscreen_glx_get_buffer_age (CoglOnscreen *onscreen);
+
+void
+_cogl_winsys_onscreen_glx_swap_region (CoglOnscreen *onscreen,
+ const int *user_rectangles,
+ int n_rectangles,
+ CoglFrameInfo *info,
+ gpointer user_data);
+
+void
+_cogl_winsys_onscreen_glx_swap_buffers_with_damage (CoglOnscreen *onscreen,
+ const int *rectangles,
+ int n_rectangles,
+ CoglFrameInfo *info,
+ gpointer user_data);
+
+uint32_t
+_cogl_winsys_onscreen_glx_get_window_xid (CoglOnscreen *onscreen);
+
+void
+_cogl_winsys_onscreen_glx_set_visibility (CoglOnscreen *onscreen,
+ gboolean visibility);
+
+void
+_cogl_winsys_onscreen_glx_set_resizable (CoglOnscreen *onscreen,
+ gboolean resizable);
+
+void
+cogl_onscreen_glx_resize (CoglOnscreen *onscreen,
+ XConfigureEvent *configure_event);
+
+void
+cogl_onscreen_glx_update_output (CoglOnscreen *onscreen);
+
+void
+cogl_onscreen_glx_notify_swap_buffers (CoglOnscreen *onscreen,
+ GLXBufferSwapComplete *swap_event);
+
+gboolean
+cogl_onscreen_glx_is_for_window (CoglOnscreen *onscreen,
+ Window window);
+
+#endif /* COGL_ONSCREEN_GLX_H */
diff --git a/cogl/cogl/winsys/cogl-winsys-glx-private.h b/cogl/cogl/winsys/cogl-winsys-glx-private.h
index 15e7f988d3..7151a8efeb 100644
--- a/cogl/cogl/winsys/cogl-winsys-glx-private.h
+++ b/cogl/cogl/winsys/cogl-winsys-glx-private.h
@@ -34,4 +34,17 @@
COGL_EXPORT const CoglWinsysVtable *
_cogl_winsys_glx_get_vtable (void);
+gboolean
+cogl_display_glx_find_fbconfig (CoglDisplay *display,
+ const CoglFramebufferConfig *config,
+ GLXFBConfig *config_ret,
+ GError **error);
+
+void
+cogl_context_glx_set_current_drawable (CoglContext *context,
+ GLXDrawable drawable);
+
+GLXDrawable
+cogl_context_glx_get_current_drawable (CoglContext *context);
+
#endif /* __COGL_WINSYS_GLX_PRIVATE_H */
diff --git a/cogl/cogl/winsys/cogl-winsys-glx.c b/cogl/cogl/winsys/cogl-winsys-glx.c
index 8e15dee001..87867aed4e 100644
--- a/cogl/cogl/winsys/cogl-winsys-glx.c
+++ b/cogl/cogl/winsys/cogl-winsys-glx.c
@@ -54,6 +54,7 @@
#include "cogl-version.h"
#include "cogl-glx.h"
#include "driver/gl/cogl-pipeline-opengl-private.h"
+#include "winsys/cogl-onscreen-glx.h"
#include "winsys/cogl-winsys-private.h"
#include "winsys/cogl-winsys-glx-private.h"
@@ -76,27 +77,15 @@
#define GLX_GENERATE_RESET_ON_VIDEO_MEMORY_PURGE_NV 0x20F7
#endif
-#define COGL_ONSCREEN_X11_EVENT_MASK (StructureNotifyMask | ExposureMask)
#define MAX_GLX_CONFIG_ATTRIBS 30
+typedef struct _CoglOnscreenGLX CoglOnscreenGLX;
+
typedef struct _CoglContextGLX
{
GLXDrawable current_drawable;
} CoglContextGLX;
-typedef struct _CoglOnscreenGLX
-{
- Window xwin;
- int x, y;
- CoglOutput *output;
-
- GLXDrawable glxwin;
- uint32_t last_swap_vsync_counter;
- uint32_t pending_sync_notify;
- uint32_t pending_complete_notify;
- uint32_t pending_resize_notify;
-} CoglOnscreenGLX;
-
typedef struct _CoglPixmapTextureEyeGLX
{
CoglTexture *glx_tex;
@@ -172,15 +161,17 @@ find_onscreen_for_xid (CoglContext *context, uint32_t xid)
for (l = context->framebuffers; l; l = l->next)
{
CoglFramebuffer *framebuffer = l->data;
+ CoglOnscreen *onscreen;
CoglOnscreenGLX *onscreen_glx;
if (!COGL_IS_ONSCREEN (framebuffer))
continue;
- /* Does the GLXEvent have the GLXDrawable or the X Window? */
- onscreen_glx = cogl_onscreen_get_winsys (COGL_ONSCREEN (framebuffer));
- if (onscreen_glx && onscreen_glx->xwin == (Window)xid)
- return COGL_ONSCREEN (framebuffer);
+ onscreen = COGL_ONSCREEN (framebuffer);
+ onscreen_glx = cogl_onscreen_get_winsys (onscreen);
+ if (onscreen_glx &&
+ cogl_onscreen_glx_is_for_window (onscreen, (Window) xid))
+ return onscreen;
}
return NULL;
@@ -195,96 +186,6 @@ get_monotonic_time_ns (void)
return ts.tv_sec * G_GINT64_CONSTANT (1000000000) + ts.tv_nsec;
}
-static void
-ensure_ust_type (CoglRenderer *renderer,
- GLXDrawable drawable)
-{
- CoglGLXRenderer *glx_renderer = renderer->winsys;
- CoglXlibRenderer *xlib_renderer = _cogl_xlib_renderer_get_data (renderer);
- int64_t ust;
- int64_t msc;
- int64_t sbc;
- struct timeval tv;
- int64_t current_system_time;
- int64_t current_monotonic_time;
-
- if (glx_renderer->ust_type != COGL_GLX_UST_IS_UNKNOWN)
- return;
-
- glx_renderer->ust_type = COGL_GLX_UST_IS_OTHER;
-
- if (glx_renderer->glXGetSyncValues == NULL)
- goto out;
-
- if (!glx_renderer->glXGetSyncValues (xlib_renderer->xdpy, drawable,
- &ust, &msc, &sbc))
- goto out;
-
- /* This is the time source that existing (buggy) linux drm drivers
- * use */
- gettimeofday (&tv, NULL);
- current_system_time = (tv.tv_sec * G_GINT64_CONSTANT (1000000)) + tv.tv_usec;
-
- if (current_system_time > ust - 1000000 &&
- current_system_time < ust + 1000000)
- {
- glx_renderer->ust_type = COGL_GLX_UST_IS_GETTIMEOFDAY;
- goto out;
- }
-
- /* This is the time source that the newer (fixed) linux drm
- * drivers use (Linux >= 3.8) */
- current_monotonic_time = get_monotonic_time_ns () / 1000;
-
- if (current_monotonic_time > ust - 1000000 &&
- current_monotonic_time < ust + 1000000)
- {
- glx_renderer->ust_type = COGL_GLX_UST_IS_MONOTONIC_TIME;
- goto out;
- }
-
- out:
- COGL_NOTE (WINSYS, "Classified OML system time as: %s",
- glx_renderer->ust_type == COGL_GLX_UST_IS_GETTIMEOFDAY ? "gettimeofday" :
- (glx_renderer->ust_type == COGL_GLX_UST_IS_MONOTONIC_TIME ? "monotonic" :
- "other"));
- return;
-}
-
-static int64_t
-ust_to_nanoseconds (CoglRenderer *renderer,
- GLXDrawable drawable,
- int64_t ust)
-{
- CoglGLXRenderer *glx_renderer = renderer->winsys;
-
- ensure_ust_type (renderer, drawable);
-
- switch (glx_renderer->ust_type)
- {
- case COGL_GLX_UST_IS_UNKNOWN:
- g_assert_not_reached ();
- break;
- case COGL_GLX_UST_IS_GETTIMEOFDAY:
- case COGL_GLX_UST_IS_MONOTONIC_TIME:
- return 1000 * ust;
- case COGL_GLX_UST_IS_OTHER:
- /* In this case the scale of UST is undefined so we can't easily
- * scale to nanoseconds.
- *
- * For example the driver may be reporting the rdtsc CPU counter
- * as UST values and so the scale would need to be determined
- * empirically.
- *
- * Potentially we could block for a known duration within
- * ensure_ust_type() to measure the timescale of UST but for now
- * we just ignore unknown time sources */
- return 0;
- }
-
- return 0;
-}
-
static int64_t
_cogl_winsys_get_clock_time (CoglContext *context)
{
@@ -321,226 +222,28 @@ _cogl_winsys_get_clock_time (CoglContext *context)
return 0;
}
-static void
-flush_pending_notifications_cb (void *data,
- void *user_data)
-{
- CoglFramebuffer *framebuffer = data;
-
- if (COGL_IS_ONSCREEN (framebuffer))
- {
- CoglOnscreen *onscreen = COGL_ONSCREEN (framebuffer);
- CoglOnscreenGLX *glx_onscreen = cogl_onscreen_get_winsys (onscreen);
-
- while (glx_onscreen->pending_sync_notify > 0 ||
- glx_onscreen->pending_complete_notify > 0 ||
- glx_onscreen->pending_resize_notify > 0)
- {
- if (glx_onscreen->pending_sync_notify > 0)
- {
- CoglFrameInfo *info;
-
- info = cogl_onscreen_peek_head_frame_info (onscreen);
- _cogl_onscreen_notify_frame_sync (onscreen, info);
- glx_onscreen->pending_sync_notify--;
- }
-
- if (glx_onscreen->pending_complete_notify > 0)
- {
- CoglFrameInfo *info;
-
- info = cogl_onscreen_pop_head_frame_info (onscreen);
- _cogl_onscreen_notify_complete (onscreen, info);
- cogl_object_unref (info);
- glx_onscreen->pending_complete_notify--;
- }
-
- if (glx_onscreen->pending_resize_notify > 0)
- {
- _cogl_onscreen_notify_resize (onscreen);
- glx_onscreen->pending_resize_notify--;
- }
- }
- }
-}
-
-static void
-flush_pending_notifications_idle (void *user_data)
-{
- CoglContext *context = user_data;
- CoglRenderer *renderer = context->display->renderer;
- CoglGLXRenderer *glx_renderer = renderer->winsys;
-
- /* This needs to be disconnected before invoking the callbacks in
- * case the callbacks cause it to be queued again */
- _cogl_closure_disconnect (glx_renderer->flush_notifications_idle);
- glx_renderer->flush_notifications_idle = NULL;
-
- g_list_foreach (context->framebuffers,
- flush_pending_notifications_cb,
- NULL);
-}
-
-static void
-set_sync_pending (CoglOnscreen *onscreen)
-{
- CoglOnscreenGLX *glx_onscreen = cogl_onscreen_get_winsys (onscreen);
- CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen);
- CoglContext *context = cogl_framebuffer_get_context (framebuffer);
- CoglRenderer *renderer = context->display->renderer;
- CoglGLXRenderer *glx_renderer = renderer->winsys;
-
- /* We only want to dispatch sync events when the application calls
- * cogl_context_dispatch so instead of immediately notifying we
- * queue an idle callback */
- if (!glx_renderer->flush_notifications_idle)
- {
- glx_renderer->flush_notifications_idle =
- _cogl_poll_renderer_add_idle (renderer,
- flush_pending_notifications_idle,
- context,
- NULL);
- }
-
- glx_onscreen->pending_sync_notify++;
-}
-
-static void
-set_complete_pending (CoglOnscreen *onscreen)
-{
- CoglOnscreenGLX *glx_onscreen = cogl_onscreen_get_winsys (onscreen);
- CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen);
- CoglContext *context = cogl_framebuffer_get_context (framebuffer);
- CoglRenderer *renderer = context->display->renderer;
- CoglGLXRenderer *glx_renderer = renderer->winsys;
-
- /* We only want to notify swap completion when the application calls
- * cogl_context_dispatch so instead of immediately notifying we
- * queue an idle callback */
- if (!glx_renderer->flush_notifications_idle)
- {
- glx_renderer->flush_notifications_idle =
- _cogl_poll_renderer_add_idle (renderer,
- flush_pending_notifications_idle,
- context,
- NULL);
- }
-
- glx_onscreen->pending_complete_notify++;
-}
-
static void
notify_swap_buffers (CoglContext *context, GLXBufferSwapComplete *swap_event)
{
CoglOnscreen *onscreen = find_onscreen_for_xid (context, (uint32_t)swap_event->drawable);
- CoglOnscreenGLX *glx_onscreen;
if (!onscreen)
return;
- glx_onscreen = cogl_onscreen_get_winsys (onscreen);
-
- /* We only want to notify that the swap is complete when the
- application calls cogl_context_dispatch so instead of immediately
- notifying we'll set a flag to remember to notify later */
- set_sync_pending (onscreen);
-
- if (swap_event->ust != 0)
- {
- CoglFrameInfo *info = cogl_onscreen_peek_head_frame_info (onscreen);
- info->presentation_time =
- ust_to_nanoseconds (context->display->renderer,
- glx_onscreen->glxwin,
- swap_event->ust);
- }
-
- set_complete_pending (onscreen);
-}
-
-static void
-update_output (CoglOnscreen *onscreen)
-{
- CoglOnscreenGLX *onscreen_glx = cogl_onscreen_get_winsys (onscreen);
- CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen);
- CoglContext *context = cogl_framebuffer_get_context (framebuffer);
- CoglDisplay *display = context->display;
- CoglOutput *output;
- int width, height;
-
- width = cogl_framebuffer_get_width (framebuffer);
- height = cogl_framebuffer_get_height (framebuffer);
- output = _cogl_xlib_renderer_output_for_rectangle (display->renderer,
- onscreen_glx->x,
- onscreen_glx->y,
- width, height);
- if (onscreen_glx->output != output)
- {
- if (onscreen_glx->output)
- cogl_object_unref (onscreen_glx->output);
-
- onscreen_glx->output = output;
-
- if (output)
- cogl_object_ref (onscreen_glx->output);
- }
+ cogl_onscreen_glx_notify_swap_buffers (onscreen, swap_event);
}
static void
notify_resize (CoglContext *context,
XConfigureEvent *configure_event)
{
- CoglOnscreen *onscreen = find_onscreen_for_xid (context,
- configure_event->window);
- CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen);
- CoglRenderer *renderer = context->display->renderer;
- CoglGLXRenderer *glx_renderer = renderer->winsys;
- CoglOnscreenGLX *glx_onscreen;
+ CoglOnscreen *onscreen;
+ onscreen = find_onscreen_for_xid (context, configure_event->window);
if (!onscreen)
return;
- glx_onscreen = cogl_onscreen_get_winsys (onscreen);
-
- _cogl_framebuffer_winsys_update_size (framebuffer,
- configure_event->width,
- configure_event->height);
-
- /* We only want to notify that a resize happened when the
- * application calls cogl_context_dispatch so instead of immediately
- * notifying we queue an idle callback */
- if (!glx_renderer->flush_notifications_idle)
- {
- glx_renderer->flush_notifications_idle =
- _cogl_poll_renderer_add_idle (renderer,
- flush_pending_notifications_idle,
- context,
- NULL);
- }
-
- glx_onscreen->pending_resize_notify++;
-
- {
- int x, y;
-
- if (configure_event->send_event)
- {
- x = configure_event->x;
- y = configure_event->y;
- }
- else
- {
- Window child;
- XTranslateCoordinates (configure_event->display,
- configure_event->window,
- DefaultRootWindow (configure_event->display),
- 0, 0, &x, &y, &child);
- }
-
- glx_onscreen->x = x;
- glx_onscreen->y = y;
-
- update_output (onscreen);
- }
+ cogl_onscreen_glx_resize (onscreen, configure_event);
}
static CoglFilterReturn
@@ -630,7 +333,7 @@ update_all_outputs (CoglRenderer *renderer)
if (!COGL_IS_ONSCREEN (framebuffer))
continue;
- update_output (COGL_ONSCREEN (framebuffer));
+ cogl_onscreen_glx_update_output (COGL_ONSCREEN (framebuffer));
}
return TRUE;
@@ -902,11 +605,11 @@ glx_attributes_from_framebuffer_config (CoglDisplay *display,
/* It seems the GLX spec never defined an invalid GLXFBConfig that
* we could overload as an indication of error, so we have to return
* an explicit boolean status. */
-static gboolean
-find_fbconfig (CoglDisplay *display,
- const CoglFramebufferConfig *config,
- GLXFBConfig *config_ret,
- GError **error)
+gboolean
+cogl_display_glx_find_fbconfig (CoglDisplay *display,
+ const CoglFramebufferConfig *config,
+ GLXFBConfig *config_ret,
+ GError **error)
{
CoglXlibRenderer *xlib_renderer =
_cogl_xlib_renderer_get_data (display->renderer);
@@ -1026,8 +729,10 @@ create_context (CoglDisplay *display, GError **error)
g_return_val_if_fail (glx_display->glx_context == NULL, TRUE);
glx_display->found_fbconfig =
- find_fbconfig (display, &display->onscreen_template->config, &config,
- &fbconfig_error);
+ cogl_display_glx_find_fbconfig (display,
+ &display->onscreen_template->config,
+ &config,
+ &fbconfig_error);
if (!glx_display->found_fbconfig)
{
g_set_error (error, COGL_WINSYS_ERROR,
@@ -1226,757 +931,6 @@ _cogl_winsys_context_deinit (CoglContext *context)
g_free (context->winsys);
}
-static gboolean
-_cogl_winsys_onscreen_init (CoglOnscreen *onscreen,
- GError **error)
-{
- CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen);
- CoglContext *context = cogl_framebuffer_get_context (framebuffer);
- CoglDisplay *display = context->display;
- CoglGLXDisplay *glx_display = display->winsys;
- CoglXlibRenderer *xlib_renderer =
- _cogl_xlib_renderer_get_data (display->renderer);
- CoglGLXRenderer *glx_renderer = display->renderer->winsys;
- Window xwin;
- CoglOnscreenGLX *glx_onscreen;
- const CoglFramebufferConfig *config;
- GLXFBConfig fbconfig;
- GError *fbconfig_error = NULL;
- CoglOnscreenGLX *winsys;
-
- g_return_val_if_fail (glx_display->glx_context, FALSE);
-
- config = cogl_framebuffer_get_config (framebuffer);
- if (!find_fbconfig (display, config,
- &fbconfig,
- &fbconfig_error))
- {
- g_set_error (error, COGL_WINSYS_ERROR,
- COGL_WINSYS_ERROR_CREATE_CONTEXT,
- "Unable to find suitable fbconfig for the GLX context: %s",
- fbconfig_error->message);
- g_error_free (fbconfig_error);
- return FALSE;
- }
-
- /* Update the real number of samples_per_pixel now that we have
- * found an fbconfig... */
- if (config->samples_per_pixel)
- {
- int samples;
- int status = glx_renderer->glXGetFBConfigAttrib (xlib_renderer->xdpy,
- fbconfig,
- GLX_SAMPLES,
- &samples);
- g_return_val_if_fail (status == Success, TRUE);
- cogl_framebuffer_update_samples_per_pixel (framebuffer, samples);
- }
-
- /* FIXME: We need to explicitly Select for ConfigureNotify events.
- * We need to document that for windows we create then toolkits
- * must be careful not to clear event mask bits that we select.
- */
- {
- int width;
- int height;
- CoglXlibTrapState state;
- XVisualInfo *xvisinfo;
- XSetWindowAttributes xattr;
- unsigned long mask;
- int xerror;
-
- width = cogl_framebuffer_get_width (framebuffer);
- height = cogl_framebuffer_get_height (framebuffer);
-
- _cogl_xlib_renderer_trap_errors (display->renderer, &state);
-
- xvisinfo = glx_renderer->glXGetVisualFromFBConfig (xlib_renderer->xdpy,
- fbconfig);
- if (xvisinfo == NULL)
- {
- g_set_error_literal (error, COGL_WINSYS_ERROR,
- COGL_WINSYS_ERROR_CREATE_ONSCREEN,
- "Unable to retrieve the X11 visual of context's "
- "fbconfig");
- return FALSE;
- }
-
- /* window attributes */
- xattr.background_pixel = WhitePixel (xlib_renderer->xdpy,
- DefaultScreen (xlib_renderer->xdpy));
- xattr.border_pixel = 0;
- /* XXX: is this an X resource that we are leaking‽... */
- xattr.colormap = XCreateColormap (xlib_renderer->xdpy,
- DefaultRootWindow (xlib_renderer->xdpy),
- xvisinfo->visual,
- AllocNone);
- xattr.event_mask = COGL_ONSCREEN_X11_EVENT_MASK;
-
- mask = CWBorderPixel | CWColormap | CWEventMask;
-
- xwin = XCreateWindow (xlib_renderer->xdpy,
- DefaultRootWindow (xlib_renderer->xdpy),
- 0, 0,
- width, height,
- 0,
- xvisinfo->depth,
- InputOutput,
- xvisinfo->visual,
- mask, &xattr);
-
- XFree (xvisinfo);
-
- XSync (xlib_renderer->xdpy, False);
- xerror = _cogl_xlib_renderer_untrap_errors (display->renderer, &state);
- if (xerror)
- {
- char message[1000];
- XGetErrorText (xlib_renderer->xdpy, xerror,
- message, sizeof (message));
- g_set_error (error, COGL_WINSYS_ERROR,
- COGL_WINSYS_ERROR_CREATE_ONSCREEN,
- "X error while creating Window for CoglOnscreen: %s",
- message);
- return FALSE;
- }
- }
-
- winsys = g_slice_new0 (CoglOnscreenGLX);
- cogl_onscreen_set_winsys (onscreen, winsys);
- glx_onscreen = cogl_onscreen_get_winsys (onscreen);
-
- glx_onscreen->xwin = xwin;
-
- /* Try and create a GLXWindow to use with extensions dependent on
- * GLX versions >= 1.3 that don't accept regular X Windows as GLX
- * drawables. */
- if (glx_renderer->glx_major == 1 && glx_renderer->glx_minor >= 3)
- {
- glx_onscreen->glxwin =
- glx_renderer->glXCreateWindow (xlib_renderer->xdpy,
- fbconfig,
- glx_onscreen->xwin,
- NULL);
- }
-
-#ifdef GLX_INTEL_swap_event
- if (_cogl_winsys_has_feature (COGL_WINSYS_FEATURE_SYNC_AND_COMPLETE_EVENT))
- {
- GLXDrawable drawable =
- glx_onscreen->glxwin ? glx_onscreen->glxwin : glx_onscreen->xwin;
-
- /* similarly to above, we unconditionally select this event
- * because we rely on it to advance the master clock, and
- * drive redraw/relayout, animations and event handling.
- */
- glx_renderer->glXSelectEvent (xlib_renderer->xdpy,
- drawable,
- GLX_BUFFER_SWAP_COMPLETE_INTEL_MASK);
- }
-#endif /* GLX_INTEL_swap_event */
-
- return TRUE;
-}
-
-static void
-_cogl_winsys_onscreen_deinit (CoglOnscreen *onscreen)
-{
- CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen);
- CoglContext *context = cogl_framebuffer_get_context (framebuffer);
- CoglContextGLX *glx_context = context->winsys;
- CoglGLXDisplay *glx_display = context->display->winsys;
- CoglXlibRenderer *xlib_renderer =
- _cogl_xlib_renderer_get_data (context->display->renderer);
- CoglGLXRenderer *glx_renderer = context->display->renderer->winsys;
- CoglXlibTrapState old_state;
- CoglOnscreenGLX *glx_onscreen = cogl_onscreen_get_winsys (onscreen);
- GLXDrawable drawable;
-
- /* If we never successfully allocated then there's nothing to do */
- if (glx_onscreen == NULL)
- return;
-
- if (glx_onscreen->output != NULL)
- {
- cogl_object_unref (glx_onscreen->output);
- glx_onscreen->output = NULL;
- }
-
- _cogl_xlib_renderer_trap_errors (context->display->renderer, &old_state);
-
- drawable =
- glx_onscreen->glxwin == None ? glx_onscreen->xwin : glx_onscreen->glxwin;
-
- /* Cogl always needs a valid context bound to something so if we are
- * destroying the onscreen that is currently bound we'll switch back
- * to the dummy drawable. Although the documentation for
- * glXDestroyWindow states that a currently bound window won't
- * actually be destroyed until it is unbound, it looks like this
- * doesn't work if the X window itself is destroyed */
- if (drawable == glx_context->current_drawable)
- {
- GLXDrawable dummy_drawable = (glx_display->dummy_glxwin == None ?
- glx_display->dummy_xwin :
- glx_display->dummy_glxwin);
-
- glx_renderer->glXMakeContextCurrent (xlib_renderer->xdpy,
- dummy_drawable,
- dummy_drawable,
- glx_display->glx_context);
- glx_context->current_drawable = dummy_drawable;
- }
-
- if (glx_onscreen->glxwin != None)
- {
- glx_renderer->glXDestroyWindow (xlib_renderer->xdpy,
- glx_onscreen->glxwin);
- glx_onscreen->glxwin = None;
- }
-
- if (glx_onscreen->xwin != None)
- {
- XDestroyWindow (xlib_renderer->xdpy, glx_onscreen->xwin);
- glx_onscreen->xwin = None;
- }
- else
- {
- glx_onscreen->xwin = None;
- }
-
- XSync (xlib_renderer->xdpy, False);
-
- _cogl_xlib_renderer_untrap_errors (context->display->renderer, &old_state);
-
- g_slice_free (CoglOnscreenGLX, cogl_onscreen_get_winsys (onscreen));
- cogl_onscreen_set_winsys (onscreen, NULL);
-}
-
-static void
-_cogl_winsys_onscreen_bind (CoglOnscreen *onscreen)
-{
- CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen);
- CoglContext *context = cogl_framebuffer_get_context (framebuffer);
- CoglContextGLX *glx_context = context->winsys;
- CoglGLXDisplay *glx_display = context->display->winsys;
- CoglXlibRenderer *xlib_renderer =
- _cogl_xlib_renderer_get_data (context->display->renderer);
- CoglGLXRenderer *glx_renderer = context->display->renderer->winsys;
- CoglOnscreenGLX *glx_onscreen = cogl_onscreen_get_winsys (onscreen);
- CoglXlibTrapState old_state;
- GLXDrawable drawable;
-
- drawable =
- glx_onscreen->glxwin ? glx_onscreen->glxwin : glx_onscreen->xwin;
-
- if (glx_context->current_drawable == drawable)
- return;
-
- _cogl_xlib_renderer_trap_errors (context->display->renderer, &old_state);
-
- COGL_NOTE (WINSYS,
- "MakeContextCurrent dpy: %p, window: 0x%x, context: %p",
- xlib_renderer->xdpy,
- (unsigned int) drawable,
- glx_display->glx_context);
-
- glx_renderer->glXMakeContextCurrent (xlib_renderer->xdpy,
- drawable,
- drawable,
- glx_display->glx_context);
-
- /* In case we are using GLX_SGI_swap_control for vblank syncing
- * we need call glXSwapIntervalSGI here to make sure that it
- * affects the current drawable.
- *
- * Note: we explicitly set to 0 when we aren't using the swap
- * interval to synchronize since some drivers have a default
- * swap interval of 1. Sadly some drivers even ignore requests
- * to disable the swap interval.
- *
- * NB: glXSwapIntervalSGI applies to the context not the
- * drawable which is why we can't just do this once when the
- * framebuffer is allocated.
- *
- * FIXME: We should check for GLX_EXT_swap_control which allows
- * per framebuffer swap intervals. GLX_MESA_swap_control also
- * allows per-framebuffer swap intervals but the semantics tend
- * to be more muddled since Mesa drivers tend to expose both the
- * MESA and SGI extensions which should technically be mutually
- * exclusive.
- */
- if (glx_renderer->glXSwapInterval)
- glx_renderer->glXSwapInterval (1);
-
- XSync (xlib_renderer->xdpy, False);
-
- /* FIXME: We should be reporting a GError here */
- if (_cogl_xlib_renderer_untrap_errors (context->display->renderer,
- &old_state))
- {
- g_warning ("X Error received while making drawable 0x%08lX current",
- drawable);
- return;
- }
-
- glx_context->current_drawable = drawable;
-}
-
-static void
-_cogl_winsys_wait_for_gpu (CoglOnscreen *onscreen)
-{
- CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen);
- CoglContext *ctx = cogl_framebuffer_get_context (framebuffer);
-
- ctx->glFinish ();
-}
-
-static void
-_cogl_winsys_wait_for_vblank (CoglOnscreen *onscreen)
-{
- CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen);
- CoglContext *ctx = cogl_framebuffer_get_context (framebuffer);
- CoglGLXRenderer *glx_renderer;
- CoglXlibRenderer *xlib_renderer;
- CoglGLXDisplay *glx_display;
-
- glx_renderer = ctx->display->renderer->winsys;
- xlib_renderer = _cogl_xlib_renderer_get_data (ctx->display->renderer);
- glx_display = ctx->display->winsys;
-
- if (glx_display->can_vblank_wait)
- {
- CoglFrameInfo *info = cogl_onscreen_peek_tail_frame_info (onscreen);
-
- if (glx_renderer->glXWaitForMsc)
- {
- CoglOnscreenGLX *glx_onscreen = cogl_onscreen_get_winsys (onscreen);
- Drawable drawable = glx_onscreen->glxwin;
- int64_t ust;
- int64_t msc;
- int64_t sbc;
-
- glx_renderer->glXWaitForMsc (xlib_renderer->xdpy, drawable,
- 0, 1, 0,
- &ust, &msc, &sbc);
- info->presentation_time = ust_to_nanoseconds (ctx->display->renderer,
- drawable,
- ust);
- }
- else
- {
- uint32_t current_count;
-
- glx_renderer->glXGetVideoSync (¤t_count);
- glx_renderer->glXWaitVideoSync (2,
- (current_count + 1) % 2,
- ¤t_count);
-
- info->presentation_time = get_monotonic_time_ns ();
- }
- }
-}
-
-static uint32_t
-_cogl_winsys_get_vsync_counter (CoglContext *ctx)
-{
- uint32_t video_sync_count;
- CoglGLXRenderer *glx_renderer;
-
- glx_renderer = ctx->display->renderer->winsys;
-
- glx_renderer->glXGetVideoSync (&video_sync_count);
-
- return video_sync_count;
-}
-
-#ifndef GLX_BACK_BUFFER_AGE_EXT
-#define GLX_BACK_BUFFER_AGE_EXT 0x20F4
-#endif
-
-static int
-_cogl_winsys_onscreen_get_buffer_age (CoglOnscreen *onscreen)
-{
- CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen);
- CoglContext *context = cogl_framebuffer_get_context (framebuffer);
- CoglXlibRenderer *xlib_renderer = _cogl_xlib_renderer_get_data (context->display->renderer);
- CoglGLXRenderer *glx_renderer = context->display->renderer->winsys;
- CoglOnscreenGLX *glx_onscreen = cogl_onscreen_get_winsys (onscreen);
- GLXDrawable drawable;
- unsigned int age;
-
- if (!_cogl_winsys_has_feature (COGL_WINSYS_FEATURE_BUFFER_AGE))
- return 0;
-
- drawable = glx_onscreen->glxwin ? glx_onscreen->glxwin : glx_onscreen->xwin;
- glx_renderer->glXQueryDrawable (xlib_renderer->xdpy,
- drawable,
- GLX_BACK_BUFFER_AGE_EXT,
- &age);
-
- return age;
-}
-
-static void
-set_frame_info_output (CoglOnscreen *onscreen,
- CoglOutput *output)
-{
- CoglFrameInfo *info = cogl_onscreen_peek_tail_frame_info (onscreen);
-
- if (output)
- {
- float refresh_rate = cogl_output_get_refresh_rate (output);
- if (refresh_rate != 0.0)
- info->refresh_rate = refresh_rate;
- }
-}
-
-static void
-_cogl_winsys_onscreen_swap_region (CoglOnscreen *onscreen,
- const int *user_rectangles,
- int n_rectangles,
- CoglFrameInfo *info,
- gpointer user_data)
-{
- CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen);
- CoglContext *context = cogl_framebuffer_get_context (framebuffer);
- CoglXlibRenderer *xlib_renderer =
- _cogl_xlib_renderer_get_data (context->display->renderer);
- CoglGLXRenderer *glx_renderer = context->display->renderer->winsys;
- CoglGLXDisplay *glx_display = context->display->winsys;
- CoglOnscreenGLX *glx_onscreen = cogl_onscreen_get_winsys (onscreen);
- GLXDrawable drawable =
- glx_onscreen->glxwin ? glx_onscreen->glxwin : glx_onscreen->xwin;
- uint32_t end_frame_vsync_counter = 0;
- gboolean have_counter;
- gboolean can_wait;
- int x_min = 0, x_max = 0, y_min = 0, y_max = 0;
-
- /*
- * We assume that glXCopySubBuffer is synchronized which means it won't prevent multiple
- * blits per retrace if they can all be performed in the blanking period. If that's the
- * case then we still want to use the vblank sync menchanism but
- * we only need it to throttle redraws.
- */
- gboolean blit_sub_buffer_is_synchronized =
- _cogl_winsys_has_feature (COGL_WINSYS_FEATURE_SWAP_REGION_SYNCHRONIZED);
-
- int framebuffer_width = cogl_framebuffer_get_width (framebuffer);
- int framebuffer_height = cogl_framebuffer_get_height (framebuffer);
- int *rectangles = g_alloca (sizeof (int) * n_rectangles * 4);
- int i;
-
- /* glXCopySubBuffer expects rectangles relative to the bottom left corner but
- * we are given rectangles relative to the top left so we need to flip
- * them... */
- memcpy (rectangles, user_rectangles, sizeof (int) * n_rectangles * 4);
- for (i = 0; i < n_rectangles; i++)
- {
- int *rect = &rectangles[4 * i];
-
- if (i == 0)
- {
- x_min = rect[0];
- x_max = rect[0] + rect[2];
- y_min = rect[1];
- y_max = rect[1] + rect[3];
- }
- else
- {
- x_min = MIN (x_min, rect[0]);
- x_max = MAX (x_max, rect[0] + rect[2]);
- y_min = MIN (y_min, rect[1]);
- y_max = MAX (y_max, rect[1] + rect[3]);
- }
-
- rect[1] = framebuffer_height - rect[1] - rect[3];
-
- }
-
- _cogl_framebuffer_flush_state (framebuffer,
- framebuffer,
- COGL_FRAMEBUFFER_STATE_BIND);
-
- have_counter = glx_display->have_vblank_counter;
- can_wait = glx_display->can_vblank_wait;
-
- /* We need to ensure that all the rendering is done, otherwise
- * redraw operations that are slower than the framerate can
- * queue up in the pipeline during a heavy animation, causing a
- * larger and larger backlog of rendering visible as lag to the
- * user.
- *
- * For an exaggerated example consider rendering at 60fps (so 16ms
- * per frame) and you have a really slow frame that takes 160ms to
- * render, even though painting the scene and issuing the commands
- * to the GPU takes no time at all. If all we did was use the
- * video_sync extension to throttle the painting done by the CPU
- * then every 16ms we would have another frame queued up even though
- * the GPU has only rendered one tenth of the current frame. By the
- * time the GPU would get to the 2nd frame there would be 9 frames
- * waiting to be rendered.
- *
- * The problem is that we don't currently have a good way to throttle
- * the GPU, only the CPU so we have to resort to synchronizing the
- * GPU with the CPU to throttle it.
- *
- * Note: since calling glFinish() and synchronizing the CPU with
- * the GPU is far from ideal, we hope that this is only a short
- * term solution.
- * - One idea is to using sync objects to track render
- * completion so we can throttle the backlog (ideally with an
- * additional extension that lets us get notifications in our
- * mainloop instead of having to busy wait for the
- * completion.)
- * - Another option is to support clipped redraws by reusing the
- * contents of old back buffers such that we can flip instead
- * of using a blit and then we can use GLX_INTEL_swap_events
- * to throttle. For this though we would still probably want an
- * additional extension so we can report the limited region of
- * the window damage to X/compositors.
- */
- _cogl_winsys_wait_for_gpu (onscreen);
-
- if (blit_sub_buffer_is_synchronized && have_counter && can_wait)
- {
- end_frame_vsync_counter = _cogl_winsys_get_vsync_counter (context);
-
- /* If we have the GLX_SGI_video_sync extension then we can
- * be a bit smarter about how we throttle blits by avoiding
- * any waits if we can see that the video sync count has
- * already progressed. */
- if (glx_onscreen->last_swap_vsync_counter == end_frame_vsync_counter)
- _cogl_winsys_wait_for_vblank (onscreen);
- }
- else if (can_wait)
- _cogl_winsys_wait_for_vblank (onscreen);
-
- if (glx_renderer->glXCopySubBuffer)
- {
- Display *xdpy = xlib_renderer->xdpy;
- int i;
- for (i = 0; i < n_rectangles; i++)
- {
- int *rect = &rectangles[4 * i];
- glx_renderer->glXCopySubBuffer (xdpy, drawable,
- rect[0], rect[1], rect[2], rect[3]);
- }
- }
- else if (context->glBlitFramebuffer)
- {
- int i;
- /* XXX: checkout how this state interacts with the code to use
- * glBlitFramebuffer in Neil's texture atlasing branch */
-
- /* glBlitFramebuffer is affected by the scissor so we need to
- * ensure we have flushed an empty clip stack to get rid of it.
- * We also mark that the clip state is dirty so that it will be
- * flushed to the correct state the next time something is
- * drawn */
- _cogl_clip_stack_flush (NULL, framebuffer);
- context->current_draw_buffer_changes |= COGL_FRAMEBUFFER_STATE_CLIP;
-
- context->glDrawBuffer (GL_FRONT);
- for (i = 0; i < n_rectangles; i++)
- {
- int *rect = &rectangles[4 * i];
- int x2 = rect[0] + rect[2];
- int y2 = rect[1] + rect[3];
- context->glBlitFramebuffer (rect[0], rect[1], x2, y2,
- rect[0], rect[1], x2, y2,
- GL_COLOR_BUFFER_BIT, GL_NEAREST);
- }
- context->glDrawBuffer (context->current_gl_draw_buffer);
- }
-
- /* NB: unlike glXSwapBuffers, glXCopySubBuffer and
- * glBlitFramebuffer don't issue an implicit glFlush() so we
- * have to flush ourselves if we want the request to complete in
- * a finite amount of time since otherwise the driver can batch
- * the command indefinitely. */
- context->glFlush ();
-
- /* NB: It's important we save the counter we read before acting on
- * the swap request since if we are mixing and matching different
- * swap methods between frames we don't want to read the timer e.g.
- * after calling glFinish() some times and not for others.
- *
- * In other words; this way we consistently save the time at the end
- * of the applications frame such that the counter isn't muddled by
- * the varying costs of different swap methods.
- */
- if (have_counter)
- glx_onscreen->last_swap_vsync_counter = end_frame_vsync_counter;
-
- {
- CoglOutput *output;
-
- x_min = CLAMP (x_min, 0, framebuffer_width);
- x_max = CLAMP (x_max, 0, framebuffer_width);
- y_min = CLAMP (y_min, 0, framebuffer_width);
- y_max = CLAMP (y_max, 0, framebuffer_height);
-
- output =
- _cogl_xlib_renderer_output_for_rectangle (context->display->renderer,
- glx_onscreen->x + x_min,
- glx_onscreen->y + y_min,
- x_max - x_min,
- y_max - y_min);
-
- set_frame_info_output (onscreen, output);
- }
-
- /* XXX: we don't get SwapComplete events based on how we implement
- * the _swap_region() API but if cogl-onscreen.c knows we are
- * handling _SYNC and _COMPLETE events in the winsys then we need to
- * send fake events in this case.
- */
- if (_cogl_winsys_has_feature (COGL_WINSYS_FEATURE_SYNC_AND_COMPLETE_EVENT))
- {
- set_sync_pending (onscreen);
- set_complete_pending (onscreen);
- }
-}
-
-static void
-_cogl_winsys_onscreen_swap_buffers_with_damage (CoglOnscreen *onscreen,
- const int *rectangles,
- int n_rectangles,
- CoglFrameInfo *info,
- gpointer user_data)
-{
- CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen);
- CoglContext *context = cogl_framebuffer_get_context (framebuffer);
- CoglXlibRenderer *xlib_renderer =
- _cogl_xlib_renderer_get_data (context->display->renderer);
- CoglGLXRenderer *glx_renderer = context->display->renderer->winsys;
- CoglGLXDisplay *glx_display = context->display->winsys;
- CoglOnscreenGLX *glx_onscreen = cogl_onscreen_get_winsys (onscreen);
- gboolean have_counter;
- GLXDrawable drawable;
-
- /* XXX: theoretically this shouldn't be necessary but at least with
- * the Intel drivers we have see that if we don't call
- * glXMakeContextCurrent for the drawable we are swapping then
- * we get a BadDrawable error from the X server. */
- _cogl_framebuffer_flush_state (framebuffer,
- framebuffer,
- COGL_FRAMEBUFFER_STATE_BIND);
-
- drawable = glx_onscreen->glxwin ? glx_onscreen->glxwin : glx_onscreen->xwin;
-
- have_counter = glx_display->have_vblank_counter;
-
- if (!glx_renderer->glXSwapInterval)
- {
- gboolean can_wait = have_counter || glx_display->can_vblank_wait;
-
- uint32_t end_frame_vsync_counter = 0;
-
- /* If the swap_region API is also being used then we need to track
- * the vsync counter for each swap request so we can manually
- * throttle swap_region requests. */
- if (have_counter)
- end_frame_vsync_counter = _cogl_winsys_get_vsync_counter (context);
-
- /* If we are going to wait for VBLANK manually, we not only
- * need to flush out pending drawing to the GPU before we
- * sleep, we need to wait for it to finish. Otherwise, we
- * may end up with the situation:
- *
- * - We finish drawing - GPU drawing continues
- * - We go to sleep - GPU drawing continues
- * VBLANK - We call glXSwapBuffers - GPU drawing continues
- * - GPU drawing continues
- * - Swap buffers happens
- *
- * Producing a tear. Calling glFinish() first will cause us
- * to properly wait for the next VBLANK before we swap. This
- * obviously does not happen when we use _GLX_SWAP and let
- * the driver do the right thing
- */
- _cogl_winsys_wait_for_gpu (onscreen);
-
- if (have_counter && can_wait)
- {
- if (glx_onscreen->last_swap_vsync_counter ==
- end_frame_vsync_counter)
- _cogl_winsys_wait_for_vblank (onscreen);
- }
- else if (can_wait)
- _cogl_winsys_wait_for_vblank (onscreen);
- }
-
- glx_renderer->glXSwapBuffers (xlib_renderer->xdpy, drawable);
-
- if (have_counter)
- glx_onscreen->last_swap_vsync_counter =
- _cogl_winsys_get_vsync_counter (context);
-
- set_frame_info_output (onscreen, glx_onscreen->output);
-}
-
-static uint32_t
-_cogl_winsys_onscreen_x11_get_window_xid (CoglOnscreen *onscreen)
-{
- CoglOnscreenGLX *glx_onscreen = cogl_onscreen_get_winsys (onscreen);
-
- return glx_onscreen->xwin;
-}
-
-static void
-_cogl_winsys_onscreen_set_visibility (CoglOnscreen *onscreen,
- gboolean visibility)
-{
- CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen);
- CoglContext *context = cogl_framebuffer_get_context (framebuffer);
- CoglXlibRenderer *xlib_renderer =
- _cogl_xlib_renderer_get_data (context->display->renderer);
- CoglOnscreenGLX *glx_onscreen = cogl_onscreen_get_winsys (onscreen);
-
- if (visibility)
- XMapWindow (xlib_renderer->xdpy, glx_onscreen->xwin);
- else
- XUnmapWindow (xlib_renderer->xdpy, glx_onscreen->xwin);
-}
-
-static void
-_cogl_winsys_onscreen_set_resizable (CoglOnscreen *onscreen,
- gboolean resizable)
-{
- CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen);
- CoglContext *context = cogl_framebuffer_get_context (framebuffer);
- CoglXlibRenderer *xlib_renderer =
- _cogl_xlib_renderer_get_data (context->display->renderer);
- CoglOnscreenGLX *glx_onscreen = cogl_onscreen_get_winsys (onscreen);
-
- XSizeHints *size_hints = XAllocSizeHints ();
-
- if (resizable)
- {
- /* TODO: Add cogl_onscreen_request_minimum_size () */
- size_hints->min_width = 1;
- size_hints->min_height = 1;
-
- size_hints->max_width = INT_MAX;
- size_hints->max_height = INT_MAX;
- }
- else
- {
- int width = cogl_framebuffer_get_width (framebuffer);
- int height = cogl_framebuffer_get_height (framebuffer);
-
- size_hints->min_width = width;
- size_hints->min_height = height;
-
- size_hints->max_width = width;
- size_hints->max_height = height;
- }
-
- XSetWMNormalHints (xlib_renderer->xdpy, glx_onscreen->xwin, size_hints);
-
- XFree (size_hints);
-}
-
static gboolean
get_fbconfig_for_depth (CoglContext *context,
unsigned int depth,
@@ -2485,6 +1439,23 @@ _cogl_winsys_texture_pixmap_x11_get_texture (CoglTexturePixmapX11 *tex_pixmap,
return glx_tex_pixmap->left.glx_tex;
}
+void
+cogl_context_glx_set_current_drawable (CoglContext *context,
+ GLXDrawable drawable)
+{
+ CoglContextGLX *glx_context = context->winsys;
+
+ glx_context->current_drawable = drawable;
+}
+
+GLXDrawable
+cogl_context_glx_get_current_drawable (CoglContext *context)
+{
+ CoglContextGLX *glx_context = context->winsys;
+
+ return glx_context->current_drawable;
+}
+
static CoglWinsysVtable _cogl_winsys_vtable =
{
.id = COGL_WINSYS_ID_GLX,
@@ -2501,18 +1472,18 @@ static CoglWinsysVtable _cogl_winsys_vtable =
.context_init = _cogl_winsys_context_init,
.context_deinit = _cogl_winsys_context_deinit,
.context_get_clock_time = _cogl_winsys_get_clock_time,
- .onscreen_init = _cogl_winsys_onscreen_init,
- .onscreen_deinit = _cogl_winsys_onscreen_deinit,
- .onscreen_bind = _cogl_winsys_onscreen_bind,
+ .onscreen_init = _cogl_winsys_onscreen_glx_init,
+ .onscreen_deinit = _cogl_winsys_onscreen_glx_deinit,
+ .onscreen_bind = _cogl_winsys_onscreen_glx_bind,
.onscreen_swap_buffers_with_damage =
- _cogl_winsys_onscreen_swap_buffers_with_damage,
- .onscreen_swap_region = _cogl_winsys_onscreen_swap_region,
- .onscreen_get_buffer_age = _cogl_winsys_onscreen_get_buffer_age,
+ _cogl_winsys_onscreen_glx_swap_buffers_with_damage,
+ .onscreen_swap_region = _cogl_winsys_onscreen_glx_swap_region,
+ .onscreen_get_buffer_age = _cogl_winsys_onscreen_glx_get_buffer_age,
.onscreen_x11_get_window_xid =
- _cogl_winsys_onscreen_x11_get_window_xid,
- .onscreen_set_visibility = _cogl_winsys_onscreen_set_visibility,
+ _cogl_winsys_onscreen_glx_get_window_xid,
+ .onscreen_set_visibility = _cogl_winsys_onscreen_glx_set_visibility,
.onscreen_set_resizable =
- _cogl_winsys_onscreen_set_resizable,
+ _cogl_winsys_onscreen_glx_set_resizable,
/* X11 tfp support... */
/* XXX: instead of having a rather monolithic winsys vtable we could
diff --git a/src/backends/x11/meta-renderer-x11.c b/src/backends/x11/meta-renderer-x11.c
index 96beb4eb86..82f70f0eae 100644
--- a/src/backends/x11/meta-renderer-x11.c
+++ b/src/backends/x11/meta-renderer-x11.c
@@ -34,12 +34,17 @@
#include "clutter/x11/clutter-x11.h"
#include "cogl/cogl-xlib.h"
#include "cogl/cogl.h"
-#include "cogl/winsys/cogl-winsys-egl-x11-private.h"
-#include "cogl/winsys/cogl-winsys-glx-private.h"
#include "core/boxes-private.h"
#include "meta/meta-backend.h"
#include "meta/util.h"
+#ifdef COGL_HAS_EGL_SUPPORT
+#include "cogl/winsys/cogl-winsys-egl-x11-private.h"
+#endif
+#ifdef COGL_HAS_GLX_SUPPORT
+#include "cogl/winsys/cogl-winsys-glx-private.h"
+#endif
+
G_DEFINE_TYPE (MetaRendererX11, meta_renderer_x11, META_TYPE_RENDERER)
static const CoglWinsysVtable *
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]