Compare commits
	
		
			5 Commits
		
	
	
		
			gbsneto/co
			...
			wip/xtogls
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					3ac563307e | ||
| 
						 | 
					24956fadcc | ||
| 
						 | 
					fc5de438b5 | ||
| 
						 | 
					94cee3ef09 | ||
| 
						 | 
					1e6b042778 | 
@@ -324,6 +324,11 @@ fi
 | 
			
		||||
 | 
			
		||||
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)
 | 
			
		||||
 | 
			
		||||
# 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-stage.h			\
 | 
			
		||||
	compositor/meta-stage.c			\
 | 
			
		||||
	compositor/meta-sync-ring.c		\
 | 
			
		||||
	compositor/meta-sync-ring.h		\
 | 
			
		||||
	compositor/meta-texture-rectangle.c	\
 | 
			
		||||
	compositor/meta-texture-rectangle.h	\
 | 
			
		||||
	compositor/meta-texture-tower.c		\
 | 
			
		||||
 
 | 
			
		||||
@@ -15,7 +15,8 @@ struct _MetaCompositor
 | 
			
		||||
{
 | 
			
		||||
  MetaDisplay    *display;
 | 
			
		||||
 | 
			
		||||
  guint           repaint_func_id;
 | 
			
		||||
  guint           pre_paint_func_id;
 | 
			
		||||
  guint           post_paint_func_id;
 | 
			
		||||
 | 
			
		||||
  gint64          server_time_query_time;
 | 
			
		||||
  gint64          server_time_offset;
 | 
			
		||||
@@ -38,6 +39,9 @@ struct _MetaCompositor
 | 
			
		||||
  gint                   switch_workspace_in_progress;
 | 
			
		||||
 | 
			
		||||
  MetaPluginManager *plugin_mgr;
 | 
			
		||||
 | 
			
		||||
  gboolean frame_has_updated_xsurfaces;
 | 
			
		||||
  gboolean have_x11_sync_object;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Wait 2ms after vblank before starting to draw next frame */
 | 
			
		||||
 
 | 
			
		||||
@@ -79,6 +79,7 @@
 | 
			
		||||
#include "frame.h"
 | 
			
		||||
#include <X11/extensions/shape.h>
 | 
			
		||||
#include <X11/extensions/Xcomposite.h>
 | 
			
		||||
#include "meta-sync-ring.h"
 | 
			
		||||
 | 
			
		||||
#include "backends/meta-backend.h"
 | 
			
		||||
#include "backends/x11/meta-backend-x11.h"
 | 
			
		||||
@@ -136,7 +137,11 @@ meta_switch_workspace_completed (MetaCompositor *compositor)
 | 
			
		||||
void
 | 
			
		||||
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
 | 
			
		||||
@@ -146,6 +151,8 @@ process_damage (MetaCompositor     *compositor,
 | 
			
		||||
{
 | 
			
		||||
  MetaWindowActor *window_actor = META_WINDOW_ACTOR (meta_window_get_compositor_private (window));
 | 
			
		||||
  meta_window_actor_process_x11_damage (window_actor, event);
 | 
			
		||||
 | 
			
		||||
  compositor->frame_has_updated_xsurfaces = TRUE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Window
 | 
			
		||||
@@ -569,6 +576,8 @@ meta_compositor_manage (MetaCompositor *compositor)
 | 
			
		||||
       * contents until we show the stage.
 | 
			
		||||
       */
 | 
			
		||||
      XMapWindow (xdisplay, compositor->output);
 | 
			
		||||
 | 
			
		||||
      compositor->have_x11_sync_object = meta_sync_ring_init (display);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  redirect_windows (display->screen);
 | 
			
		||||
@@ -780,6 +789,10 @@ meta_compositor_process_event (MetaCompositor *compositor,
 | 
			
		||||
  if (!meta_is_wayland_compositor () && event->type == MapNotify)
 | 
			
		||||
    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
 | 
			
		||||
   * 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.
 | 
			
		||||
@@ -1125,11 +1138,12 @@ frame_callback (CoglOnscreen  *onscreen,
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
pre_paint_windows (MetaCompositor *compositor)
 | 
			
		||||
static gboolean
 | 
			
		||||
meta_pre_paint_func (gpointer data)
 | 
			
		||||
{
 | 
			
		||||
  GList *l;
 | 
			
		||||
  MetaWindowActor *top_window;
 | 
			
		||||
  MetaCompositor *compositor = data;
 | 
			
		||||
 | 
			
		||||
  if (compositor->onscreen == NULL)
 | 
			
		||||
    {
 | 
			
		||||
@@ -1141,7 +1155,7 @@ pre_paint_windows (MetaCompositor *compositor)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  if (compositor->windows == NULL)
 | 
			
		||||
    return;
 | 
			
		||||
    return TRUE;
 | 
			
		||||
 | 
			
		||||
  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)
 | 
			
		||||
    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
 | 
			
		||||
meta_repaint_func (gpointer data)
 | 
			
		||||
meta_post_paint_func (gpointer 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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -1198,10 +1252,16 @@ meta_compositor_new (MetaDisplay *display)
 | 
			
		||||
                    G_CALLBACK (on_shadow_factory_changed),
 | 
			
		||||
                    compositor);
 | 
			
		||||
 | 
			
		||||
  compositor->repaint_func_id = clutter_threads_add_repaint_func (meta_repaint_func,
 | 
			
		||||
                                                                  compositor,
 | 
			
		||||
                                                                  NULL);
 | 
			
		||||
 | 
			
		||||
  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,
 | 
			
		||||
                                           NULL);
 | 
			
		||||
  return compositor;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -238,26 +238,6 @@ meta_surface_actor_x11_pre_paint (MetaSurfaceActor *actor)
 | 
			
		||||
      XDamageSubtract (xdisplay, priv->damage, None, None);
 | 
			
		||||
      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;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
gboolean
 | 
			
		||||
meta_display_has_sync (MetaDisplay *display)
 | 
			
		||||
{
 | 
			
		||||
  return META_DISPLAY_HAS_XSYNC (display);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * meta_display_get_focus_window:
 | 
			
		||||
 * @display: a #MetaDisplay
 | 
			
		||||
@@ -3148,6 +3154,12 @@ meta_display_get_shape_event_base (MetaDisplay *display)
 | 
			
		||||
  return display->shape_event_base;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
meta_display_get_sync_event_base (MetaDisplay *display)
 | 
			
		||||
{
 | 
			
		||||
  return display->xsync_event_base;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * meta_display_clear_mouse_mode:
 | 
			
		||||
 * @display: a #MetaDisplay
 | 
			
		||||
 
 | 
			
		||||
@@ -75,6 +75,7 @@ Display *meta_display_get_xdisplay (MetaDisplay *display);
 | 
			
		||||
MetaCompositor *meta_display_get_compositor (MetaDisplay *display);
 | 
			
		||||
 | 
			
		||||
gboolean meta_display_has_shape (MetaDisplay *display);
 | 
			
		||||
gboolean meta_display_has_sync (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_shape_event_base (MetaDisplay *display);
 | 
			
		||||
int meta_display_get_sync_event_base (MetaDisplay *display);
 | 
			
		||||
 | 
			
		||||
gboolean meta_display_xserver_time_is_before (MetaDisplay *display,
 | 
			
		||||
                                              guint32      time1,
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user