[mutter] clutter: Paint views with individual frame clocks



commit a9a9a0d1c593ee27b449234fd6fbb66e36d5a6ab
Author: Jonas Ã…dahl <jadahl gmail com>
Date:   Sat May 30 00:27:56 2020 +0200

    clutter: Paint views with individual frame clocks
    
    Replace the default master clock with multiple frame clocks, each
    driving its own stage view. As each stage view represents one CRTC, this
    means we draw each CRTC with its own designated frame clock,
    disconnected from all the others.
    
    For example this means we when using the native backend will never need
    to wait for one monitor to vsync before painting another, so e.g. having
    a 144 Hz monitor next to a 60 Hz monitor, things including both Wayland
    and X11 applications and shell UI will be able to render at the
    corresponding monitor refresh rate.
    
    This also changes a warning about missed frames when sending
    _NETWM_FRAME_TIMINGS messages to a debug log entry, as it's expected
    that we'll start missing frames e.g. when a X11 window (via Xwayland) is
    exclusively within a stage view that was not painted, while another one
    was, still increasing the global frame clock.
    
    Addititonally, this also requires the X11 window actor to schedule
    timeouts for _NET_WM_FRAME_DRAWN/_NET_WM_FRAME_TIMINGS event emitting,
    if the actor wasn't on any stage views, as now we'll only get the frame
    callbacks on actors when they actually were painted, while in the past,
    we'd invoke that vfunc when anything was painted.
    
    Closes: https://gitlab.gnome.org/GNOME/mutter/-/issues/903
    Closes: https://gitlab.gnome.org/GNOME/mutter/-/issues/3
    
    https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1285

 clutter/clutter/clutter-main.c                 |   1 -
 clutter/clutter/clutter-master-clock-default.c | 609 -------------------------
 clutter/clutter/clutter-master-clock-default.h |  48 --
 clutter/clutter/clutter-master-clock.c         | 132 ------
 clutter/clutter/clutter-master-clock.h         |  63 ---
 clutter/clutter/clutter-mutter.h               |   6 -
 clutter/clutter/clutter-pan-action.c           |   1 +
 clutter/clutter/clutter-private.h              |   4 -
 clutter/clutter/clutter-stage-private.h        |  17 +-
 clutter/clutter/clutter-stage-view-private.h   |   5 +
 clutter/clutter/clutter-stage-view.c           |  83 +++-
 clutter/clutter/clutter-stage-window.c         |  75 +--
 clutter/clutter/clutter-stage-window.h         |  15 +-
 clutter/clutter/clutter-stage.c                | 338 ++------------
 clutter/clutter/clutter-timeline.c             | 144 +++---
 clutter/clutter/cogl/clutter-stage-cogl.c      | 335 ++++----------
 clutter/clutter/cogl/clutter-stage-cogl.h      |  14 -
 clutter/clutter/meson.build                    |   4 -
 src/backends/meta-backend.c                    |  15 -
 src/backends/meta-cursor-renderer.c            |  15 +-
 src/backends/meta-renderer.c                   |  47 ++
 src/backends/meta-renderer.h                   |   4 +
 src/backends/native/meta-backend-native.c      |   7 +-
 src/backends/native/meta-renderer-native.c     | 193 +-------
 src/backends/native/meta-stage-native.c        |  81 ----
 src/backends/x11/meta-stage-x11.c              |  30 --
 src/compositor/compositor-private.h            |   6 +-
 src/compositor/compositor.c                    |  50 +-
 src/compositor/meta-compositor-native.c        |   5 +-
 src/compositor/meta-compositor-x11.c           |  15 +-
 src/compositor/meta-later.c                    |   5 +-
 src/compositor/meta-window-actor-private.h     |  12 +-
 src/compositor/meta-window-actor-wayland.c     |   6 +-
 src/compositor/meta-window-actor-x11.c         |  41 +-
 src/compositor/meta-window-actor.c             |  10 +-
 src/tests/clutter/conform/actor-clone.c        |   1 +
 src/tests/stage-view-tests.c                   |   5 +-
 src/wayland/meta-wayland.c                     |   6 +
 38 files changed, 540 insertions(+), 1908 deletions(-)
---
diff --git a/clutter/clutter/clutter-main.c b/clutter/clutter/clutter-main.c
index d052b14744..e78a33e2d4 100644
--- a/clutter/clutter/clutter-main.c
+++ b/clutter/clutter/clutter-main.c
@@ -61,7 +61,6 @@
 #include "clutter-input-pointer-a11y-private.h"
 #include "clutter-graphene.h"
 #include "clutter-main.h"
-#include "clutter-master-clock.h"
 #include "clutter-mutter.h"
 #include "clutter-paint-node-private.h"
 #include "clutter-private.h"
diff --git a/clutter/clutter/clutter-mutter.h b/clutter/clutter/clutter-mutter.h
index cc1ba9a972..5aa9a7e145 100644
--- a/clutter/clutter/clutter-mutter.h
+++ b/clutter/clutter/clutter-mutter.h
@@ -68,12 +68,6 @@ gboolean clutter_stage_paint_to_buffer (ClutterStage                 *stage,
                                         ClutterPaintFlag              paint_flags,
                                         GError                      **error);
 
-CLUTTER_EXPORT
-void clutter_stage_freeze_updates (ClutterStage *stage);
-
-CLUTTER_EXPORT
-void clutter_stage_thaw_updates (ClutterStage *stage);
-
 CLUTTER_EXPORT
 void clutter_stage_clear_stage_views (ClutterStage *stage);
 
diff --git a/clutter/clutter/clutter-pan-action.c b/clutter/clutter/clutter-pan-action.c
index b481467834..6a42627e49 100644
--- a/clutter/clutter/clutter-pan-action.c
+++ b/clutter/clutter/clutter-pan-action.c
@@ -62,6 +62,7 @@
 #include "clutter-gesture-action-private.h"
 #include "clutter-marshal.h"
 #include "clutter-private.h"
+#include "clutter-timeline.h"
 #include <math.h>
 
 #define FLOAT_EPSILON   (1e-15)
diff --git a/clutter/clutter/clutter-private.h b/clutter/clutter/clutter-private.h
index a71eac2526..e3f89ed09c 100644
--- a/clutter/clutter/clutter-private.h
+++ b/clutter/clutter/clutter-private.h
@@ -37,7 +37,6 @@
 #include "clutter-feature.h"
 #include "clutter-id-pool.h"
 #include "clutter-layout-manager.h"
-#include "clutter-master-clock.h"
 #include "clutter-settings.h"
 #include "clutter-stage-manager.h"
 #include "clutter-stage.h"
@@ -122,9 +121,6 @@ struct _ClutterMainContext
   /* the object holding all the stage instances */
   ClutterStageManager *stage_manager;
 
-  /* the clock driving all the frame operations */
-  ClutterMasterClock *master_clock;
-
   /* the main event queue */
   GQueue *events_queue;
 
diff --git a/clutter/clutter/clutter-stage-private.h b/clutter/clutter/clutter-stage-private.h
index 0c4c7066b0..7297d56a6d 100644
--- a/clutter/clutter/clutter-stage-private.h
+++ b/clutter/clutter/clutter-stage-private.h
@@ -40,10 +40,14 @@ void                clutter_stage_paint_view             (ClutterStage
                                                           ClutterStageView      *view,
                                                           const cairo_region_t  *redraw_clip);
 
-void                clutter_stage_emit_before_update     (ClutterStage          *stage);
-void                clutter_stage_emit_before_paint      (ClutterStage          *stage);
-void                clutter_stage_emit_after_paint       (ClutterStage          *stage);
-void                clutter_stage_emit_after_update      (ClutterStage          *stage);
+void                clutter_stage_emit_before_update     (ClutterStage          *stage,
+                                                          ClutterStageView      *view);
+void                clutter_stage_emit_before_paint      (ClutterStage          *stage,
+                                                          ClutterStageView      *view);
+void                clutter_stage_emit_after_paint       (ClutterStage          *stage,
+                                                          ClutterStageView      *view);
+void                clutter_stage_emit_after_update      (ClutterStage          *stage,
+                                                          ClutterStageView      *view);
 
 CLUTTER_EXPORT
 void                _clutter_stage_set_window            (ClutterStage          *stage,
@@ -67,8 +71,6 @@ GSList *            clutter_stage_find_updated_devices   (ClutterStage
 void                clutter_stage_update_devices         (ClutterStage          *stage,
                                                           GSList                *devices);
 void                clutter_stage_update_actor_stage_views (ClutterStage        *stage);
-gboolean            _clutter_stage_needs_update          (ClutterStage          *stage);
-gboolean            _clutter_stage_do_update             (ClutterStage          *stage);
 
 CLUTTER_EXPORT
 void     _clutter_stage_queue_event                       (ClutterStage *stage,
@@ -77,8 +79,6 @@ void     _clutter_stage_queue_event                       (ClutterStage *stage,
 gboolean _clutter_stage_has_queued_events                 (ClutterStage *stage);
 void     _clutter_stage_process_queued_events             (ClutterStage *stage);
 void     _clutter_stage_update_input_devices              (ClutterStage *stage);
-gint64    _clutter_stage_get_update_time                  (ClutterStage *stage);
-void     _clutter_stage_clear_update_time                 (ClutterStage *stage);
 gboolean _clutter_stage_has_full_redraw_queued            (ClutterStage *stage);
 
 void clutter_stage_log_pick (ClutterStage           *stage,
@@ -135,6 +135,7 @@ void                    _clutter_stage_set_scale_factor (ClutterStage      *stag
                                                          int                factor);
 
 void            clutter_stage_presented                 (ClutterStage      *stage,
+                                                         ClutterStageView  *view,
                                                          ClutterFrameInfo  *frame_info);
 
 void            clutter_stage_queue_actor_relayout      (ClutterStage *stage,
diff --git a/clutter/clutter/clutter-stage-view-private.h b/clutter/clutter/clutter-stage-view-private.h
index 02eb22b68a..71392f3551 100644
--- a/clutter/clutter/clutter-stage-view-private.h
+++ b/clutter/clutter/clutter-stage-view-private.h
@@ -63,6 +63,11 @@ void clutter_stage_view_transform_rect_to_onscreen (ClutterStageView
                                                     int                          dst_height,
                                                     cairo_rectangle_int_t       *dst_rect);
 
+void clutter_stage_view_schedule_update (ClutterStageView *view);
+
 float clutter_stage_view_get_refresh_rate (ClutterStageView *view);
 
+void clutter_stage_view_notify_presented (ClutterStageView *view,
+                                          ClutterFrameInfo *frame_info);
+
 #endif /* __CLUTTER_STAGE_VIEW_PRIVATE_H__ */
diff --git a/clutter/clutter/clutter-stage-view.c b/clutter/clutter/clutter-stage-view.c
index b5b04c5183..5dd8fc0d96 100644
--- a/clutter/clutter/clutter-stage-view.c
+++ b/clutter/clutter/clutter-stage-view.c
@@ -27,6 +27,7 @@
 #include "clutter/clutter-frame-clock.h"
 #include "clutter/clutter-private.h"
 #include "clutter/clutter-mutter.h"
+#include "clutter/clutter-stage-private.h"
 #include "cogl/cogl.h"
 
 enum
@@ -1001,6 +1002,15 @@ clutter_stage_view_take_scanout (ClutterStageView *view)
   return g_steal_pointer (&priv->next_scanout);
 }
 
+void
+clutter_stage_view_schedule_update (ClutterStageView *view)
+{
+  ClutterStageViewPrivate *priv =
+    clutter_stage_view_get_instance_private (view);
+
+  clutter_frame_clock_schedule_update (priv->frame_clock);
+}
+
 float
 clutter_stage_view_get_refresh_rate (ClutterStageView *view)
 {
@@ -1040,7 +1050,57 @@ handle_frame_clock_frame (ClutterFrameClock *frame_clock,
                           int64_t            time_us,
                           gpointer           user_data)
 {
-  return CLUTTER_FRAME_RESULT_IDLE;
+  ClutterStageView *view = user_data;
+  ClutterStageViewPrivate *priv =
+    clutter_stage_view_get_instance_private (view);
+  ClutterStage *stage = priv->stage;
+  g_autoptr (GSList) devices = NULL;
+  ClutterFrameResult result;
+
+  if (CLUTTER_ACTOR_IN_DESTRUCTION (stage))
+    return CLUTTER_FRAME_RESULT_IDLE;
+
+  if (!clutter_actor_is_realized (CLUTTER_ACTOR (stage)))
+    return CLUTTER_FRAME_RESULT_IDLE;
+
+  if (!clutter_actor_is_mapped (CLUTTER_ACTOR (stage)))
+    return CLUTTER_FRAME_RESULT_IDLE;
+
+  _clutter_run_repaint_functions (CLUTTER_REPAINT_FLAGS_PRE_PAINT);
+  clutter_stage_emit_before_update (stage, view);
+
+  clutter_stage_maybe_relayout (CLUTTER_ACTOR (stage));
+  clutter_stage_update_actor_stage_views (stage);
+  clutter_stage_maybe_finish_queue_redraws (stage);
+
+  devices = clutter_stage_find_updated_devices (stage);
+
+  if (clutter_stage_view_has_redraw_clip (view))
+    {
+      ClutterStageWindow *stage_window;
+
+      clutter_stage_emit_before_paint (stage, view);
+
+      stage_window = _clutter_stage_get_window (stage);
+      _clutter_stage_window_redraw_view (stage_window, view);
+
+      clutter_stage_emit_after_paint (stage, view);
+
+      _clutter_stage_window_finish_frame (stage_window);
+
+      result = CLUTTER_FRAME_RESULT_PENDING_PRESENTED;
+    }
+  else
+    {
+      result = CLUTTER_FRAME_RESULT_IDLE;
+    }
+
+  clutter_stage_update_devices (stage, devices);
+
+  _clutter_run_repaint_functions (CLUTTER_REPAINT_FLAGS_POST_PAINT);
+  clutter_stage_emit_after_update (stage, view);
+
+  return result;
 }
 
 static const ClutterFrameListenerIface frame_clock_listener_iface = {
@@ -1048,6 +1108,17 @@ static const ClutterFrameListenerIface frame_clock_listener_iface = {
   .frame = handle_frame_clock_frame,
 };
 
+void
+clutter_stage_view_notify_presented (ClutterStageView *view,
+                                     ClutterFrameInfo *frame_info)
+{
+  ClutterStageViewPrivate *priv =
+    clutter_stage_view_get_instance_private (view);
+
+  clutter_stage_presented (priv->stage, view, frame_info);
+  clutter_frame_clock_notify_presented (priv->frame_clock, frame_info);
+}
+
 static void
 sanity_check_framebuffer (ClutterStageView *view)
 {
@@ -1071,10 +1142,12 @@ clutter_stage_view_set_framebuffer (ClutterStageView *view,
   ClutterStageViewPrivate *priv =
     clutter_stage_view_get_instance_private (view);
 
-  priv->framebuffer = cogl_object_ref (framebuffer);
-
-  if (priv->framebuffer)
-    sanity_check_framebuffer (view);
+  g_warn_if_fail (!priv->framebuffer);
+  if (framebuffer)
+    {
+      priv->framebuffer = cogl_object_ref (framebuffer);
+      sanity_check_framebuffer (view);
+    }
 }
 
 static void
diff --git a/clutter/clutter/clutter-stage-window.c b/clutter/clutter/clutter-stage-window.c
index fe09aa1dba..e4e358e6fe 100644
--- a/clutter/clutter/clutter-stage-window.c
+++ b/clutter/clutter/clutter-stage-window.c
@@ -103,81 +103,12 @@ _clutter_stage_window_get_geometry (ClutterStageWindow    *window,
 }
 
 void
-_clutter_stage_window_schedule_update  (ClutterStageWindow *window,
-                                        int                 sync_delay)
+_clutter_stage_window_redraw_view (ClutterStageWindow *window,
+                                   ClutterStageView   *view)
 {
-  ClutterStageWindowInterface *iface;
-
-  g_return_if_fail (CLUTTER_IS_STAGE_WINDOW (window));
-
-  iface = CLUTTER_STAGE_WINDOW_GET_IFACE (window);
-  if (iface->schedule_update == NULL)
-    {
-      g_assert (!clutter_feature_available (CLUTTER_FEATURE_SWAP_EVENTS));
-      return;
-    }
-
-  iface->schedule_update (window, sync_delay);
-}
-
-/**
- * _clutter_stage_window_get_update_time:
- * @window: a #ClutterStageWindow object
- *
- * See _clutter_stage_get_update_time() for more info.
- *
- * Returns: The timestamp of the update time
- */
-gint64
-_clutter_stage_window_get_update_time (ClutterStageWindow *window)
-{
-  ClutterStageWindowInterface *iface;
-
-  g_return_val_if_fail (CLUTTER_IS_STAGE_WINDOW (window), 0);
-
-  iface = CLUTTER_STAGE_WINDOW_GET_IFACE (window);
-  if (iface->get_update_time == NULL)
-    {
-      g_assert (!clutter_feature_available (CLUTTER_FEATURE_SWAP_EVENTS));
-      return 0;
-    }
-
-  return iface->get_update_time (window);
-}
-
-/**
- * _clutter_stage_window_clear_update_time:
- * @window: a #ClutterStageWindow object
- *
- * Clears the update time. See _clutter_stage_clear_update_time() for more info.
- */
-void
-_clutter_stage_window_clear_update_time (ClutterStageWindow *window)
-{
-  ClutterStageWindowInterface *iface;
-
-  g_return_if_fail (CLUTTER_IS_STAGE_WINDOW (window));
-
-  iface = CLUTTER_STAGE_WINDOW_GET_IFACE (window);
-  if (iface->clear_update_time == NULL)
-    {
-      g_assert (!clutter_feature_available (CLUTTER_FEATURE_SWAP_EVENTS));
-      return;
-    }
-
-  iface->clear_update_time (window);
-}
-
-void
-_clutter_stage_window_redraw (ClutterStageWindow *window)
-{
-  ClutterStageWindowInterface *iface;
-
   g_return_if_fail (CLUTTER_IS_STAGE_WINDOW (window));
 
-  iface = CLUTTER_STAGE_WINDOW_GET_IFACE (window);
-  if (iface->redraw)
-    iface->redraw (window);
+  CLUTTER_STAGE_WINDOW_GET_IFACE (window)->redraw_view (window, view);
 }
 
 gboolean
diff --git a/clutter/clutter/clutter-stage-window.h b/clutter/clutter/clutter-stage-window.h
index 9f78ed25c2..b4bb1a1bd0 100644
--- a/clutter/clutter/clutter-stage-window.h
+++ b/clutter/clutter/clutter-stage-window.h
@@ -44,12 +44,8 @@ struct _ClutterStageWindowInterface
   void              (* get_geometry)            (ClutterStageWindow *stage_window,
                                                  cairo_rectangle_int_t *geometry);
 
-  void              (* schedule_update)         (ClutterStageWindow *stage_window,
-                                                 int                 sync_delay);
-  gint64            (* get_update_time)         (ClutterStageWindow *stage_window);
-  void              (* clear_update_time)       (ClutterStageWindow *stage_window);
-
-  void              (* redraw)                  (ClutterStageWindow *stage_window);
+  void              (* redraw_view)             (ClutterStageWindow *stage_window,
+                                                 ClutterStageView   *view);
 
   gboolean          (* can_clip_redraws)        (ClutterStageWindow *stage_window);
 
@@ -78,15 +74,12 @@ void              _clutter_stage_window_resize                  (ClutterStageWin
 CLUTTER_EXPORT
 void              _clutter_stage_window_get_geometry            (ClutterStageWindow *window,
                                                                  cairo_rectangle_int_t *geometry);
-void              _clutter_stage_window_schedule_update         (ClutterStageWindow *window,
-                                                                 int                 sync_delay);
-gint64            _clutter_stage_window_get_update_time         (ClutterStageWindow *window);
-void              _clutter_stage_window_clear_update_time       (ClutterStageWindow *window);
 
 void              _clutter_stage_window_set_accept_focus        (ClutterStageWindow *window,
                                                                  gboolean            accept_focus);
 
-void              _clutter_stage_window_redraw                  (ClutterStageWindow *window);
+void               _clutter_stage_window_redraw_view            (ClutterStageWindow *window,
+                                                                 ClutterStageView   *view);
 
 gboolean          _clutter_stage_window_can_clip_redraws        (ClutterStageWindow *window);
 
diff --git a/clutter/clutter/clutter-stage.c b/clutter/clutter/clutter-stage.c
index 1f5f03987f..e9368fe444 100644
--- a/clutter/clutter/clutter-stage.c
+++ b/clutter/clutter/clutter-stage.c
@@ -60,11 +60,11 @@
 #include "clutter-debug.h"
 #include "clutter-enum-types.h"
 #include "clutter-event-private.h"
+#include "clutter-frame-clock.h"
 #include "clutter-id-pool.h"
 #include "clutter-input-device-private.h"
 #include "clutter-main.h"
 #include "clutter-marshal.h"
-#include "clutter-master-clock.h"
 #include "clutter-mutter.h"
 #include "clutter-paint-context-private.h"
 #include "clutter-paint-volume-private.h"
@@ -139,7 +139,6 @@ struct _ClutterStagePrivate
 
   int update_freeze_count;
 
-  gboolean needs_update;
   gboolean needs_update_devices;
   gboolean pending_finish_queue_redraws;
 
@@ -893,27 +892,31 @@ clutter_stage_paint_view (ClutterStage         *stage,
 }
 
 void
-clutter_stage_emit_before_update (ClutterStage *stage)
+clutter_stage_emit_before_update (ClutterStage     *stage,
+                                  ClutterStageView *view)
 {
-  g_signal_emit (stage, stage_signals[BEFORE_UPDATE], 0);
+  g_signal_emit (stage, stage_signals[BEFORE_UPDATE], 0, view);
 }
 
 void
-clutter_stage_emit_before_paint (ClutterStage *stage)
+clutter_stage_emit_before_paint (ClutterStage     *stage,
+                                 ClutterStageView *view)
 {
-  g_signal_emit (stage, stage_signals[BEFORE_PAINT], 0);
+  g_signal_emit (stage, stage_signals[BEFORE_PAINT], 0, view);
 }
 
 void
-clutter_stage_emit_after_paint (ClutterStage *stage)
+clutter_stage_emit_after_paint (ClutterStage     *stage,
+                                ClutterStageView *view)
 {
-  g_signal_emit (stage, stage_signals[AFTER_PAINT], 0);
+  g_signal_emit (stage, stage_signals[AFTER_PAINT], 0, view);
 }
 
 void
-clutter_stage_emit_after_update (ClutterStage *stage)
+clutter_stage_emit_after_update (ClutterStage     *stage,
+                                 ClutterStageView *view)
 {
-  g_signal_emit (stage, stage_signals[AFTER_UPDATE], 0);
+  g_signal_emit (stage, stage_signals[AFTER_UPDATE], 0, view);
 }
 
 static gboolean
@@ -1072,11 +1075,7 @@ _clutter_stage_queue_event (ClutterStage *stage,
   g_queue_push_tail (priv->event_queue, event);
 
   if (first_event)
-    {
-      ClutterMasterClock *master_clock = _clutter_master_clock_get_default ();
-      _clutter_master_clock_start_running (master_clock);
-      clutter_stage_schedule_update (stage);
-    }
+    clutter_stage_schedule_update (stage);
 }
 
 gboolean
@@ -1190,28 +1189,6 @@ _clutter_stage_process_queued_events (ClutterStage *stage)
   g_object_unref (stage);
 }
 
-/**
- * _clutter_stage_needs_update:
- * @stage: A #ClutterStage
- *
- * Determines if _clutter_stage_do_update() needs to be called.
- *
- * Return value: %TRUE if the stage need layout or painting
- */
-gboolean
-_clutter_stage_needs_update (ClutterStage *stage)
-{
-  ClutterStagePrivate *priv;
-
-  g_return_val_if_fail (CLUTTER_IS_STAGE (stage), FALSE);
-
-  priv = stage->priv;
-
-  return (priv->redraw_pending ||
-          priv->needs_update ||
-          priv->pending_relayouts != NULL);
-}
-
 void
 clutter_stage_queue_actor_relayout (ClutterStage *stage,
                                     ClutterActor *actor)
@@ -1276,52 +1253,6 @@ clutter_stage_maybe_relayout (ClutterActor *actor)
     priv->needs_update_devices = TRUE;
 }
 
-static void
-clutter_stage_do_redraw (ClutterStage *stage)
-{
-  ClutterActor *actor = CLUTTER_ACTOR (stage);
-  ClutterStagePrivate *priv = stage->priv;
-
-  if (CLUTTER_ACTOR_IN_DESTRUCTION (stage))
-    return;
-
-  if (priv->impl == NULL)
-    return;
-
-  COGL_TRACE_BEGIN_SCOPED (ClutterStagePaint, "Paint");
-
-  CLUTTER_NOTE (PAINT, "Redraw started for stage '%s'[%p]",
-                _clutter_actor_get_debug_name (actor),
-                stage);
-
-  if (_clutter_context_get_show_fps ())
-    {
-      if (priv->fps_timer == NULL)
-        priv->fps_timer = g_timer_new ();
-    }
-
-  _clutter_stage_window_redraw (priv->impl);
-
-  if (_clutter_context_get_show_fps ())
-    {
-      priv->timer_n_frames += 1;
-
-      if (g_timer_elapsed (priv->fps_timer, NULL) >= 1.0)
-        {
-          g_print ("*** FPS for %s: %i ***\n",
-                   _clutter_actor_get_debug_name (actor),
-                   priv->timer_n_frames);
-
-          priv->timer_n_frames = 0;
-          g_timer_start (priv->fps_timer);
-        }
-    }
-
-  CLUTTER_NOTE (PAINT, "Redraw finished for stage '%s'[%p]",
-                _clutter_actor_get_debug_name (actor),
-                stage);
-}
-
 GSList *
 clutter_stage_find_updated_devices (ClutterStage *stage)
 {
@@ -1431,75 +1362,6 @@ clutter_stage_update_devices (ClutterStage *stage,
     }
 }
 
-/**
- * _clutter_stage_do_update:
- * @stage: A #ClutterStage
- *
- * Handles per-frame layout and repaint for the stage.
- *
- * Return value: %TRUE if the stage was updated
- */
-gboolean
-_clutter_stage_do_update (ClutterStage *stage)
-{
-  ClutterStagePrivate *priv = stage->priv;
-  g_autoptr (GSList) devices = NULL;
-
-  priv->needs_update = FALSE;
-
-  /* if the stage is being destroyed, or if the destruction already
-   * happened and we don't have an StageWindow any more, then we
-   * should bail out
-   */
-  if (CLUTTER_ACTOR_IN_DESTRUCTION (stage) || priv->impl == NULL)
-    return FALSE;
-
-  if (!CLUTTER_ACTOR_IS_REALIZED (stage))
-    return FALSE;
-
-  COGL_TRACE_BEGIN_SCOPED (ClutterStageDoUpdate, "Update");
-
-  clutter_stage_emit_before_update (stage);
-
-  /* NB: We need to ensure we have an up to date layout *before* we
-   * check or clear the pending redraws flag since a relayout may
-   * queue a redraw.
-   */
-  clutter_stage_maybe_relayout (CLUTTER_ACTOR (stage));
-
-  if (!priv->redraw_pending)
-    {
-      clutter_stage_emit_after_update (stage);
-      return FALSE;
-    }
-
-  clutter_stage_update_actor_stage_views (stage);
-  clutter_stage_maybe_finish_queue_redraws (stage);
-
-  devices = clutter_stage_find_updated_devices (stage);
-
-  clutter_stage_do_redraw (stage);
-
-  /* reset the guard, so that new redraws are possible */
-  priv->redraw_pending = FALSE;
-
-#ifdef CLUTTER_ENABLE_DEBUG
-  if (priv->redraw_count > 0)
-    {
-      CLUTTER_NOTE (SCHEDULER, "Queued %lu redraws during the last cycle",
-                    priv->redraw_count);
-
-      priv->redraw_count = 0;
-    }
-#endif /* CLUTTER_ENABLE_DEBUG */
-
-  clutter_stage_update_devices (stage, devices);
-
-  clutter_stage_emit_after_update (stage);
-
-  return TRUE;
-}
-
 static void
 clutter_stage_real_queue_relayout (ClutterActor *self)
 {
@@ -2028,6 +1890,7 @@ clutter_stage_class_init (ClutterStageClass *klass)
   /**
    * ClutterStage::before-update:
    * @stage: the #ClutterStage
+   * @view: a #ClutterStageView
    */
   stage_signals[BEFORE_UPDATE] =
     g_signal_new (I_("before-update"),
@@ -2035,11 +1898,13 @@ clutter_stage_class_init (ClutterStageClass *klass)
                   G_SIGNAL_RUN_LAST,
                   0,
                   NULL, NULL, NULL,
-                  G_TYPE_NONE, 0);
+                  G_TYPE_NONE, 1,
+                  CLUTTER_TYPE_STAGE_VIEW);
 
   /**
    * ClutterStage::before-paint:
    * @stage: the stage that received the event
+   * @view: a #ClutterStageView
    *
    * The ::before-paint signal is emitted before the stage is painted.
    */
@@ -2049,11 +1914,12 @@ clutter_stage_class_init (ClutterStageClass *klass)
                   G_SIGNAL_RUN_LAST,
                   0,
                   NULL, NULL, NULL,
-                  G_TYPE_NONE, 0);
+                  G_TYPE_NONE, 1,
+                  CLUTTER_TYPE_STAGE_VIEW);
   /**
    * ClutterStage::after-paint:
    * @stage: the stage that received the event
-   * @paint_Context: the paint context
+   * @view: a #ClutterStageView
    *
    * The ::after-paint signal is emitted after the stage is painted,
    * but before the results are displayed on the screen.
@@ -2066,11 +1932,13 @@ clutter_stage_class_init (ClutterStageClass *klass)
                   G_SIGNAL_RUN_LAST,
                   0, /* no corresponding vfunc */
                   NULL, NULL, NULL,
-                  G_TYPE_NONE, 0);
+                  G_TYPE_NONE, 1,
+                  CLUTTER_TYPE_STAGE_VIEW);
 
   /**
    * ClutterStage::after-update:
    * @stage: the #ClutterStage
+   * @view: a #ClutterStageView
    */
   stage_signals[AFTER_UPDATE] =
     g_signal_new (I_("after-update"),
@@ -2078,7 +1946,8 @@ clutter_stage_class_init (ClutterStageClass *klass)
                   G_SIGNAL_RUN_LAST,
                   0,
                   NULL, NULL, NULL,
-                  G_TYPE_NONE, 0);
+                  G_TYPE_NONE, 1,
+                  CLUTTER_TYPE_STAGE_VIEW);
 
   /**
    * ClutterStage::paint-view:
@@ -2106,6 +1975,7 @@ clutter_stage_class_init (ClutterStageClass *klass)
   /**
    * ClutterStage::presented: (skip)
    * @stage: the stage that received the event
+   * @view: the #ClutterStageView presented
    * @frame_info: a #ClutterFrameInfo
    *
    * Signals that the #ClutterStage was presented on the screen to the user.
@@ -2116,7 +1986,8 @@ clutter_stage_class_init (ClutterStageClass *klass)
                   G_SIGNAL_RUN_LAST,
                   0,
                   NULL, NULL, NULL,
-                  G_TYPE_NONE, 1,
+                  G_TYPE_NONE, 2,
+                  CLUTTER_TYPE_STAGE_VIEW,
                   G_TYPE_POINTER);
 
   klass->activate = clutter_stage_real_activate;
@@ -3204,6 +3075,7 @@ void
 clutter_stage_schedule_update (ClutterStage *stage)
 {
   ClutterStageWindow *stage_window;
+  GList *l;
 
   if (CLUTTER_ACTOR_IN_DESTRUCTION (stage))
     return;
@@ -3212,54 +3084,12 @@ clutter_stage_schedule_update (ClutterStage *stage)
   if (stage_window == NULL)
     return;
 
-  stage->priv->needs_update = TRUE;
-
-  return _clutter_stage_window_schedule_update (stage_window,
-                                                stage->priv->sync_delay);
-}
-
-/**
- * _clutter_stage_get_update_time:
- * @stage: a #ClutterStage actor
- *
- * Returns the earliest time in which the stage is ready to update. The update
- * time is set when clutter_stage_schedule_update() is called. This can then
- * be used by e.g. the #ClutterMasterClock to know when the stage needs to be
- * redrawn.
- *
- * Returns: -1 if no redraw is needed; 0 if the backend doesn't know, or the
- * timestamp (in microseconds) otherwise.
- */
-gint64
-_clutter_stage_get_update_time (ClutterStage *stage)
-{
-  ClutterStageWindow *stage_window;
-
-  if (CLUTTER_ACTOR_IN_DESTRUCTION (stage))
-    return 0;
-
-  stage_window = _clutter_stage_get_window (stage);
-  if (stage_window == NULL)
-    return 0;
-
-  return _clutter_stage_window_get_update_time (stage_window);
-}
-
-/**
- * _clutter_stage_clear_update_time:
- * @stage: a #ClutterStage actor
- *
- * Resets the update time. Call this after a redraw, so that the update time
- * can again be updated.
- */
-void
-_clutter_stage_clear_update_time (ClutterStage *stage)
-{
-  ClutterStageWindow *stage_window;
+  for (l = clutter_stage_peek_stage_views (stage); l; l = l->next)
+    {
+      ClutterStageView *view = l->data;
 
-  stage_window = _clutter_stage_get_window (stage);
-  if (stage_window)
-    _clutter_stage_window_clear_update_time (stage_window);
+      clutter_stage_view_schedule_update (view);
+    }
 }
 
 ClutterPaintVolume *
@@ -3327,19 +3157,26 @@ _clutter_stage_queue_actor_redraw (ClutterStage                 *stage,
    */
   priv->cached_pick_mode = CLUTTER_PICK_NONE;
 
-  priv->pending_finish_queue_redraws = TRUE;
+  if (!priv->pending_finish_queue_redraws)
+    {
+      GList *l;
+
+      for (l = clutter_stage_peek_stage_views (stage); l; l = l->next)
+        {
+          ClutterStageView *view = l->data;
+
+          clutter_stage_view_schedule_update (view);
+        }
+
+      priv->pending_finish_queue_redraws = TRUE;
+    }
 
   if (!priv->redraw_pending)
     {
-      ClutterMasterClock *master_clock;
-
       CLUTTER_NOTE (PAINT, "First redraw request");
 
       clutter_stage_schedule_update (stage);
       priv->redraw_pending = TRUE;
-
-      master_clock = _clutter_master_clock_get_default ();
-      _clutter_master_clock_start_running (master_clock);
     }
 #ifdef CLUTTER_ENABLE_DEBUG
   else
@@ -3749,27 +3586,6 @@ clutter_stage_set_sync_delay (ClutterStage *stage,
   stage->priv->sync_delay = sync_delay;
 }
 
-/**
- * clutter_stage_skip_sync_delay:
- * @stage: a #ClutterStage
- *
- * Causes the next frame for the stage to be drawn as quickly as
- * possible, ignoring any delay that clutter_stage_set_sync_delay()
- * would normally cause.
- *
- * Since: 1.14
- * Stability: unstable
- */
-void
-clutter_stage_skip_sync_delay (ClutterStage *stage)
-{
-  ClutterStageWindow *stage_window;
-
-  stage_window = _clutter_stage_get_window (stage);
-  if (stage_window)
-    _clutter_stage_window_schedule_update (stage_window, -1);
-}
-
 int64_t
 clutter_stage_get_frame_counter (ClutterStage          *stage)
 {
@@ -3781,9 +3597,10 @@ clutter_stage_get_frame_counter (ClutterStage          *stage)
 
 void
 clutter_stage_presented (ClutterStage     *stage,
+                         ClutterStageView *view,
                          ClutterFrameInfo *frame_info)
 {
-  g_signal_emit (stage, stage_signals[PRESENTED], 0, frame_info);
+  g_signal_emit (stage, stage_signals[PRESENTED], 0, view, frame_info);
 }
 
 static void
@@ -4123,61 +3940,6 @@ clutter_stage_capture_into (ClutterStage          *stage,
     }
 }
 
-/**
- * clutter_stage_freeze_updates:
- *
- * Freezing updates makes Clutter stop processing events,
- * redrawing, and advancing timelines, by pausing the master clock. This is
- * necessary when implementing a display server, to ensure that Clutter doesn't
- * keep trying to page flip when DRM master has been dropped, e.g. when VT
- * switched away.
- *
- * The master clock starts out running, so if you are VT switched away on
- * startup, you need to call this immediately.
- *
- * To thaw updates, use clutter_stage_thaw_updates().
- */
-void
-clutter_stage_freeze_updates (ClutterStage *stage)
-{
-  ClutterStagePrivate *priv = stage->priv;
-
-  priv->update_freeze_count++;
-  if (priv->update_freeze_count == 1)
-    {
-      ClutterMasterClock *master_clock;
-
-      master_clock = _clutter_master_clock_get_default ();
-      _clutter_master_clock_set_paused (master_clock, TRUE);
-    }
-}
-
-/**
- * clutter_stage_thaw_updates:
- *
- * Resumes a master clock that has previously been frozen with
- * clutter_stage_freeze_updates(), and start pumping the master clock
- * again at the next iteration. Note that if you're switching back to your
- * own VT, you should probably also queue a stage redraw with
- * clutter_stage_ensure_redraw().
- */
-void
-clutter_stage_thaw_updates (ClutterStage *stage)
-{
-  ClutterStagePrivate *priv = stage->priv;
-
-  g_assert (priv->update_freeze_count > 0);
-
-  priv->update_freeze_count--;
-  if (priv->update_freeze_count == 0)
-    {
-      ClutterMasterClock *master_clock;
-
-      master_clock = _clutter_master_clock_get_default ();
-      _clutter_master_clock_set_paused (master_clock, FALSE);
-    }
-}
-
 /**
  * clutter_stage_peek_stage_views: (skip)
  */
diff --git a/clutter/clutter/clutter-timeline.c b/clutter/clutter/clutter-timeline.c
index 353a24ca9b..2cf460d5f2 100644
--- a/clutter/clutter/clutter-timeline.c
+++ b/clutter/clutter/clutter-timeline.c
@@ -103,7 +103,6 @@
 #include "clutter-frame-clock.h"
 #include "clutter-main.h"
 #include "clutter-marshal.h"
-#include "clutter-master-clock.h"
 #include "clutter-private.h"
 #include "clutter-scriptable.h"
 #include "clutter-timeline-private.h"
@@ -112,10 +111,12 @@ struct _ClutterTimelinePrivate
 {
   ClutterTimelineDirection direction;
 
+  ClutterFrameClock *custom_frame_clock;
   ClutterFrameClock *frame_clock;
 
   ClutterActor *actor;
   gulong actor_destroy_handler_id;
+  gulong actor_stage_views_handler_id;
 
   guint delay_id;
 
@@ -322,6 +323,70 @@ clutter_timeline_get_actor (ClutterTimeline *timeline)
   return priv->actor;
 }
 
+static void
+maybe_add_timeline (ClutterTimeline *timeline)
+{
+  ClutterTimelinePrivate *priv = timeline->priv;
+
+  if (!priv->frame_clock)
+    return;
+
+  clutter_frame_clock_add_timeline (priv->frame_clock, timeline);
+}
+
+static void
+maybe_remove_timeline (ClutterTimeline *timeline)
+{
+  ClutterTimelinePrivate *priv = timeline->priv;
+
+  if (!priv->frame_clock)
+    return;
+
+  clutter_frame_clock_remove_timeline (priv->frame_clock, timeline);
+}
+
+static void
+set_frame_clock_internal (ClutterTimeline   *timeline,
+                          ClutterFrameClock *frame_clock)
+{
+  ClutterTimelinePrivate *priv = timeline->priv;
+
+  if (priv->frame_clock == frame_clock)
+    return;
+
+  if (priv->frame_clock && priv->is_playing)
+    maybe_remove_timeline (timeline);
+
+  g_set_object (&priv->frame_clock, frame_clock);
+
+  g_object_notify_by_pspec (G_OBJECT (timeline),
+                            obj_props[PROP_FRAME_CLOCK]);
+
+  if (priv->is_playing)
+    maybe_add_timeline (timeline);
+}
+
+static void
+update_frame_clock (ClutterTimeline *timeline)
+{
+  ClutterTimelinePrivate *priv = timeline->priv;
+  ClutterFrameClock *frame_clock;
+
+  if (priv->actor)
+    frame_clock = clutter_actor_pick_frame_clock (priv->actor);
+  else
+    frame_clock = NULL;
+
+  set_frame_clock_internal (timeline, frame_clock);
+}
+
+static void
+on_actor_stage_views_changed (ClutterActor    *actor,
+                              ClutterTimeline *timeline)
+{
+  update_frame_clock (timeline);
+}
+
 /**
  * clutter_timeline_set_actor:
  * @timeline: a #ClutterTimeline
@@ -335,9 +400,18 @@ clutter_timeline_set_actor (ClutterTimeline *timeline,
 {
   ClutterTimelinePrivate *priv = timeline->priv;
 
+  g_return_if_fail (!actor || (actor && !priv->custom_frame_clock));
+
   if (priv->actor)
     {
       g_clear_signal_handler (&priv->actor_destroy_handler_id, priv->actor);
+      g_clear_signal_handler (&priv->actor_stage_views_handler_id, priv->actor);
+      priv->actor = NULL;
+
+      if (priv->is_playing)
+        maybe_remove_timeline (timeline);
+
+      priv->frame_clock = NULL;
     }
 
   priv->actor = actor;
@@ -348,7 +422,13 @@ clutter_timeline_set_actor (ClutterTimeline *timeline,
         g_signal_connect (priv->actor, "destroy",
                           G_CALLBACK (on_actor_destroyed),
                           timeline);
+      priv->actor_stage_views_handler_id =
+        g_signal_connect (priv->actor, "stage-views-changed",
+                          G_CALLBACK (on_actor_stage_views_changed),
+                          timeline);
     }
+
+  update_frame_clock (timeline);
 }
 
 /* Scriptable */
@@ -579,42 +659,6 @@ clutter_timeline_get_property (GObject    *object,
     }
 }
 
-static void
-add_timeline (ClutterTimeline *timeline)
-{
-  ClutterTimelinePrivate *priv = timeline->priv;
-
-  if (priv->frame_clock)
-    {
-      clutter_frame_clock_add_timeline (priv->frame_clock, timeline);
-    }
-  else
-    {
-      ClutterMasterClock *master_clock;
-
-      master_clock = _clutter_master_clock_get_default ();
-      _clutter_master_clock_add_timeline (master_clock, timeline);
-    }
-}
-
-static void
-remove_timeline (ClutterTimeline *timeline)
-{
-  ClutterTimelinePrivate *priv = timeline->priv;
-
-  if (priv->frame_clock)
-    {
-      clutter_frame_clock_remove_timeline (priv->frame_clock, timeline);
-    }
-  else
-    {
-      ClutterMasterClock *master_clock;
-
-      master_clock = _clutter_master_clock_get_default ();
-      _clutter_master_clock_remove_timeline (master_clock, timeline);
-    }
-}
-
 static void
 clutter_timeline_constructed (GObject *object)
 {
@@ -636,7 +680,7 @@ clutter_timeline_finalize (GObject *object)
     g_hash_table_destroy (priv->markers_by_name);
 
   if (priv->is_playing)
-    remove_timeline (self);
+    maybe_remove_timeline (self);
 
   g_clear_object (&priv->frame_clock);
 
@@ -656,6 +700,7 @@ clutter_timeline_dispose (GObject *object)
   if (priv->actor)
     {
       g_clear_signal_handler (&priv->actor_destroy_handler_id, priv->actor);
+      g_clear_signal_handler (&priv->actor_stage_views_handler_id, priv->actor);
       priv->actor = NULL;
     }
 
@@ -1093,11 +1138,11 @@ set_is_playing (ClutterTimeline *timeline,
       priv->waiting_first_tick = TRUE;
       priv->current_repeat = 0;
 
-      add_timeline (timeline);
+      maybe_add_timeline (timeline);
     }
   else
     {
-      remove_timeline (timeline);
+      maybe_remove_timeline (timeline);
     }
 }
 
@@ -1797,7 +1842,7 @@ _clutter_timeline_do_tick (ClutterTimeline *timeline,
 
   /* Check the is_playing variable before performing the timeline tick.
    * This is necessary, as if a timeline is stopped in response to a
-   * master-clock generated signal of a different timeline, this code can
+   * frame clock generated signal of a different timeline, this code can
    * still be reached.
    */
   if (!priv->is_playing)
@@ -2637,17 +2682,10 @@ clutter_timeline_set_frame_clock (ClutterTimeline   *timeline,
 
   priv = timeline->priv;
 
-  if (priv->frame_clock == frame_clock)
-    return;
-
-  if (priv->frame_clock && priv->is_playing)
-    remove_timeline (timeline);
-
-  g_set_object (&priv->frame_clock, frame_clock);
+  g_assert (!frame_clock || (frame_clock && !priv->actor));
+  g_return_if_fail (!frame_clock || (frame_clock && !priv->actor));
 
-  g_object_notify_by_pspec (G_OBJECT (timeline),
-                            obj_props[PROP_FRAME_CLOCK]);
-
-  if (priv->is_playing)
-    add_timeline (timeline);
+  priv->custom_frame_clock = frame_clock;
+  if (!priv->actor)
+    set_frame_clock_internal (timeline, frame_clock);
 }
diff --git a/clutter/clutter/cogl/clutter-stage-cogl.c b/clutter/clutter/cogl/clutter-stage-cogl.c
index 6aa912e715..695c594bb4 100644
--- a/clutter/clutter/cogl/clutter-stage-cogl.c
+++ b/clutter/clutter/cogl/clutter-stage-cogl.c
@@ -47,6 +47,7 @@
 #include "clutter-private.h"
 #include "clutter-stage-private.h"
 #include "clutter-stage-view-private.h"
+#include "cogl.h"
 
 #define MAX_STACK_RECTS 256
 
@@ -55,6 +56,8 @@ typedef struct _ClutterStageViewCoglPrivate
   /* Damage history, in stage view render target framebuffer coordinate space.
    */
   ClutterDamageHistory *damage_history;
+
+  guint notify_presented_handle_id;
 } ClutterStageViewCoglPrivate;
 
 G_DEFINE_TYPE_WITH_PRIVATE (ClutterStageViewCogl, clutter_stage_view_cogl,
@@ -83,67 +86,12 @@ enum
   PROP_LAST
 };
 
-static void
-clutter_stage_cogl_schedule_update (ClutterStageWindow *stage_window,
-                                    gint                sync_delay);
-
 static void
 clutter_stage_cogl_unrealize (ClutterStageWindow *stage_window)
 {
   CLUTTER_NOTE (BACKEND, "Unrealizing Cogl stage [%p]", stage_window);
 }
 
-void
-_clutter_stage_cogl_presented (ClutterStageCogl *stage_cogl,
-                               CoglFrameEvent    frame_event,
-                               ClutterFrameInfo *frame_info)
-{
-
-  if (frame_event == COGL_FRAME_EVENT_SYNC)
-    {
-      /* Early versions of the swap_event implementation in Mesa
-       * deliver BufferSwapComplete event when not selected for,
-       * so if we get a swap event we aren't expecting, just ignore it.
-       *
-       * https://bugs.freedesktop.org/show_bug.cgi?id=27962
-       *
-       * FIXME: This issue can be hidden inside Cogl so we shouldn't
-       * need to care about this bug here.
-       */
-      if (stage_cogl->pending_swaps > 0)
-        stage_cogl->pending_swaps--;
-    }
-  else if (frame_event == COGL_FRAME_EVENT_COMPLETE)
-    {
-      gint64 presentation_time_cogl = frame_info->presentation_time;
-
-      if (presentation_time_cogl != 0)
-        {
-          ClutterBackend *backend = stage_cogl->backend;
-          CoglContext *context = clutter_backend_get_cogl_context (backend);
-          gint64 current_time_cogl = cogl_get_clock_time (context);
-          gint64 now = g_get_monotonic_time ();
-
-          stage_cogl->last_presentation_time =
-            now + (presentation_time_cogl - current_time_cogl) / 1000;
-        }
-
-      stage_cogl->refresh_rate = frame_info->refresh_rate;
-    }
-
-  clutter_stage_presented (stage_cogl->wrapper, frame_info);
-
-  if (frame_event == COGL_FRAME_EVENT_COMPLETE &&
-      stage_cogl->update_time != -1)
-    {
-      ClutterStageWindow *stage_window = CLUTTER_STAGE_WINDOW (stage_cogl);
-
-      stage_cogl->update_time = -1;
-      clutter_stage_cogl_schedule_update (stage_window,
-                                          stage_cogl->last_sync_delay);
-    }
-}
-
 static gboolean
 clutter_stage_cogl_realize (ClutterStageWindow *stage_window)
 {
@@ -174,103 +122,6 @@ clutter_stage_cogl_get_frame_counter (ClutterStageWindow *stage_window)
   return priv->global_frame_counter;
 }
 
-static void
-clutter_stage_cogl_schedule_update (ClutterStageWindow *stage_window,
-                                    gint                sync_delay)
-{
-  ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_window);
-  gint64 now;
-  float refresh_rate;
-  gint64 refresh_interval;
-  int64_t min_render_time_allowed;
-  int64_t max_render_time_allowed;
-  int64_t next_presentation_time;
-
-  if (stage_cogl->update_time != -1)
-    return;
-
-  stage_cogl->last_sync_delay = sync_delay;
-
-  now = g_get_monotonic_time ();
-
-  if (sync_delay < 0)
-    {
-      stage_cogl->update_time = now;
-      return;
-    }
-
-  refresh_rate = stage_cogl->refresh_rate;
-  if (refresh_rate <= 0.0)
-    refresh_rate = clutter_get_default_frame_rate ();
-
-  refresh_interval = (gint64) (0.5 + G_USEC_PER_SEC / refresh_rate);
-  if (refresh_interval == 0)
-    {
-      stage_cogl->update_time = now;
-      return;
-    }
-
-  min_render_time_allowed = refresh_interval / 2;
-  max_render_time_allowed = refresh_interval - 1000 * sync_delay;
-
-  /* Be robust in the case of incredibly bogus refresh rate */
-  if (max_render_time_allowed <= 0)
-    {
-      g_warning ("Unsupported monitor refresh rate detected. "
-                 "(Refresh rate: %.3f, refresh interval: %" G_GINT64_FORMAT ")",
-                 refresh_rate,
-                 refresh_interval);
-      stage_cogl->update_time = now;
-      return;
-    }
-
-  if (min_render_time_allowed > max_render_time_allowed)
-    min_render_time_allowed = max_render_time_allowed;
-
-  next_presentation_time = stage_cogl->last_presentation_time + refresh_interval;
-
-  /* Get next_presentation_time closer to its final value, to reduce
-   * the number of while iterations below.
-   */
-  if (next_presentation_time < now)
-    {
-      int64_t last_virtual_presentation_time = now - now % refresh_interval;
-      int64_t hardware_clock_phase =
-        stage_cogl->last_presentation_time % refresh_interval;
-
-      next_presentation_time =
-        last_virtual_presentation_time + hardware_clock_phase;
-    }
-
-  while (next_presentation_time < now + min_render_time_allowed)
-    next_presentation_time += refresh_interval;
-
-  stage_cogl->update_time = next_presentation_time - max_render_time_allowed;
-
-  if (stage_cogl->update_time == stage_cogl->last_update_time)
-    stage_cogl->update_time = stage_cogl->last_update_time + refresh_interval;
-}
-
-static gint64
-clutter_stage_cogl_get_update_time (ClutterStageWindow *stage_window)
-{
-  ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_window);
-
-  if (stage_cogl->pending_swaps)
-    return -1; /* in the future, indefinite */
-
-  return stage_cogl->update_time;
-}
-
-static void
-clutter_stage_cogl_clear_update_time (ClutterStageWindow *stage_window)
-{
-  ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_window);
-
-  stage_cogl->last_update_time = stage_cogl->update_time;
-  stage_cogl->update_time = -1;
-}
-
 static ClutterActor *
 clutter_stage_cogl_get_wrapper (ClutterStageWindow *stage_window)
 {
@@ -371,7 +222,27 @@ paint_damage_region (ClutterStageWindow *stage_window,
   cogl_framebuffer_pop_matrix (framebuffer);
 }
 
+typedef struct _NotifyPresentedClosure
+{
+  ClutterStageView *view;
+  ClutterFrameInfo frame_info;
+} NotifyPresentedClosure;
+
 static gboolean
+notify_presented_idle (gpointer user_data)
+{
+  NotifyPresentedClosure *closure = user_data;
+  ClutterStageViewCogl *view_cogl = CLUTTER_STAGE_VIEW_COGL (closure->view);
+  ClutterStageViewCoglPrivate *view_priv =
+    clutter_stage_view_cogl_get_instance_private (view_cogl);
+
+  view_priv->notify_presented_handle_id = 0;
+  clutter_stage_view_notify_presented (closure->view, &closure->frame_info);
+
+  return G_SOURCE_REMOVE;
+}
+
+static void
 swap_framebuffer (ClutterStageWindow *stage_window,
                   ClutterStageView   *view,
                   cairo_region_t     *swap_region,
@@ -416,8 +287,6 @@ swap_framebuffer (ClutterStageWindow *stage_window,
           cogl_onscreen_swap_region (onscreen,
                                      damage, n_rects,
                                      frame_info);
-
-          return FALSE;
         }
       else
         {
@@ -427,17 +296,33 @@ swap_framebuffer (ClutterStageWindow *stage_window,
           cogl_onscreen_swap_buffers_with_damage (onscreen,
                                                   damage, n_rects,
                                                   frame_info);
-
-          return TRUE;
         }
     }
   else
     {
+      ClutterStageViewCogl *view_cogl = CLUTTER_STAGE_VIEW_COGL (view);
+      ClutterStageViewCoglPrivate *view_priv =
+        clutter_stage_view_cogl_get_instance_private (view_cogl);
+      NotifyPresentedClosure *closure;
+
       CLUTTER_NOTE (BACKEND, "cogl_framebuffer_finish (framebuffer: %p)",
                     framebuffer);
       cogl_framebuffer_finish (framebuffer);
 
-      return FALSE;
+      closure = g_new0 (NotifyPresentedClosure, 1);
+      closure->view = view;
+      closure->frame_info = (ClutterFrameInfo) {
+        .frame_counter = priv->global_frame_counter,
+        .refresh_rate = clutter_stage_view_get_refresh_rate (view),
+        .presentation_time = g_get_monotonic_time (),
+      };
+      priv->global_frame_counter++;
+
+      g_warn_if_fail (view_priv->notify_presented_handle_id == 0);
+      view_priv->notify_presented_handle_id =
+        g_idle_add_full (G_PRIORITY_DEFAULT,
+                         notify_presented_idle,
+                         closure, g_free);
     }
 }
 
@@ -564,11 +449,11 @@ is_buffer_age_enabled (void)
          cogl_clutter_winsys_has_feature (COGL_WINSYS_FEATURE_BUFFER_AGE);
 }
 
-static gboolean
-clutter_stage_cogl_redraw_view (ClutterStageWindow *stage_window,
-                                ClutterStageView   *view)
+static void
+clutter_stage_cogl_redraw_view_primary (ClutterStageCogl *stage_cogl,
+                                        ClutterStageView *view)
 {
-  ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_window);
+  ClutterStageWindow *stage_window = CLUTTER_STAGE_WINDOW (stage_cogl);
   ClutterStageViewCogl *view_cogl = CLUTTER_STAGE_VIEW_COGL (view);
   ClutterStageViewCoglPrivate *view_priv =
     clutter_stage_view_cogl_get_instance_private (view_cogl);
@@ -587,7 +472,6 @@ clutter_stage_cogl_redraw_view (ClutterStageWindow *stage_window,
   float fb_scale;
   int fb_width, fb_height;
   int buffer_age = 0;
-  gboolean res;
 
   clutter_stage_view_get_layout (view, &view_rect);
   fb_scale = clutter_stage_view_get_scale (view);
@@ -654,7 +538,7 @@ clutter_stage_cogl_redraw_view (ClutterStageWindow *stage_window,
       redraw_clip = cairo_region_create_rectangle (&view_rect);
     }
 
-  g_return_val_if_fail (!cairo_region_is_empty (fb_clip_region), FALSE);
+  g_return_if_fail (!cairo_region_is_empty (fb_clip_region));
 
   swap_with_damage = FALSE;
   if (has_buffer_age)
@@ -755,14 +639,12 @@ clutter_stage_cogl_redraw_view (ClutterStageWindow *stage_window,
       cairo_region_destroy (queued_redraw_clip);
     }
 
-  res = swap_framebuffer (stage_window,
-                          view,
-                          swap_region,
-                          swap_with_damage);
+  swap_framebuffer (stage_window,
+                    view,
+                    swap_region,
+                    swap_with_damage);
 
   cairo_region_destroy (swap_region);
-
-  return res;
 }
 
 static void
@@ -787,68 +669,17 @@ clutter_stage_cogl_scanout_view (ClutterStageCogl *stage_cogl,
 }
 
 static void
-clutter_stage_cogl_redraw (ClutterStageWindow *stage_window)
+clutter_stage_cogl_redraw_view (ClutterStageWindow *stage_window,
+                                ClutterStageView   *view)
 {
   ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_window);
-  gboolean has_redraw_clip = FALSE;
-  gboolean swap_event = FALSE;
-  GList *l;
-
-  COGL_TRACE_BEGIN (ClutterStageCoglRedraw, "Paint (Cogl Redraw)");
-
-  for (l = _clutter_stage_window_get_views (stage_window); l; l = l->next)
-    {
-      ClutterStageView *view = l->data;
-
-      if (!clutter_stage_view_has_redraw_clip (view))
-        continue;
-
-      has_redraw_clip = TRUE;
-      break;
-    }
-
-  if (has_redraw_clip)
-    clutter_stage_emit_before_paint (stage_cogl->wrapper);
-
-  for (l = _clutter_stage_window_get_views (stage_window); l; l = l->next)
-    {
-      ClutterStageView *view = l->data;
-      g_autoptr (CoglScanout) scanout = NULL;
+  g_autoptr (CoglScanout) scanout = NULL;
 
-      if (!clutter_stage_view_has_redraw_clip (view))
-        continue;
-
-      scanout = clutter_stage_view_take_scanout (view);
-      if (scanout)
-        {
-          clutter_stage_cogl_scanout_view (stage_cogl,
-                                           view,
-                                           scanout);
-          swap_event = TRUE;
-        }
-      else
-        {
-          swap_event |= clutter_stage_cogl_redraw_view (stage_window, view);
-        }
-    }
-
-  if (has_redraw_clip)
-    clutter_stage_emit_after_paint (stage_cogl->wrapper);
-
-  _clutter_stage_window_finish_frame (stage_window);
-
-  if (swap_event)
-    {
-      /* If we have swap buffer events then cogl_onscreen_swap_buffers
-       * will return immediately and we need to track that there is a
-       * swap in progress... */
-      if (clutter_feature_available (CLUTTER_FEATURE_SWAP_EVENTS))
-        stage_cogl->pending_swaps++;
-    }
-
-  stage_cogl->frame_count++;
-
-  COGL_TRACE_END (ClutterStageCoglRedraw);
+  scanout = clutter_stage_view_take_scanout (view);
+  if (scanout)
+    clutter_stage_cogl_scanout_view (stage_cogl, view, scanout);
+  else
+    clutter_stage_cogl_redraw_view_primary (stage_cogl, view);
 }
 
 static void
@@ -861,10 +692,7 @@ clutter_stage_window_iface_init (ClutterStageWindowInterface *iface)
   iface->show = clutter_stage_cogl_show;
   iface->hide = clutter_stage_cogl_hide;
   iface->get_frame_counter = clutter_stage_cogl_get_frame_counter;
-  iface->schedule_update = clutter_stage_cogl_schedule_update;
-  iface->get_update_time = clutter_stage_cogl_get_update_time;
-  iface->clear_update_time = clutter_stage_cogl_clear_update_time;
-  iface->redraw = clutter_stage_cogl_redraw;
+  iface->redraw_view = clutter_stage_cogl_redraw_view;
 }
 
 static void
@@ -905,10 +733,43 @@ _clutter_stage_cogl_class_init (ClutterStageCoglClass *klass)
 static void
 _clutter_stage_cogl_init (ClutterStageCogl *stage)
 {
-  stage->last_presentation_time = 0;
-  stage->refresh_rate = 0.0;
+}
+
+static void
+frame_cb (CoglOnscreen  *onscreen,
+          CoglFrameEvent frame_event,
+          CoglFrameInfo *frame_info,
+          void          *user_data)
+{
+  ClutterStageView *view = user_data;
+  ClutterFrameInfo clutter_frame_info;
+
+  if (frame_event == COGL_FRAME_EVENT_SYNC)
+    return;
+
+  clutter_frame_info = (ClutterFrameInfo) {
+    .frame_counter = cogl_frame_info_get_global_frame_counter (frame_info),
+    .refresh_rate = cogl_frame_info_get_refresh_rate (frame_info),
+    .presentation_time = ns2us (cogl_frame_info_get_presentation_time (frame_info)),
+  };
+
+  clutter_stage_view_notify_presented (view, &clutter_frame_info);
+}
+
+static void
+on_framebuffer_set (ClutterStageView *view)
+{
+  CoglFramebuffer *framebuffer;
 
-  stage->update_time = -1;
+  framebuffer = clutter_stage_view_get_onscreen (view);
+
+  if (framebuffer && cogl_is_onscreen (framebuffer))
+    {
+      cogl_onscreen_add_frame_callback (COGL_ONSCREEN (framebuffer),
+                                        frame_cb,
+                                        view,
+                                        NULL);
+    }
 }
 
 static void
@@ -918,6 +779,7 @@ clutter_stage_view_cogl_finalize (GObject *object)
   ClutterStageViewCoglPrivate *view_priv =
     clutter_stage_view_cogl_get_instance_private (view_cogl);
 
+  g_clear_handle_id (&view_priv->notify_presented_handle_id, g_source_remove);
   clutter_damage_history_free (view_priv->damage_history);
 
   G_OBJECT_CLASS (clutter_stage_view_cogl_parent_class)->finalize (object);
@@ -930,6 +792,9 @@ clutter_stage_view_cogl_init (ClutterStageViewCogl *view_cogl)
     clutter_stage_view_cogl_get_instance_private (view_cogl);
 
   view_priv->damage_history = clutter_damage_history_new ();
+
+  g_signal_connect (view_cogl, "notify::framebuffer",
+                    G_CALLBACK (on_framebuffer_set), NULL);
 }
 
 static void
diff --git a/clutter/clutter/cogl/clutter-stage-cogl.h b/clutter/clutter/cogl/clutter-stage-cogl.h
index 1eaa02e8fe..a67ba96152 100644
--- a/clutter/clutter/cogl/clutter-stage-cogl.h
+++ b/clutter/clutter/cogl/clutter-stage-cogl.h
@@ -41,20 +41,6 @@ struct _ClutterStageCogl
 
   /* back pointer to the backend */
   ClutterBackend *backend;
-
-  float refresh_rate;
-  int pending_swaps;
-
-  gint64 last_presentation_time;
-  gint64 update_time;
-  int64_t last_update_time;
-
-  /* We only enable clipped redraws after 2 frames, since we've seen
-   * a lot of drivers can struggle to get going and may output some
-   * junk frames to start with. */
-  unsigned int frame_count;
-
-  gint last_sync_delay;
 };
 
 struct _ClutterStageCoglClass
diff --git a/clutter/clutter/meson.build b/clutter/clutter/meson.build
index 42dbaa5c41..fa556905e3 100644
--- a/clutter/clutter/meson.build
+++ b/clutter/clutter/meson.build
@@ -141,8 +141,6 @@ clutter_sources = [
   'clutter-layout-manager.c',
   'clutter-layout-meta.c',
   'clutter-main.c',
-  'clutter-master-clock.c',
-  'clutter-master-clock-default.c',
   'clutter-offscreen-effect.c',
   'clutter-page-turn-effect.c',
   'clutter-paint-context.c',
@@ -200,8 +198,6 @@ clutter_private_headers = [
   'clutter-input-focus-private.h',
   'clutter-input-method-private.h',
   'clutter-input-pointer-a11y-private.h',
-  'clutter-master-clock.h',
-  'clutter-master-clock-default.h',
   'clutter-offscreen-effect-private.h',
   'clutter-paint-context-private.h',
   'clutter-paint-node-private.h',
diff --git a/src/backends/meta-backend.c b/src/backends/meta-backend.c
index 39f2754896..7694d90a78 100644
--- a/src/backends/meta-backend.c
+++ b/src/backends/meta-backend.c
@@ -170,8 +170,6 @@ struct _MetaBackendPrivate
   guint sleep_signal_id;
   GCancellable *cancellable;
   GDBusConnection *system_bus;
-
-  gboolean was_headless;
 };
 typedef struct _MetaBackendPrivate MetaBackendPrivate;
 
@@ -292,19 +290,6 @@ meta_backend_monitors_changed (MetaBackend *backend)
     }
 
   meta_cursor_renderer_force_update (priv->cursor_renderer);
-
-  if (meta_monitor_manager_is_headless (priv->monitor_manager) &&
-      !priv->was_headless)
-    {
-      clutter_stage_freeze_updates (CLUTTER_STAGE (priv->stage));
-      priv->was_headless = TRUE;
-    }
-  else if (!meta_monitor_manager_is_headless (priv->monitor_manager) &&
-           priv->was_headless)
-    {
-      clutter_stage_thaw_updates (CLUTTER_STAGE (priv->stage));
-      priv->was_headless = FALSE;
-    }
 }
 
 void
diff --git a/src/backends/meta-cursor-renderer.c b/src/backends/meta-cursor-renderer.c
index 73f651c70f..5580136fc8 100644
--- a/src/backends/meta-cursor-renderer.c
+++ b/src/backends/meta-cursor-renderer.c
@@ -32,6 +32,7 @@
 #include "clutter/clutter.h"
 #include "clutter/clutter-mutter.h"
 #include "cogl/cogl.h"
+#include "core/boxes-private.h"
 #include "meta/meta-backend.h"
 #include "meta/util.h"
 
@@ -155,13 +156,25 @@ queue_redraw (MetaCursorRenderer *renderer,
 
 static void
 meta_cursor_renderer_after_paint (ClutterStage       *stage,
+                                  ClutterStageView   *stage_view,
                                   MetaCursorRenderer *renderer)
 {
   MetaCursorRendererPrivate *priv =
     meta_cursor_renderer_get_instance_private (renderer);
 
   if (priv->displayed_cursor && !priv->handled_by_backend)
-    meta_cursor_renderer_emit_painted (renderer, priv->displayed_cursor);
+    {
+      graphene_rect_t rect;
+      MetaRectangle view_layout;
+      graphene_rect_t view_rect;
+
+      rect = meta_cursor_renderer_calculate_rect (renderer,
+                                                  priv->displayed_cursor);
+      clutter_stage_view_get_layout (stage_view, &view_layout);
+      view_rect = meta_rectangle_to_graphene_rect (&view_layout);
+      if (graphene_rect_intersection (&rect, &view_rect, NULL))
+        meta_cursor_renderer_emit_painted (renderer, priv->displayed_cursor);
+    }
 }
 
 static gboolean
diff --git a/src/backends/meta-renderer.c b/src/backends/meta-renderer.c
index 8260ef6238..c0bbb61336 100644
--- a/src/backends/meta-renderer.c
+++ b/src/backends/meta-renderer.c
@@ -64,6 +64,7 @@ typedef struct _MetaRendererPrivate
 {
   MetaBackend *backend;
   GList *views;
+  gboolean is_paused;
 } MetaRendererPrivate;
 
 G_DEFINE_TYPE_WITH_PRIVATE (MetaRenderer, meta_renderer, G_TYPE_OBJECT)
@@ -247,6 +248,14 @@ meta_renderer_add_view (MetaRenderer     *renderer,
   MetaRendererPrivate *priv = meta_renderer_get_instance_private (renderer);
 
   priv->views = g_list_append (priv->views, view);
+
+  if (priv->is_paused)
+    {
+      ClutterFrameClock *frame_clock =
+        clutter_stage_view_get_frame_clock (CLUTTER_STAGE_VIEW (view));
+
+      clutter_frame_clock_inhibit (frame_clock);
+    }
 }
 
 /**
@@ -267,6 +276,44 @@ meta_renderer_get_views (MetaRenderer *renderer)
   return priv->views;
 }
 
+void
+meta_renderer_pause (MetaRenderer *renderer)
+{
+  MetaRendererPrivate *priv = meta_renderer_get_instance_private (renderer);
+  GList *l;
+
+  g_return_if_fail (!priv->is_paused);
+  priv->is_paused = TRUE;
+
+  for (l = priv->views; l; l = l->next)
+    {
+      ClutterStageView *stage_view = l->data;
+      ClutterFrameClock *frame_clock =
+        clutter_stage_view_get_frame_clock (stage_view);
+
+      clutter_frame_clock_inhibit (frame_clock);
+    }
+}
+
+void
+meta_renderer_resume (MetaRenderer *renderer)
+{
+  MetaRendererPrivate *priv = meta_renderer_get_instance_private (renderer);
+  GList *l;
+
+  g_return_if_fail (priv->is_paused);
+  priv->is_paused = FALSE;
+
+  for (l = priv->views; l; l = l->next)
+    {
+      ClutterStageView *stage_view = l->data;
+      ClutterFrameClock *frame_clock =
+        clutter_stage_view_get_frame_clock (stage_view);
+
+      clutter_frame_clock_uninhibit (frame_clock);
+    }
+}
+
 gboolean
 meta_renderer_is_hardware_accelerated (MetaRenderer *renderer)
 {
diff --git a/src/backends/meta-renderer.h b/src/backends/meta-renderer.h
index 278e645820..134d23abf6 100644
--- a/src/backends/meta-renderer.h
+++ b/src/backends/meta-renderer.h
@@ -67,4 +67,8 @@ GList * meta_renderer_get_views (MetaRenderer *renderer);
 
 gboolean meta_renderer_is_hardware_accelerated (MetaRenderer *renderer);
 
+void meta_renderer_pause (MetaRenderer *renderer);
+
+void meta_renderer_resume (MetaRenderer *renderer);
+
 #endif /* META_RENDERER_H */
diff --git a/src/backends/native/meta-backend-native.c b/src/backends/native/meta-backend-native.c
index 12f2812b21..17f47b80de 100644
--- a/src/backends/native/meta-backend-native.c
+++ b/src/backends/native/meta-backend-native.c
@@ -781,7 +781,6 @@ void
 meta_backend_native_pause (MetaBackendNative *native)
 {
   MetaBackend *backend = META_BACKEND (native);
-  ClutterStage *stage = CLUTTER_STAGE (meta_backend_get_stage (backend));
   MetaMonitorManager *monitor_manager =
     meta_backend_get_monitor_manager (backend);
   MetaMonitorManagerKms *monitor_manager_kms =
@@ -789,12 +788,13 @@ meta_backend_native_pause (MetaBackendNative *native)
   ClutterBackend *clutter_backend = meta_backend_get_clutter_backend (backend);
   MetaSeatNative *seat =
     META_SEAT_NATIVE (clutter_backend_get_default_seat (clutter_backend));
+  MetaRenderer *renderer = meta_backend_get_renderer (backend);
 
   COGL_TRACE_BEGIN_SCOPED (MetaBackendNativePause,
                            "Backend (pause)");
 
   meta_seat_native_release_devices (seat);
-  clutter_stage_freeze_updates (stage);
+  meta_renderer_pause (renderer);
 
   disconnect_udev_device_added_handler (native);
 
@@ -814,6 +814,7 @@ void meta_backend_native_resume (MetaBackendNative *native)
   ClutterBackend *clutter_backend = meta_backend_get_clutter_backend (backend);
   MetaSeatNative *seat =
     META_SEAT_NATIVE (clutter_backend_get_default_seat (clutter_backend));
+  MetaRenderer *renderer = meta_backend_get_renderer (backend);
 
   COGL_TRACE_BEGIN_SCOPED (MetaBackendNativeResume,
                            "Backend (resume)");
@@ -823,7 +824,7 @@ void meta_backend_native_resume (MetaBackendNative *native)
   connect_udev_device_added_handler (native);
 
   meta_seat_native_reclaim_devices (seat);
-  clutter_stage_thaw_updates (stage);
+  meta_renderer_resume (renderer);
 
   clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
 
diff --git a/src/backends/native/meta-renderer-native.c b/src/backends/native/meta-renderer-native.c
index 255a4ce727..f3a6bd7e45 100644
--- a/src/backends/native/meta-renderer-native.c
+++ b/src/backends/native/meta-renderer-native.c
@@ -169,8 +169,6 @@ typedef struct _MetaOnscreenNativeSecondaryGpuState
     MetaDumbBuffer dumb_fbs[2];
   } cpu;
 
-  int pending_flips;
-
   gboolean noted_primary_gpu_copy_ok;
   gboolean noted_primary_gpu_copy_failed;
   MetaSharedFramebufferImportStatus import_status;
@@ -199,15 +197,9 @@ typedef struct _MetaOnscreenNative
   } egl;
 #endif
 
-  gboolean pending_swap_notify;
-
   gboolean pending_set_crtc;
 
-  int64_t pending_queue_swap_notify_frame_count;
-  int64_t pending_swap_notify_frame_count;
-
   MetaRendererView *view;
-  int total_pending_flips;
 } MetaOnscreenNative;
 
 struct _MetaRendererNative
@@ -720,60 +712,6 @@ meta_renderer_native_disconnect (CoglRenderer *cogl_renderer)
   g_slice_free (CoglRendererEGL, cogl_renderer_egl);
 }
 
-static void
-flush_pending_swap_notify (CoglFramebuffer *framebuffer)
-{
-  if (framebuffer->type == COGL_FRAMEBUFFER_TYPE_ONSCREEN)
-    {
-      CoglOnscreen *onscreen = COGL_ONSCREEN (framebuffer);
-      CoglOnscreenEGL *onscreen_egl = onscreen->winsys;
-      MetaOnscreenNative *onscreen_native = onscreen_egl->platform;
-
-      if (onscreen_native->pending_swap_notify)
-        {
-          CoglFrameInfo *info;
-
-          while ((info = g_queue_peek_head (&onscreen->pending_frame_infos)) &&
-                 info->global_frame_counter <= onscreen_native->pending_swap_notify_frame_count)
-            {
-              _cogl_onscreen_notify_frame_sync (onscreen, info);
-              _cogl_onscreen_notify_complete (onscreen, info);
-              cogl_object_unref (info);
-              g_queue_pop_head (&onscreen->pending_frame_infos);
-            }
-
-          onscreen_native->pending_swap_notify = FALSE;
-          cogl_object_unref (onscreen);
-        }
-    }
-}
-
-static void
-flush_pending_swap_notify_idle (void *user_data)
-{
-  CoglContext *cogl_context = user_data;
-  CoglRendererEGL *cogl_renderer_egl = cogl_context->display->renderer->winsys;
-  MetaRendererNativeGpuData *renderer_gpu_data = cogl_renderer_egl->platform;
-  MetaRendererNative *renderer_native = renderer_gpu_data->renderer_native;
-  GList *l;
-
-  /* This needs to be disconnected before invoking the callbacks in
-   * case the callbacks cause it to be queued again */
-  _cogl_closure_disconnect (renderer_native->swap_notify_idle);
-  renderer_native->swap_notify_idle = NULL;
-
-  l = cogl_context->framebuffers;
-  while (l)
-    {
-      GList *next = l->next;
-      CoglFramebuffer *framebuffer = l->data;
-
-      flush_pending_swap_notify (framebuffer);
-
-      l = next;
-    }
-}
-
 static void
 free_current_secondary_bo (CoglOnscreen *onscreen)
 {
@@ -801,40 +739,14 @@ free_current_bo (CoglOnscreen *onscreen)
 static void
 meta_onscreen_native_queue_swap_notify (CoglOnscreen *onscreen)
 {
-  CoglOnscreenEGL *onscreen_egl =  onscreen->winsys;
-  MetaOnscreenNative *onscreen_native = onscreen_egl->platform;
-  MetaRendererNative *renderer_native = onscreen_native->renderer_native;
+  CoglFrameInfo *info;
 
-  onscreen_native->pending_swap_notify_frame_count =
-    onscreen_native->pending_queue_swap_notify_frame_count;
+  g_assert (onscreen->pending_frame_infos.length == 1);
 
-  if (onscreen_native->pending_swap_notify)
-    return;
-
-  /* We only want to notify that the swap is complete when the
-   * application calls cogl_context_dispatch so instead of
-   * immediately notifying we queue an idle callback */
-  if (!renderer_native->swap_notify_idle)
-    {
-      CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen);
-      CoglContext *cogl_context = framebuffer->context;
-      CoglRenderer *cogl_renderer = cogl_context->display->renderer;
-
-      renderer_native->swap_notify_idle =
-        _cogl_poll_renderer_add_idle (cogl_renderer,
-                                      flush_pending_swap_notify_idle,
-                                      cogl_context,
-                                      NULL);
-    }
-
-  /*
-   * The framebuffer will have its own referenc while the swap notify is
-   * pending. Otherwise when destroying the view would drop the pending
-   * notification with if the destruction happens before the idle callback
-   * is invoked.
-   */
-  cogl_object_ref (onscreen);
-  onscreen_native->pending_swap_notify = TRUE;
+  info = g_queue_pop_head (&onscreen->pending_frame_infos);
+  _cogl_onscreen_notify_frame_sync (onscreen, info);
+  _cogl_onscreen_notify_complete (onscreen, info);
+  cogl_object_unref (info);
 }
 
 static gboolean
@@ -1147,10 +1059,9 @@ notify_view_crtc_presented (MetaRendererView *view,
   CoglOnscreenEGL *onscreen_egl =  onscreen->winsys;
   MetaOnscreenNative *onscreen_native = onscreen_egl->platform;
   MetaRendererNative *renderer_native = onscreen_native->renderer_native;
-  MetaGpuKms *render_gpu = onscreen_native->render_gpu;
   CoglFrameInfo *frame_info;
   MetaCrtc *crtc;
-  MetaGpuKms *gpu_kms;
+  MetaRendererNativeGpuData *renderer_gpu_data;
 
   /* Only keep the frame info for the fastest CRTC in use, which may not be
    * the first one to complete a flip. By only telling the compositor about the
@@ -1162,35 +1073,21 @@ notify_view_crtc_presented (MetaRendererView *view,
   crtc = META_CRTC (meta_crtc_kms_from_kms_crtc (kms_crtc));
   maybe_update_frame_info (crtc, frame_info, time_ns);
 
-  gpu_kms = META_GPU_KMS (meta_crtc_get_gpu (crtc));
-  if (gpu_kms != render_gpu)
-    {
-      MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state =
-        onscreen_native->secondary_gpu_state;
 
-      secondary_gpu_state->pending_flips--;
-    }
+  meta_onscreen_native_queue_swap_notify (onscreen);
 
-  onscreen_native->total_pending_flips--;
-  if (onscreen_native->total_pending_flips == 0)
+  renderer_gpu_data =
+    meta_renderer_native_get_gpu_data (renderer_native,
+                                       onscreen_native->render_gpu);
+  switch (renderer_gpu_data->mode)
     {
-      MetaRendererNativeGpuData *renderer_gpu_data;
-
-      meta_onscreen_native_queue_swap_notify (onscreen);
-
-      renderer_gpu_data =
-        meta_renderer_native_get_gpu_data (renderer_native,
-                                           onscreen_native->render_gpu);
-      switch (renderer_gpu_data->mode)
-        {
-        case META_RENDERER_NATIVE_MODE_GBM:
-          meta_onscreen_native_swap_drm_fb (onscreen);
-          break;
+    case META_RENDERER_NATIVE_MODE_GBM:
+      meta_onscreen_native_swap_drm_fb (onscreen);
+      break;
 #ifdef HAVE_EGL_DEVICE
-        case META_RENDERER_NATIVE_MODE_EGL_DEVICE:
-          break;
+    case META_RENDERER_NATIVE_MODE_EGL_DEVICE:
+      break;
 #endif
-        }
     }
 }
 
@@ -1404,10 +1301,6 @@ meta_onscreen_native_flip_crtc (CoglOnscreen     *onscreen,
                                g_object_ref (view),
                                kms_update);
 
-      onscreen_native->total_pending_flips++;
-      if (secondary_gpu_state)
-        secondary_gpu_state->pending_flips++;
-
       break;
 #ifdef HAVE_EGL_DEVICE
     case META_RENDERER_NATIVE_MODE_EGL_DEVICE:
@@ -1417,7 +1310,6 @@ meta_onscreen_native_flip_crtc (CoglOnscreen     *onscreen,
                                         g_object_ref (view),
                                         custom_egl_stream_page_flip,
                                         onscreen_native);
-      onscreen_native->total_pending_flips++;
       break;
 #endif
     }
@@ -1485,40 +1377,6 @@ meta_onscreen_native_flip_crtcs (CoglOnscreen  *onscreen,
     }
 }
 
-static void
-wait_for_pending_flips (CoglOnscreen *onscreen)
-{
-  CoglOnscreenEGL *onscreen_egl = onscreen->winsys;
-  MetaOnscreenNative *onscreen_native = onscreen_egl->platform;
-  MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state;
-  GError *error = NULL;
-
-  secondary_gpu_state = onscreen_native->secondary_gpu_state;
-  if (secondary_gpu_state)
-    {
-      while (secondary_gpu_state->pending_flips)
-        {
-          if (!meta_gpu_kms_wait_for_flip (secondary_gpu_state->gpu_kms, &error))
-            {
-              g_warning ("Failed to wait for flip on secondary GPU: %s",
-                         error->message);
-              g_clear_error (&error);
-              break;
-            }
-        }
-    }
-
-  while (onscreen_native->total_pending_flips)
-    {
-      if (!meta_gpu_kms_wait_for_flip (onscreen_native->render_gpu, &error))
-        {
-          g_warning ("Failed to wait for flip: %s", error->message);
-          g_clear_error (&error);
-          break;
-        }
-    }
-}
-
 static gboolean
 import_shared_framebuffer (CoglOnscreen                        *onscreen,
                            MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state)
@@ -2095,15 +1953,6 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen  *onscreen,
 
   kms_update = meta_kms_ensure_pending_update (kms);
 
-  /*
-   * Wait for the flip callback before continuing, as we might have started the
-   * animation earlier due to the animation being driven by some other monitor.
-   */
-  COGL_TRACE_BEGIN (MetaRendererNativeSwapBuffersWait,
-                    "Onscreen (waiting for page flips)");
-  wait_for_pending_flips (onscreen);
-  COGL_TRACE_END (MetaRendererNativeSwapBuffersWait);
-
   update_secondary_gpu_state_pre_swap_buffers (onscreen);
 
   parent_vtable->onscreen_swap_buffers_with_damage (onscreen,
@@ -2143,9 +1992,6 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen  *onscreen,
   update_secondary_gpu_state_post_swap_buffers (onscreen, &egl_context_changed);
 
   ensure_crtc_modes (onscreen, kms_update);
-
-  onscreen_native->pending_queue_swap_notify_frame_count =
-    cogl_frame_info_get_global_frame_counter (frame_info);
   meta_onscreen_native_flip_crtcs (onscreen, kms_update);
 
   /*
@@ -2315,8 +2161,6 @@ meta_onscreen_native_direct_scanout (CoglOnscreen  *onscreen,
 
   kms_update = meta_kms_ensure_pending_update (kms);
 
-  wait_for_pending_flips (onscreen);
-
   renderer_gpu_data = meta_renderer_native_get_gpu_data (renderer_native,
                                                          render_gpu);
 
@@ -2326,9 +2170,6 @@ meta_onscreen_native_direct_scanout (CoglOnscreen  *onscreen,
   g_set_object (&onscreen_native->gbm.next_fb, META_DRM_BUFFER (scanout));
 
   ensure_crtc_modes (onscreen, kms_update);
-
-  onscreen_native->pending_queue_swap_notify_frame_count =
-    cogl_frame_info_get_global_frame_counter (frame_info);
   meta_onscreen_native_flip_crtcs (onscreen, kms_update);
 
   meta_kms_post_pending_update_sync (kms);
diff --git a/src/backends/native/meta-stage-native.c b/src/backends/native/meta-stage-native.c
index 4b0551d915..ab519886d2 100644
--- a/src/backends/native/meta-stage-native.c
+++ b/src/backends/native/meta-stage-native.c
@@ -52,86 +52,6 @@ G_DEFINE_TYPE_WITH_CODE (MetaStageNative, meta_stage_native,
                          G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_STAGE_WINDOW,
                                                 clutter_stage_window_iface_init))
 
-static void
-frame_cb (CoglOnscreen  *onscreen,
-          CoglFrameEvent frame_event,
-          CoglFrameInfo *frame_info,
-          void          *user_data)
-
-{
-  MetaStageNative *stage_native = user_data;
-  ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_native);
-  int64_t global_frame_counter;
-  int64_t presented_frame_counter;
-  ClutterFrameInfo clutter_frame_info;
-
-  global_frame_counter = cogl_frame_info_get_global_frame_counter (frame_info);
-
-  switch (frame_event)
-    {
-    case COGL_FRAME_EVENT_SYNC:
-      presented_frame_counter = stage_native->presented_frame_counter_sync;
-      stage_native->presented_frame_counter_sync = global_frame_counter;
-      break;
-    case COGL_FRAME_EVENT_COMPLETE:
-      presented_frame_counter = stage_native->presented_frame_counter_complete;
-      stage_native->presented_frame_counter_complete = global_frame_counter;
-      break;
-    default:
-      g_assert_not_reached ();
-    }
-
-  if (global_frame_counter <= presented_frame_counter)
-    return;
-
-  clutter_frame_info = (ClutterFrameInfo) {
-    .frame_counter = global_frame_counter,
-    .refresh_rate = cogl_frame_info_get_refresh_rate (frame_info),
-    .presentation_time = cogl_frame_info_get_presentation_time (frame_info)
-  };
-
-  _clutter_stage_cogl_presented (stage_cogl, frame_event, &clutter_frame_info);
-}
-
-static void
-ensure_frame_callback (MetaStageNative  *stage_native,
-                       ClutterStageView *stage_view)
-{
-  CoglFramebuffer *framebuffer;
-  CoglOnscreen *onscreen;
-  CoglClosure *closure;
-
-  closure = g_object_get_qdata (G_OBJECT (stage_view),
-                                quark_view_frame_closure);
-  if (closure)
-    return;
-
-  framebuffer = clutter_stage_view_get_onscreen (stage_view);
-  onscreen = COGL_ONSCREEN (framebuffer);
-  closure = cogl_onscreen_add_frame_callback (onscreen,
-                                              frame_cb,
-                                              stage_native,
-                                              NULL);
-  g_object_set_qdata (G_OBJECT (stage_view),
-                      quark_view_frame_closure,
-                      closure);
-}
-
-static void
-ensure_frame_callbacks (MetaStageNative *stage_native)
-{
-  MetaBackend *backend = meta_get_backend ();
-  MetaRenderer *renderer = meta_backend_get_renderer (backend);
-  GList *l;
-
-  for (l = meta_renderer_get_views (renderer); l; l = l->next)
-    {
-      ClutterStageView *stage_view = l->data;
-
-      ensure_frame_callback (stage_native, stage_view);
-    }
-}
-
 void
 meta_stage_native_rebuild_views (MetaStageNative *stage_native)
 {
@@ -141,7 +61,6 @@ meta_stage_native_rebuild_views (MetaStageNative *stage_native)
 
   meta_renderer_rebuild_views (renderer);
   clutter_stage_clear_stage_views (CLUTTER_STAGE (stage));
-  ensure_frame_callbacks (stage_native);
 }
 
 static gboolean
diff --git a/src/backends/x11/meta-stage-x11.c b/src/backends/x11/meta-stage-x11.c
index 13db11fe46..674cd3e477 100644
--- a/src/backends/x11/meta-stage-x11.c
+++ b/src/backends/x11/meta-stage-x11.c
@@ -247,35 +247,11 @@ meta_stage_x11_unrealize (ClutterStageWindow *stage_window)
                            GINT_TO_POINTER (stage_x11->xwin));
     }
 
-  if (stage_x11->frame_closure)
-    {
-      cogl_onscreen_remove_frame_callback (stage_x11->onscreen,
-                                           stage_x11->frame_closure);
-      stage_x11->frame_closure = NULL;
-    }
-
   clutter_stage_window_parent_iface->unrealize (stage_window);
 
   g_clear_pointer (&stage_x11->onscreen, cogl_object_unref);
 }
 
-static void
-frame_cb (CoglOnscreen  *onscreen,
-          CoglFrameEvent frame_event,
-          CoglFrameInfo *frame_info,
-          void          *user_data)
-
-{
-  ClutterStageCogl *stage_cogl = user_data;
-  ClutterFrameInfo clutter_frame_info = {
-    .frame_counter = cogl_frame_info_get_frame_counter (frame_info),
-    .presentation_time = cogl_frame_info_get_presentation_time (frame_info),
-    .refresh_rate = cogl_frame_info_get_refresh_rate (frame_info)
-  };
-
-  _clutter_stage_cogl_presented (stage_cogl, frame_event, &clutter_frame_info);
-}
-
 static gboolean
 meta_stage_x11_realize (ClutterStageWindow *stage_window)
 {
@@ -291,12 +267,6 @@ meta_stage_x11_realize (ClutterStageWindow *stage_window)
 
   stage_x11->onscreen = cogl_onscreen_new (backend->cogl_context, width, height);
 
-  stage_x11->frame_closure =
-    cogl_onscreen_add_frame_callback (stage_x11->onscreen,
-                                      frame_cb,
-                                      stage_cogl,
-                                      NULL);
-
   if (META_IS_BACKEND_X11_CM (stage_x11->backend))
     {
       MetaRenderer *renderer = meta_backend_get_renderer (stage_x11->backend);
diff --git a/src/compositor/compositor-private.h b/src/compositor/compositor-private.h
index f993ac300c..feb60b7e6b 100644
--- a/src/compositor/compositor-private.h
+++ b/src/compositor/compositor-private.h
@@ -23,8 +23,10 @@ struct _MetaCompositorClass
   gboolean (* manage) (MetaCompositor  *compositor,
                        GError         **error);
   void (* unmanage) (MetaCompositor *compositor);
-  void (* before_paint) (MetaCompositor *compositor);
-  void (* after_paint) (MetaCompositor *compositor);
+  void (* before_paint) (MetaCompositor   *compositor,
+                         ClutterStageView *stage_view);
+  void (* after_paint) (MetaCompositor   *compositor,
+                        ClutterStageView *stage_view);
   void (* remove_window) (MetaCompositor *compositor,
                           MetaWindow     *window);
 };
diff --git a/src/compositor/compositor.c b/src/compositor/compositor.c
index ca4cdfc0c7..79c93cf0ae 100644
--- a/src/compositor/compositor.c
+++ b/src/compositor/compositor.c
@@ -139,6 +139,7 @@ G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (MetaCompositor, meta_compositor,
 
 static void
 on_presented (ClutterStage     *stage,
+              ClutterStageView *stage_view,
               ClutterFrameInfo *frame_info,
               MetaCompositor   *compositor);
 
@@ -1034,6 +1035,7 @@ meta_compositor_sync_window_geometry (MetaCompositor *compositor,
 
 static void
 on_presented (ClutterStage     *stage,
+              ClutterStageView *stage_view,
               ClutterFrameInfo *frame_info,
               MetaCompositor   *compositor)
 {
@@ -1072,34 +1074,42 @@ on_presented (ClutterStage     *stage,
   for (l = priv->windows; l; l = l->next)
     {
       ClutterActor *actor = l->data;
+      GList *actor_stage_views;
 
-      meta_window_actor_frame_complete (META_WINDOW_ACTOR (actor),
-                                        frame_info,
-                                        presentation_time);
+      actor_stage_views = clutter_actor_peek_stage_views (actor);
+      if (g_list_find (actor_stage_views, stage_view))
+        {
+          meta_window_actor_frame_complete (META_WINDOW_ACTOR (actor),
+                                            frame_info,
+                                            presentation_time);
+        }
     }
 }
 
 static void
-meta_compositor_real_before_paint (MetaCompositor *compositor)
+meta_compositor_real_before_paint (MetaCompositor   *compositor,
+                                   ClutterStageView *stage_view)
 {
   MetaCompositorPrivate *priv =
     meta_compositor_get_instance_private (compositor);
   GList *l;
 
   for (l = priv->windows; l; l = l->next)
-    meta_window_actor_before_paint (l->data);
+    meta_window_actor_before_paint (l->data, stage_view);
 }
 
 static void
-meta_compositor_before_paint (MetaCompositor *compositor)
+meta_compositor_before_paint (MetaCompositor   *compositor,
+                              ClutterStageView *stage_view)
 {
   COGL_TRACE_BEGIN_SCOPED (MetaCompositorPrePaint,
                            "Compositor (before-paint)");
-  META_COMPOSITOR_GET_CLASS (compositor)->before_paint (compositor);
+  META_COMPOSITOR_GET_CLASS (compositor)->before_paint (compositor, stage_view);
 }
 
 static void
-meta_compositor_real_after_paint (MetaCompositor *compositor)
+meta_compositor_real_after_paint (MetaCompositor   *compositor,
+                                  ClutterStageView *stage_view)
 {
   MetaCompositorPrivate *priv =
     meta_compositor_get_instance_private (compositor);
@@ -1132,31 +1142,37 @@ meta_compositor_real_after_paint (MetaCompositor *compositor)
   for (l = priv->windows; l; l = l->next)
     {
       ClutterActor *actor = l->data;
+      GList *actor_stage_views;
 
-      meta_window_actor_after_paint (META_WINDOW_ACTOR (actor));
+      actor_stage_views = clutter_actor_peek_stage_views (actor);
+      if (g_list_find (actor_stage_views, stage_view))
+        meta_window_actor_after_paint (META_WINDOW_ACTOR (actor), stage_view);
     }
 }
 
 static void
-meta_compositor_after_paint (MetaCompositor *compositor)
+meta_compositor_after_paint (MetaCompositor   *compositor,
+                             ClutterStageView *stage_view)
 {
   COGL_TRACE_BEGIN_SCOPED (MetaCompositorPostPaint,
                            "Compositor (after-paint)");
-  META_COMPOSITOR_GET_CLASS (compositor)->after_paint (compositor);
+  META_COMPOSITOR_GET_CLASS (compositor)->after_paint (compositor, stage_view);
 }
 
 static void
-on_before_paint (ClutterStage   *stage,
-                 MetaCompositor *compositor)
+on_before_paint (ClutterStage     *stage,
+                 ClutterStageView *stage_view,
+                 MetaCompositor   *compositor)
 {
-  meta_compositor_before_paint (compositor);
+  meta_compositor_before_paint (compositor, stage_view);
 }
 
 static void
-on_after_paint (ClutterStage   *stage,
-                MetaCompositor *compositor)
+on_after_paint (ClutterStage     *stage,
+                ClutterStageView *stage_view,
+                MetaCompositor   *compositor)
 {
-  meta_compositor_after_paint (compositor);
+  meta_compositor_after_paint (compositor, stage_view);
 }
 
 static void
diff --git a/src/compositor/meta-compositor-native.c b/src/compositor/meta-compositor-native.c
index 71e3c01b42..92b0aedf9c 100644
--- a/src/compositor/meta-compositor-native.c
+++ b/src/compositor/meta-compositor-native.c
@@ -116,14 +116,15 @@ maybe_assign_primary_plane (MetaCompositor *compositor)
 }
 
 static void
-meta_compositor_native_before_paint (MetaCompositor *compositor)
+meta_compositor_native_before_paint (MetaCompositor   *compositor,
+                                     ClutterStageView *stage_view)
 {
   MetaCompositorClass *parent_class;
 
   maybe_assign_primary_plane (compositor);
 
   parent_class = META_COMPOSITOR_CLASS (meta_compositor_native_parent_class);
-  parent_class->before_paint (compositor);
+  parent_class->before_paint (compositor, stage_view);
 }
 
 MetaCompositorNative *
diff --git a/src/compositor/meta-compositor-x11.c b/src/compositor/meta-compositor-x11.c
index 251dee850f..fcd292a1f3 100644
--- a/src/compositor/meta-compositor-x11.c
+++ b/src/compositor/meta-compositor-x11.c
@@ -293,8 +293,9 @@ out:
 }
 
 static void
-on_before_update (ClutterStage   *stage,
-                  MetaCompositor *compositor)
+on_before_update (ClutterStage     *stage,
+                  ClutterStageView *stage_view,
+                  MetaCompositor   *compositor)
 {
   MetaCompositorX11 *compositor_x11 = META_COMPOSITOR_X11 (compositor);
 
@@ -330,7 +331,8 @@ on_before_update (ClutterStage   *stage,
 }
 
 static void
-meta_compositor_x11_before_paint (MetaCompositor *compositor)
+meta_compositor_x11_before_paint (MetaCompositor   *compositor,
+                                  ClutterStageView *stage_view)
 {
   MetaCompositorX11 *compositor_x11 = META_COMPOSITOR_X11 (compositor);
   MetaCompositorClass *parent_class;
@@ -338,11 +340,12 @@ meta_compositor_x11_before_paint (MetaCompositor *compositor)
   maybe_unredirect_top_window (compositor_x11);
 
   parent_class = META_COMPOSITOR_CLASS (meta_compositor_x11_parent_class);
-  parent_class->before_paint (compositor);
+  parent_class->before_paint (compositor, stage_view);
 }
 
 static void
-meta_compositor_x11_after_paint (MetaCompositor *compositor)
+meta_compositor_x11_after_paint (MetaCompositor   *compositor,
+                                 ClutterStageView *stage_view)
 {
   MetaCompositorX11 *compositor_x11 = META_COMPOSITOR_X11 (compositor);
   MetaCompositorClass *parent_class;
@@ -356,7 +359,7 @@ meta_compositor_x11_after_paint (MetaCompositor *compositor)
     }
 
   parent_class = META_COMPOSITOR_CLASS (meta_compositor_x11_parent_class);
-  parent_class->after_paint (compositor);
+  parent_class->after_paint (compositor, stage_view);
 }
 
 static void
diff --git a/src/compositor/meta-later.c b/src/compositor/meta-later.c
index 9265f2e44a..dff091d759 100644
--- a/src/compositor/meta-later.c
+++ b/src/compositor/meta-later.c
@@ -165,8 +165,9 @@ run_repaint_laters (GSList **laters_list)
 }
 
 static void
-on_before_update (ClutterStage *stage,
-                  MetaLaters   *laters)
+on_before_update (ClutterStage     *stage,
+                  ClutterStageView *stage_view,
+                  MetaLaters       *laters)
 {
   unsigned int i;
   GSList *l;
diff --git a/src/compositor/meta-window-actor-private.h b/src/compositor/meta-window-actor-private.h
index 4d0091f27b..57dbd0ad07 100644
--- a/src/compositor/meta-window-actor-private.h
+++ b/src/compositor/meta-window-actor-private.h
@@ -23,8 +23,10 @@ struct _MetaWindowActorClass
   void (*queue_frame_drawn) (MetaWindowActor *actor,
                              gboolean         skip_sync_delay);
 
-  void (*before_paint) (MetaWindowActor *actor);
-  void (*after_paint) (MetaWindowActor *actor);
+  void (*before_paint) (MetaWindowActor  *actor,
+                        ClutterStageView *stage_view);
+  void (*after_paint) (MetaWindowActor  *actor,
+                       ClutterStageView *stage_view);
 
   void (*queue_destroy) (MetaWindowActor *actor);
   void (*set_frozen) (MetaWindowActor *actor,
@@ -50,8 +52,10 @@ void meta_window_actor_size_change   (MetaWindowActor *self,
                                       MetaRectangle   *old_frame_rect,
                                       MetaRectangle   *old_buffer_rect);
 
-void meta_window_actor_before_paint   (MetaWindowActor    *self);
-void meta_window_actor_after_paint    (MetaWindowActor    *self);
+void meta_window_actor_before_paint   (MetaWindowActor    *self,
+                                       ClutterStageView   *stage_view);
+void meta_window_actor_after_paint    (MetaWindowActor    *self,
+                                       ClutterStageView   *stage_view);
 void meta_window_actor_frame_complete (MetaWindowActor    *self,
                                        ClutterFrameInfo   *frame_info,
                                        gint64              presentation_time);
diff --git a/src/compositor/meta-window-actor-wayland.c b/src/compositor/meta-window-actor-wayland.c
index c41d85f334..a287fdcbac 100644
--- a/src/compositor/meta-window-actor-wayland.c
+++ b/src/compositor/meta-window-actor-wayland.c
@@ -119,12 +119,14 @@ meta_window_actor_wayland_queue_frame_drawn (MetaWindowActor *actor,
 }
 
 static void
-meta_window_actor_wayland_before_paint (MetaWindowActor *actor)
+meta_window_actor_wayland_before_paint (MetaWindowActor  *actor,
+                                        ClutterStageView *stage_view)
 {
 }
 
 static void
-meta_window_actor_wayland_after_paint (MetaWindowActor *actor)
+meta_window_actor_wayland_after_paint (MetaWindowActor  *actor,
+                                       ClutterStageView *stage_view)
 {
 }
 
diff --git a/src/compositor/meta-window-actor-x11.c b/src/compositor/meta-window-actor-x11.c
index b79e014d19..a2bc1d9394 100644
--- a/src/compositor/meta-window-actor-x11.c
+++ b/src/compositor/meta-window-actor-x11.c
@@ -25,6 +25,7 @@
 #include "compositor/meta-window-actor-x11.h"
 
 #include "backends/meta-logical-monitor.h"
+#include "clutter/clutter-frame-clock.h"
 #include "compositor/compositor-private.h"
 #include "compositor/meta-cullable.h"
 #include "compositor/meta-shaped-texture-private.h"
@@ -58,6 +59,8 @@ struct _MetaWindowActorX11
 
   guint send_frame_messages_timer;
   int64_t frame_drawn_time;
+  gboolean pending_schedule_update_now;
+  ClutterFrameClock *frame_clock;
 
   gulong repaint_scheduled_id;
   gulong size_changed_id;
@@ -372,8 +375,8 @@ meta_window_actor_x11_frame_complete (MetaWindowActor  *actor,
             g_warning ("%s: Frame has assigned frame counter but no frame drawn time",
                        window->desc);
           if (G_UNLIKELY (frame->frame_counter < frame_counter))
-            g_warning ("%s: frame_complete callback never occurred for frame %" G_GINT64_FORMAT,
-                       window->desc, frame->frame_counter);
+            g_debug ("%s: frame_complete callback never occurred for frame %" G_GINT64_FORMAT,
+                     window->desc, frame->frame_counter);
 
           actor_x11->frames = g_list_delete_link (actor_x11->frames, l);
           send_frame_timings (actor_x11, frame, frame_info, presentation_time);
@@ -451,8 +454,10 @@ meta_window_actor_x11_queue_frame_drawn (MetaWindowActor *actor,
 
   if (skip_sync_delay)
     {
-      ClutterActor *stage = clutter_actor_get_stage (CLUTTER_ACTOR (actor_x11));
-      clutter_stage_skip_sync_delay (CLUTTER_STAGE (stage));
+      if (actor_x11->frame_clock)
+        clutter_frame_clock_schedule_update_now (actor_x11->frame_clock);
+      else
+        actor_x11->pending_schedule_update_now = TRUE;
     }
 
   if (!actor_x11->repaint_scheduled)
@@ -473,9 +478,11 @@ meta_window_actor_x11_queue_frame_drawn (MetaWindowActor *actor,
        * before_paint/after_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 the window
-       * is completely obscured we fire off the send_frame_messages timeout.
+       * is completely obscured, or completely off screen we fire off the
+       * send_frame_messages timeout.
        */
-      if (is_obscured)
+      if (is_obscured ||
+          !clutter_actor_peek_stage_views (CLUTTER_ACTOR (actor)))
         {
           queue_send_frame_messages_timeout (actor_x11);
         }
@@ -1226,7 +1233,21 @@ handle_updates (MetaWindowActorX11 *actor_x11)
 }
 
 static void
-meta_window_actor_x11_before_paint (MetaWindowActor *actor)
+handle_stage_views_changed (MetaWindowActorX11 *actor_x11)
+{
+  ClutterActor *actor = CLUTTER_ACTOR (actor_x11);
+
+  actor_x11->frame_clock = clutter_actor_pick_frame_clock (actor);
+  if (actor_x11->frame_clock && actor_x11->pending_schedule_update_now)
+    {
+      clutter_frame_clock_schedule_update_now (actor_x11->frame_clock);
+      actor_x11->pending_schedule_update_now = FALSE;
+    }
+}
+
+static void
+meta_window_actor_x11_before_paint (MetaWindowActor  *actor,
+                                    ClutterStageView *stage_view)
 {
   MetaWindowActorX11 *actor_x11 = META_WINDOW_ACTOR_X11 (actor);
 
@@ -1304,7 +1325,8 @@ meta_window_actor_x11_paint (ClutterActor        *actor,
 }
 
 static void
-meta_window_actor_x11_after_paint (MetaWindowActor *actor)
+meta_window_actor_x11_after_paint (MetaWindowActor  *actor,
+                                   ClutterStageView *stage_view)
 {
   MetaWindowActorX11 *actor_x11 = META_WINDOW_ACTOR_X11 (actor);
   MetaWindow *window;
@@ -1641,6 +1663,9 @@ meta_window_actor_x11_init (MetaWindowActorX11 *self)
   /* We do this now since we might be going right back into the frozen state. */
   g_signal_connect (self, "thawed", G_CALLBACK (handle_updates), NULL);
 
+  g_signal_connect (self, "stage-views-changed",
+                    G_CALLBACK (handle_stage_views_changed), NULL);
+
   self->shadow_factory = meta_shadow_factory_get_default ();
   self->shadow_factory_changed_handler_id =
     g_signal_connect_swapped (self->shadow_factory,
diff --git a/src/compositor/meta-window-actor.c b/src/compositor/meta-window-actor.c
index 5e4b07ab69..8d2548fbb7 100644
--- a/src/compositor/meta-window-actor.c
+++ b/src/compositor/meta-window-actor.c
@@ -1021,21 +1021,23 @@ meta_window_actor_sync_visibility (MetaWindowActor *self)
 }
 
 void
-meta_window_actor_before_paint (MetaWindowActor *self)
+meta_window_actor_before_paint (MetaWindowActor  *self,
+                                ClutterStageView *stage_view)
 {
   if (meta_window_actor_is_destroyed (self))
     return;
 
-  META_WINDOW_ACTOR_GET_CLASS (self)->before_paint (self);
+  META_WINDOW_ACTOR_GET_CLASS (self)->before_paint (self, stage_view);
 }
 
 void
-meta_window_actor_after_paint (MetaWindowActor *self)
+meta_window_actor_after_paint (MetaWindowActor  *self,
+                               ClutterStageView *stage_view)
 {
   MetaWindowActorPrivate *priv =
     meta_window_actor_get_instance_private (self);
 
-  META_WINDOW_ACTOR_GET_CLASS (self)->after_paint (self);
+  META_WINDOW_ACTOR_GET_CLASS (self)->after_paint (self, stage_view);
 
   if (meta_window_actor_is_destroyed (self))
     return;
diff --git a/src/tests/clutter/conform/actor-clone.c b/src/tests/clutter/conform/actor-clone.c
index 6a27c51992..582ddbbf30 100644
--- a/src/tests/clutter/conform/actor-clone.c
+++ b/src/tests/clutter/conform/actor-clone.c
@@ -7,6 +7,7 @@
 
 static void
 on_presented (ClutterStage     *stage,
+              ClutterStageView *view,
               ClutterFrameInfo *frame_info,
               gboolean         *was_presented)
 {
diff --git a/src/tests/stage-view-tests.c b/src/tests/stage-view-tests.c
index 76da1db45e..65bfe52bd1 100644
--- a/src/tests/stage-view-tests.c
+++ b/src/tests/stage-view-tests.c
@@ -121,8 +121,9 @@ meta_test_stage_views_exist (void)
 }
 
 static void
-on_after_paint (ClutterStage *stage,
-                gboolean     *was_painted)
+on_after_paint (ClutterStage     *stage,
+                ClutterStageView *view,
+                gboolean         *was_painted)
 {
   *was_painted = TRUE;
 }
diff --git a/src/wayland/meta-wayland.c b/src/wayland/meta-wayland.c
index 30d3979ba1..705b37c19f 100644
--- a/src/wayland/meta-wayland.c
+++ b/src/wayland/meta-wayland.c
@@ -196,6 +196,7 @@ meta_wayland_compositor_update (MetaWaylandCompositor *compositor,
 
 static void
 on_after_update (ClutterStage          *stage,
+                 ClutterStageView      *stage_view,
                  MetaWaylandCompositor *compositor)
 {
   GList *l;
@@ -209,6 +210,7 @@ on_after_update (ClutterStage          *stage,
       GList *l_cur = l;
       MetaWaylandSurface *surface = l->data;
       MetaSurfaceActor *actor;
+      GList *stage_views;
       MetaWaylandActorSurface *actor_surface;
 
       l = l->next;
@@ -221,6 +223,10 @@ on_after_update (ClutterStage          *stage,
           meta_surface_actor_is_obscured (actor))
         continue;
 
+      stage_views = clutter_actor_peek_stage_views (CLUTTER_ACTOR (actor));
+      if (!g_list_find (stage_views, stage_view))
+        continue;
+
       actor_surface = META_WAYLAND_ACTOR_SURFACE (surface->role);
       meta_wayland_actor_surface_emit_frame_callbacks (actor_surface,
                                                        now_us / 1000);


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]