Browse Source

Merge tag 'upstream/2.8.1'

Upstream version 2.8.1
jessie-backports
Maximiliano Curia 8 years ago
parent
commit
946028d5d3
  1. 11
      configure.ac
  2. 12
      src/Makefile.am
  3. 3
      src/compositor/cogl-utils.h
  4. 25
      src/compositor/compositor-private.h
  5. 271
      src/compositor/compositor.c
  6. 2
      src/compositor/meta-background-actor.c
  7. 2
      src/compositor/meta-shadow-factory.c
  8. 3
      src/compositor/meta-shaped-texture.c
  9. 579
      src/compositor/meta-sync-ring.c
  10. 14
      src/compositor/meta-sync-ring.h
  11. 3
      src/compositor/meta-texture-rectangle.c
  12. 11
      src/compositor/meta-window-actor-private.h
  13. 318
      src/compositor/meta-window-actor.c
  14. 11
      src/core/constraints.c
  15. 14
      src/core/display-private.h
  16. 124
      src/core/display.c
  17. 24
      src/core/keybindings.c
  18. 4
      src/core/main.c
  19. 10
      src/core/place.c
  20. 10
      src/core/screen-private.h
  21. 49
      src/core/screen.c
  22. 26
      src/core/window-private.h
  23. 62
      src/core/window-props.c
  24. 480
      src/core/window.c
  25. 34
      src/core/xprops.c
  26. 8
      src/core/xprops.h
  27. 2
      src/meta/atomnames.h
  28. 10
      src/meta/compositor.h
  29. 1
      src/meta/screen.h
  30. 2
      src/meta/window.h
  31. 2
      src/org.cinnamon.muffin.gschema.xml.in

11
configure.ac

@ -1,7 +1,7 @@
AC_PREREQ(2.50)
m4_define([muffin_major_version], [2])
m4_define([muffin_minor_version], [6])
m4_define([muffin_minor_version], [8])
m4_define([muffin_micro_version], [1])
m4_define([muffin_version],
@ -85,8 +85,8 @@ MUFFIN_PC_MODULES="
cairo >= 1.10.0
cinnamon-desktop >= 2.4.0
xcomposite >= 0.3 xfixes xrender xdamage
$CLUTTER_PACKAGE >= 1.10.0
cogl-1.0 >= 1.9.6
$CLUTTER_PACKAGE >= 1.14.3
cogl-1.0 >= 1.13.3
gl
"
@ -421,6 +421,11 @@ GNOME_DOC_INIT([0.8.0])
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

12
src/Makefile.am

@ -6,6 +6,8 @@ lib_LTLIBRARIES = libmuffin.la
SUBDIRS=wm-tester tools compositor/plugins
AM_CPPFLAGS= \
-DCLUTTER_ENABLE_EXPERIMENTAL_API \
-DCOGL_ENABLE_EXPERIMENTAL_API \
$(MUFFIN_CFLAGS) \
-I$(srcdir) \
-I$(srcdir)/core \
@ -52,6 +54,8 @@ libmuffin_la_SOURCES = \
compositor/meta-shadow-factory.c \
compositor/meta-shadow-factory-private.h \
compositor/meta-shaped-texture.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 \
@ -264,8 +268,8 @@ gsettings_SCHEMAS = org.cinnamon.muffin.gschema.xml
IMAGES=stock_maximize.png stock_minimize.png stock_delete.png
VARIABLES=stock_maximize_data $(srcdir)/stock_maximize.png \
stock_minimize_data $(srcdir)/stock_minimize.png \
stock_delete_data $(srcdir)/stock_delete.png
stock_minimize_data $(srcdir)/stock_minimize.png \
stock_delete_data $(srcdir)/stock_delete.png
BUILT_SOURCES = inlinepixbufs.h
CLEANFILES = \
@ -303,7 +307,7 @@ muffin-enum-types.h: stamp-muffin-enum-types.h Makefile
stamp-muffin-enum-types.h: $(libmuffininclude_base_headers) muffin-enum-types.h.in
$(AM_V_GEN) ( cd $(srcdir) && \
$(GLIB_MKENUMS) \
--template muffin-enum-types.h.in \
--template muffin-enum-types.h.in \
$(libmuffininclude_base_headers) ) >> xgen-teth && \
(cmp -s xgen-teth muffin-enum-types.h || cp xgen-teth muffin-enum-types.h) && \
rm -f xgen-teth && \
@ -312,7 +316,7 @@ stamp-muffin-enum-types.h: $(libmuffininclude_base_headers) muffin-enum-types.h.
muffin-enum-types.c: stamp-muffin-enum-types.h muffin-enum-types.c.in
$(AM_V_GEN) ( cd $(srcdir) && \
$(GLIB_MKENUMS) \
--template muffin-enum-types.c.in \
--template muffin-enum-types.c.in \
$(libmuffininclude_base_headers) ) >> xgen-tetc && \
cp xgen-tetc muffin-enum-types.c && \
rm -f xgen-tetc

3
src/compositor/cogl-utils.h

@ -23,9 +23,6 @@
#ifndef __META_COGL_UTILS_H__
#define __META_COGL_UTILS_H__
#define COGL_ENABLE_EXPERIMENTAL_API
#define CLUTTER_ENABLE_EXPERIMENTAL_API
#include <cogl/cogl.h>
#include <clutter/clutter.h>

25
src/compositor/compositor-private.h

@ -20,15 +20,23 @@ struct _MetaCompositor
Atom atom_x_root_pixmap;
Atom atom_x_set_root;
Atom atom_net_wm_window_opacity;
guint repaint_func_id;
guint pre_paint_func_id;
guint post_paint_func_id;
ClutterActor *shadow_src;
MetaPlugin *modal_plugin;
gboolean show_redraw : 1;
gboolean debug : 1;
gboolean no_mipmaps : 1;
gint64 server_time_query_time;
gint64 server_time_offset;
guint server_time_is_monotonic_time : 1;
guint show_redraw : 1;
guint debug : 1;
guint no_mipmaps : 1;
gboolean frame_has_updated_xsurfaces;
gboolean have_x11_sync_object;
};
struct _MetaCompScreen
@ -42,6 +50,9 @@ struct _MetaCompScreen
GHashTable *windows_by_xid;
Window output;
CoglOnscreen *onscreen;
CoglFrameClosure *frame_closure;
/* Used for unredirecting fullscreen windows */
guint disable_unredirect_count;
MetaWindowActor *unredirected_window;
@ -54,6 +65,9 @@ struct _MetaCompScreen
MetaPluginManager *plugin_mgr;
};
/* Wait 2ms after vblank before starting to draw next frame */
#define META_SYNC_DELAY 2
void meta_switch_workspace_completed (MetaScreen *screen);
gboolean meta_begin_modal_for_plugin (MetaScreen *screen,
@ -66,6 +80,9 @@ void meta_end_modal_for_plugin (MetaScreen *screen,
MetaPlugin *plugin,
guint32 timestamp);
gint64 meta_compositor_monotonic_time_to_server_time (MetaDisplay *display,
gint64 monotonic_time);
void meta_check_end_modal (MetaScreen *screen);
#endif /* META_COMPOSITOR_PRIVATE_H */

271
src/compositor/compositor.c

@ -76,6 +76,7 @@
#include "display-private.h" /* for meta_display_lookup_x_window() */
#include <X11/extensions/shape.h>
#include <X11/extensions/Xcomposite.h>
#include "meta-sync-ring.h"
/* #define DEBUG_TRACE g_print */
#define DEBUG_TRACE(X)
@ -132,7 +133,11 @@ meta_switch_workspace_completed (MetaScreen *screen)
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
@ -163,6 +168,8 @@ process_damage (MetaCompositor *compositor,
return;
meta_window_actor_process_damage (window_actor, event);
compositor->frame_has_updated_xsurfaces = TRUE;
}
static void
@ -526,11 +533,22 @@ meta_check_end_modal (MetaScreen *screen)
meta_plugin_get_screen (compositor->modal_plugin) == screen)
{
meta_end_modal_for_plugin (screen,
compositor->modal_plugin,
CurrentTime);
compositor->modal_plugin,
CurrentTime);
}
}
static void
after_stage_paint (ClutterStage *stage,
gpointer data)
{
MetaCompScreen *info = (MetaCompScreen*) data;
GList *l;
for (l = info->windows; l; l = l->next)
meta_window_actor_post_paint (l->data);
}
void
meta_compositor_manage_screen (MetaCompositor *compositor,
MetaScreen *screen)
@ -603,6 +621,18 @@ meta_compositor_manage_screen (MetaCompositor *compositor,
info->stage = clutter_stage_new ();
#if CLUTTER_CHECK_VERSION (1, 19, 5) /* Unstable API change */
g_signal_connect_after (CLUTTER_STAGE (info->stage), "after-paint",
G_CALLBACK (after_stage_paint), info);
#else
clutter_stage_set_paint_callback (CLUTTER_STAGE (info->stage),
after_stage_paint,
info,
NULL);
#endif
clutter_stage_set_sync_delay (CLUTTER_STAGE (info->stage), META_SYNC_DELAY);
meta_screen_get_size (screen, &width, &height);
clutter_actor_realize (info->stage);
@ -676,6 +706,8 @@ meta_compositor_manage_screen (MetaCompositor *compositor,
clutter_actor_show (info->overlay_group);
clutter_actor_show (info->stage);
compositor->have_x11_sync_object = meta_sync_ring_init (xdisplay);
}
void
@ -776,10 +808,33 @@ meta_compositor_remove_window (MetaCompositor *compositor,
}
void
meta_compositor_set_updates (MetaCompositor *compositor,
MetaWindow *window,
gboolean updates)
meta_compositor_set_updates_frozen (MetaCompositor *compositor,
MetaWindow *window,
gboolean updates_frozen)
{
MetaWindowActor *window_actor;
DEBUG_TRACE ("meta_compositor_set_updates_frozen\n");
window_actor = META_WINDOW_ACTOR (meta_window_get_compositor_private (window));
if (!window_actor)
return;
meta_window_actor_set_updates_frozen (window_actor, updates_frozen);
}
void
meta_compositor_queue_frame_drawn (MetaCompositor *compositor,
MetaWindow *window,
gboolean no_delay_frame)
{
MetaWindowActor *window_actor;
DEBUG_TRACE ("meta_compositor_queue_frame_drawn\n");
window_actor = META_WINDOW_ACTOR (meta_window_get_compositor_private (window));
if (!window_actor)
return;
meta_window_actor_queue_frame_drawn (window_actor, no_delay_frame);
}
static gboolean
@ -839,10 +894,10 @@ meta_compositor_process_event (MetaCompositor *compositor,
info = meta_screen_get_compositor_data (screen);
if (meta_plugin_manager_xevent_filter (info->plugin_mgr, event))
{
DEBUG_TRACE ("meta_compositor_process_event (filtered,window==NULL)\n");
return TRUE;
}
{
DEBUG_TRACE ("meta_compositor_process_event (filtered,window==NULL)\n");
return TRUE;
}
}
else
{
@ -851,20 +906,20 @@ meta_compositor_process_event (MetaCompositor *compositor,
l = meta_display_get_screens (compositor->display);
while (l)
{
MetaScreen *screen = l->data;
MetaCompScreen *info;
{
MetaScreen *screen = l->data;
MetaCompScreen *info;
info = meta_screen_get_compositor_data (screen);
info = meta_screen_get_compositor_data (screen);
if (meta_plugin_manager_xevent_filter (info->plugin_mgr, event))
{
DEBUG_TRACE ("meta_compositor_process_event (filtered,window==NULL)\n");
return TRUE;
}
if (meta_plugin_manager_xevent_filter (info->plugin_mgr, event))
{
DEBUG_TRACE ("meta_compositor_process_event (filtered,window==NULL)\n");
return TRUE;
}
l = l->next;
}
l = l->next;
}
}
switch (event->type)
@ -891,6 +946,9 @@ meta_compositor_process_event (MetaCompositor *compositor,
break;
}
if (compositor->have_x11_sync_object)
meta_sync_ring_handle_event (event);
/* Clutter needs to know about MapNotify events otherwise it will
think the stage is invisible */
if (event->type == MapNotify)
@ -1197,7 +1255,8 @@ meta_compositor_window_unmapped (MetaCompositor *compositor,
void
meta_compositor_sync_window_geometry (MetaCompositor *compositor,
MetaWindow *window)
MetaWindow *window,
gboolean did_placement)
{
MetaWindowActor *window_actor = META_WINDOW_ACTOR (meta_window_get_compositor_private (window));
MetaScreen *screen = meta_window_get_screen (window);
@ -1209,7 +1268,7 @@ meta_compositor_sync_window_geometry (MetaCompositor *compositor,
if (!window_actor)
return;
meta_window_actor_sync_actor_position (window_actor);
meta_window_actor_sync_actor_geometry (window_actor, did_placement);
}
void
@ -1239,14 +1298,68 @@ meta_compositor_sync_screen_size (MetaCompositor *compositor,
}
static void
pre_paint_windows (MetaCompScreen *info)
frame_callback (CoglOnscreen *onscreen,
CoglFrameEvent event,
CoglFrameInfo *frame_info,
void *user_data)
{
MetaCompScreen *info = user_data;
GList *l;
if (event == COGL_FRAME_EVENT_COMPLETE)
{
gint64 presentation_time_cogl = cogl_frame_info_get_presentation_time (frame_info);
gint64 presentation_time;
if (presentation_time_cogl != 0)
{
/* Cogl reports presentation in terms of its own clock, which is
* guaranteed to be in nanoseconds but with no specified base. The
* normal case with the open source GPU drivers on Linux 3.8 and
* newer is that the base of cogl_get_clock_time() is that of
* clock_gettime(CLOCK_MONOTONIC), so the same as g_get_monotonic_time),
* but there's no exposure of that through the API. clock_gettime()
* is fairly fast, so calling it twice and subtracting to get a
* nearly-zero number is acceptable, if a litle ugly.
*/
CoglContext *context = cogl_framebuffer_get_context (COGL_FRAMEBUFFER (onscreen));
gint64 current_cogl_time = cogl_get_clock_time (context);
gint64 current_monotonic_time = g_get_monotonic_time ();
presentation_time =
current_monotonic_time + (presentation_time_cogl - current_cogl_time) / 1000;
}
else
{
presentation_time = 0;
}
for (l = info->windows; l; l = l->next)
meta_window_actor_frame_complete (l->data, frame_info, presentation_time);
}
}
static gboolean
meta_pre_paint_func (gpointer data)
{
GList *l;
MetaCompositor *compositor = data;
GSList *screens = meta_display_get_screens (compositor->display);
MetaCompScreen *info = meta_screen_get_compositor_data (screens->data);
MetaWindowActor *top_window;
MetaWindowActor *expected_unredirected_window = NULL;
if (info->onscreen == NULL)
{
info->onscreen = COGL_ONSCREEN (cogl_get_draw_framebuffer ());
info->frame_closure = cogl_onscreen_add_frame_callback (info->onscreen,
frame_callback,
info,
NULL);
}
if (info->windows == NULL)
return;
return TRUE;;
top_window = g_list_last (info->windows)->data;
@ -1275,23 +1388,51 @@ pre_paint_windows (MetaCompScreen *info)
for (l = info->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;
GSList *screens = meta_display_get_screens (compositor->display);
GSList *l;
for (l = screens; l; l = l->next)
if (compositor->frame_has_updated_xsurfaces)
{
MetaScreen *screen = l->data;
MetaCompScreen *info = meta_screen_get_compositor_data (screen);
if (!info)
continue;
if (compositor->have_x11_sync_object)
compositor->have_x11_sync_object = meta_sync_ring_after_frame ();
pre_paint_windows (info);
compositor->frame_has_updated_xsurfaces = FALSE;
}
return TRUE;
@ -1356,10 +1497,16 @@ meta_compositor_new (MetaDisplay *display)
compositor->atom_x_set_root = atoms[1];
compositor->atom_net_wm_window_opacity = atoms[2];
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;
}
@ -1511,3 +1658,51 @@ meta_compositor_hide_hud_preview (MetaCompositor *compositor,
meta_plugin_manager_hide_hud_preview (info->plugin_mgr);
}
/**
* meta_compositor_monotonic_time_to_server_time:
* @display: a #MetaDisplay
* @monotonic_time: time in the units of g_get_monotonic_time()
*
* _NET_WM_FRAME_DRAWN and _NET_WM_FRAME_TIMINGS messages represent time
* as a "high resolution server time" - this is the server time interpolated
* to microsecond resolution. The advantage of this time representation
* is that if X server is running on the same computer as a client, and
* the Xserver uses 'clock_gettime(CLOCK_MONOTONIC, ...)' for the server
* time, the client can detect this, and all such clients will share a
* a time representation with high accuracy. If there is not a common
* time source, then the time synchronization will be less accurate.
*/
gint64
meta_compositor_monotonic_time_to_server_time (MetaDisplay *display,
gint64 monotonic_time)
{
MetaCompositor *compositor = display->compositor;
if (compositor->server_time_query_time == 0 ||
(!compositor->server_time_is_monotonic_time &&
monotonic_time > compositor->server_time_query_time + 10*1000*1000)) /* 10 seconds */
{
guint32 server_time = meta_display_get_current_time_roundtrip (display);
gint64 server_time_usec = (gint64)server_time * 1000;
gint64 current_monotonic_time = g_get_monotonic_time ();
compositor->server_time_query_time = current_monotonic_time;
/* If the server time is within a second of the monotonic time,
* we assume that they are identical. This seems like a big margin,
* but we want to be as robust as possible even if the system
* is under load and our processing of the server response is
* delayed.
*/
if (server_time_usec > current_monotonic_time - 1000*1000 &&
server_time_usec < current_monotonic_time + 1000*1000)
compositor->server_time_is_monotonic_time = TRUE;
compositor->server_time_offset = server_time_usec - current_monotonic_time;
}
if (compositor->server_time_is_monotonic_time)
return monotonic_time;
else
return monotonic_time + compositor->server_time_offset;
}

2
src/compositor/meta-background-actor.c

@ -31,10 +31,8 @@
#include <config.h>
#define COGL_ENABLE_EXPERIMENTAL_API
#include <cogl/cogl-texture-pixmap-x11.h>
#define CLUTTER_ENABLE_EXPERIMENTAL_API
#include <clutter/clutter.h>
#include <X11/Xatom.h>

2
src/compositor/meta-shadow-factory.c

@ -137,7 +137,7 @@ static MetaShadowClassInfo default_shadow_classes[] = {
{ "popup-menu", { 1, -1, 0, 1, 128 }, { 1, -1, 0, 1, 128 } },
{ "dropdown-menu", { 1, 10, 0, 1, 128 }, { 1, 10, 0, 1, 128 } },
{ "attached", { 2, 50, 0, 1, 255 }, { 1, 50, 0, 1, 128 } }
{ "attached", { 6, -1, 0, 1, 255 }, { 3, -1, 0, 3, 128 } }
};
G_DEFINE_TYPE (MetaShadowFactory, meta_shadow_factory, G_TYPE_OBJECT);

3
src/compositor/meta-shaped-texture.c

@ -31,9 +31,6 @@
#include <config.h>
#define CLUTTER_ENABLE_EXPERIMENTAL_API
#define COGL_ENABLE_EXPERIMENTAL_API
#include <meta/meta-shaped-texture.h>
#include "meta-texture-tower.h"
#include "meta-texture-rectangle.h"

579
src/compositor/meta-sync-ring.c

@ -0,0 +1,579 @@
/*
* 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 gl_x11_sync;
GLsync gpu_fence;
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 GLsync (*meta_gl_fence_sync) (GLenum condition,
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;
if (!load_gl_symbol ("glFenceSync", (void **) &meta_gl_fence_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->gl_x11_sync, 0, GL_TIMEOUT_IGNORED);
self->gpu_fence = meta_gl_fence_sync (GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
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->gpu_fence, 0, timeout);
if (status == GL_ALREADY_SIGNALED || status == GL_CONDITION_SATISFIED)
{
self->state = META_SYNC_STATE_DONE;
meta_gl_delete_sync (self->gpu_fence);
self->gpu_fence = 0;
}
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->gl_x11_sync = meta_gl_import_sync (GL_SYNC_X11_FENCE_EXT, self->xfence, 0);
self->gpu_fence = 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:
meta_gl_delete_sync (self->gpu_fence);
break;
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->gl_x11_sync);
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_val_if_fail (ring->xdisplay != NULL, FALSE);
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_val_if_fail (ring->xdisplay != NULL, FALSE);
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

@ -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_ */

3
src/compositor/meta-texture-rectangle.c

@ -25,9 +25,6 @@
#include <config.h>
#define CLUTTER_ENABLE_EXPERIMENTAL_API
#define COGL_ENABLE_EXPERIMENTAL_API
#include <clutter/clutter.h>
#include "meta-texture-rectangle.h"

11
src/compositor/meta-window-actor-private.h

@ -32,6 +32,10 @@ void meta_window_actor_process_damage (MetaWindowActor *self,
XDamageNotifyEvent *event);
void meta_window_actor_pre_paint (MetaWindowActor *self);
void meta_window_actor_post_paint (MetaWindowActor *self);
void meta_window_actor_frame_complete (MetaWindowActor *self,
CoglFrameInfo *frame_info,
gint64 presentation_time);
void meta_window_actor_invalidate_shadow (MetaWindowActor *self);
@ -43,12 +47,17 @@ void meta_window_actor_get_shape_bounds (MetaWindowActor *self,
cairo_rectangle_int_t *bounds);
gboolean meta_window_actor_effect_in_progress (MetaWindowActor *self);
void meta_window_actor_sync_actor_position (MetaWindowActor *self);
void meta_window_actor_sync_actor_geometry (MetaWindowActor *self,
gboolean did_placement);
void meta_window_actor_sync_visibility (MetaWindowActor *self);
void meta_window_actor_update_shape (MetaWindowActor *self);
void meta_window_actor_update_opacity (MetaWindowActor *self);
void meta_window_actor_mapped (MetaWindowActor *self);
void meta_window_actor_unmapped (MetaWindowActor *self);
void meta_window_actor_set_updates_frozen (MetaWindowActor *self,
gboolean updates_frozen);
void meta_window_actor_queue_frame_drawn (MetaWindowActor *self,
gboolean no_delay_frame);
cairo_region_t *meta_window_actor_get_obscured_region (MetaWindowActor *self);

318
src/compositor/meta-window-actor.c

@ -16,7 +16,6 @@
#include <X11/extensions/Xrender.h>
#include <clutter/x11/clutter-x11.h>
#define COGL_ENABLE_EXPERIMENTAL_API
#include <cogl/cogl-texture-pixmap-x11.h>
#include <gdk/gdk.h> /* for gdk_rectangle_union() */
@ -101,6 +100,9 @@ struct _MetaWindowActorPrivate
gint map_in_progress;
gint destroy_in_progress;
/* List of FrameData for recent frames */
GList *frames;
guint visible : 1;
guint mapped : 1;
guint argb32 : 1;
@ -109,12 +111,18 @@ struct _MetaWindowActorPrivate
guint needs_damage_all : 1;
guint received_damage : 1;
guint repaint_scheduled : 1;
/* If set, the client needs to be sent a _NET_WM_FRAME_DRAWN
* client message using the most recent frame in ->frames */
guint needs_frame_drawn : 1;
guint needs_pixmap : 1;
guint needs_reshape : 1;
guint recompute_focused_shadow : 1;
guint recompute_unfocused_shadow : 1;
guint size_changed : 1;
guint updates_frozen : 1;
guint needs_destroy : 1;
@ -131,6 +139,15 @@ struct _MetaWindowActorPrivate
guint has_desat_effect : 1;
};
typedef struct _FrameData FrameData;
struct _FrameData
{
int64_t frame_counter;
guint64 sync_request_serial;
gint64 frame_drawn_time;
};
enum
{
PROP_META_WINDOW = 1,
@ -170,10 +187,18 @@ static void meta_window_actor_clear_shape_region (MetaWindowActor *self);
static void meta_window_actor_clear_bounding_region (MetaWindowActor *self);
static void meta_window_actor_clear_shadow_clip (MetaWindowActor *self);
static void meta_window_actor_handle_updates (MetaWindowActor *self);
static void check_needs_reshape (MetaWindowActor *self);
G_DEFINE_TYPE (MetaWindowActor, meta_window_actor, CLUTTER_TYPE_GROUP);
static void
frame_data_free (FrameData *frame)
{
g_slice_free (FrameData, frame);
}
static void
meta_window_actor_class_init (MetaWindowActorClass *klass)
{
@ -758,7 +783,7 @@ meta_window_actor_get_paint_volume (ClutterActor *actor,
/* The paint volume is computed before paint functions are called
* so our bounds might not be updated yet. Force an update. */
meta_window_actor_pre_paint (self);
meta_window_actor_handle_updates (self);
meta_window_actor_get_shape_bounds (self, &bounds);
@ -1002,6 +1027,7 @@ meta_window_actor_damage_all (MetaWindowActor *self)
cogl_texture_get_height (texture));
priv->needs_damage_all = FALSE;
priv->repaint_scheduled = TRUE;
}
static void
@ -1019,14 +1045,57 @@ meta_window_actor_thaw (MetaWindowActor *self)
if (self->priv->freeze_count)
return;
/* We sometimes ignore moves and resizes on frozen windows */
meta_window_actor_sync_actor_geometry (self, FALSE);
/* We do this now since we might be going right back into the
* frozen state */
meta_window_actor_handle_updates (self);
/* Since we ignore damage events while a window is frozen for certain effects
* we may need to issue an update_area() covering the whole pixmap if we
* don't know what real damage has happened. */
if (self->priv->needs_damage_all)
meta_window_actor_damage_all (self);
}
void
meta_window_actor_queue_frame_drawn (MetaWindowActor *self,
gboolean no_delay_frame)
{
MetaWindowActorPrivate *priv = self->priv;
FrameData *frame = g_slice_new0 (FrameData);
priv->needs_frame_drawn = TRUE;
frame->sync_request_serial = priv->window->sync_request_serial;
priv->frames = g_list_prepend (priv->frames, frame);
if (no_delay_frame)
{
ClutterActor *stage = clutter_actor_get_stage (CLUTTER_ACTOR (self));
clutter_stage_skip_sync_delay (CLUTTER_STAGE (stage));
}
if (!priv->repaint_scheduled)
{
/* A frame was marked by the client without actually doing any
* damage, or while we had the window frozen (e.g. during an
* interactive resize.) We need to make sure that the
* pre_paint/post_paint functions get called, enabling us to
* send a _NET_WM_FRAME_DRAWN. We do a 1-pixel redraw to get
* consistent timing with non-empty frames.
*/
if (priv->mapped && !priv->needs_pixmap)
{
const cairo_rectangle_int_t clip = { 0, 0, 1, 1 };
clutter_actor_queue_redraw_with_clip (priv->actor, &clip);
priv->repaint_scheduled = TRUE;
}
}
}
LOCAL_SYMBOL gboolean
meta_window_actor_effect_in_progress (MetaWindowActor *self)
{
@ -1038,6 +1107,12 @@ meta_window_actor_effect_in_progress (MetaWindowActor *self)
self->priv->destroy_in_progress);
}
static gboolean
is_frozen (MetaWindowActor *self)
{
return self->priv->freeze_count ? TRUE : FALSE;
}
static void
meta_window_actor_queue_create_pixmap (MetaWindowActor *self)
{
@ -1048,6 +1123,9 @@ meta_window_actor_queue_create_pixmap (MetaWindowActor *self)
if (!priv->mapped)
return;
if (is_frozen (self))
return;
/* This will cause the compositor paint function to be run
* if the actor is visible or a clone of the actor is visible.
* if the actor isn't visible in any way, then we don't
@ -1140,7 +1218,7 @@ meta_window_actor_after_effects (MetaWindowActor *self)
}
meta_window_actor_sync_visibility (self);
meta_window_actor_sync_actor_position (self);
meta_window_actor_sync_actor_geometry (self, FALSE);
if (!meta_window_is_mapped (priv->window))
meta_window_actor_detach (self);
@ -1372,7 +1450,8 @@ meta_window_actor_destroy (MetaWindowActor *self)
}
LOCAL_SYMBOL void
meta_window_actor_sync_actor_position (MetaWindowActor *self)
meta_window_actor_sync_actor_geometry (MetaWindowActor *self,
gboolean did_placement)
{
MetaWindowActorPrivate *priv = self->priv;
MetaRectangle window_rect;
@ -1383,12 +1462,27 @@ meta_window_actor_sync_actor_position (MetaWindowActor *self)
priv->last_height != window_rect.height)
{
priv->size_changed = TRUE;
meta_window_actor_queue_create_pixmap (self);
priv->last_width = window_rect.width;
priv->last_height = window_rect.height;
}
/* Normally we want freezing a window to also freeze its position; this allows
* windows to atomically move and resize together, either under app control,
* or because the user is resizing from the left/top. But on initial placement
* we need to assign a position, since immediately after the window
* is shown, the map effect will go into effect and prevent further geometry
* updates.
*/
if (is_frozen (self) && !did_placement)
return;
meta_window_get_input_rect (priv->window, &window_rect);
if (priv->size_changed)
{
meta_window_actor_queue_create_pixmap (self);
}
if (meta_window_actor_effect_in_progress (self))
return;
@ -1606,7 +1700,16 @@ meta_window_actor_new (MetaWindow *window)
if (priv->mapped)
meta_window_actor_queue_create_pixmap (self);
meta_window_actor_sync_actor_position (self);
meta_window_actor_set_updates_frozen (self,
meta_window_updates_are_frozen (priv->window));
/* If a window doesn't start off with updates frozen, we should
* we should send a _NET_WM_FRAME_DRAWN immediately after the first drawn.
*/
if (priv->window->extended_sync_request_counter && !priv->updates_frozen)
meta_window_actor_queue_frame_drawn (self, FALSE);
meta_window_actor_sync_actor_geometry (self, priv->window->placed);
/* Hang our compositor window state off the MetaWindow for fast retrieval */
meta_window_set_compositor_private (window, G_OBJECT (self));
@ -2068,12 +2171,6 @@ check_needs_shadow (MetaWindowActor *self)
meta_shadow_unref (old_shadow);
}
static gboolean
is_frozen (MetaWindowActor *self)
{
return self->priv->freeze_count ? TRUE : FALSE;
}
LOCAL_SYMBOL void
meta_window_actor_process_damage (MetaWindowActor *self,
XDamageNotifyEvent *event)