From d048f7a41cfae532b671089db9d648e74ae8ee9f Mon Sep 17 00:00:00 2001 From: leigh123linux Date: Oct 11 2015 15:48:36 +0000 Subject: add mtwebster's nvidia sync fixes --- diff --git a/0001-Implement-frame-sync-between-apps-and-compositor.patch b/0001-Implement-frame-sync-between-apps-and-compositor.patch new file mode 100644 index 0000000..e95a03e --- /dev/null +++ b/0001-Implement-frame-sync-between-apps-and-compositor.patch @@ -0,0 +1,1951 @@ +From 0b1e3586b71084c98d3cafb35037a19ea59c6ae8 Mon Sep 17 00:00:00 2001 +From: Michael Webster +Date: Sat, 3 Oct 2015 18:38:22 -0400 +Subject: [PATCH 1/4] Implement frame sync between apps and compositor. + +Based on https://bugzilla.gnome.org/show_bug.cgi?id=685463 +--- + configure.ac | 4 +- + src/Makefile.am | 10 +- + src/compositor/compositor-private.h | 21 +- + src/compositor/compositor.c | 157 +++++++++++- + src/compositor/meta-background-actor.c | 2 - + src/compositor/meta-shaped-texture.c | 3 - + src/compositor/meta-texture-rectangle.c | 3 - + src/compositor/meta-window-actor-private.h | 11 +- + src/compositor/meta-window-actor.c | 318 ++++++++++++++++++++--- + src/core/display-private.h | 14 +- + src/core/display.c | 124 ++++----- + src/core/window-private.h | 20 +- + src/core/window-props.c | 30 ++- + src/core/window.c | 391 ++++++++++++++++++++++------- + src/core/xprops.c | 34 ++- + src/core/xprops.h | 8 +- + src/meta/atomnames.h | 2 + + src/meta/compositor.h | 10 +- + 18 files changed, 921 insertions(+), 241 deletions(-) + +diff --git a/configure.ac b/configure.ac +index bcada11..3b4c65c 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -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 + " + +diff --git a/src/Makefile.am b/src/Makefile.am +index 12e8909..f928663 100644 +--- a/src/Makefile.am ++++ b/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 \ +@@ -264,8 +266,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 +305,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 +314,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 +diff --git a/src/compositor/compositor-private.h b/src/compositor/compositor-private.h +index 8b6982d..bb1da9a 100644 +--- a/src/compositor/compositor-private.h ++++ b/src/compositor/compositor-private.h +@@ -26,9 +26,13 @@ struct _MetaCompositor + + 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; + }; + + struct _MetaCompScreen +@@ -42,6 +46,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; +@@ -52,8 +59,13 @@ struct _MetaCompScreen + gint switch_workspace_in_progress; + + MetaPluginManager *plugin_mgr; ++ ++ gboolean frame_has_updated_xsurfaces; + }; + ++/* 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 +78,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 */ +diff --git a/src/compositor/compositor.c b/src/compositor/compositor.c +index 5658392..23f6093 100644 +--- a/src/compositor/compositor.c ++++ b/src/compositor/compositor.c +@@ -163,6 +163,8 @@ process_damage (MetaCompositor *compositor, + return; + + meta_window_actor_process_damage (window_actor, event); ++ ++ compositor->frame_has_updated_xsurfaces = TRUE; + } + + static void +@@ -526,11 +528,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 +616,13 @@ meta_compositor_manage_screen (MetaCompositor *compositor, + + info->stage = clutter_stage_new (); + ++ clutter_stage_set_paint_callback (CLUTTER_STAGE (info->stage), ++ after_stage_paint, ++ info, ++ NULL); ++ ++ clutter_stage_set_sync_delay (CLUTTER_STAGE (info->stage), META_SYNC_DELAY); ++ + meta_screen_get_size (screen, &width, &height); + clutter_actor_realize (info->stage); + +@@ -776,10 +796,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 +@@ -1197,7 +1240,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 +1253,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,12 +1283,63 @@ meta_compositor_sync_screen_size (MetaCompositor *compositor, + } + + static void ++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 void + pre_paint_windows (MetaCompScreen *info) + { + GList *l; + 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; + +@@ -1511,3 +1606,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; ++} +diff --git a/src/compositor/meta-background-actor.c b/src/compositor/meta-background-actor.c +index 28ff2ac..5920abf 100644 +--- a/src/compositor/meta-background-actor.c ++++ b/src/compositor/meta-background-actor.c +@@ -31,10 +31,8 @@ + + #include + +-#define COGL_ENABLE_EXPERIMENTAL_API + #include + +-#define CLUTTER_ENABLE_EXPERIMENTAL_API + #include + + #include +diff --git a/src/compositor/meta-shaped-texture.c b/src/compositor/meta-shaped-texture.c +index 947c1b5..a9bdb7f 100644 +--- a/src/compositor/meta-shaped-texture.c ++++ b/src/compositor/meta-shaped-texture.c +@@ -31,9 +31,6 @@ + + #include + +-#define CLUTTER_ENABLE_EXPERIMENTAL_API +-#define COGL_ENABLE_EXPERIMENTAL_API +- + #include + #include "meta-texture-tower.h" + #include "meta-texture-rectangle.h" +diff --git a/src/compositor/meta-texture-rectangle.c b/src/compositor/meta-texture-rectangle.c +index 237109b..48498a5 100644 +--- a/src/compositor/meta-texture-rectangle.c ++++ b/src/compositor/meta-texture-rectangle.c +@@ -25,9 +25,6 @@ + + #include + +-#define CLUTTER_ENABLE_EXPERIMENTAL_API +-#define COGL_ENABLE_EXPERIMENTAL_API +- + #include + #include "meta-texture-rectangle.h" + +diff --git a/src/compositor/meta-window-actor-private.h b/src/compositor/meta-window-actor-private.h +index b430bea..81ecf8a 100644 +--- a/src/compositor/meta-window-actor-private.h ++++ b/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); + +diff --git a/src/compositor/meta-window-actor.c b/src/compositor/meta-window-actor.c +index 3bb0fe9..f91d139 100644 +--- a/src/compositor/meta-window-actor.c ++++ b/src/compositor/meta-window-actor.c +@@ -16,7 +16,6 @@ + #include + + #include +-#define COGL_ENABLE_EXPERIMENTAL_API + #include + #include /* 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,11 +187,19 @@ 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) + { + GObjectClass *object_class = G_OBJECT_CLASS (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) +@@ -2132,6 +2229,7 @@ meta_window_actor_process_damage (MetaWindowActor *self, + event->area.y, + event->area.width, + event->area.height); ++ priv->repaint_scheduled = TRUE; + } + + LOCAL_SYMBOL void +@@ -2280,6 +2378,12 @@ check_needs_reshape (MetaWindowActor *self) + meta_shaped_texture_set_shape_region (META_SHAPED_TEXTURE (priv->actor), NULL); + meta_window_actor_clear_shape_region (self); + ++ if (priv->shadow_shape != NULL) ++ { ++ meta_window_shape_unref (priv->shadow_shape); ++ priv->shadow_shape = NULL; ++ } ++ + meta_frame_calc_borders (priv->window->frame, &borders); + + region = meta_window_get_frame_bounds (priv->window); +@@ -2364,17 +2468,15 @@ meta_window_actor_update_shape (MetaWindowActor *self) + MetaWindowActorPrivate *priv = self->priv; + + priv->needs_reshape = TRUE; +- if (priv->shadow_shape != NULL) +- { +- meta_window_shape_unref (priv->shadow_shape); +- priv->shadow_shape = NULL; +- } ++ ++ if (is_frozen (self)) ++ return; + + clutter_actor_queue_redraw (priv->actor); + } + + LOCAL_SYMBOL void +-meta_window_actor_pre_paint (MetaWindowActor *self) ++meta_window_actor_handle_updates (MetaWindowActor *self) + { + MetaWindowActorPrivate *priv = self->priv; + MetaScreen *screen = priv->screen; +@@ -2400,26 +2502,6 @@ meta_window_actor_pre_paint (MetaWindowActor *self) + 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; + } + +@@ -2428,6 +2510,140 @@ meta_window_actor_pre_paint (MetaWindowActor *self) + check_needs_shadow (self); + } + ++void ++meta_window_actor_pre_paint (MetaWindowActor *self) ++{ ++ MetaWindowActorPrivate *priv = self->priv; ++ GList *l; ++ ++ meta_window_actor_handle_updates (self); ++ ++ for (l = priv->frames; l != NULL; l = l->next) ++ { ++ FrameData *frame = l->data; ++ ++ if (frame->frame_counter == 0) ++ { ++ CoglOnscreen *onscreen = COGL_ONSCREEN (cogl_get_draw_framebuffer()); ++ frame->frame_counter = cogl_onscreen_get_frame_counter (onscreen); ++ } ++ } ++} ++ ++void ++meta_window_actor_post_paint (MetaWindowActor *self) ++{ ++ MetaWindowActorPrivate *priv = self->priv; ++ ++ priv->repaint_scheduled = FALSE; ++ ++ if (priv->needs_frame_drawn) ++ { ++ MetaScreen *screen = priv->screen; ++ MetaDisplay *display = meta_screen_get_display (screen); ++ Display *xdisplay = meta_display_get_xdisplay (display); ++ ++ XClientMessageEvent ev = { 0, }; ++ ++ FrameData *frame = priv->frames->data; ++ ++ frame->frame_drawn_time = meta_compositor_monotonic_time_to_server_time (display, ++ g_get_monotonic_time ()); ++ ++ ev.type = ClientMessage; ++ ev.window = meta_window_get_xwindow (priv->window); ++ ev.message_type = display->atom__NET_WM_FRAME_DRAWN; ++ ev.format = 32; ++ ev.data.l[0] = frame->sync_request_serial & G_GUINT64_CONSTANT(0xffffffff); ++ ev.data.l[1] = frame->sync_request_serial >> 32; ++ ev.data.l[2] = frame->frame_drawn_time & G_GUINT64_CONSTANT(0xffffffff); ++ ev.data.l[3] = frame->frame_drawn_time >> 32; ++ ++ meta_error_trap_push (display); ++ XSendEvent (xdisplay, ev.window, False, 0, (XEvent*) &ev); ++ XFlush (xdisplay); ++ meta_error_trap_pop (display); ++ ++ priv->needs_frame_drawn = FALSE; ++ } ++} ++ ++static void ++send_frame_timings (MetaWindowActor *self, ++ FrameData *frame, ++ CoglFrameInfo *frame_info, ++ gint64 presentation_time) ++{ ++ MetaWindowActorPrivate *priv = self->priv; ++ MetaDisplay *display = meta_screen_get_display (priv->screen); ++ Display *xdisplay = meta_display_get_xdisplay (display); ++ float refresh_rate; ++ int refresh_interval; ++ ++ XClientMessageEvent ev = { 0, }; ++ ++ ev.type = ClientMessage; ++ ev.window = meta_window_get_xwindow (priv->window); ++ ev.message_type = display->atom__NET_WM_FRAME_TIMINGS; ++ ev.format = 32; ++ ev.data.l[0] = frame->sync_request_serial & G_GUINT64_CONSTANT(0xffffffff); ++ ev.data.l[1] = frame->sync_request_serial >> 32; ++ ++ refresh_rate = cogl_frame_info_get_refresh_rate (frame_info); ++ /* 0.0 is a flag for not known, but sanity-check against other odd numbers */ ++ if (refresh_rate >= 1.0) ++ refresh_interval = (int) (0.5 + 1000000 / refresh_rate); ++ else ++ refresh_interval = 0; ++ ++ if (presentation_time != 0) ++ { ++ gint64 presentation_time_server = meta_compositor_monotonic_time_to_server_time (display, ++ presentation_time); ++ gint64 presentation_time_offset = presentation_time_server - frame->frame_drawn_time; ++ if (presentation_time_offset == 0) ++ presentation_time_offset = 1; ++ ++ if ((gint32)presentation_time_offset == presentation_time_offset) ++ ev.data.l[2] = presentation_time_offset; ++ } ++ ++ ev.data.l[3] = refresh_interval; ++ ev.data.l[4] = 1000 * META_SYNC_DELAY; ++ ++ meta_error_trap_push (display); ++ XSendEvent (xdisplay, ev.window, False, 0, (XEvent*) &ev); ++ XFlush (xdisplay); ++ meta_error_trap_pop (display); ++} ++ ++void ++meta_window_actor_frame_complete (MetaWindowActor *self, ++ CoglFrameInfo *frame_info, ++ gint64 presentation_time) ++{ ++ MetaWindowActorPrivate *priv = self->priv; ++ GList *l; ++ ++ for (l = priv->frames; l;) ++ { ++ GList *l_next = l->next; ++ FrameData *frame = l->data; ++ ++ if (frame->frame_counter == cogl_frame_info_get_frame_counter (frame_info)) ++ { ++ if (frame->frame_drawn_time != 0) ++ { ++ priv->frames = g_list_delete_link (priv->frames, l); ++ send_frame_timings (self, frame, frame_info, presentation_time); ++ frame_data_free (frame); ++ } ++ } ++ ++ l = l_next; ++ } ++} ++ + LOCAL_SYMBOL void + meta_window_actor_invalidate_shadow (MetaWindowActor *self) + { +@@ -2435,6 +2651,10 @@ meta_window_actor_invalidate_shadow (MetaWindowActor *self) + + priv->recompute_focused_shadow = TRUE; + priv->recompute_unfocused_shadow = TRUE; ++ ++ if (is_frozen (self)) ++ return; ++ + clutter_actor_queue_redraw (CLUTTER_ACTOR (self)); + } + +@@ -2460,3 +2680,21 @@ meta_window_actor_update_opacity (MetaWindowActor *self) + self->priv->opacity = opacity; + clutter_actor_set_opacity (self->priv->actor, opacity); + } ++ ++void ++meta_window_actor_set_updates_frozen (MetaWindowActor *self, ++ gboolean updates_frozen) ++{ ++ MetaWindowActorPrivate *priv = self->priv; ++ ++ updates_frozen = updates_frozen != FALSE; ++ ++ if (priv->updates_frozen != updates_frozen) ++ { ++ priv->updates_frozen = updates_frozen; ++ if (updates_frozen) ++ meta_window_actor_freeze (self); ++ else ++ meta_window_actor_thaw (self); ++ } ++} +diff --git a/src/core/display-private.h b/src/core/display-private.h +index 8a3de93..6a51bfb 100644 +--- a/src/core/display-private.h ++++ b/src/core/display-private.h +@@ -207,10 +207,6 @@ struct _MetaDisplay + int xkb_base_event_type; + guint32 last_bell_time; + #endif +-#ifdef HAVE_XSYNC +- /* alarm monitoring client's _NET_WM_SYNC_REQUEST_COUNTER */ +- XSyncAlarm grab_sync_request_alarm; +-#endif + int grab_resize_timeout_id; + + /* Keybindings stuff */ +@@ -352,6 +348,16 @@ void meta_display_register_x_window (MetaDisplay *display, + void meta_display_unregister_x_window (MetaDisplay *display, + Window xwindow); + ++#ifdef HAVE_XSYNC ++MetaWindow* meta_display_lookup_sync_alarm (MetaDisplay *display, ++ XSyncAlarm alarm); ++void meta_display_register_sync_alarm (MetaDisplay *display, ++ XSyncAlarm *alarmp, ++ MetaWindow *window); ++void meta_display_unregister_sync_alarm (MetaDisplay *display, ++ XSyncAlarm alarm); ++#endif /* HAVE_XSYNC */ ++ + void meta_display_notify_window_created (MetaDisplay *display, + MetaWindow *window); + +diff --git a/src/core/display.c b/src/core/display.c +index 274e736..5df8c6d 100644 +--- a/src/core/display.c ++++ b/src/core/display.c +@@ -507,10 +507,6 @@ meta_display_open (void) + the_display->allow_terminal_deactivation = TRUE; /* Only relevant for when a + terminal has the focus */ + +-#ifdef HAVE_XSYNC +- the_display->grab_sync_request_alarm = None; +-#endif +- + /* FIXME copy the checks from GDK probably */ + the_display->static_gravity_works = g_getenv ("MUFFIN_USE_STATIC_GRAVITY") != NULL; + +@@ -621,8 +617,11 @@ meta_display_open (void) + the_display->xsync_event_base = 0; + } + else +- the_display->have_xsync = TRUE; +- ++ { ++ the_display->have_xsync = TRUE; ++ XSyncSetPriority (the_display->xdisplay, None, 10); ++ } ++ + meta_verbose ("Attempted to init Xsync, found version %d.%d error base %d event base %d\n", + major, minor, + the_display->xsync_error_base, +@@ -1719,16 +1718,19 @@ event_callback (XEvent *event, + } + + #ifdef HAVE_XSYNC +- if (META_DISPLAY_HAS_XSYNC (display) && +- event->type == (display->xsync_event_base + XSyncAlarmNotify) && +- ((XSyncAlarmNotifyEvent*)event)->alarm == display->grab_sync_request_alarm) ++ if (META_DISPLAY_HAS_XSYNC (display) && ++ event->type == (display->xsync_event_base + XSyncAlarmNotify)) + { +- filter_out_event = TRUE; /* GTK doesn't want to see this really */ +- +- if (display->grab_op != META_GRAB_OP_NONE && +- display->grab_window != NULL && +- grab_op_is_mouse (display->grab_op)) +- meta_window_handle_mouse_grab_op_event (display->grab_window, event); ++ MetaWindow *alarm_window = meta_display_lookup_sync_alarm (display, ++ ((XSyncAlarmNotifyEvent*)event)->alarm); ++ if (alarm_window != NULL) ++ { ++ XSyncValue value = ((XSyncAlarmNotifyEvent*)event)->counter_value; ++ gint64 new_counter_value; ++ new_counter_value = XSyncValueLow32 (value) + ((gint64)XSyncValueHigh32 (value) << 32); ++ meta_window_update_sync_request_counter (alarm_window, new_counter_value); ++ filter_out_event = TRUE; /* GTK doesn't want to see this really */ ++ } + } + #endif /* HAVE_XSYNC */ + +@@ -3684,7 +3686,6 @@ meta_display_begin_grab_op (MetaDisplay *display, + display->grab_motion_notify_time = 0; + display->grab_old_window_stacking = NULL; + #ifdef HAVE_XSYNC +- display->grab_sync_request_alarm = None; + display->grab_last_user_action_was_snap = FALSE; + #endif + display->grab_frame_action = frame_action; +@@ -3704,55 +3705,9 @@ meta_display_begin_grab_op (MetaDisplay *display, + + #ifdef HAVE_XSYNC + if ( meta_grab_op_is_resizing (display->grab_op) && +- display->grab_window->sync_request_counter != None) ++ display->grab_window->sync_request_counter != None) + { +- XSyncAlarmAttributes values; +- XSyncValue init; +- +- meta_error_trap_push_with_return (display); +- +- /* Set the counter to 0, so we know that the application's +- * responses to the client messages will always trigger +- * a PositiveTransition +- */ +- +- XSyncIntToValue (&init, 0); +- XSyncSetCounter (display->xdisplay, +- display->grab_window->sync_request_counter, init); +- +- display->grab_window->sync_request_serial = 0; +- display->grab_window->sync_request_time.tv_sec = 0; +- display->grab_window->sync_request_time.tv_usec = 0; +- +- values.trigger.counter = display->grab_window->sync_request_counter; +- values.trigger.value_type = XSyncAbsolute; +- values.trigger.test_type = XSyncPositiveTransition; +- XSyncIntToValue (&values.trigger.wait_value, +- display->grab_window->sync_request_serial + 1); +- +- /* After triggering, increment test_value by this. +- * (NOT wait_value above) +- */ +- XSyncIntToValue (&values.delta, 1); +- +- /* we want events (on by default anyway) */ +- values.events = True; +- +- display->grab_sync_request_alarm = XSyncCreateAlarm (display->xdisplay, +- XSyncCACounter | +- XSyncCAValueType | +- XSyncCAValue | +- XSyncCATestType | +- XSyncCADelta | +- XSyncCAEvents, +- &values); +- +- if (meta_error_trap_pop_with_return (display) != Success) +- display->grab_sync_request_alarm = None; +- +- meta_topic (META_DEBUG_RESIZING, +- "Created update alarm 0x%lx\n", +- display->grab_sync_request_alarm); ++ meta_window_create_sync_request_alarm (display->grab_window); + } + #endif + } +@@ -3785,6 +3740,39 @@ meta_display_begin_grab_op (MetaDisplay *display, + return TRUE; + } + ++#ifdef HAVE_XSYNC ++/* We store sync alarms in the window ID hash table, because they are ++ * just more types of XIDs in the same global space, but we have ++ * typesafe functions to register/unregister for readability. ++ */ ++ ++MetaWindow* ++meta_display_lookup_sync_alarm (MetaDisplay *display, ++ XSyncAlarm alarm) ++{ ++ return g_hash_table_lookup (display->window_ids, &alarm); ++} ++ ++void ++meta_display_register_sync_alarm (MetaDisplay *display, ++ XSyncAlarm *alarmp, ++ MetaWindow *window) ++{ ++ g_return_if_fail (g_hash_table_lookup (display->window_ids, alarmp) == NULL); ++ ++ g_hash_table_insert (display->window_ids, alarmp, window); ++} ++ ++void ++meta_display_unregister_sync_alarm (MetaDisplay *display, ++ XSyncAlarm alarm) ++{ ++ g_return_if_fail (g_hash_table_lookup (display->window_ids, &alarm) != NULL); ++ ++ g_hash_table_remove (display->window_ids, &alarm); ++} ++#endif /* HAVE_XSYNC */ ++ + void + meta_display_end_grab_op (MetaDisplay *display, + guint32 timestamp) +@@ -3860,14 +3848,6 @@ meta_display_end_grab_op (MetaDisplay *display, + meta_screen_ungrab_all_keys (display->grab_screen, timestamp); + } + +-#ifdef HAVE_XSYNC +- if (display->grab_sync_request_alarm != None) +- { +- XSyncDestroyAlarm (display->xdisplay, +- display->grab_sync_request_alarm); +- display->grab_sync_request_alarm = None; +- } +-#endif /* HAVE_XSYNC */ + + display->grab_window = NULL; + display->grab_screen = NULL; +diff --git a/src/core/window-private.h b/src/core/window-private.h +index 9dd7156..60a1790 100644 +--- a/src/core/window-private.h ++++ b/src/core/window-private.h +@@ -383,14 +383,21 @@ struct _MetaWindow + /* if non-NULL, the bounds of the window frame */ + cairo_region_t *frame_bounds; + ++ /* if TRUE, the we have the new form of sync request counter which ++ * also handles application frames */ ++ guint extended_sync_request_counter : 1; ++ + /* Note: can be NULL */ + GSList *struts; + + #ifdef HAVE_XSYNC + /* XSync update counter */ + XSyncCounter sync_request_counter; +- guint sync_request_serial; +- GTimeVal sync_request_time; ++ gint64 sync_request_serial; ++ gint64 sync_request_wait_serial; ++ guint sync_request_timeout_id; ++ /* alarm monitoring client's _NET_WM_SYNC_REQUEST_COUNTER */ ++ XSyncAlarm sync_request_alarm; + #endif + + /* Number of UnmapNotify that are caused by us, if +@@ -650,6 +657,8 @@ void meta_window_send_icccm_message (MetaWindow *window, + Atom atom, + guint32 timestamp); + ++void meta_window_create_sync_request_alarm (MetaWindow *window); ++void meta_window_destroy_sync_request_alarm (MetaWindow *window); + + void meta_window_move_resize_request(MetaWindow *window, + guint value_mask, +@@ -683,6 +692,11 @@ void meta_window_shove_titlebar_onscreen (MetaWindow *window); + void meta_window_set_gravity (MetaWindow *window, + int gravity); + ++#ifdef HAVE_XSYNC ++ void meta_window_update_sync_request_counter (MetaWindow *window, ++ gint64 new_counter_value); ++#endif /* HAVE_XSYNC */ ++ + void meta_window_handle_mouse_grab_op_event (MetaWindow *window, + XEvent *event); + +@@ -790,4 +804,6 @@ MetaWindowTileType meta_window_get_tile_type (MetaWindow *window); + gboolean meta_window_is_client_decorated (MetaWindow *window); + + ++gboolean meta_window_updates_are_frozen (MetaWindow *window); ++ + #endif +diff --git a/src/core/window-props.c b/src/core/window-props.c +index 101a8e2..9d1a01f 100644 +--- a/src/core/window-props.c ++++ b/src/core/window-props.c +@@ -956,12 +956,32 @@ reload_update_counter (MetaWindow *window, + { + if (value->type != META_PROP_VALUE_INVALID) + { ++ meta_window_destroy_sync_request_alarm (window); ++ window->sync_request_counter = None; ++ + #ifdef HAVE_XSYNC +- XSyncCounter counter = value->v.xcounter; ++ if (value->v.xcounter_list.n_counters == 0) ++ { ++ meta_warning ("_NET_WM_SYNC_REQUEST_COUNTER is empty\n"); ++ return; ++ } ++ ++ if (value->v.xcounter_list.n_counters == 1) ++ { ++ window->sync_request_counter = value->v.xcounter_list.counters[0]; ++ window->extended_sync_request_counter = FALSE; ++ } ++ else ++ { ++ window->sync_request_counter = value->v.xcounter_list.counters[1]; ++ window->extended_sync_request_counter = TRUE; ++ } ++ meta_verbose ("Window has _NET_WM_SYNC_REQUEST_COUNTER 0x%lx (extended=%s)\n", ++ window->sync_request_counter, ++ window->extended_sync_request_counter ? "true" : "false"); + +- window->sync_request_counter = counter; +- meta_verbose ("Window has _NET_WM_SYNC_REQUEST_COUNTER 0x%lx\n", +- window->sync_request_counter); ++ if (window->extended_sync_request_counter) ++ meta_window_create_sync_request_alarm (window); + #endif + } + } +@@ -1751,7 +1771,7 @@ meta_display_init_window_prop_hooks (MetaDisplay *display) + { XA_WM_ICON_NAME, META_PROP_VALUE_TEXT_PROPERTY, reload_wm_icon_name, TRUE, FALSE }, + { display->atom__NET_WM_DESKTOP, META_PROP_VALUE_CARDINAL, reload_net_wm_desktop, TRUE, FALSE }, + { display->atom__NET_STARTUP_ID, META_PROP_VALUE_UTF8, reload_net_startup_id, TRUE, FALSE }, +- { display->atom__NET_WM_SYNC_REQUEST_COUNTER, META_PROP_VALUE_SYNC_COUNTER, reload_update_counter, TRUE, FALSE }, ++ { display->atom__NET_WM_SYNC_REQUEST_COUNTER, META_PROP_VALUE_SYNC_COUNTER_LIST, reload_update_counter, TRUE, TRUE }, + { XA_WM_NORMAL_HINTS, META_PROP_VALUE_SIZE_HINTS, reload_normal_hints, TRUE, FALSE }, + { display->atom_WM_PROTOCOLS, META_PROP_VALUE_ATOM_LIST, reload_wm_protocols, TRUE, FALSE }, + { XA_WM_HINTS, META_PROP_VALUE_WM_HINTS, reload_wm_hints, TRUE, FALSE }, +diff --git a/src/core/window.c b/src/core/window.c +index f22010e..c2fe796 100644 +--- a/src/core/window.c ++++ b/src/core/window.c +@@ -1037,8 +1037,8 @@ meta_window_new_with_attrs (MetaDisplay *display, + #ifdef HAVE_XSYNC + window->sync_request_counter = None; + window->sync_request_serial = 0; +- window->sync_request_time.tv_sec = 0; +- window->sync_request_time.tv_usec = 0; ++ window->sync_request_timeout_id = 0; ++ window->sync_request_alarm = None; + #endif + + window->screen = screen; +@@ -1802,6 +1802,12 @@ meta_window_unmanage (MetaWindow *window, + invalidate_work_areas (window); + } + ++ if (window->sync_request_timeout_id) ++ { ++ g_source_remove (window->sync_request_timeout_id); ++ window->sync_request_timeout_id = 0; ++ } ++ + if (window->display->grab_window == window) + meta_display_end_grab_op (window->display, timestamp); + +@@ -1856,6 +1862,8 @@ meta_window_unmanage (MetaWindow *window, + if (!window->override_redirect) + meta_stack_remove (window->screen->stack, window); + ++ meta_window_destroy_sync_request_alarm (window); ++ + if (window->frame) + meta_window_destroy_frame (window); + +@@ -4653,16 +4661,147 @@ static_gravity_works (MetaDisplay *display) + return display->static_gravity_works; + } + ++void ++meta_window_create_sync_request_alarm (MetaWindow *window) ++{ ++#ifdef HAVE_XSYNC ++ XSyncAlarmAttributes values; ++ XSyncValue init; ++ ++ if (window->sync_request_counter == None || ++ window->sync_request_alarm != None) ++ return; ++ ++ meta_error_trap_push_with_return (window->display); ++ ++ /* In the new (extended style), the counter value is initialized by ++ * the client before mapping the window. In the old style, we're ++ * responsible for setting the initial value of the counter. ++ */ ++ if (window->extended_sync_request_counter) ++ { ++ if (!XSyncQueryCounter(window->display->xdisplay, ++ window->sync_request_counter, ++ &init)) ++ { ++ meta_error_trap_pop_with_return (window->display); ++ window->sync_request_counter = None; ++ return; ++ } ++ ++ window->sync_request_serial = ++ XSyncValueLow32 (init) + ((gint64)XSyncValueHigh32 (init) << 32); ++ ++ /* if the value is odd, the window starts off with updates frozen */ ++ meta_compositor_set_updates_frozen (window->display->compositor, window, ++ meta_window_updates_are_frozen (window)); ++ } ++ else ++ { ++ XSyncIntToValue (&init, 0); ++ XSyncSetCounter (window->display->xdisplay, ++ window->sync_request_counter, init); ++ window->sync_request_serial = 0; ++ } ++ ++ values.trigger.counter = window->sync_request_counter; ++ values.trigger.test_type = XSyncPositiveComparison; ++ ++ /* Initialize to one greater than the current value */ ++ values.trigger.value_type = XSyncRelative; ++ XSyncIntToValue (&values.trigger.wait_value, 1); ++ ++ /* After triggering, increment test_value by this until ++ * until the test condition is false */ ++ XSyncIntToValue (&values.delta, 1); ++ ++ /* we want events (on by default anyway) */ ++ values.events = True; ++ ++ window->sync_request_alarm = XSyncCreateAlarm (window->display->xdisplay, ++ XSyncCACounter | ++ XSyncCAValueType | ++ XSyncCAValue | ++ XSyncCATestType | ++ XSyncCADelta | ++ XSyncCAEvents, ++ &values); ++ ++ if (meta_error_trap_pop_with_return (window->display) == Success) ++ meta_display_register_sync_alarm (window->display, &window->sync_request_alarm, window); ++ else ++ { ++ window->sync_request_alarm = None; ++ window->sync_request_counter = None; ++ } ++#endif ++} ++ ++void ++meta_window_destroy_sync_request_alarm (MetaWindow *window) ++{ ++#ifdef HAVE_XSYNC ++ if (window->sync_request_alarm != None) ++ { ++ /* Has to be unregistered _before_ clearing the structure field */ ++ meta_display_unregister_sync_alarm (window->display, window->sync_request_alarm); ++ XSyncDestroyAlarm (window->display->xdisplay, ++ window->sync_request_alarm); ++ window->sync_request_alarm = None; ++ } ++#endif /* HAVE_XSYNC */ ++} ++ + #ifdef HAVE_XSYNC ++static gboolean ++sync_request_timeout (gpointer data) ++{ ++ MetaWindow *window = data; ++ ++ window->sync_request_timeout_id = 0; ++ ++ /* We have now waited for more than a second for the ++ * application to respond to the sync request ++ */ ++ window->disable_sync = TRUE; ++ ++ /* Reset the wait serial, so we don't continue freezing ++ * window updates ++ */ ++ window->sync_request_wait_serial = 0; ++ meta_compositor_set_updates_frozen (window->display->compositor, window, ++ meta_window_updates_are_frozen (window)); ++ ++ if (window == window->display->grab_window && ++ meta_grab_op_is_resizing (window->display->grab_op)) ++ { ++ update_resize (window, ++ window->display->grab_last_user_action_was_snap, ++ window->display->grab_latest_motion_x, ++ window->display->grab_latest_motion_y, ++ TRUE, ++ FALSE); ++ } ++ ++ return FALSE; ++} ++ + static void + send_sync_request (MetaWindow *window) + { +- XSyncValue value; + XClientMessageEvent ev; ++ gint64 wait_serial; ++ ++ /* For the old style of _NET_WM_SYNC_REQUEST_COUNTER, we just have to ++ * increase the value, but for the new "extended" style we need to ++ * pick an even (unfrozen) value sufficiently ahead of the last serial ++ * that we received from the client; the same code still works ++ * for the old style. The increment of 240 is specified by the EWMH ++ * and is (1 second) * (60fps) * (an increment of 4 per frame). ++ */ ++ wait_serial = window->sync_request_serial + 240; + +- window->sync_request_serial++; +- +- XSyncIntToValue (&value, window->sync_request_serial); ++ window->sync_request_wait_serial = wait_serial; + + ev.type = ClientMessage; + ev.window = window->xwindow; +@@ -4675,8 +4814,9 @@ send_sync_request (MetaWindow *window) + * want to use _roundtrip, though? + */ + ev.data.l[1] = meta_display_get_current_time (window->display); +- ev.data.l[2] = XSyncValueLow32 (value); +- ev.data.l[3] = XSyncValueHigh32 (value); ++ ev.data.l[2] = wait_serial & G_GUINT64_CONSTANT(0xffffffff); ++ ev.data.l[3] = wait_serial >> 32; ++ ev.data.l[4] = window->extended_sync_request_counter ? 1 : 0; + + /* We don't need to trap errors here as we are already + * inside an error_trap_push()/pop() pair. +@@ -4684,10 +4824,46 @@ send_sync_request (MetaWindow *window) + XSendEvent (window->display->xdisplay, + window->xwindow, False, 0, (XEvent*) &ev); + +- g_get_current_time (&window->sync_request_time); ++ /* We give the window 1 sec to respond to _NET_WM_SYNC_REQUEST; ++ * if this time expires, we consider the window unresponsive ++ * and resize it unsynchonized. ++ */ ++ window->sync_request_timeout_id = g_timeout_add (1000, ++ sync_request_timeout, ++ window); ++ ++ meta_compositor_set_updates_frozen (window->display->compositor, window, ++ meta_window_updates_are_frozen (window)); + } + #endif + ++/** ++ * meta_window_updates_are_frozen: ++ * @window: a #MetaWindow ++ * ++ * Gets whether the compositor should be updating the window contents; ++ * window content updates may be frozen at client request by setting ++ * an odd value in the extended _NET_WM_SYNC_REQUEST_COUNTER counter r ++ * by the window manager during a resize operation while waiting for ++ * the client to redraw. ++ * ++ * Return value: %TRUE if updates are currently frozen ++ */ ++gboolean ++meta_window_updates_are_frozen (MetaWindow *window) ++{ ++#ifdef HAVE_XSYNC ++ if (window->extended_sync_request_counter && ++ window->sync_request_serial % 2 == 1) ++ return TRUE; ++ ++ if (window->sync_request_serial < window->sync_request_wait_serial) ++ return TRUE; ++#endif ++ ++ return FALSE; ++} ++ + static gboolean + maybe_move_attached_dialog (MetaWindow *window, + void *data) +@@ -4856,6 +5032,7 @@ meta_window_move_resize_internal (MetaWindow *window, + gboolean is_configure_request; + gboolean do_gravity_adjust; + gboolean is_user_action; ++ gboolean did_placement; + gboolean configure_frame_first; + gboolean use_static_gravity; + /* used for the configure request, but may not be final +@@ -4931,6 +5108,8 @@ meta_window_move_resize_internal (MetaWindow *window, + new_rect.x, new_rect.y); + } + ++ did_placement = !window->placed && window->calc_placement; ++ + meta_window_constrain (window, + window->frame ? &borders : NULL, + flags, +@@ -5142,12 +5321,28 @@ meta_window_move_resize_internal (MetaWindow *window, + * efficiently as possible + */ + +- /* configure frame first if we grow more than we shrink ++ /* Normally, we configure the frame first depending on whether ++ * we grow the frame more than we shrink. The idea is to avoid ++ * messing up the window contents by having a temporary situation ++ * where the frame is smaller than the window. However, if we're ++ * cooperating with the client to create an atomic frame upate, ++ * and the window is redirected, then we should always update ++ * the frame first, since updating the frame will force a new ++ * backing pixmap to be allocated, and the old backing pixmap ++ * will be left undisturbed for us to paint to the screen until ++ * the client finishes redrawing. + */ +- size_dx = w - window->rect.width; +- size_dy = h - window->rect.height; ++ if (window->extended_sync_request_counter) ++ { ++ configure_frame_first = TRUE; ++ } ++ else ++ { ++ size_dx = w - window->rect.width; ++ size_dy = h - window->rect.height; + +- configure_frame_first = (size_dx + size_dy >= 0); ++ configure_frame_first = size_dx + size_dy >= 0; ++ } + + if (use_static_gravity) + meta_window_set_gravity (window, StaticGravity); +@@ -5188,15 +5383,13 @@ meta_window_move_resize_internal (MetaWindow *window, + meta_error_trap_push (window->display); + + #ifdef HAVE_XSYNC +- if (window->sync_request_counter != None && +- window->display->grab_sync_request_alarm != None && +- window->sync_request_time.tv_usec == 0 && +- window->sync_request_time.tv_sec == 0) ++ if (window == window->display->grab_window && ++ meta_grab_op_is_resizing (window->display->grab_op) && ++ !window->disable_sync && ++ window->sync_request_counter != None && ++ window->sync_request_alarm != None && ++ window->sync_request_timeout_id == 0) + { +- /* turn off updating */ +- if (window->display->compositor) +- meta_compositor_set_updates (window->display->compositor, window, FALSE); +- + send_sync_request (window); + } + #endif +@@ -5227,7 +5420,8 @@ meta_window_move_resize_internal (MetaWindow *window, + save_user_window_placement (window); + + if (need_move_frame || need_resize_frame || +- need_move_client || need_resize_client) ++ need_move_client || need_resize_client || ++ did_placement) + { + int newx, newy; + meta_window_get_position (window, &newx, &newy); +@@ -5238,7 +5432,8 @@ meta_window_move_resize_internal (MetaWindow *window, + window->user_rect.width, window->user_rect.height); + if (window->display->compositor) + meta_compositor_sync_window_geometry (window->display->compositor, +- window); ++ window, ++ did_placement); + } + else + { +@@ -5576,7 +5771,7 @@ meta_window_configure_notify (MetaWindow *window, + meta_warning ("Unhandled change of windows override redirect status\n"); + + if (window->display->compositor) +- meta_compositor_sync_window_geometry (window->display->compositor, window); ++ meta_compositor_sync_window_geometry (window->display->compositor, window, FALSE); + } + + LOCAL_SYMBOL void +@@ -8880,73 +9075,39 @@ check_moveresize_frequency (MetaWindow *window, + gdouble *remaining) + { + GTimeVal current_time; ++ const double max_resizes_per_second = 25.0; ++ const double ms_between_resizes = 1000.0 / max_resizes_per_second; ++ double elapsed; + + g_get_current_time (¤t_time); + + #ifdef HAVE_XSYNC ++ /* If we are throttling via _NET_WM_SYNC_REQUEST, we don't need ++ * an artificial timeout-based throttled */ + if (!window->disable_sync && +- window->display->grab_sync_request_alarm != None) +- { +- if (window->sync_request_time.tv_sec != 0 || +- window->sync_request_time.tv_usec != 0) +- { +- double elapsed = +- time_diff (¤t_time, &window->sync_request_time); +- +- if (elapsed < 100.0) +- { +- /* We want to be sure that the timeout happens at +- * a time where elapsed will definitely be +- * greater than 1000, so we can disable sync +- */ +- if (remaining) +- *remaining = 100.0 - elapsed + 10; +- +- return FALSE; +- } +- else +- { +- /* We have now waited for more than a second for the +- * application to respond to the sync request +- */ +- window->disable_sync = TRUE; +- return TRUE; +- } +- } +- else +- { +- /* No outstanding sync requests. Go ahead and resize +- */ +- return TRUE; +- } +- } +- else ++ window->sync_request_alarm != None) ++ return TRUE; + #endif /* HAVE_XSYNC */ +- { +- const double max_resizes_per_second = 25.0; +- const double ms_between_resizes = 1000.0 / max_resizes_per_second; +- double elapsed; + +- elapsed = time_diff (¤t_time, &window->display->grab_last_moveresize_time); ++ elapsed = time_diff (¤t_time, &window->display->grab_last_moveresize_time); + +- if (elapsed >= 0.0 && elapsed < ms_between_resizes) +- { +- meta_topic (META_DEBUG_RESIZING, +- "Delaying move/resize as only %g of %g ms elapsed\n", +- elapsed, ms_between_resizes); ++ if (elapsed >= 0.0 && elapsed < ms_between_resizes) ++ { ++ meta_topic (META_DEBUG_RESIZING, ++ "Delaying move/resize as only %g of %g ms elapsed\n", ++ elapsed, ms_between_resizes); + +- if (remaining) +- *remaining = (ms_between_resizes - elapsed); ++ if (remaining) ++ *remaining = (ms_between_resizes - elapsed); + +- return FALSE; +- } ++ return FALSE; ++ } + +- meta_topic (META_DEBUG_RESIZING, +- " Checked moveresize freq, allowing move/resize now (%g of %g seconds elapsed)\n", +- elapsed / 1000.0, 1.0 / max_resizes_per_second); ++ meta_topic (META_DEBUG_RESIZING, ++ " Checked moveresize freq, allowing move/resize now (%g of %g seconds elapsed)\n", ++ elapsed / 1000.0, 1.0 / max_resizes_per_second); + +- return TRUE; +- } ++ return TRUE; + } + + static gboolean +@@ -9623,6 +9784,15 @@ update_resize (MetaWindow *window, + break; + } + ++#ifdef HAVE_XSYNC ++ /* If we're waiting for a request for _NET_WM_SYNC_REQUEST, we'll ++ * resize the window when the window responds, or when we time ++ * the response out. ++ */ ++ if (window->sync_request_timeout_id != 0) ++ return; ++#endif ++ + if (!check_moveresize_frequency (window, &remaining) && !force) + { + /* we are ignoring an event here, so we schedule a +@@ -9639,10 +9809,6 @@ update_resize (MetaWindow *window, + return; + } + +- /* If we get here, it means the client should have redrawn itself */ +- if (window->display->compositor) +- meta_compositor_set_updates (window->display->compositor, window, TRUE); +- + /* Remove any scheduled compensation events */ + if (window->display->grab_resize_timeout_id) + { +@@ -9833,6 +9999,60 @@ update_tile_mode (MetaWindow *window) + } + } + ++#ifdef HAVE_XSYNC ++void ++meta_window_update_sync_request_counter (MetaWindow *window, ++ gint64 new_counter_value) ++{ ++ gboolean needs_frame_drawn = FALSE; ++ gboolean no_delay_frame = FALSE; ++ ++ if (window->extended_sync_request_counter && new_counter_value % 2 == 0) ++ { ++ needs_frame_drawn = TRUE; ++ no_delay_frame = new_counter_value == window->sync_request_serial + 1; ++ } ++ ++ window->sync_request_serial = new_counter_value; ++ meta_compositor_set_updates_frozen (window->display->compositor, window, ++ meta_window_updates_are_frozen (window)); ++ ++ if (window == window->display->grab_window && ++ meta_grab_op_is_resizing (window->display->grab_op) && ++ new_counter_value >= window->sync_request_wait_serial && ++ (!window->extended_sync_request_counter || new_counter_value % 2 == 0) && ++ window->sync_request_timeout_id) ++ { ++ meta_topic (META_DEBUG_RESIZING, ++ "Alarm event received last motion x = %d y = %d\n", ++ window->display->grab_latest_motion_x, ++ window->display->grab_latest_motion_y); ++ ++ g_source_remove (window->sync_request_timeout_id); ++ window->sync_request_timeout_id = 0; ++ ++ /* This means we are ready for another configure; ++ * no pointer round trip here, to keep in sync */ ++ update_resize (window, ++ window->display->grab_last_user_action_was_snap, ++ window->display->grab_latest_motion_x, ++ window->display->grab_latest_motion_y, ++ TRUE, ++ FALSE); ++ } ++ ++ /* If sync was previously disabled, turn it back on and hope ++ * the application has come to its senses (maybe it was just ++ * busy with a pagefault or a long computation). ++ */ ++ window->disable_sync = FALSE; ++ ++ if (needs_frame_drawn) ++ meta_compositor_queue_frame_drawn (window->display->compositor, window, ++ no_delay_frame); ++} ++#endif /* HAVE_XSYNC */ ++ + LOCAL_SYMBOL void + meta_window_handle_mouse_grab_op_event (MetaWindow *window, + XEvent *event) +@@ -9850,8 +10070,7 @@ meta_window_handle_mouse_grab_op_event (MetaWindow *window, + * busy with a pagefault or a long computation). + */ + window->disable_sync = FALSE; +- window->sync_request_time.tv_sec = 0; +- window->sync_request_time.tv_usec = 0; ++ meta_window_destroy_sync_request_alarm (window); + + /* This means we are ready for another configure. */ + switch (window->display->grab_op) +@@ -9929,8 +10148,6 @@ meta_window_handle_mouse_grab_op_event (MetaWindow *window, + event->xbutton.y_root, + TRUE, + TRUE); +- if (window->display->compositor) +- meta_compositor_set_updates (window->display->compositor, window, TRUE); + + /* If a tiled window has been dragged free with a + * mouse resize without snapping back to the tiled +diff --git a/src/core/xprops.c b/src/core/xprops.c +index d246830..f1beda9 100644 +--- a/src/core/xprops.c ++++ b/src/core/xprops.c +@@ -580,6 +580,23 @@ counter_from_results (GetPropertyResults *results, + + return TRUE; + } ++ ++static gboolean ++counter_list_from_results (GetPropertyResults *results, ++ XSyncCounter **counters_p, ++ int *n_counters_p) ++{ ++ if (!validate_or_free_results (results, 32, ++ XA_CARDINAL, ++ FALSE)) ++ return FALSE; ++ ++ *counters_p = (XSyncCounter*) results->prop; ++ *n_counters_p = results->n_items; ++ results->prop = NULL; ++ ++ return TRUE; ++} + #endif + + LOCAL_SYMBOL gboolean +@@ -1015,6 +1032,7 @@ meta_prop_get_values (MetaDisplay *display, + values[i].required_type = XA_WM_SIZE_HINTS; + break; + case META_PROP_VALUE_SYNC_COUNTER: ++ case META_PROP_VALUE_SYNC_COUNTER_LIST: + values[i].required_type = XA_CARDINAL; + break; + } +@@ -1165,20 +1183,29 @@ meta_prop_get_values (MetaDisplay *display, + &values[i].v.size_hints.flags)) + values[i].type = META_PROP_VALUE_INVALID; + break; +- case META_PROP_VALUE_SYNC_COUNTER: + #ifdef HAVE_XSYNC ++ case META_PROP_VALUE_SYNC_COUNTER: + if (!counter_from_results (&results, + &values[i].v.xcounter)) + values[i].type = META_PROP_VALUE_INVALID; ++ break; ++ case META_PROP_VALUE_SYNC_COUNTER_LIST: ++ if (!counter_list_from_results (&results, ++ &values[i].v.xcounter_list.counters, ++ &values[i].v.xcounter_list.n_counters)) ++ values[i].type = META_PROP_VALUE_INVALID; ++ break; + #else ++ case META_PROP_VALUE_SYNC_COUNTER: ++ case META_PROP_VALUE_SYNC_COUNTER_LIST: + values[i].type = META_PROP_VALUE_INVALID; + if (results.prop) + { + XFree (results.prop); + results.prop = NULL; + } +-#endif + break; ++#endif + } + + next: +@@ -1231,6 +1258,9 @@ free_value (MetaPropValue *value) + break; + case META_PROP_VALUE_SYNC_COUNTER: + break; ++ case META_PROP_VALUE_SYNC_COUNTER_LIST: ++ meta_XFree (value->v.xcounter_list.counters); ++ break; + } + } + +diff --git a/src/core/xprops.h b/src/core/xprops.h +index b507bf8..06dcf5b 100644 +--- a/src/core/xprops.h ++++ b/src/core/xprops.h +@@ -157,7 +157,8 @@ typedef enum + META_PROP_VALUE_WM_HINTS, + META_PROP_VALUE_CLASS_HINT, + META_PROP_VALUE_SIZE_HINTS, +- META_PROP_VALUE_SYNC_COUNTER /* comes back as CARDINAL */ ++ META_PROP_VALUE_SYNC_COUNTER, /* comes back as CARDINAL */ ++ META_PROP_VALUE_SYNC_COUNTER_LIST /* comes back as CARDINAL */ + } MetaPropValueType; + + /* used to request/return/store property values */ +@@ -177,6 +178,11 @@ typedef struct + XClassHint class_hint; + #ifdef HAVE_XSYNC + XSyncCounter xcounter; ++ struct ++ { ++ gulong *counters; ++ int n_counters; ++ } xcounter_list; + #endif + + struct +diff --git a/src/meta/atomnames.h b/src/meta/atomnames.h +index 29b084c..f870059 100644 +--- a/src/meta/atomnames.h ++++ b/src/meta/atomnames.h +@@ -176,6 +176,8 @@ item(_NET_WM_FULLSCREEN_MONITORS) + item(_NET_WM_STATE_FOCUSED) + item(_NET_WM_BYPASS_COMPOSITOR) + item(_NET_WM_DONT_BYPASS_COMPOSITOR) ++item(_NET_WM_FRAME_DRAWN) ++item(_NET_WM_FRAME_TIMINGS) + + #if 0 + /* We apparently never use: */ +diff --git a/src/meta/compositor.h b/src/meta/compositor.h +index 8b4ec22..e0c635f 100644 +--- a/src/meta/compositor.h ++++ b/src/meta/compositor.h +@@ -148,10 +148,14 @@ void meta_compositor_window_mapped (MetaCompositor *compositor, + void meta_compositor_window_unmapped (MetaCompositor *compositor, + MetaWindow *window); + void meta_compositor_sync_window_geometry (MetaCompositor *compositor, +- MetaWindow *window); +-void meta_compositor_set_updates (MetaCompositor *compositor, + MetaWindow *window, +- gboolean updates); ++ gboolean did_placement); ++void meta_compositor_set_updates_frozen (MetaCompositor *compositor, ++ MetaWindow *window, ++ gboolean updates_frozen); ++void meta_compositor_queue_frame_drawn (MetaCompositor *compositor, ++ MetaWindow *window, ++ gboolean no_delay_frame); + + void meta_compositor_sync_stack (MetaCompositor *compositor, + MetaScreen *screen, +-- +2.4.3 + diff --git a/0002-Add-meta-sync-ring-and-nvidia-patch.patch b/0002-Add-meta-sync-ring-and-nvidia-patch.patch new file mode 100644 index 0000000..13189c7 --- /dev/null +++ b/0002-Add-meta-sync-ring-and-nvidia-patch.patch @@ -0,0 +1,876 @@ +From a461e4fd1e6407776b5d5903e1584fb8c07e8a0e Mon Sep 17 00:00:00 2001 +From: Michael Webster +Date: Sun, 4 Oct 2015 01:09:49 -0400 +Subject: [PATCH 2/4] Add meta-sync-ring and nvidia patch - + +Fixes https://github.com/linuxmint/muffin/issues/200 +--- + configure.ac | 5 + + src/Makefile.am | 2 + + src/compositor/compositor-private.h | 8 +- + src/compositor/compositor.c | 109 +++++-- + src/compositor/meta-sync-ring.c | 579 ++++++++++++++++++++++++++++++++++++ + src/compositor/meta-sync-ring.h | 14 + + 6 files changed, 683 insertions(+), 34 deletions(-) + create mode 100644 src/compositor/meta-sync-ring.c + create mode 100644 src/compositor/meta-sync-ring.h + +diff --git a/configure.ac b/configure.ac +index 3b4c65c..771c19a 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -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 ]) ++ + #### Warnings (last since -Werror can disturb other tests) + + # Stay command-line compatible with the gnome-common configure option. Here +diff --git a/src/Makefile.am b/src/Makefile.am +index f928663..55da40d 100644 +--- a/src/Makefile.am ++++ b/src/Makefile.am +@@ -54,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 \ +diff --git a/src/compositor/compositor-private.h b/src/compositor/compositor-private.h +index bb1da9a..206c112 100644 +--- a/src/compositor/compositor-private.h ++++ b/src/compositor/compositor-private.h +@@ -20,7 +20,8 @@ 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; + +@@ -33,6 +34,9 @@ struct _MetaCompositor + guint show_redraw : 1; + guint debug : 1; + guint no_mipmaps : 1; ++ ++ gboolean frame_has_updated_xsurfaces; ++ gboolean have_x11_sync_object; + }; + + struct _MetaCompScreen +@@ -59,8 +63,6 @@ struct _MetaCompScreen + gint switch_workspace_in_progress; + + MetaPluginManager *plugin_mgr; +- +- gboolean frame_has_updated_xsurfaces; + }; + + /* Wait 2ms after vblank before starting to draw next frame */ +diff --git a/src/compositor/compositor.c b/src/compositor/compositor.c +index 23f6093..f6a41da 100644 +--- a/src/compositor/compositor.c ++++ b/src/compositor/compositor.c +@@ -76,6 +76,7 @@ + #include "display-private.h" /* for meta_display_lookup_x_window() */ + #include + #include ++#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 +@@ -696,6 +701,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 +@@ -882,10 +889,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 + { +@@ -894,20 +901,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) +@@ -934,6 +941,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) +@@ -1324,10 +1334,13 @@ frame_callback (CoglOnscreen *onscreen, + } + } + +-static void +-pre_paint_windows (MetaCompScreen *info) ++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; + +@@ -1370,23 +1383,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; +@@ -1451,10 +1492,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; + } + +diff --git a/src/compositor/meta-sync-ring.c b/src/compositor/meta-sync-ring.c +new file mode 100644 +index 0000000..217ebe5 +--- /dev/null ++++ b/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 ++ */ ++ ++#include ++ ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include ++ ++#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); ++} +diff --git a/src/compositor/meta-sync-ring.h b/src/compositor/meta-sync-ring.h +new file mode 100644 +index 0000000..6dca8ef +--- /dev/null ++++ b/src/compositor/meta-sync-ring.h +@@ -0,0 +1,14 @@ ++#ifndef _META_SYNC_RING_H_ ++#define _META_SYNC_RING_H_ ++ ++#include ++ ++#include ++ ++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_ */ +-- +2.4.3 + diff --git a/0003-Fix-some-warnings.patch b/0003-Fix-some-warnings.patch new file mode 100644 index 0000000..c01f7c8 --- /dev/null +++ b/0003-Fix-some-warnings.patch @@ -0,0 +1,41 @@ +From c9c265187a96a35d108608e7d45089117db734c3 Mon Sep 17 00:00:00 2001 +From: Michael Webster +Date: Fri, 9 Oct 2015 16:39:56 -0400 +Subject: [PATCH 3/4] Fix some warnings + +--- + src/compositor/cogl-utils.h | 3 --- + src/compositor/compositor.c | 2 +- + src/core/constraints.c | 2 -- + 3 files changed, 1 insertion(+), 6 deletions(-) + +diff --git a/src/compositor/cogl-utils.h b/src/compositor/cogl-utils.h +index 1ad626b..e201039 100644 +--- a/src/compositor/cogl-utils.h ++++ b/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 + #include + +diff --git a/src/compositor/compositor.c b/src/compositor/compositor.c +index f6a41da..7d6270d 100644 +--- a/src/compositor/compositor.c ++++ b/src/compositor/compositor.c +@@ -1354,7 +1354,7 @@ meta_pre_paint_func (gpointer data) + } + + if (info->windows == NULL) +- return; ++ return TRUE;; + + top_window = g_list_last (info->windows)->data; + +-- +2.4.3 + diff --git a/0004-compositor.c-handle-unstable-API-break.patch b/0004-compositor.c-handle-unstable-API-break.patch new file mode 100644 index 0000000..5d53d08 --- /dev/null +++ b/0004-compositor.c-handle-unstable-API-break.patch @@ -0,0 +1,32 @@ +From 1cfb059a2854e382aef24ec12c7b822cd00f6ab1 Mon Sep 17 00:00:00 2001 +From: mtwebster +Date: Sun, 11 Oct 2015 10:19:38 -0400 +Subject: [PATCH 4/4] compositor.c: handle unstable API break + +--- + src/compositor/compositor.c | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/src/compositor/compositor.c b/src/compositor/compositor.c +index 7d6270d..91455a7 100644 +--- a/src/compositor/compositor.c ++++ b/src/compositor/compositor.c +@@ -621,10 +621,15 @@ 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); + +-- +2.4.3 + diff --git a/muffin.spec b/muffin.spec index edcf440..55bb4c7 100644 --- a/muffin.spec +++ b/muffin.spec @@ -2,7 +2,7 @@ Name: muffin Version: 2.6.1 -Release: 2%{?dist} +Release: 3%{?dist} Summary: Window and compositing manager based on Clutter Group: User Interface/Desktops @@ -21,6 +21,11 @@ Source0: http://leigh123linux.fedorapeople.org/pub/muffin/source/muffin-%{ # to fix https://bugzilla.redhat.com/show_bug.cgi?id=1082118 Patch0: revert_ibus.patch Patch1: remove_muffin_binary.patch +# https://github.com/linuxmint/muffin/issues/200 +Patch2: 0001-Implement-frame-sync-between-apps-and-compositor.patch +Patch3: 0002-Add-meta-sync-ring-and-nvidia-patch.patch +Patch4: 0003-Fix-some-warnings.patch +Patch5: 0004-compositor.c-handle-unstable-API-break.patch BuildRequires: pkgconfig(clutter-1.0) >= 1.7.5 BuildRequires: pkgconfig(sm) @@ -109,6 +114,9 @@ fi %{_mandir}/man1/muffin-* %changelog +* Sun Oct 11 2015 Leigh Scott - 2.6.1-3 +- add mtwebster's nvidia sync fixes + * Tue Jun 16 2015 Leigh Scott - 2.6.1-2 - fix packaging error (bz 1230988)