Compare commits
5 Commits
3.30.2
...
wip/xtogls
Author | SHA1 | Date | |
---|---|---|---|
![]() |
3ac563307e | ||
![]() |
24956fadcc | ||
![]() |
fc5de438b5 | ||
![]() |
94cee3ef09 | ||
![]() |
1e6b042778 |
@@ -324,6 +324,11 @@ fi
|
|||||||
|
|
||||||
GTK_DOC_CHECK([1.15], [--flavour no-tmpl])
|
GTK_DOC_CHECK([1.15], [--flavour no-tmpl])
|
||||||
|
|
||||||
|
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
|
||||||
|
@@ -118,6 +118,8 @@ libmutter_la_SOURCES = \
|
|||||||
compositor/meta-surface-actor-wayland.h \
|
compositor/meta-surface-actor-wayland.h \
|
||||||
compositor/meta-stage.h \
|
compositor/meta-stage.h \
|
||||||
compositor/meta-stage.c \
|
compositor/meta-stage.c \
|
||||||
|
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 \
|
||||||
|
@@ -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;
|
||||||
@@ -38,6 +39,9 @@ struct _MetaCompositor
|
|||||||
gint switch_workspace_in_progress;
|
gint switch_workspace_in_progress;
|
||||||
|
|
||||||
MetaPluginManager *plugin_mgr;
|
MetaPluginManager *plugin_mgr;
|
||||||
|
|
||||||
|
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 */
|
||||||
|
@@ -79,6 +79,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/meta-backend.h"
|
#include "backends/meta-backend.h"
|
||||||
#include "backends/x11/meta-backend-x11.h"
|
#include "backends/x11/meta-backend-x11.h"
|
||||||
@@ -136,7 +137,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
|
||||||
@@ -146,6 +151,8 @@ process_damage (MetaCompositor *compositor,
|
|||||||
{
|
{
|
||||||
MetaWindowActor *window_actor = META_WINDOW_ACTOR (meta_window_get_compositor_private (window));
|
MetaWindowActor *window_actor = META_WINDOW_ACTOR (meta_window_get_compositor_private (window));
|
||||||
meta_window_actor_process_x11_damage (window_actor, event);
|
meta_window_actor_process_x11_damage (window_actor, event);
|
||||||
|
|
||||||
|
compositor->frame_has_updated_xsurfaces = TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Window
|
static Window
|
||||||
@@ -569,6 +576,8 @@ 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 (display);
|
||||||
}
|
}
|
||||||
|
|
||||||
redirect_windows (display->screen);
|
redirect_windows (display->screen);
|
||||||
@@ -780,6 +789,10 @@ meta_compositor_process_event (MetaCompositor *compositor,
|
|||||||
if (!meta_is_wayland_compositor () && event->type == MapNotify)
|
if (!meta_is_wayland_compositor () && event->type == MapNotify)
|
||||||
clutter_x11_handle_event (event);
|
clutter_x11_handle_event (event);
|
||||||
|
|
||||||
|
if (compositor->have_x11_sync_object &&
|
||||||
|
event->type == (compositor->display->xsync_event_base + XSyncAlarmNotify))
|
||||||
|
meta_sync_ring_handle_event ((XSyncAlarmNotifyEvent *) event);
|
||||||
|
|
||||||
/* The above handling is basically just "observing" the events, so we return
|
/* The above handling is basically just "observing" the events, so we return
|
||||||
* FALSE to indicate that the event should not be filtered out; if we have
|
* FALSE to indicate that the event should not be filtered out; if we have
|
||||||
* GTK+ windows in the same process, GTK+ needs the ConfigureNotify event, for example.
|
* GTK+ windows in the same process, GTK+ needs the ConfigureNotify event, for example.
|
||||||
@@ -1125,11 +1138,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)
|
||||||
{
|
{
|
||||||
@@ -1141,7 +1155,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;
|
||||||
|
|
||||||
@@ -1153,13 +1167,53 @@ pre_paint_windows (MetaCompositor *compositor)
|
|||||||
|
|
||||||
for (l = compositor->windows; l; l = l->next)
|
for (l = compositor->windows; l; l = l->next)
|
||||||
meta_window_actor_pre_paint (l->data);
|
meta_window_actor_pre_paint (l->data);
|
||||||
|
|
||||||
|
if (compositor->frame_has_updated_xsurfaces)
|
||||||
|
{
|
||||||
|
/* We need to make sure that any X drawing that happens before
|
||||||
|
* the XDamageSubtract() for each window above is visible to
|
||||||
|
* subsequent GL rendering; the standardized way to do this is
|
||||||
|
* GL_EXT_X11_sync_object. Since this isn't implemented yet in
|
||||||
|
* mesa, we also have a path that relies on the implementation
|
||||||
|
* of the open source drivers.
|
||||||
|
*
|
||||||
|
* Anything else, we just hope for the best.
|
||||||
|
*
|
||||||
|
* Xorg and open source driver specifics:
|
||||||
|
*
|
||||||
|
* The X server makes sure to flush drawing to the kernel before
|
||||||
|
* sending out damage events, but since we use
|
||||||
|
* DamageReportBoundingBox there may be drawing between the last
|
||||||
|
* damage event and the XDamageSubtract() that needs to be
|
||||||
|
* flushed as well.
|
||||||
|
*
|
||||||
|
* Xorg always makes sure that drawing is flushed to the kernel
|
||||||
|
* before writing events or responses to the client, so any
|
||||||
|
* round trip request at this point is sufficient to flush the
|
||||||
|
* GLX buffers.
|
||||||
|
*/
|
||||||
|
if (compositor->have_x11_sync_object)
|
||||||
|
compositor->have_x11_sync_object = meta_sync_ring_insert_wait ();
|
||||||
|
else
|
||||||
|
XSync (compositor->display->xdisplay, 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1198,10 +1252,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 =
|
||||||
compositor,
|
clutter_threads_add_repaint_func_full (CLUTTER_REPAINT_FLAGS_PRE_PAINT,
|
||||||
NULL);
|
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,
|
||||||
|
NULL);
|
||||||
return compositor;
|
return compositor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -238,26 +238,6 @@ meta_surface_actor_x11_pre_paint (MetaSurfaceActor *actor)
|
|||||||
XDamageSubtract (xdisplay, priv->damage, None, None);
|
XDamageSubtract (xdisplay, priv->damage, None, None);
|
||||||
meta_error_trap_pop (display);
|
meta_error_trap_pop (display);
|
||||||
|
|
||||||
/* We need to make sure that any X drawing that happens before the
|
|
||||||
* XDamageSubtract() above is visible to subsequent GL rendering;
|
|
||||||
* the only standardized way to do this is EXT_x11_sync_object,
|
|
||||||
* which isn't yet widely available. For now, we count on details
|
|
||||||
* of Xorg and the open source drivers, and hope for the best
|
|
||||||
* otherwise.
|
|
||||||
*
|
|
||||||
* Xorg and open source driver specifics:
|
|
||||||
*
|
|
||||||
* The X server makes sure to flush drawing to the kernel before
|
|
||||||
* sending out damage events, but since we use DamageReportBoundingBox
|
|
||||||
* there may be drawing between the last damage event and the
|
|
||||||
* XDamageSubtract() that needs to be flushed as well.
|
|
||||||
*
|
|
||||||
* Xorg always makes sure that drawing is flushed to the kernel
|
|
||||||
* before writing events or responses to the client, so any round trip
|
|
||||||
* request at this point is sufficient to flush the GLX buffers.
|
|
||||||
*/
|
|
||||||
XSync (xdisplay, False);
|
|
||||||
|
|
||||||
priv->received_damage = FALSE;
|
priv->received_damage = FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
574
src/compositor/meta-sync-ring.c
Normal file
574
src/compositor/meta-sync-ring.c
Normal file
@@ -0,0 +1,574 @@
|
|||||||
|
/*
|
||||||
|
* 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 <cogl/cogl.h>
|
||||||
|
|
||||||
|
#include <meta/display.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
|
||||||
|
{
|
||||||
|
MetaDisplay *display;
|
||||||
|
|
||||||
|
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 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 GLenum (*meta_gl_get_error) (void);
|
||||||
|
static GLboolean (*meta_gl_is_sync) (GLsync sync);
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
int num_extensions, i;
|
||||||
|
gboolean 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)
|
||||||
|
sync = TRUE;
|
||||||
|
else if (g_strcmp0 ("GL_EXT_x11_sync_object", ext) == 0)
|
||||||
|
x11_sync_object = TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return sync && x11_sync_object;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 ("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;
|
||||||
|
|
||||||
|
if (!load_gl_symbol ("glGetError", (void **) &meta_gl_get_error))
|
||||||
|
goto out;
|
||||||
|
if (!load_gl_symbol ("glIsSync", (void **) &meta_gl_is_sync))
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
success = TRUE;
|
||||||
|
out:
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
print_gl_error (void)
|
||||||
|
{
|
||||||
|
GLenum e;
|
||||||
|
switch (e = meta_gl_get_error ())
|
||||||
|
{
|
||||||
|
case GL_NO_ERROR:
|
||||||
|
meta_warning ("GL_NO_ERROR\n");
|
||||||
|
break;
|
||||||
|
case GL_INVALID_ENUM:
|
||||||
|
meta_warning ("GL_INVALID_ENUM\n");
|
||||||
|
break;
|
||||||
|
case GL_INVALID_VALUE:
|
||||||
|
meta_warning ("GL_INVALID_VALUE\n");
|
||||||
|
break;
|
||||||
|
case GL_INVALID_OPERATION:
|
||||||
|
meta_warning ("GL_INVALID_OPERATION\n");
|
||||||
|
break;
|
||||||
|
case GL_INVALID_FRAMEBUFFER_OPERATION:
|
||||||
|
meta_warning ("GL_INVALID_FRAMEBUFFER_OPERATION\n");
|
||||||
|
break;
|
||||||
|
case GL_OUT_OF_MEMORY:
|
||||||
|
meta_warning ("GL_OUT_OF_MEMORY\n");
|
||||||
|
break;
|
||||||
|
case GL_STACK_OVERFLOW:
|
||||||
|
meta_warning ("GL_STACK_OVERFLOW\n");
|
||||||
|
break;
|
||||||
|
case GL_STACK_UNDERFLOW:
|
||||||
|
meta_warning ("GL_STACK_UNDERFLOW\n");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
meta_warning ("GL error 0x%x\n", e);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
meta_warning ("created X fence 0x%x\n", self->xfence);
|
||||||
|
self->glsync = meta_gl_import_sync (GL_SYNC_X11_FENCE_EXT, self->xfence, 0);
|
||||||
|
print_gl_error ();
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
int xsync_event_base;
|
||||||
|
MetaSyncRing *ring = meta_sync_ring_get ();
|
||||||
|
|
||||||
|
if (!ring)
|
||||||
|
return False;
|
||||||
|
|
||||||
|
xsync_event_base = meta_display_get_sync_event_base (ring->display);
|
||||||
|
|
||||||
|
if (event->type == 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 (MetaDisplay *display)
|
||||||
|
{
|
||||||
|
guint i;
|
||||||
|
MetaSyncRing *ring = meta_sync_ring_get ();
|
||||||
|
|
||||||
|
if (!ring)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
g_return_val_if_fail (display != NULL, FALSE);
|
||||||
|
g_return_val_if_fail (ring->display == NULL, FALSE);
|
||||||
|
|
||||||
|
if (!load_required_symbols ())
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
if (!meta_display_has_sync (display))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
print_gl_error ();
|
||||||
|
|
||||||
|
XSyncIntToValue (&SYNC_VALUE_ZERO, 0);
|
||||||
|
XSyncIntToValue (&SYNC_VALUE_ONE, 1);
|
||||||
|
|
||||||
|
ring->display = display;
|
||||||
|
|
||||||
|
ring->alarm_to_sync = g_hash_table_new (NULL, NULL);
|
||||||
|
|
||||||
|
for (i = 0; i < NUM_SYNCS; ++i)
|
||||||
|
{
|
||||||
|
MetaSync *sync = meta_sync_new (meta_display_get_xdisplay (display));
|
||||||
|
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->display != 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->display = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
meta_sync_ring_reboot (MetaDisplay *display)
|
||||||
|
{
|
||||||
|
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 (display);
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
meta_sync_ring_after_frame (void)
|
||||||
|
{
|
||||||
|
MetaSyncRing *ring = meta_sync_ring_get ();
|
||||||
|
|
||||||
|
if (!ring)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
g_return_if_fail (ring->display != 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->display);
|
||||||
|
}
|
||||||
|
|
||||||
|
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->display != 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->display))
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
meta_sync_insert (ring->current_sync);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
meta_sync_ring_handle_event (XSyncAlarmNotifyEvent *event)
|
||||||
|
{
|
||||||
|
MetaSync *sync;
|
||||||
|
MetaSyncRing *ring = meta_sync_ring_get ();
|
||||||
|
|
||||||
|
if (!ring)
|
||||||
|
return;
|
||||||
|
|
||||||
|
g_return_if_fail (ring->display != NULL);
|
||||||
|
|
||||||
|
sync = g_hash_table_lookup (ring->alarm_to_sync, (gpointer) event->alarm);
|
||||||
|
if (sync)
|
||||||
|
meta_sync_handle_event (sync, event);
|
||||||
|
}
|
17
src/compositor/meta-sync-ring.h
Normal file
17
src/compositor/meta-sync-ring.h
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
#ifndef _META_SYNC_RING_H_
|
||||||
|
#define _META_SYNC_RING_H_
|
||||||
|
|
||||||
|
#include <glib.h>
|
||||||
|
|
||||||
|
#include <X11/Xlib.h>
|
||||||
|
#include <X11/extensions/sync.h>
|
||||||
|
|
||||||
|
#include <meta/display.h>
|
||||||
|
|
||||||
|
gboolean meta_sync_ring_init (MetaDisplay *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 (XSyncAlarmNotifyEvent *event);
|
||||||
|
|
||||||
|
#endif /* _META_SYNC_RING_H_ */
|
@@ -3120,6 +3120,12 @@ meta_display_has_shape (MetaDisplay *display)
|
|||||||
return META_DISPLAY_HAS_SHAPE (display);
|
return META_DISPLAY_HAS_SHAPE (display);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
meta_display_has_sync (MetaDisplay *display)
|
||||||
|
{
|
||||||
|
return META_DISPLAY_HAS_XSYNC (display);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* meta_display_get_focus_window:
|
* meta_display_get_focus_window:
|
||||||
* @display: a #MetaDisplay
|
* @display: a #MetaDisplay
|
||||||
@@ -3148,6 +3154,12 @@ meta_display_get_shape_event_base (MetaDisplay *display)
|
|||||||
return display->shape_event_base;
|
return display->shape_event_base;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
meta_display_get_sync_event_base (MetaDisplay *display)
|
||||||
|
{
|
||||||
|
return display->xsync_event_base;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* meta_display_clear_mouse_mode:
|
* meta_display_clear_mouse_mode:
|
||||||
* @display: a #MetaDisplay
|
* @display: a #MetaDisplay
|
||||||
|
@@ -75,6 +75,7 @@ Display *meta_display_get_xdisplay (MetaDisplay *display);
|
|||||||
MetaCompositor *meta_display_get_compositor (MetaDisplay *display);
|
MetaCompositor *meta_display_get_compositor (MetaDisplay *display);
|
||||||
|
|
||||||
gboolean meta_display_has_shape (MetaDisplay *display);
|
gboolean meta_display_has_shape (MetaDisplay *display);
|
||||||
|
gboolean meta_display_has_sync (MetaDisplay *display);
|
||||||
|
|
||||||
MetaWindow *meta_display_get_focus_window (MetaDisplay *display);
|
MetaWindow *meta_display_get_focus_window (MetaDisplay *display);
|
||||||
|
|
||||||
@@ -83,6 +84,7 @@ gboolean meta_display_xwindow_is_a_no_focus_window (MetaDisplay *display,
|
|||||||
|
|
||||||
int meta_display_get_damage_event_base (MetaDisplay *display);
|
int meta_display_get_damage_event_base (MetaDisplay *display);
|
||||||
int meta_display_get_shape_event_base (MetaDisplay *display);
|
int meta_display_get_shape_event_base (MetaDisplay *display);
|
||||||
|
int meta_display_get_sync_event_base (MetaDisplay *display);
|
||||||
|
|
||||||
gboolean meta_display_xserver_time_is_before (MetaDisplay *display,
|
gboolean meta_display_xserver_time_is_before (MetaDisplay *display,
|
||||||
guint32 time1,
|
guint32 time1,
|
||||||
|
Reference in New Issue
Block a user