compositor: Add support for GL_EXT_x11_sync_object
If GL advertises this extension we'll use it to synchronize X with GL rendering instead of relying on the XSync() behavior with open source drivers. Some driver bugs were uncovered while working on this so if we have had to reboot the ring a few times, something is probably wrong and we're likely to just make things worse by continuing to try. Let's err on the side of caution, disable ourselves and fallback to the XSync() path in the compositor. https://bugzilla.gnome.org/show_bug.cgi?id=728464
This commit is contained in:
parent
352cac3850
commit
39763d4add
@ -319,6 +319,11 @@ if test "x$enable_debug" = "xyes"; then
|
|||||||
CFLAGS="$CFLAGS -g -O"
|
CFLAGS="$CFLAGS -g -O"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
AC_CHECK_DECL([GL_EXT_x11_sync_object],
|
||||||
|
[],
|
||||||
|
[AC_MSG_ERROR([GL_EXT_x11_sync_object definition not found, please update your GL headers])],
|
||||||
|
[#include <GL/glx.h>])
|
||||||
|
|
||||||
#### Warnings (last since -Werror can disturb other tests)
|
#### Warnings (last since -Werror can disturb other tests)
|
||||||
|
|
||||||
# Stay command-line compatible with the gnome-common configure option. Here
|
# Stay command-line compatible with the gnome-common configure option. Here
|
||||||
|
@ -138,6 +138,8 @@ libmutter_la_SOURCES = \
|
|||||||
compositor/meta-surface-actor.h \
|
compositor/meta-surface-actor.h \
|
||||||
compositor/meta-surface-actor-x11.c \
|
compositor/meta-surface-actor-x11.c \
|
||||||
compositor/meta-surface-actor-x11.h \
|
compositor/meta-surface-actor-x11.h \
|
||||||
|
compositor/meta-sync-ring.c \
|
||||||
|
compositor/meta-sync-ring.h \
|
||||||
compositor/meta-texture-rectangle.c \
|
compositor/meta-texture-rectangle.c \
|
||||||
compositor/meta-texture-rectangle.h \
|
compositor/meta-texture-rectangle.h \
|
||||||
compositor/meta-texture-tower.c \
|
compositor/meta-texture-tower.c \
|
||||||
|
@ -45,6 +45,7 @@
|
|||||||
#include <meta/util.h>
|
#include <meta/util.h>
|
||||||
#include "display-private.h"
|
#include "display-private.h"
|
||||||
#include "compositor/compositor-private.h"
|
#include "compositor/compositor-private.h"
|
||||||
|
#include "compositor/meta-sync-ring.h"
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
/* We're a traditional CM running under the host. */
|
/* We're a traditional CM running under the host. */
|
||||||
@ -267,6 +268,8 @@ handle_host_xevent (MetaBackend *backend,
|
|||||||
MetaCompositor *compositor = display->compositor;
|
MetaCompositor *compositor = display->compositor;
|
||||||
if (meta_plugin_manager_xevent_filter (compositor->plugin_mgr, event))
|
if (meta_plugin_manager_xevent_filter (compositor->plugin_mgr, event))
|
||||||
bypass_clutter = TRUE;
|
bypass_clutter = TRUE;
|
||||||
|
if (compositor->have_x11_sync_object)
|
||||||
|
meta_sync_ring_handle_event (event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,7 +15,8 @@ struct _MetaCompositor
|
|||||||
{
|
{
|
||||||
MetaDisplay *display;
|
MetaDisplay *display;
|
||||||
|
|
||||||
guint repaint_func_id;
|
guint pre_paint_func_id;
|
||||||
|
guint post_paint_func_id;
|
||||||
|
|
||||||
gint64 server_time_query_time;
|
gint64 server_time_query_time;
|
||||||
gint64 server_time_offset;
|
gint64 server_time_offset;
|
||||||
@ -40,6 +41,7 @@ struct _MetaCompositor
|
|||||||
MetaPluginManager *plugin_mgr;
|
MetaPluginManager *plugin_mgr;
|
||||||
|
|
||||||
gboolean frame_has_updated_xsurfaces;
|
gboolean frame_has_updated_xsurfaces;
|
||||||
|
gboolean have_x11_sync_object;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Wait 2ms after vblank before starting to draw next frame */
|
/* Wait 2ms after vblank before starting to draw next frame */
|
||||||
|
@ -74,6 +74,7 @@
|
|||||||
#include "frame.h"
|
#include "frame.h"
|
||||||
#include <X11/extensions/shape.h>
|
#include <X11/extensions/shape.h>
|
||||||
#include <X11/extensions/Xcomposite.h>
|
#include <X11/extensions/Xcomposite.h>
|
||||||
|
#include "meta-sync-ring.h"
|
||||||
|
|
||||||
#include "backends/x11/meta-backend-x11.h"
|
#include "backends/x11/meta-backend-x11.h"
|
||||||
|
|
||||||
@ -120,7 +121,11 @@ meta_switch_workspace_completed (MetaCompositor *compositor)
|
|||||||
void
|
void
|
||||||
meta_compositor_destroy (MetaCompositor *compositor)
|
meta_compositor_destroy (MetaCompositor *compositor)
|
||||||
{
|
{
|
||||||
clutter_threads_remove_repaint_func (compositor->repaint_func_id);
|
clutter_threads_remove_repaint_func (compositor->pre_paint_func_id);
|
||||||
|
clutter_threads_remove_repaint_func (compositor->post_paint_func_id);
|
||||||
|
|
||||||
|
if (compositor->have_x11_sync_object)
|
||||||
|
meta_sync_ring_destroy ();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -463,13 +468,11 @@ meta_compositor_manage (MetaCompositor *compositor)
|
|||||||
MetaDisplay *display = compositor->display;
|
MetaDisplay *display = compositor->display;
|
||||||
Display *xdisplay = display->xdisplay;
|
Display *xdisplay = display->xdisplay;
|
||||||
MetaScreen *screen = display->screen;
|
MetaScreen *screen = display->screen;
|
||||||
|
MetaBackend *backend = meta_get_backend ();
|
||||||
|
|
||||||
meta_screen_set_cm_selection (display->screen);
|
meta_screen_set_cm_selection (display->screen);
|
||||||
|
|
||||||
{
|
|
||||||
MetaBackend *backend = meta_get_backend ();
|
|
||||||
compositor->stage = meta_backend_get_stage (backend);
|
compositor->stage = meta_backend_get_stage (backend);
|
||||||
}
|
|
||||||
|
|
||||||
/* We use connect_after() here to accomodate code in GNOME Shell that,
|
/* We use connect_after() here to accomodate code in GNOME Shell that,
|
||||||
* when benchmarking drawing performance, connects to ::after-paint
|
* when benchmarking drawing performance, connects to ::after-paint
|
||||||
@ -505,7 +508,7 @@ meta_compositor_manage (MetaCompositor *compositor)
|
|||||||
|
|
||||||
compositor->output = screen->composite_overlay_window;
|
compositor->output = screen->composite_overlay_window;
|
||||||
|
|
||||||
xwin = meta_backend_x11_get_xwindow (META_BACKEND_X11 (meta_get_backend ()));
|
xwin = meta_backend_x11_get_xwindow (META_BACKEND_X11 (backend));
|
||||||
|
|
||||||
XReparentWindow (xdisplay, xwin, compositor->output, 0, 0);
|
XReparentWindow (xdisplay, xwin, compositor->output, 0, 0);
|
||||||
|
|
||||||
@ -525,6 +528,9 @@ meta_compositor_manage (MetaCompositor *compositor)
|
|||||||
* contents until we show the stage.
|
* contents until we show the stage.
|
||||||
*/
|
*/
|
||||||
XMapWindow (xdisplay, compositor->output);
|
XMapWindow (xdisplay, compositor->output);
|
||||||
|
|
||||||
|
compositor->have_x11_sync_object =
|
||||||
|
meta_sync_ring_init (meta_backend_x11_get_xdisplay (META_BACKEND_X11 (backend)));
|
||||||
}
|
}
|
||||||
|
|
||||||
redirect_windows (display->screen);
|
redirect_windows (display->screen);
|
||||||
@ -1030,11 +1036,12 @@ frame_callback (CoglOnscreen *onscreen,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static gboolean
|
||||||
pre_paint_windows (MetaCompositor *compositor)
|
meta_pre_paint_func (gpointer data)
|
||||||
{
|
{
|
||||||
GList *l;
|
GList *l;
|
||||||
MetaWindowActor *top_window;
|
MetaWindowActor *top_window;
|
||||||
|
MetaCompositor *compositor = data;
|
||||||
|
|
||||||
if (compositor->onscreen == NULL)
|
if (compositor->onscreen == NULL)
|
||||||
{
|
{
|
||||||
@ -1046,7 +1053,7 @@ pre_paint_windows (MetaCompositor *compositor)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (compositor->windows == NULL)
|
if (compositor->windows == NULL)
|
||||||
return;
|
return TRUE;
|
||||||
|
|
||||||
top_window = g_list_last (compositor->windows)->data;
|
top_window = g_list_last (compositor->windows)->data;
|
||||||
|
|
||||||
@ -1063,10 +1070,12 @@ pre_paint_windows (MetaCompositor *compositor)
|
|||||||
{
|
{
|
||||||
/* We need to make sure that any X drawing that happens before
|
/* We need to make sure that any X drawing that happens before
|
||||||
* the XDamageSubtract() for each window above is visible to
|
* the XDamageSubtract() for each window above is visible to
|
||||||
* subsequent GL rendering; the only standardized way to do this
|
* subsequent GL rendering; the standardized way to do this is
|
||||||
* is EXT_x11_sync_object, which isn't yet widely available. For
|
* GL_EXT_X11_sync_object. Since this isn't implemented yet in
|
||||||
* now, we count on details of Xorg and the open source drivers,
|
* mesa, we also have a path that relies on the implementation
|
||||||
* and hope for the best otherwise.
|
* of the open source drivers.
|
||||||
|
*
|
||||||
|
* Anything else, we just hope for the best.
|
||||||
*
|
*
|
||||||
* Xorg and open source driver specifics:
|
* Xorg and open source driver specifics:
|
||||||
*
|
*
|
||||||
@ -1081,17 +1090,28 @@ pre_paint_windows (MetaCompositor *compositor)
|
|||||||
* round trip request at this point is sufficient to flush the
|
* round trip request at this point is sufficient to flush the
|
||||||
* GLX buffers.
|
* GLX buffers.
|
||||||
*/
|
*/
|
||||||
|
if (compositor->have_x11_sync_object)
|
||||||
|
compositor->have_x11_sync_object = meta_sync_ring_insert_wait ();
|
||||||
|
else
|
||||||
XSync (compositor->display->xdisplay, False);
|
XSync (compositor->display->xdisplay, False);
|
||||||
|
|
||||||
compositor->frame_has_updated_xsurfaces = FALSE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
meta_repaint_func (gpointer data)
|
meta_post_paint_func (gpointer data)
|
||||||
{
|
{
|
||||||
MetaCompositor *compositor = data;
|
MetaCompositor *compositor = data;
|
||||||
pre_paint_windows (compositor);
|
|
||||||
|
if (compositor->frame_has_updated_xsurfaces)
|
||||||
|
{
|
||||||
|
if (compositor->have_x11_sync_object)
|
||||||
|
compositor->have_x11_sync_object = meta_sync_ring_after_frame ();
|
||||||
|
|
||||||
|
compositor->frame_has_updated_xsurfaces = FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1126,10 +1146,16 @@ meta_compositor_new (MetaDisplay *display)
|
|||||||
G_CALLBACK (on_shadow_factory_changed),
|
G_CALLBACK (on_shadow_factory_changed),
|
||||||
compositor);
|
compositor);
|
||||||
|
|
||||||
compositor->repaint_func_id = clutter_threads_add_repaint_func (meta_repaint_func,
|
compositor->pre_paint_func_id =
|
||||||
|
clutter_threads_add_repaint_func_full (CLUTTER_REPAINT_FLAGS_PRE_PAINT,
|
||||||
|
meta_pre_paint_func,
|
||||||
|
compositor,
|
||||||
|
NULL);
|
||||||
|
compositor->post_paint_func_id =
|
||||||
|
clutter_threads_add_repaint_func_full (CLUTTER_REPAINT_FLAGS_POST_PAINT,
|
||||||
|
meta_post_paint_func,
|
||||||
compositor,
|
compositor,
|
||||||
NULL);
|
NULL);
|
||||||
|
|
||||||
return compositor;
|
return compositor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
566
src/compositor/meta-sync-ring.c
Normal file
566
src/compositor/meta-sync-ring.c
Normal file
@ -0,0 +1,566 @@
|
|||||||
|
/*
|
||||||
|
* This is based on an original C++ implementation for compiz that
|
||||||
|
* carries the following copyright notice:
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Copyright © 2011 NVIDIA Corporation
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, distribute, and sell this software
|
||||||
|
* and its documentation for any purpose is hereby granted without
|
||||||
|
* fee, provided that the above copyright notice appear in all copies
|
||||||
|
* and that both that copyright notice and this permission notice
|
||||||
|
* appear in supporting documentation, and that the name of NVIDIA
|
||||||
|
* Corporation not be used in advertising or publicity pertaining to
|
||||||
|
* distribution of the software without specific, written prior
|
||||||
|
* permission. NVIDIA Corporation makes no representations about the
|
||||||
|
* suitability of this software for any purpose. It is provided "as
|
||||||
|
* is" without express or implied warranty.
|
||||||
|
*
|
||||||
|
* NVIDIA CORPORATION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
|
||||||
|
* SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||||
|
* FITNESS, IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY
|
||||||
|
* SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
|
||||||
|
* AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
|
||||||
|
* SOFTWARE.
|
||||||
|
*
|
||||||
|
* Authors: James Jones <jajones@nvidia.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <GL/gl.h>
|
||||||
|
#include <GL/glx.h>
|
||||||
|
#include <X11/extensions/sync.h>
|
||||||
|
|
||||||
|
#include <cogl/cogl.h>
|
||||||
|
#include <clutter/clutter.h>
|
||||||
|
|
||||||
|
#include <meta/util.h>
|
||||||
|
|
||||||
|
#include "meta-sync-ring.h"
|
||||||
|
|
||||||
|
/* Theory of operation:
|
||||||
|
*
|
||||||
|
* We use a ring of NUM_SYNCS fence objects. On each frame we advance
|
||||||
|
* to the next fence in the ring. For each fence we do:
|
||||||
|
*
|
||||||
|
* 1. fence is XSyncTriggerFence()'d and glWaitSync()'d
|
||||||
|
* 2. NUM_SYNCS / 2 frames later, fence should be triggered
|
||||||
|
* 3. fence is XSyncResetFence()'d
|
||||||
|
* 4. NUM_SYNCS / 2 frames later, fence should be reset
|
||||||
|
* 5. go back to 1 and re-use fence
|
||||||
|
*
|
||||||
|
* glClientWaitSync() and XAlarms are used in steps 2 and 4,
|
||||||
|
* respectively, to double-check the expectections.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define NUM_SYNCS 10
|
||||||
|
#define MAX_SYNC_WAIT_TIME (1 * 1000 * 1000 * 1000) /* one sec */
|
||||||
|
#define MAX_REBOOT_ATTEMPTS 2
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
META_SYNC_STATE_READY,
|
||||||
|
META_SYNC_STATE_WAITING,
|
||||||
|
META_SYNC_STATE_DONE,
|
||||||
|
META_SYNC_STATE_RESET_PENDING,
|
||||||
|
} MetaSyncState;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
Display *xdisplay;
|
||||||
|
|
||||||
|
XSyncFence xfence;
|
||||||
|
GLsync glsync;
|
||||||
|
|
||||||
|
XSyncCounter xcounter;
|
||||||
|
XSyncAlarm xalarm;
|
||||||
|
XSyncValue next_counter_value;
|
||||||
|
|
||||||
|
MetaSyncState state;
|
||||||
|
} MetaSync;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
Display *xdisplay;
|
||||||
|
int xsync_event_base;
|
||||||
|
int xsync_error_base;
|
||||||
|
|
||||||
|
GHashTable *alarm_to_sync;
|
||||||
|
|
||||||
|
MetaSync *syncs_array[NUM_SYNCS];
|
||||||
|
guint current_sync_idx;
|
||||||
|
MetaSync *current_sync;
|
||||||
|
guint warmup_syncs;
|
||||||
|
|
||||||
|
guint reboots;
|
||||||
|
} MetaSyncRing;
|
||||||
|
|
||||||
|
static MetaSyncRing meta_sync_ring = { 0 };
|
||||||
|
|
||||||
|
static XSyncValue SYNC_VALUE_ZERO;
|
||||||
|
static XSyncValue SYNC_VALUE_ONE;
|
||||||
|
|
||||||
|
static const char* (*meta_gl_get_string) (GLenum name);
|
||||||
|
static void (*meta_gl_get_integerv) (GLenum pname,
|
||||||
|
GLint *params);
|
||||||
|
static const char* (*meta_gl_get_stringi) (GLenum name,
|
||||||
|
GLuint index);
|
||||||
|
static void (*meta_gl_delete_sync) (GLsync sync);
|
||||||
|
static GLenum (*meta_gl_client_wait_sync) (GLsync sync,
|
||||||
|
GLbitfield flags,
|
||||||
|
GLuint64 timeout);
|
||||||
|
static void (*meta_gl_wait_sync) (GLsync sync,
|
||||||
|
GLbitfield flags,
|
||||||
|
GLuint64 timeout);
|
||||||
|
static GLsync (*meta_gl_import_sync) (GLenum external_sync_type,
|
||||||
|
GLintptr external_sync,
|
||||||
|
GLbitfield flags);
|
||||||
|
|
||||||
|
static MetaSyncRing *
|
||||||
|
meta_sync_ring_get (void)
|
||||||
|
{
|
||||||
|
if (meta_sync_ring.reboots > MAX_REBOOT_ATTEMPTS)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return &meta_sync_ring;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
load_gl_symbol (const char *name,
|
||||||
|
void **func)
|
||||||
|
{
|
||||||
|
*func = cogl_get_proc_address (name);
|
||||||
|
if (!*func)
|
||||||
|
{
|
||||||
|
meta_verbose ("MetaSyncRing: failed to resolve required GL symbol \"%s\"\n", name);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
check_gl_extensions (void)
|
||||||
|
{
|
||||||
|
ClutterBackend *backend;
|
||||||
|
CoglContext *cogl_context;
|
||||||
|
CoglDisplay *cogl_display;
|
||||||
|
CoglRenderer *cogl_renderer;
|
||||||
|
|
||||||
|
backend = clutter_get_default_backend ();
|
||||||
|
cogl_context = clutter_backend_get_cogl_context (backend);
|
||||||
|
cogl_display = cogl_context_get_display (cogl_context);
|
||||||
|
cogl_renderer = cogl_display_get_renderer (cogl_display);
|
||||||
|
|
||||||
|
switch (cogl_renderer_get_driver (cogl_renderer))
|
||||||
|
{
|
||||||
|
case COGL_DRIVER_GL3:
|
||||||
|
{
|
||||||
|
int num_extensions, i;
|
||||||
|
gboolean arb_sync = FALSE;
|
||||||
|
gboolean x11_sync_object = FALSE;
|
||||||
|
|
||||||
|
meta_gl_get_integerv (GL_NUM_EXTENSIONS, &num_extensions);
|
||||||
|
|
||||||
|
for (i = 0; i < num_extensions; ++i)
|
||||||
|
{
|
||||||
|
const char *ext = meta_gl_get_stringi (GL_EXTENSIONS, i);
|
||||||
|
|
||||||
|
if (g_strcmp0 ("GL_ARB_sync", ext) == 0)
|
||||||
|
arb_sync = TRUE;
|
||||||
|
else if (g_strcmp0 ("GL_EXT_x11_sync_object", ext) == 0)
|
||||||
|
x11_sync_object = TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return arb_sync && x11_sync_object;
|
||||||
|
}
|
||||||
|
case COGL_DRIVER_GL:
|
||||||
|
{
|
||||||
|
const char *extensions = meta_gl_get_string (GL_EXTENSIONS);
|
||||||
|
return (extensions != NULL &&
|
||||||
|
strstr (extensions, "GL_ARB_sync") != NULL &&
|
||||||
|
strstr (extensions, "GL_EXT_x11_sync_object") != NULL);
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
load_required_symbols (void)
|
||||||
|
{
|
||||||
|
static gboolean success = FALSE;
|
||||||
|
|
||||||
|
if (success)
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
/* We don't link against libGL directly because cogl may want to
|
||||||
|
* use something else. This assumes that cogl has been initialized
|
||||||
|
* and dynamically loaded libGL at this point.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (!load_gl_symbol ("glGetString", (void **) &meta_gl_get_string))
|
||||||
|
goto out;
|
||||||
|
if (!load_gl_symbol ("glGetIntegerv", (void **) &meta_gl_get_integerv))
|
||||||
|
goto out;
|
||||||
|
if (!load_gl_symbol ("glGetStringi", (void **) &meta_gl_get_stringi))
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
if (!check_gl_extensions ())
|
||||||
|
{
|
||||||
|
meta_verbose ("MetaSyncRing: couldn't find required GL extensions\n");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!load_gl_symbol ("glDeleteSync", (void **) &meta_gl_delete_sync))
|
||||||
|
goto out;
|
||||||
|
if (!load_gl_symbol ("glClientWaitSync", (void **) &meta_gl_client_wait_sync))
|
||||||
|
goto out;
|
||||||
|
if (!load_gl_symbol ("glWaitSync", (void **) &meta_gl_wait_sync))
|
||||||
|
goto out;
|
||||||
|
if (!load_gl_symbol ("glImportSyncEXT", (void **) &meta_gl_import_sync))
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
success = TRUE;
|
||||||
|
out:
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
meta_sync_insert (MetaSync *self)
|
||||||
|
{
|
||||||
|
g_return_if_fail (self->state == META_SYNC_STATE_READY);
|
||||||
|
|
||||||
|
XSyncTriggerFence (self->xdisplay, self->xfence);
|
||||||
|
XFlush (self->xdisplay);
|
||||||
|
|
||||||
|
meta_gl_wait_sync (self->glsync, 0, GL_TIMEOUT_IGNORED);
|
||||||
|
|
||||||
|
self->state = META_SYNC_STATE_WAITING;
|
||||||
|
}
|
||||||
|
|
||||||
|
static GLenum
|
||||||
|
meta_sync_check_update_finished (MetaSync *self,
|
||||||
|
GLuint64 timeout)
|
||||||
|
{
|
||||||
|
GLenum status = GL_WAIT_FAILED;
|
||||||
|
|
||||||
|
switch (self->state)
|
||||||
|
{
|
||||||
|
case META_SYNC_STATE_DONE:
|
||||||
|
status = GL_ALREADY_SIGNALED;
|
||||||
|
break;
|
||||||
|
case META_SYNC_STATE_WAITING:
|
||||||
|
status = meta_gl_client_wait_sync (self->glsync, 0, timeout);
|
||||||
|
if (status == GL_ALREADY_SIGNALED || status == GL_CONDITION_SATISFIED)
|
||||||
|
self->state = META_SYNC_STATE_DONE;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_warn_if_fail (status != GL_WAIT_FAILED);
|
||||||
|
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
meta_sync_reset (MetaSync *self)
|
||||||
|
{
|
||||||
|
XSyncAlarmAttributes attrs;
|
||||||
|
int overflow;
|
||||||
|
|
||||||
|
g_return_if_fail (self->state == META_SYNC_STATE_DONE);
|
||||||
|
|
||||||
|
XSyncResetFence (self->xdisplay, self->xfence);
|
||||||
|
|
||||||
|
attrs.trigger.wait_value = self->next_counter_value;
|
||||||
|
|
||||||
|
XSyncChangeAlarm (self->xdisplay, self->xalarm, XSyncCAValue, &attrs);
|
||||||
|
XSyncSetCounter (self->xdisplay, self->xcounter, self->next_counter_value);
|
||||||
|
|
||||||
|
XSyncValueAdd (&self->next_counter_value,
|
||||||
|
self->next_counter_value,
|
||||||
|
SYNC_VALUE_ONE,
|
||||||
|
&overflow);
|
||||||
|
|
||||||
|
self->state = META_SYNC_STATE_RESET_PENDING;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
meta_sync_handle_event (MetaSync *self,
|
||||||
|
XSyncAlarmNotifyEvent *event)
|
||||||
|
{
|
||||||
|
g_return_if_fail (event->alarm == self->xalarm);
|
||||||
|
g_return_if_fail (self->state == META_SYNC_STATE_RESET_PENDING);
|
||||||
|
|
||||||
|
self->state = META_SYNC_STATE_READY;
|
||||||
|
}
|
||||||
|
|
||||||
|
static MetaSync *
|
||||||
|
meta_sync_new (Display *xdisplay)
|
||||||
|
{
|
||||||
|
MetaSync *self;
|
||||||
|
XSyncAlarmAttributes attrs;
|
||||||
|
|
||||||
|
self = g_malloc0 (sizeof (MetaSync));
|
||||||
|
|
||||||
|
self->xdisplay = xdisplay;
|
||||||
|
|
||||||
|
self->xfence = XSyncCreateFence (xdisplay, DefaultRootWindow (xdisplay), FALSE);
|
||||||
|
self->glsync = meta_gl_import_sync (GL_SYNC_X11_FENCE_EXT, self->xfence, 0);
|
||||||
|
|
||||||
|
self->xcounter = XSyncCreateCounter (xdisplay, SYNC_VALUE_ZERO);
|
||||||
|
|
||||||
|
attrs.trigger.counter = self->xcounter;
|
||||||
|
attrs.trigger.value_type = XSyncAbsolute;
|
||||||
|
attrs.trigger.wait_value = SYNC_VALUE_ONE;
|
||||||
|
attrs.trigger.test_type = XSyncPositiveTransition;
|
||||||
|
attrs.events = TRUE;
|
||||||
|
self->xalarm = XSyncCreateAlarm (xdisplay,
|
||||||
|
XSyncCACounter |
|
||||||
|
XSyncCAValueType |
|
||||||
|
XSyncCAValue |
|
||||||
|
XSyncCATestType |
|
||||||
|
XSyncCAEvents,
|
||||||
|
&attrs);
|
||||||
|
|
||||||
|
XSyncIntToValue (&self->next_counter_value, 1);
|
||||||
|
|
||||||
|
self->state = META_SYNC_STATE_READY;
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Bool
|
||||||
|
alarm_event_predicate (Display *dpy,
|
||||||
|
XEvent *event,
|
||||||
|
XPointer data)
|
||||||
|
{
|
||||||
|
MetaSyncRing *ring = meta_sync_ring_get ();
|
||||||
|
|
||||||
|
if (!ring)
|
||||||
|
return False;
|
||||||
|
|
||||||
|
if (event->type == ring->xsync_event_base + XSyncAlarmNotify)
|
||||||
|
{
|
||||||
|
if (((MetaSync *) data)->xalarm == ((XSyncAlarmNotifyEvent *) event)->alarm)
|
||||||
|
return True;
|
||||||
|
}
|
||||||
|
return False;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
meta_sync_free (MetaSync *self)
|
||||||
|
{
|
||||||
|
/* When our assumptions don't hold, something has gone wrong but we
|
||||||
|
* don't know what, so we reboot the ring. While doing that, we
|
||||||
|
* trigger fences before deleting them to try to get ourselves out
|
||||||
|
* of a potentially stuck GPU state.
|
||||||
|
*/
|
||||||
|
switch (self->state)
|
||||||
|
{
|
||||||
|
case META_SYNC_STATE_WAITING:
|
||||||
|
case META_SYNC_STATE_DONE:
|
||||||
|
/* nothing to do */
|
||||||
|
break;
|
||||||
|
case META_SYNC_STATE_RESET_PENDING:
|
||||||
|
{
|
||||||
|
XEvent event;
|
||||||
|
XIfEvent (self->xdisplay, &event, alarm_event_predicate, (XPointer) self);
|
||||||
|
meta_sync_handle_event (self, (XSyncAlarmNotifyEvent *) &event);
|
||||||
|
}
|
||||||
|
/* fall through */
|
||||||
|
case META_SYNC_STATE_READY:
|
||||||
|
XSyncTriggerFence (self->xdisplay, self->xfence);
|
||||||
|
XFlush (self->xdisplay);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
meta_gl_delete_sync (self->glsync);
|
||||||
|
XSyncDestroyFence (self->xdisplay, self->xfence);
|
||||||
|
XSyncDestroyCounter (self->xdisplay, self->xcounter);
|
||||||
|
XSyncDestroyAlarm (self->xdisplay, self->xalarm);
|
||||||
|
|
||||||
|
g_free (self);
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
meta_sync_ring_init (Display *xdisplay)
|
||||||
|
{
|
||||||
|
gint major, minor;
|
||||||
|
guint i;
|
||||||
|
MetaSyncRing *ring = meta_sync_ring_get ();
|
||||||
|
|
||||||
|
if (!ring)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
g_return_val_if_fail (xdisplay != NULL, FALSE);
|
||||||
|
g_return_val_if_fail (ring->xdisplay == NULL, FALSE);
|
||||||
|
|
||||||
|
if (!load_required_symbols ())
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
if (!XSyncQueryExtension (xdisplay, &ring->xsync_event_base, &ring->xsync_error_base) ||
|
||||||
|
!XSyncInitialize (xdisplay, &major, &minor))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
XSyncIntToValue (&SYNC_VALUE_ZERO, 0);
|
||||||
|
XSyncIntToValue (&SYNC_VALUE_ONE, 1);
|
||||||
|
|
||||||
|
ring->xdisplay = xdisplay;
|
||||||
|
|
||||||
|
ring->alarm_to_sync = g_hash_table_new (NULL, NULL);
|
||||||
|
|
||||||
|
for (i = 0; i < NUM_SYNCS; ++i)
|
||||||
|
{
|
||||||
|
MetaSync *sync = meta_sync_new (ring->xdisplay);
|
||||||
|
ring->syncs_array[i] = sync;
|
||||||
|
g_hash_table_replace (ring->alarm_to_sync, (gpointer) sync->xalarm, sync);
|
||||||
|
}
|
||||||
|
|
||||||
|
ring->current_sync_idx = 0;
|
||||||
|
ring->current_sync = ring->syncs_array[0];
|
||||||
|
ring->warmup_syncs = 0;
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
meta_sync_ring_destroy (void)
|
||||||
|
{
|
||||||
|
guint i;
|
||||||
|
MetaSyncRing *ring = meta_sync_ring_get ();
|
||||||
|
|
||||||
|
if (!ring)
|
||||||
|
return;
|
||||||
|
|
||||||
|
g_return_if_fail (ring->xdisplay != NULL);
|
||||||
|
|
||||||
|
ring->current_sync_idx = 0;
|
||||||
|
ring->current_sync = NULL;
|
||||||
|
ring->warmup_syncs = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < NUM_SYNCS; ++i)
|
||||||
|
meta_sync_free (ring->syncs_array[i]);
|
||||||
|
|
||||||
|
g_hash_table_destroy (ring->alarm_to_sync);
|
||||||
|
|
||||||
|
ring->xsync_event_base = 0;
|
||||||
|
ring->xsync_error_base = 0;
|
||||||
|
ring->xdisplay = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
meta_sync_ring_reboot (Display *xdisplay)
|
||||||
|
{
|
||||||
|
MetaSyncRing *ring = meta_sync_ring_get ();
|
||||||
|
|
||||||
|
if (!ring)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
meta_sync_ring_destroy ();
|
||||||
|
|
||||||
|
ring->reboots += 1;
|
||||||
|
|
||||||
|
if (!meta_sync_ring_get ())
|
||||||
|
{
|
||||||
|
meta_warning ("MetaSyncRing: Too many reboots -- disabling\n");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return meta_sync_ring_init (xdisplay);
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
meta_sync_ring_after_frame (void)
|
||||||
|
{
|
||||||
|
MetaSyncRing *ring = meta_sync_ring_get ();
|
||||||
|
|
||||||
|
if (!ring)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
g_return_if_fail (ring->xdisplay != NULL);
|
||||||
|
|
||||||
|
if (ring->warmup_syncs >= NUM_SYNCS / 2)
|
||||||
|
{
|
||||||
|
guint reset_sync_idx = (ring->current_sync_idx + NUM_SYNCS - (NUM_SYNCS / 2)) % NUM_SYNCS;
|
||||||
|
MetaSync *sync_to_reset = ring->syncs_array[reset_sync_idx];
|
||||||
|
|
||||||
|
GLenum status = meta_sync_check_update_finished (sync_to_reset, 0);
|
||||||
|
if (status == GL_TIMEOUT_EXPIRED)
|
||||||
|
{
|
||||||
|
meta_warning ("MetaSyncRing: We should never wait for a sync -- add more syncs?\n");
|
||||||
|
status = meta_sync_check_update_finished (sync_to_reset, MAX_SYNC_WAIT_TIME);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status != GL_ALREADY_SIGNALED && status != GL_CONDITION_SATISFIED)
|
||||||
|
{
|
||||||
|
meta_warning ("MetaSyncRing: Timed out waiting for sync object.\n");
|
||||||
|
return meta_sync_ring_reboot (ring->xdisplay);
|
||||||
|
}
|
||||||
|
|
||||||
|
meta_sync_reset (sync_to_reset);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ring->warmup_syncs += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ring->current_sync_idx += 1;
|
||||||
|
ring->current_sync_idx %= NUM_SYNCS;
|
||||||
|
|
||||||
|
ring->current_sync = ring->syncs_array[ring->current_sync_idx];
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
meta_sync_ring_insert_wait (void)
|
||||||
|
{
|
||||||
|
MetaSyncRing *ring = meta_sync_ring_get ();
|
||||||
|
|
||||||
|
if (!ring)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
g_return_if_fail (ring->xdisplay != NULL);
|
||||||
|
|
||||||
|
if (ring->current_sync->state != META_SYNC_STATE_READY)
|
||||||
|
{
|
||||||
|
meta_warning ("MetaSyncRing: Sync object is not ready -- were events handled properly?\n");
|
||||||
|
if (!meta_sync_ring_reboot (ring->xdisplay))
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
meta_sync_insert (ring->current_sync);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
meta_sync_ring_handle_event (XEvent *xevent)
|
||||||
|
{
|
||||||
|
XSyncAlarmNotifyEvent *event;
|
||||||
|
MetaSync *sync;
|
||||||
|
MetaSyncRing *ring = meta_sync_ring_get ();
|
||||||
|
|
||||||
|
if (!ring)
|
||||||
|
return;
|
||||||
|
|
||||||
|
g_return_if_fail (ring->xdisplay != NULL);
|
||||||
|
|
||||||
|
if (xevent->type != (ring->xsync_event_base + XSyncAlarmNotify))
|
||||||
|
return;
|
||||||
|
|
||||||
|
event = (XSyncAlarmNotifyEvent *) xevent;
|
||||||
|
|
||||||
|
sync = g_hash_table_lookup (ring->alarm_to_sync, (gpointer) event->alarm);
|
||||||
|
if (sync)
|
||||||
|
meta_sync_handle_event (sync, event);
|
||||||
|
}
|
14
src/compositor/meta-sync-ring.h
Normal file
14
src/compositor/meta-sync-ring.h
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
#ifndef _META_SYNC_RING_H_
|
||||||
|
#define _META_SYNC_RING_H_
|
||||||
|
|
||||||
|
#include <glib.h>
|
||||||
|
|
||||||
|
#include <X11/Xlib.h>
|
||||||
|
|
||||||
|
gboolean meta_sync_ring_init (Display *dpy);
|
||||||
|
void meta_sync_ring_destroy (void);
|
||||||
|
gboolean meta_sync_ring_after_frame (void);
|
||||||
|
gboolean meta_sync_ring_insert_wait (void);
|
||||||
|
void meta_sync_ring_handle_event (XEvent *event);
|
||||||
|
|
||||||
|
#endif /* _META_SYNC_RING_H_ */
|
Loading…
Reference in New Issue
Block a user