[mutter] backend/native: Add and use transactional KMS API



commit 75dff3e7c9b1ea210205de71f8291c15e6813feb
Author: Jonas Ã…dahl <jadahl gmail com>
Date:   Thu Apr 4 22:36:41 2019 +0200

    backend/native: Add and use transactional KMS API
    
    This commit introduces, and makes use of, a transactional API used for
    setting up KMS state, later to be applied, potentially atomically. From
    an API point of view, so is always the case, but in the current
    implementation, it still uses legacy drmMode* API to apply the state
    non-atomically.
    
    The API consists of various buliding blocks:
    
     * MetaKmsUpdate - a set of configuration changes, the higher level
    handle for handing over configuration to the impl backend. It's used to
    set mode, assign framebuffers to planes, queue page flips and set
    connector properties.
     * MetaKmsPlaneAssignment - the assignment of a framebuffer to a plane.
    Currently used to map a framebuffer to the primary plane of a CRTC. In
    the legacy KMS implementation, the plane assignment is used to derive
    the framebuffer used for mode setting and page flipping.
    
    This also means various high level changes:
    
    State, excluding configuring the cursor plane and creating/destroying
    DRM framebuffer handles, are applied in the end of a clutter frame, in
    one go. From an API point of view, this is done atomically, but as
    mentioned, only the non-atomic implementation exists so far.
    
    From MetaRendererNative's point of view, a page flip now initially
    always succeeds; the handling of EBUSY errors are done asynchronously in
    the MetaKmsImpl backend (still by retrying at refresh rate, but
    postponing flip callbacks instead of manipulating the frame clock).
    Handling of falling back to mode setting instead of page flipping is
    notified after the fact by a more precise page flip feedback API.
    
    EGLStream based page flipping relies on the impl backend not being
    atomic, as the page flipping is done in the EGLStream backend (e.g.
    nvidia driver). It uses a 'custom' page flip queueing method, keeping
    the EGLStream logic inside meta-renderer-native.c.
    
    Page flip handling is moved to meta-kms-impl-device.c from
    meta-gpu-kms.c. It goes via an extra idle callback before reaching
    meta-renderer-native.c to make sure callbacks are invoked outside of the
    impl context.
    
    While dummy power save page flipping is kept in meta-renderer-native.c, the
    EBUSY handling is moved to meta-kms-impl-simple.c. Instead of freezing the
    frame clock, actual page flip callbacks are postponed until all EBUSY retries
    have either succeeded or failed due to some other error than EBUSY. This
    effectively inhibits new frames to be drawn, meaning we won't stall waiting on
    the file descriptor for pending page flips.
    
    https://gitlab.gnome.org/GNOME/mutter/issues/548
    https://gitlab.gnome.org/GNOME/mutter/merge_requests/525

 src/backends/native/meta-crtc-kms.c              | 203 +++---
 src/backends/native/meta-crtc-kms.h              |  20 +-
 src/backends/native/meta-gpu-kms.c               | 313 +--------
 src/backends/native/meta-gpu-kms.h               |  18 +-
 src/backends/native/meta-kms-connector.c         |  92 +++
 src/backends/native/meta-kms-connector.h         |  14 +
 src/backends/native/meta-kms-crtc.c              |   1 +
 src/backends/native/meta-kms-device.c            |  29 +
 src/backends/native/meta-kms-device.h            |   3 +
 src/backends/native/meta-kms-impl-device.c       |  84 ++-
 src/backends/native/meta-kms-impl-device.h       |   4 +
 src/backends/native/meta-kms-impl-simple.c       | 774 ++++++++++++++++++++++
 src/backends/native/meta-kms-impl.c              |  22 +
 src/backends/native/meta-kms-impl.h              |  17 +
 src/backends/native/meta-kms-page-flip-private.h |  57 ++
 src/backends/native/meta-kms-page-flip.c         | 196 ++++++
 src/backends/native/meta-kms-plane.c             |  19 +
 src/backends/native/meta-kms-plane.h             |   6 +
 src/backends/native/meta-kms-private.h           |   9 +-
 src/backends/native/meta-kms-types.h             |  19 +
 src/backends/native/meta-kms-update-private.h    |  89 +++
 src/backends/native/meta-kms-update.c            | 236 +++++++
 src/backends/native/meta-kms-update.h            |  92 +++
 src/backends/native/meta-kms.c                   | 131 +++-
 src/backends/native/meta-kms.h                   |   9 +
 src/backends/native/meta-monitor-manager-kms.c   |  19 +-
 src/backends/native/meta-output-kms.c            | 132 +---
 src/backends/native/meta-output-kms.h            |  11 +-
 src/backends/native/meta-renderer-native.c       | 807 ++++++++---------------
 src/meson.build                                  |  13 +-
 src/meta-marshal.list                            |   1 -
 31 files changed, 2360 insertions(+), 1080 deletions(-)
---
diff --git a/src/backends/native/meta-crtc-kms.c b/src/backends/native/meta-crtc-kms.c
index 7d5e72198..10d344e62 100644
--- a/src/backends/native/meta-crtc-kms.c
+++ b/src/backends/native/meta-crtc-kms.c
@@ -25,9 +25,12 @@
 #include "backends/native/meta-crtc-kms.h"
 
 #include "backends/meta-backend-private.h"
+#include "backends/meta-logical-monitor.h"
 #include "backends/native/meta-gpu-kms.h"
+#include "backends/native/meta-output-kms.h"
 #include "backends/native/meta-kms-device.h"
 #include "backends/native/meta-kms-plane.h"
+#include "backends/native/meta-kms-update.h"
 
 #define ALL_TRANSFORMS_MASK ((1 << META_MONITOR_N_TRANSFORMS) - 1)
 
@@ -35,12 +38,11 @@ typedef struct _MetaCrtcKms
 {
   MetaKmsCrtc *kms_crtc;
 
-  uint32_t rotation_prop_id;
-  uint32_t rotation_map[META_MONITOR_N_TRANSFORMS];
-
   MetaKmsPlane *primary_plane;
 } MetaCrtcKms;
 
+static GQuark kms_crtc_crtc_kms_quark;
+
 gboolean
 meta_crtc_kms_is_transform_handled (MetaCrtc             *crtc,
                                     MetaMonitorTransform  transform)
@@ -55,60 +57,123 @@ meta_crtc_kms_is_transform_handled (MetaCrtc             *crtc,
 }
 
 void
-meta_crtc_kms_apply_transform (MetaCrtc *crtc)
+meta_crtc_kms_apply_transform (MetaCrtc               *crtc,
+                               MetaKmsPlaneAssignment *kms_plane_assignment)
 {
   MetaCrtcKms *crtc_kms = crtc->driver_private;
-  MetaGpu *gpu = meta_crtc_get_gpu (crtc);
-  MetaGpuKms *gpu_kms = META_GPU_KMS (gpu);
-  int kms_fd;
   MetaMonitorTransform hw_transform;
 
-  kms_fd = meta_gpu_kms_get_fd (gpu_kms);
-
   hw_transform = crtc->transform;
   if (!meta_crtc_kms_is_transform_handled (crtc, hw_transform))
     hw_transform = META_MONITOR_TRANSFORM_NORMAL;
   if (!meta_crtc_kms_is_transform_handled (crtc, hw_transform))
     return;
 
-  if (drmModeObjectSetProperty (kms_fd,
-                                meta_kms_plane_get_id (crtc_kms->primary_plane),
-                                DRM_MODE_OBJECT_PLANE,
-                                crtc_kms->rotation_prop_id,
-                                crtc_kms->rotation_map[hw_transform]) != 0)
-    g_warning ("Failed to apply DRM plane transform %d: %m", hw_transform);
+  meta_kms_plane_update_set_rotation (crtc_kms->primary_plane,
+                                      kms_plane_assignment,
+                                      hw_transform);
 }
 
-static int
-find_property_index (MetaGpu                    *gpu,
-                     drmModeObjectPropertiesPtr  props,
-                     const char                 *prop_name,
-                     drmModePropertyPtr         *out_prop)
+void
+meta_crtc_kms_assign_primary_plane (MetaCrtc      *crtc,
+                                    uint32_t       fb_id,
+                                    MetaKmsUpdate *kms_update)
 {
-  MetaGpuKms *gpu_kms = META_GPU_KMS (gpu);
-  int kms_fd;
-  unsigned int i;
+  MetaRectangle logical_monitor_rect;
+  int x, y;
+  MetaFixed16Rectangle src_rect;
+  MetaFixed16Rectangle dst_rect;
+  MetaKmsCrtc *kms_crtc;
+  MetaKmsDevice *kms_device;
+  MetaKmsPlane *primary_kms_plane;
+  MetaKmsPlaneAssignment *plane_assignment;
+
+  logical_monitor_rect =
+    meta_logical_monitor_get_layout (crtc->logical_monitor);
+  x = crtc->rect.x - logical_monitor_rect.x;
+  y = crtc->rect.y - logical_monitor_rect.y;
+  src_rect = (MetaFixed16Rectangle) {
+    .x = meta_fixed_16_from_int (x),
+    .y = meta_fixed_16_from_int (y),
+    .width = meta_fixed_16_from_int (crtc->rect.width),
+    .height = meta_fixed_16_from_int (crtc->rect.height),
+  };
+  dst_rect = (MetaFixed16Rectangle) {
+    .x = meta_fixed_16_from_int (0),
+    .y = meta_fixed_16_from_int (0),
+    .width = meta_fixed_16_from_int (crtc->rect.width),
+    .height = meta_fixed_16_from_int (crtc->rect.height),
+  };
+
+  kms_crtc = meta_crtc_kms_get_kms_crtc (crtc);
+  kms_device = meta_kms_crtc_get_device (kms_crtc);
+  primary_kms_plane = meta_kms_device_get_primary_plane_for (kms_device,
+                                                             kms_crtc);
+  plane_assignment = meta_kms_update_assign_plane (kms_update,
+                                                   kms_crtc,
+                                                   primary_kms_plane,
+                                                   fb_id,
+                                                   src_rect,
+                                                   dst_rect);
+  meta_crtc_kms_apply_transform (crtc, plane_assignment);
+}
 
-  kms_fd = meta_gpu_kms_get_fd (gpu_kms);
+static GList *
+generate_crtc_connector_list (MetaGpu  *gpu,
+                              MetaCrtc *crtc)
+{
+  GList *connectors = NULL;
+  GList *l;
 
-  for (i = 0; i < props->count_props; i++)
+  for (l = meta_gpu_get_outputs (gpu); l; l = l->next)
     {
-      drmModePropertyPtr prop;
+      MetaOutput *output = l->data;
+      MetaCrtc *assigned_crtc;
 
-      prop = drmModeGetProperty (kms_fd, props->props[i]);
-      if (!prop)
-        continue;
-
-      if (strcmp (prop->name, prop_name) == 0)
+      assigned_crtc = meta_output_get_assigned_crtc (output);
+      if (assigned_crtc == crtc)
         {
-          *out_prop = prop;
-          return i;
-        }
+          MetaKmsConnector *kms_connector =
+            meta_output_kms_get_kms_connector (output);
 
-      drmModeFreeProperty (prop);
+          connectors = g_list_prepend (connectors, kms_connector);
+        }
     }
 
-  return -1;
+  return connectors;
+}
+
+void
+meta_crtc_kms_set_mode (MetaCrtc      *crtc,
+                        MetaKmsUpdate *kms_update)
+{
+  MetaGpu *gpu = meta_crtc_get_gpu (crtc);
+  GList *connectors;
+  drmModeModeInfo *mode;
+
+  connectors = generate_crtc_connector_list (gpu, crtc);
+
+  if (connectors)
+    mode = crtc->current_mode->driver_private;
+  else
+    mode = NULL;
+
+  meta_kms_update_mode_set (kms_update,
+                            meta_crtc_kms_get_kms_crtc (crtc),
+                            g_steal_pointer (&connectors),
+                            mode);
+}
+
+void
+meta_crtc_kms_page_flip (MetaCrtc                      *crtc,
+                         const MetaKmsPageFlipFeedback *page_flip_feedback,
+                         gpointer                       user_data,
+                         MetaKmsUpdate                 *kms_update)
+{
+  meta_kms_update_page_flip (kms_update,
+                             meta_crtc_kms_get_kms_crtc (crtc),
+                             page_flip_feedback,
+                             user_data);
 }
 
 MetaKmsCrtc *
@@ -176,62 +241,10 @@ meta_crtc_kms_supports_format (MetaCrtc *crtc,
                                              drm_format);
 }
 
-static void
-parse_transforms (MetaCrtc          *crtc,
-                  drmModePropertyPtr prop)
-{
-  MetaCrtcKms *crtc_kms = crtc->driver_private;
-  int i;
-
-  for (i = 0; i < prop->count_enums; i++)
-    {
-      int transform = -1;
-
-      if (strcmp (prop->enums[i].name, "rotate-0") == 0)
-        transform = META_MONITOR_TRANSFORM_NORMAL;
-      else if (strcmp (prop->enums[i].name, "rotate-90") == 0)
-        transform = META_MONITOR_TRANSFORM_90;
-      else if (strcmp (prop->enums[i].name, "rotate-180") == 0)
-        transform = META_MONITOR_TRANSFORM_180;
-      else if (strcmp (prop->enums[i].name, "rotate-270") == 0)
-        transform = META_MONITOR_TRANSFORM_270;
-
-      if (transform != -1)
-        crtc_kms->rotation_map[transform] = 1 << prop->enums[i].value;
-    }
-}
-
-static void
-init_crtc_rotations (MetaCrtc *crtc,
-                     MetaGpu  *gpu)
+MetaCrtc *
+meta_crtc_kms_from_kms_crtc (MetaKmsCrtc *kms_crtc)
 {
-  MetaCrtcKms *crtc_kms = crtc->driver_private;
-  MetaGpuKms *gpu_kms = META_GPU_KMS (gpu);
-  int kms_fd;
-  uint32_t primary_plane_id;
-  drmModePlane *drm_plane;
-  drmModeObjectPropertiesPtr props;
-  drmModePropertyPtr prop;
-  int rotation_idx;
-
-  kms_fd = meta_gpu_kms_get_fd (gpu_kms);
-  primary_plane_id = meta_kms_plane_get_id (crtc_kms->primary_plane);
-  drm_plane = drmModeGetPlane (kms_fd, primary_plane_id);
-  props = drmModeObjectGetProperties (kms_fd,
-                                      primary_plane_id,
-                                      DRM_MODE_OBJECT_PLANE);
-
-  rotation_idx = find_property_index (gpu, props,
-                                      "rotation", &prop);
-  if (rotation_idx >= 0)
-    {
-      crtc_kms->rotation_prop_id = props->props[rotation_idx];
-      parse_transforms (crtc, prop);
-      drmModeFreeProperty (prop);
-    }
-
-  drmModeFreeObjectProperties (props);
-  drmModeFreePlane (drm_plane);
+  return g_object_get_qdata (G_OBJECT (kms_crtc), kms_crtc_crtc_kms_quark);
 }
 
 static void
@@ -287,7 +300,13 @@ meta_create_kms_crtc (MetaGpuKms  *gpu_kms,
   crtc->driver_private = crtc_kms;
   crtc->driver_notify = (GDestroyNotify) meta_crtc_destroy_notify;
 
-  init_crtc_rotations (crtc, gpu);
+  if (!kms_crtc_crtc_kms_quark)
+    {
+      kms_crtc_crtc_kms_quark =
+        g_quark_from_static_string ("meta-kms-crtc-crtc-kms-quark");
+    }
+
+  g_object_set_qdata (G_OBJECT (kms_crtc), kms_crtc_crtc_kms_quark, crtc);
 
   return crtc;
 }
diff --git a/src/backends/native/meta-crtc-kms.h b/src/backends/native/meta-crtc-kms.h
index 178c282c3..898622478 100644
--- a/src/backends/native/meta-crtc-kms.h
+++ b/src/backends/native/meta-crtc-kms.h
@@ -34,7 +34,23 @@
 gboolean meta_crtc_kms_is_transform_handled (MetaCrtc             *crtc,
                                              MetaMonitorTransform  transform);
 
-void meta_crtc_kms_apply_transform (MetaCrtc *crtc);
+void meta_crtc_kms_apply_transform (MetaCrtc               *crtc,
+                                    MetaKmsPlaneAssignment *kms_plane_assignment);
+
+void meta_crtc_kms_assign_primary_plane (MetaCrtc      *crtc,
+                                         uint32_t       fb_id,
+                                         MetaKmsUpdate *kms_update);
+
+void meta_crtc_kms_set_mode (MetaCrtc      *crtc,
+                             MetaKmsUpdate *kms_update);
+
+void meta_crtc_kms_page_flip (MetaCrtc                      *crtc,
+                              const MetaKmsPageFlipFeedback *page_flip_feedback,
+                              gpointer                       user_data,
+                              MetaKmsUpdate                 *kms_update);
+
+void meta_crtc_kms_set_is_underscanning (MetaCrtc *crtc,
+                                         gboolean  is_underscanning);
 
 MetaKmsCrtc * meta_crtc_kms_get_kms_crtc (MetaCrtc *crtc);
 
@@ -48,6 +64,8 @@ gboolean
 meta_crtc_kms_supports_format (MetaCrtc *crtc,
                                uint32_t  drm_format);
 
+MetaCrtc * meta_crtc_kms_from_kms_crtc (MetaKmsCrtc *kms_crtc);
+
 MetaCrtc * meta_create_kms_crtc (MetaGpuKms  *gpu_kms,
                                  MetaKmsCrtc *kms_crtc);
 
diff --git a/src/backends/native/meta-gpu-kms.c b/src/backends/native/meta-gpu-kms.c
index 596a775f3..ea937ba3e 100644
--- a/src/backends/native/meta-gpu-kms.c
+++ b/src/backends/native/meta-gpu-kms.c
@@ -39,6 +39,7 @@
 #include "backends/native/meta-crtc-kms.h"
 #include "backends/native/meta-kms-connector.h"
 #include "backends/native/meta-kms-device.h"
+#include "backends/native/meta-kms-update.h"
 #include "backends/native/meta-kms-utils.h"
 #include "backends/native/meta-kms.h"
 #include "backends/native/meta-launcher.h"
@@ -46,21 +47,6 @@
 
 #include "meta-default-modes.h"
 
-typedef struct _MetaKmsSource
-{
-  GSource source;
-
-  gpointer fd_tag;
-  MetaGpuKms *gpu_kms;
-} MetaKmsSource;
-
-typedef struct _MetaGpuKmsFlipClosureContainer
-{
-  GClosure *flip_closure;
-  MetaGpuKms *gpu_kms;
-  MetaCrtc *crtc;
-} MetaGpuKmsFlipClosureContainer;
-
 struct _MetaGpuKms
 {
   MetaGpu parent;
@@ -69,7 +55,6 @@ struct _MetaGpuKms
 
   uint32_t id;
   int fd;
-  GSource *source;
 
   clockid_t clock_id;
 
@@ -78,124 +63,6 @@ struct _MetaGpuKms
 
 G_DEFINE_TYPE (MetaGpuKms, meta_gpu_kms, META_TYPE_GPU)
 
-static gboolean
-kms_event_check (GSource *source)
-{
-  MetaKmsSource *kms_source = (MetaKmsSource *) source;
-
-  return g_source_query_unix_fd (source, kms_source->fd_tag) & G_IO_IN;
-}
-
-static gboolean
-kms_event_dispatch (GSource     *source,
-                    GSourceFunc  callback,
-                    gpointer     user_data)
-{
-  MetaKmsSource *kms_source = (MetaKmsSource *) source;
-
-  meta_gpu_kms_wait_for_flip (kms_source->gpu_kms, NULL);
-
-  return G_SOURCE_CONTINUE;
-}
-
-static GSourceFuncs kms_event_funcs = {
-  NULL,
-  kms_event_check,
-  kms_event_dispatch
-};
-
-static void
-get_crtc_drm_connectors (MetaGpu       *gpu,
-                         MetaCrtc      *crtc,
-                         uint32_t     **connectors,
-                         unsigned int  *n_connectors)
-{
-  GArray *connectors_array = g_array_new (FALSE, FALSE, sizeof (uint32_t));
-  GList *l;
-
-  for (l = meta_gpu_get_outputs (gpu); l; l = l->next)
-    {
-      MetaOutput *output = l->data;
-      MetaCrtc *assigned_crtc;
-
-      assigned_crtc = meta_output_get_assigned_crtc (output);
-      if (assigned_crtc == crtc)
-        {
-          uint32_t connector_id;
-
-          connector_id = meta_output_kms_get_connector_id (output);
-          g_array_append_val (connectors_array, connector_id);
-        }
-    }
-
-  *n_connectors = connectors_array->len;
-  *connectors = (uint32_t *) g_array_free (connectors_array, FALSE);
-}
-
-gboolean
-meta_gpu_kms_apply_crtc_mode (MetaGpuKms *gpu_kms,
-                              MetaCrtc   *crtc,
-                              int         x,
-                              int         y,
-                              uint32_t    fb_id)
-{
-  MetaGpu *gpu = meta_crtc_get_gpu (crtc);
-  int kms_fd = meta_gpu_kms_get_fd (gpu_kms);
-  uint32_t *connectors;
-  unsigned int n_connectors;
-  drmModeModeInfo *mode;
-
-  get_crtc_drm_connectors (gpu, crtc, &connectors, &n_connectors);
-
-  if (connectors)
-    mode = crtc->current_mode->driver_private;
-  else
-    mode = NULL;
-
-  if (drmModeSetCrtc (kms_fd,
-                      crtc->crtc_id,
-                      fb_id,
-                      x, y,
-                      connectors, n_connectors,
-                      mode) != 0)
-    {
-      if (mode)
-        g_warning ("Failed to set CRTC mode %s: %m", crtc->current_mode->name);
-      else
-        g_warning ("Failed to disable CRTC");
-      g_free (connectors);
-      return FALSE;
-    }
-
-  g_free (connectors);
-
-  return TRUE;
-}
-
-static void
-invoke_flip_closure (GClosure   *flip_closure,
-                     MetaGpuKms *gpu_kms,
-                     MetaCrtc   *crtc,
-                     int64_t     page_flip_time_ns)
-{
-  GValue params[] = {
-    G_VALUE_INIT,
-    G_VALUE_INIT,
-    G_VALUE_INIT,
-    G_VALUE_INIT,
-  };
-
-  g_value_init (&params[0], G_TYPE_POINTER);
-  g_value_set_pointer (&params[0], flip_closure);
-  g_value_init (&params[1], G_TYPE_OBJECT);
-  g_value_set_object (&params[1], gpu_kms);
-  g_value_init (&params[2], G_TYPE_OBJECT);
-  g_value_set_object (&params[2], crtc);
-  g_value_init (&params[3], G_TYPE_INT64);
-  g_value_set_int64 (&params[3], page_flip_time_ns);
-  g_closure_invoke (flip_closure, NULL, 4, params, NULL);
-}
-
 gboolean
 meta_gpu_kms_is_crtc_active (MetaGpuKms *gpu_kms,
                              MetaCrtc   *crtc)
@@ -232,79 +99,6 @@ meta_gpu_kms_is_crtc_active (MetaGpuKms *gpu_kms,
   return TRUE;
 }
 
-MetaGpuKmsFlipClosureContainer *
-meta_gpu_kms_wrap_flip_closure (MetaGpuKms *gpu_kms,
-                                MetaCrtc   *crtc,
-                                GClosure   *flip_closure)
-{
-  MetaGpuKmsFlipClosureContainer *closure_container;
-
-  closure_container = g_new0 (MetaGpuKmsFlipClosureContainer, 1);
-  *closure_container = (MetaGpuKmsFlipClosureContainer) {
-    .flip_closure = g_closure_ref (flip_closure),
-    .gpu_kms = gpu_kms,
-    .crtc = crtc
-  };
-
-  return closure_container;
-}
-
-void
-meta_gpu_kms_flip_closure_container_free (MetaGpuKmsFlipClosureContainer *closure_container)
-{
-  g_closure_unref (closure_container->flip_closure);
-  g_free (closure_container);
-}
-
-gboolean
-meta_gpu_kms_flip_crtc (MetaGpuKms  *gpu_kms,
-                        MetaCrtc    *crtc,
-                        uint32_t     fb_id,
-                        GClosure    *flip_closure,
-                        GError     **error)
-{
-  MetaGpu *gpu = META_GPU (gpu_kms);
-  MetaBackend *backend = meta_gpu_get_backend (gpu);
-  MetaMonitorManager *monitor_manager =
-    meta_backend_get_monitor_manager (backend);
-  MetaGpuKmsFlipClosureContainer *closure_container;
-  int kms_fd = meta_gpu_kms_get_fd (gpu_kms);
-  uint32_t *connectors;
-  unsigned int n_connectors;
-  int ret = -1;
-
-  g_assert (meta_crtc_get_gpu (crtc) == gpu);
-  g_assert (monitor_manager);
-  g_assert (meta_monitor_manager_get_power_save_mode (monitor_manager) ==
-            META_POWER_SAVE_ON);
-
-  get_crtc_drm_connectors (gpu, crtc, &connectors, &n_connectors);
-  g_assert (n_connectors > 0);
-  g_free (connectors);
-
-  g_assert (fb_id != 0);
-
-  closure_container = meta_gpu_kms_wrap_flip_closure (gpu_kms,
-                                                      crtc,
-                                                      flip_closure);
-
-  ret = drmModePageFlip (kms_fd,
-                         crtc->crtc_id,
-                         fb_id,
-                         DRM_MODE_PAGE_FLIP_EVENT,
-                         closure_container);
-  if (ret != 0)
-    {
-      meta_gpu_kms_flip_closure_container_free (closure_container);
-      g_set_error (error, G_IO_ERROR,
-                   g_io_error_from_errno (-ret),
-                   "drmModePageFlip failed: %s", g_strerror (-ret));
-      return FALSE;
-    }
-
-  return TRUE;
-}
-
 static int64_t
 timespec_to_nanoseconds (const struct timespec *ts)
 {
@@ -313,71 +107,12 @@ timespec_to_nanoseconds (const struct timespec *ts)
   return ((int64_t) ts->tv_sec) * one_billion + ts->tv_nsec;
 }
 
-static int64_t
-timeval_to_nanoseconds (const struct timeval *tv)
-{
-  int64_t usec = ((int64_t) tv->tv_sec) * G_USEC_PER_SEC + tv->tv_usec;
-  int64_t nsec = usec * 1000;
-
-  return nsec;
-}
-
-static void
-page_flip_handler (int           fd,
-                   unsigned int  frame,
-                   unsigned int  sec,
-                   unsigned int  usec,
-                   void         *user_data)
-{
-  MetaGpuKmsFlipClosureContainer *closure_container = user_data;
-  GClosure *flip_closure = closure_container->flip_closure;
-  MetaGpuKms *gpu_kms = closure_container->gpu_kms;
-  struct timeval page_flip_time = {sec, usec};
-
-  invoke_flip_closure (flip_closure,
-                       gpu_kms,
-                       closure_container->crtc,
-                       timeval_to_nanoseconds (&page_flip_time));
-  meta_gpu_kms_flip_closure_container_free (closure_container);
-}
-
 gboolean
 meta_gpu_kms_wait_for_flip (MetaGpuKms *gpu_kms,
                             GError    **error)
 {
-  drmEventContext evctx;
-
-  memset (&evctx, 0, sizeof evctx);
-  evctx.version = 2;
-  evctx.page_flip_handler = page_flip_handler;
-
-  while (TRUE)
-    {
-      if (drmHandleEvent (gpu_kms->fd, &evctx) != 0)
-        {
-          struct pollfd pfd;
-          int ret;
-
-          if (errno != EAGAIN)
-            {
-              g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
-                                   strerror (errno));
-              return FALSE;
-            }
-
-          pfd.fd = gpu_kms->fd;
-          pfd.events = POLL_IN | POLL_ERR;
-          do
-            {
-              ret = poll (&pfd, 1, -1);
-            }
-          while (ret == -1 && errno == EINTR);
-        }
-      else
-        {
-          break;
-        }
-    }
+  if (meta_kms_device_dispatch_sync (gpu_kms->kms_device, error) < 0)
+    return FALSE;
 
   return TRUE;
 }
@@ -418,8 +153,9 @@ meta_gpu_kms_get_current_time_ns (MetaGpuKms *gpu_kms)
 }
 
 void
-meta_gpu_kms_set_power_save_mode (MetaGpuKms *gpu_kms,
-                                  uint64_t    state)
+meta_gpu_kms_set_power_save_mode (MetaGpuKms    *gpu_kms,
+                                  uint64_t       state,
+                                  MetaKmsUpdate *kms_update)
 {
   GList *l;
 
@@ -427,7 +163,7 @@ meta_gpu_kms_set_power_save_mode (MetaGpuKms *gpu_kms,
     {
       MetaOutput *output = l->data;
 
-      meta_output_kms_set_power_save_mode (output, state);
+      meta_output_kms_set_power_save_mode (output, state, kms_update);
     }
 }
 
@@ -707,16 +443,13 @@ init_outputs (MetaGpuKms *gpu_kms)
   for (l = meta_kms_device_get_connectors (gpu_kms->kms_device); l; l = l->next)
     {
       MetaKmsConnector *kms_connector = l->data;
+      const MetaKmsConnectorState *connector_state;
       MetaOutput *output;
       MetaOutput *old_output;
       GError *error = NULL;
-      uint32_t connector_id;
-      drmModeConnector *connector;
-
-      connector_id = meta_kms_connector_get_id (kms_connector);
-      connector = drmModeGetConnector (gpu_kms->fd, connector_id);
 
-      if (!connector || connector->connection != DRM_MODE_CONNECTED)
+      connector_state = meta_kms_connector_get_current_state (kms_connector);
+      if (!connector_state)
         continue;
 
       old_output =
@@ -724,7 +457,6 @@ init_outputs (MetaGpuKms *gpu_kms)
                                      meta_kms_connector_get_id (kms_connector));
       output = meta_create_kms_output (gpu_kms,
                                        kms_connector,
-                                       connector,
                                        old_output,
                                        &error);
       if (!output)
@@ -787,8 +519,6 @@ meta_gpu_kms_new (MetaBackendNative  *backend_native,
                   MetaKmsDevice      *kms_device,
                   GError            **error)
 {
-  GSource *source;
-  MetaKmsSource *kms_source;
   MetaGpuKms *gpu_kms;
   int kms_fd;
 
@@ -803,29 +533,9 @@ meta_gpu_kms_new (MetaBackendNative  *backend_native,
 
   meta_gpu_kms_read_current (META_GPU (gpu_kms), NULL);
 
-  source = g_source_new (&kms_event_funcs, sizeof (MetaKmsSource));
-  kms_source = (MetaKmsSource *) source;
-  kms_source->fd_tag = g_source_add_unix_fd (source,
-                                             gpu_kms->fd,
-                                             G_IO_IN | G_IO_ERR);
-  kms_source->gpu_kms = gpu_kms;
-
-  gpu_kms->source = source;
-  g_source_attach (gpu_kms->source, NULL);
-
   return gpu_kms;
 }
 
-static void
-meta_gpu_kms_finalize (GObject *object)
-{
-  MetaGpuKms *gpu_kms = META_GPU_KMS (object);
-
-  g_source_destroy (gpu_kms->source);
-
-  G_OBJECT_CLASS (meta_gpu_kms_parent_class)->finalize (object);
-}
-
 static void
 meta_gpu_kms_init (MetaGpuKms *gpu_kms)
 {
@@ -838,10 +548,7 @@ meta_gpu_kms_init (MetaGpuKms *gpu_kms)
 static void
 meta_gpu_kms_class_init (MetaGpuKmsClass *klass)
 {
-  GObjectClass *object_class = G_OBJECT_CLASS (klass);
   MetaGpuClass *gpu_class = META_GPU_CLASS (klass);
 
-  object_class->finalize = meta_gpu_kms_finalize;
-
   gpu_class->read_current = meta_gpu_kms_read_current;
 }
diff --git a/src/backends/native/meta-gpu-kms.h b/src/backends/native/meta-gpu-kms.h
index 1dcdc50aa..6646ead93 100644
--- a/src/backends/native/meta-gpu-kms.h
+++ b/src/backends/native/meta-gpu-kms.h
@@ -29,6 +29,7 @@
 
 #include "backends/meta-gpu.h"
 #include "backends/native/meta-backend-native.h"
+#include "backends/native/meta-kms-types.h"
 
 #define META_TYPE_GPU_KMS (meta_gpu_kms_get_type ())
 G_DECLARE_FINAL_TYPE (MetaGpuKms, meta_gpu_kms, META, GPU_KMS, MetaGpu)
@@ -39,12 +40,6 @@ MetaGpuKms * meta_gpu_kms_new (MetaBackendNative  *backend_native,
                                MetaKmsDevice      *kms_device,
                                GError            **error);
 
-gboolean meta_gpu_kms_apply_crtc_mode (MetaGpuKms *gpu_kms,
-                                       MetaCrtc   *crtc,
-                                       int         x,
-                                       int         y,
-                                       uint32_t    fb_id);
-
 gboolean meta_gpu_kms_can_have_outputs (MetaGpuKms *gpu_kms);
 
 gboolean meta_gpu_kms_is_crtc_active (MetaGpuKms *gpu_kms,
@@ -53,12 +48,6 @@ gboolean meta_gpu_kms_is_crtc_active (MetaGpuKms *gpu_kms,
 gboolean meta_gpu_kms_is_boot_vga (MetaGpuKms *gpu_kms);
 gboolean meta_gpu_kms_is_platform_device (MetaGpuKms *gpu_kms);
 
-gboolean meta_gpu_kms_flip_crtc (MetaGpuKms  *gpu_kms,
-                                 MetaCrtc    *crtc,
-                                 uint32_t     fb_id,
-                                 GClosure    *flip_closure,
-                                 GError     **error);
-
 gboolean meta_gpu_kms_wait_for_flip (MetaGpuKms *gpu_kms,
                                      GError    **error);
 
@@ -72,8 +61,9 @@ const char * meta_gpu_kms_get_file_path (MetaGpuKms *gpu_kms);
 
 int64_t meta_gpu_kms_get_current_time_ns (MetaGpuKms *gpu_kms);
 
-void meta_gpu_kms_set_power_save_mode (MetaGpuKms *gpu_kms,
-                                       uint64_t    state);
+void meta_gpu_kms_set_power_save_mode (MetaGpuKms    *gpu_kms,
+                                       uint64_t       state,
+                                       MetaKmsUpdate *kms_update);
 
 MetaCrtcMode * meta_gpu_kms_get_mode_from_drm_mode (MetaGpuKms            *gpu_kms,
                                                     const drmModeModeInfo *drm_mode);
diff --git a/src/backends/native/meta-kms-connector.c b/src/backends/native/meta-kms-connector.c
index bcd97e1c3..70d3336d3 100644
--- a/src/backends/native/meta-kms-connector.c
+++ b/src/backends/native/meta-kms-connector.c
@@ -26,6 +26,7 @@
 
 #include "backends/native/meta-kms-device-private.h"
 #include "backends/native/meta-kms-impl-device.h"
+#include "backends/native/meta-kms-update-private.h"
 
 struct _MetaKmsConnector
 {
@@ -38,6 +39,13 @@ struct _MetaKmsConnector
   char *name;
 
   MetaKmsConnectorState *current_state;
+
+  uint32_t dpms_prop_id;
+  uint32_t underscan_prop_id;
+  uint32_t underscan_hborder_prop_id;
+  uint32_t underscan_vborder_prop_id;
+  uint32_t edid_blob_id;
+  uint32_t tile_blob_id;
 };
 
 G_DEFINE_TYPE (MetaKmsConnector, meta_kms_connector, G_TYPE_OBJECT)
@@ -48,6 +56,47 @@ meta_kms_connector_get_device (MetaKmsConnector *connector)
   return connector->device;
 }
 
+void
+meta_kms_connector_update_set_dpms_state (MetaKmsConnector *connector,
+                                          MetaKmsUpdate    *update,
+                                          uint64_t          state)
+{
+  meta_kms_update_set_connector_property (update,
+                                          connector,
+                                          connector->dpms_prop_id,
+                                          state);
+}
+
+void
+meta_kms_connector_set_underscanning (MetaKmsConnector *connector,
+                                      MetaKmsUpdate    *update,
+                                      uint64_t          hborder,
+                                      uint64_t          vborder)
+{
+  meta_kms_update_set_connector_property (update,
+                                          connector,
+                                          connector->underscan_prop_id,
+                                          1);
+  meta_kms_update_set_connector_property (update,
+                                          connector,
+                                          connector->underscan_hborder_prop_id,
+                                          hborder);
+  meta_kms_update_set_connector_property (update,
+                                          connector,
+                                          connector->underscan_vborder_prop_id,
+                                          vborder);
+}
+
+void
+meta_kms_connector_unset_underscanning (MetaKmsConnector *connector,
+                                        MetaKmsUpdate    *update)
+{
+  meta_kms_update_set_connector_property (update,
+                                          connector,
+                                          connector->underscan_prop_id,
+                                          0);
+}
+
 MetaConnectorType
 meta_kms_connector_get_connector_type (MetaKmsConnector *connector)
 {
@@ -89,6 +138,12 @@ meta_kms_connector_get_current_state (MetaKmsConnector *connector)
   return connector->current_state;
 }
 
+gboolean
+meta_kms_connector_is_underscanning_supported (MetaKmsConnector *connector)
+{
+  return connector->underscan_prop_id != 0;
+}
+
 static void
 set_panel_orientation (MetaKmsConnectorState *state,
                        drmModePropertyPtr     prop,
@@ -437,6 +492,41 @@ meta_kms_connector_update_state (MetaKmsConnector *connector,
                                  drm_resources);
 }
 
+static void
+find_property_ids (MetaKmsConnector  *connector,
+                   MetaKmsImplDevice *impl_device,
+                   drmModeConnector  *drm_connector)
+{
+  int fd;
+  int i;
+
+  fd = meta_kms_impl_device_get_fd (impl_device);
+
+  for (i = 0; i < drm_connector->count_props; i++)
+    {
+      drmModePropertyPtr prop;
+
+      prop = drmModeGetProperty (fd, drm_connector->props[i]);
+      if (!prop)
+        continue;
+
+      if ((prop->flags & DRM_MODE_PROP_ENUM) &&
+          strcmp (prop->name, "DPMS") == 0)
+        connector->dpms_prop_id = prop->prop_id;
+      else if ((prop->flags & DRM_MODE_PROP_ENUM) &&
+               strcmp (prop->name, "underscan") == 0)
+        connector->underscan_prop_id = prop->prop_id;
+      else if ((prop->flags & DRM_MODE_PROP_RANGE) &&
+               strcmp (prop->name, "underscan hborder") == 0)
+        connector->underscan_hborder_prop_id = prop->prop_id;
+      else if ((prop->flags & DRM_MODE_PROP_RANGE) &&
+               strcmp (prop->name, "underscan vborder") == 0)
+        connector->underscan_vborder_prop_id = prop->prop_id;
+
+      drmModeFreeProperty (prop);
+    }
+}
+
 static char *
 make_connector_name (drmModeConnector *drm_connector)
 {
@@ -483,6 +573,8 @@ meta_kms_connector_new (MetaKmsImplDevice *impl_device,
   connector->type = (MetaConnectorType) drm_connector->connector_type;
   connector->name = make_connector_name (drm_connector);
 
+  find_property_ids (connector, impl_device, drm_connector);
+
   meta_kms_connector_read_state (connector, impl_device,
                                  drm_connector,
                                  drm_resources);
diff --git a/src/backends/native/meta-kms-connector.h b/src/backends/native/meta-kms-connector.h
index d536608e1..b6198b467 100644
--- a/src/backends/native/meta-kms-connector.h
+++ b/src/backends/native/meta-kms-connector.h
@@ -61,6 +61,18 @@ typedef struct _MetaKmsConnectorState
 
 MetaKmsDevice * meta_kms_connector_get_device (MetaKmsConnector *connector);
 
+void meta_kms_connector_update_set_dpms_state (MetaKmsConnector *connector,
+                                               MetaKmsUpdate    *update,
+                                               uint64_t          state);
+
+void meta_kms_connector_set_underscanning (MetaKmsConnector *connector,
+                                           MetaKmsUpdate    *update,
+                                           uint64_t          hborder,
+                                           uint64_t          vborder);
+
+void meta_kms_connector_unset_underscanning (MetaKmsConnector *connector,
+                                             MetaKmsUpdate    *update);
+
 MetaConnectorType meta_kms_connector_get_connector_type (MetaKmsConnector *connector);
 
 uint32_t meta_kms_connector_get_id (MetaKmsConnector *connector);
@@ -72,4 +84,6 @@ gboolean meta_kms_connector_can_clone (MetaKmsConnector *connector,
 
 const MetaKmsConnectorState * meta_kms_connector_get_current_state (MetaKmsConnector *connector);
 
+gboolean meta_kms_connector_is_underscanning_supported (MetaKmsConnector *connector);
+
 #endif /* META_KMS_CONNECTOR_H */
diff --git a/src/backends/native/meta-kms-crtc.c b/src/backends/native/meta-kms-crtc.c
index 910a5a84a..5006a1816 100644
--- a/src/backends/native/meta-kms-crtc.c
+++ b/src/backends/native/meta-kms-crtc.c
@@ -24,6 +24,7 @@
 
 #include "backends/native/meta-kms-device-private.h"
 #include "backends/native/meta-kms-impl-device.h"
+#include "backends/native/meta-kms-update-private.h"
 
 struct _MetaKmsCrtc
 {
diff --git a/src/backends/native/meta-kms-device.c b/src/backends/native/meta-kms-device.c
index a4dd0006d..3a626c58c 100644
--- a/src/backends/native/meta-kms-device.c
+++ b/src/backends/native/meta-kms-device.c
@@ -108,6 +108,35 @@ meta_kms_device_get_primary_plane_for (MetaKmsDevice *device,
   return NULL;
 }
 
+static gboolean
+dispatch_in_impl (MetaKmsImpl  *impl,
+                  gpointer      user_data,
+                  GError      **error)
+{
+  MetaKmsImplDevice *impl_device = META_KMS_IMPL_DEVICE (user_data);
+
+  return meta_kms_impl_device_dispatch (impl_device, error);
+}
+
+int
+meta_kms_device_dispatch_sync (MetaKmsDevice  *device,
+                               GError        **error)
+{
+  int callback_count;
+
+  callback_count = meta_kms_flush_callbacks (device->kms);
+  if (callback_count > 0)
+    return TRUE;
+
+  if (!meta_kms_run_impl_task_sync (device->kms,
+                                    dispatch_in_impl,
+                                    device->impl_device,
+                                    error))
+    return -1;
+
+  return meta_kms_flush_callbacks (device->kms);
+}
+
 typedef struct _CreateImplDeviceData
 {
   MetaKmsDevice *device;
diff --git a/src/backends/native/meta-kms-device.h b/src/backends/native/meta-kms-device.h
index c4ec034c3..ea0f9aab5 100644
--- a/src/backends/native/meta-kms-device.h
+++ b/src/backends/native/meta-kms-device.h
@@ -42,6 +42,9 @@ GList * meta_kms_device_get_crtcs (MetaKmsDevice *device);
 MetaKmsPlane * meta_kms_device_get_primary_plane_for (MetaKmsDevice *device,
                                                       MetaKmsCrtc   *crtc);
 
+int meta_kms_device_dispatch_sync (MetaKmsDevice  *device,
+                                   GError        **error);
+
 MetaKmsDevice * meta_kms_device_new (MetaKms            *kms,
                                      const char         *path,
                                      MetaKmsDeviceFlag   flags,
diff --git a/src/backends/native/meta-kms-impl-device.c b/src/backends/native/meta-kms-impl-device.c
index 6593f2aee..5f7a43daf 100644
--- a/src/backends/native/meta-kms-impl-device.c
+++ b/src/backends/native/meta-kms-impl-device.c
@@ -21,6 +21,7 @@
 
 #include "backends/native/meta-kms-impl-device.h"
 
+#include <errno.h>
 #include <xf86drm.h>
 
 #include "backends/native/meta-kms-connector-private.h"
@@ -28,8 +29,10 @@
 #include "backends/native/meta-kms-crtc-private.h"
 #include "backends/native/meta-kms-crtc.h"
 #include "backends/native/meta-kms-impl.h"
+#include "backends/native/meta-kms-page-flip-private.h"
 #include "backends/native/meta-kms-plane.h"
 #include "backends/native/meta-kms-private.h"
+#include "backends/native/meta-kms-update.h"
 
 struct _MetaKmsImplDevice
 {
@@ -39,6 +42,7 @@ struct _MetaKmsImplDevice
   MetaKmsImpl *impl;
 
   int fd;
+  GSource *fd_source;
 
   GList *crtcs;
   GList *connectors;
@@ -71,6 +75,77 @@ meta_kms_impl_device_copy_planes (MetaKmsImplDevice *impl_device)
   return g_list_copy (impl_device->planes);
 }
 
+static void
+page_flip_handler (int           fd,
+                   unsigned int  sequence,
+                   unsigned int  sec,
+                   unsigned int  usec,
+                   void         *user_data)
+{
+  MetaKmsPageFlipData *page_flip_data = user_data;
+  MetaKmsImpl *impl;
+
+  meta_kms_page_flip_data_set_timings_in_impl (page_flip_data,
+                                               sequence, sec, usec);
+
+  impl = meta_kms_page_flip_data_get_kms_impl (page_flip_data);
+  meta_kms_impl_handle_page_flip_callback (impl, page_flip_data);
+}
+
+gboolean
+meta_kms_impl_device_dispatch (MetaKmsImplDevice  *impl_device,
+                               GError            **error)
+{
+  drmEventContext drm_event_context;
+
+  meta_assert_in_kms_impl (meta_kms_impl_get_kms (impl_device->impl));
+
+  drm_event_context = (drmEventContext) { 0 };
+  drm_event_context.version = 2;
+  drm_event_context.page_flip_handler = page_flip_handler;
+
+  while (TRUE)
+    {
+      if (drmHandleEvent (impl_device->fd, &drm_event_context) != 0)
+        {
+          struct pollfd pfd;
+          int ret;
+
+          if (errno != EAGAIN)
+            {
+              g_set_error_literal (error, G_IO_ERROR,
+                                   g_io_error_from_errno (errno),
+                                   strerror (errno));
+              return FALSE;
+            }
+
+          pfd.fd = impl_device->fd;
+          pfd.events = POLL_IN | POLL_ERR;
+          do
+            {
+              ret = poll (&pfd, 1, -1);
+            }
+          while (ret == -1 && errno == EINTR);
+        }
+      else
+        {
+          break;
+        }
+    }
+
+  return TRUE;
+}
+
+static gboolean
+kms_event_dispatch_in_impl (MetaKmsImpl  *impl,
+                            gpointer      user_data,
+                            GError      **error)
+{
+  MetaKmsImplDevice *impl_device = user_data;
+
+  return meta_kms_impl_device_dispatch (impl_device, error);
+}
+
 drmModePropertyPtr
 meta_kms_impl_device_find_property (MetaKmsImplDevice       *impl_device,
                                     drmModeObjectProperties *props,
@@ -236,10 +311,11 @@ meta_kms_impl_device_new (MetaKmsDevice *device,
                           MetaKmsImpl   *impl,
                           int            fd)
 {
+  MetaKms *kms = meta_kms_impl_get_kms (impl);
   MetaKmsImplDevice *impl_device;
   drmModeRes *drm_resources;
 
-  meta_assert_in_kms_impl (meta_kms_impl_get_kms (impl));
+  meta_assert_in_kms_impl (kms);
 
   impl_device = g_object_new (META_TYPE_KMS_IMPL_DEVICE, NULL);
   impl_device->device = device;
@@ -256,6 +332,11 @@ meta_kms_impl_device_new (MetaKmsDevice *device,
 
   drmModeFreeResources (drm_resources);
 
+  impl_device->fd_source =
+    meta_kms_register_fd_in_impl (kms, fd,
+                                  kms_event_dispatch_in_impl,
+                                  impl_device);
+
   return impl_device;
 }
 
@@ -280,6 +361,7 @@ meta_kms_impl_device_close (MetaKmsImplDevice *impl_device)
 
   meta_assert_in_kms_impl (meta_kms_impl_get_kms (impl_device->impl));
 
+  g_clear_pointer (&impl_device->fd_source, g_source_destroy);
   fd = impl_device->fd;
   impl_device->fd = -1;
 
diff --git a/src/backends/native/meta-kms-impl-device.h b/src/backends/native/meta-kms-impl-device.h
index 974915c3d..6c4b4e540 100644
--- a/src/backends/native/meta-kms-impl-device.h
+++ b/src/backends/native/meta-kms-impl-device.h
@@ -26,6 +26,7 @@
 
 #include "backends/native/meta-kms-device.h"
 #include "backends/native/meta-kms-types.h"
+#include "backends/native/meta-kms-update.h"
 
 #define META_TYPE_KMS_IMPL_DEVICE (meta_kms_impl_device_get_type ())
 G_DECLARE_FINAL_TYPE (MetaKmsImplDevice, meta_kms_impl_device,
@@ -40,6 +41,9 @@ GList * meta_kms_impl_device_copy_crtcs (MetaKmsImplDevice *impl_device);
 
 GList * meta_kms_impl_device_copy_planes (MetaKmsImplDevice *impl_device);
 
+gboolean meta_kms_impl_device_dispatch (MetaKmsImplDevice  *impl_device,
+                                        GError            **error);
+
 drmModePropertyPtr meta_kms_impl_device_find_property (MetaKmsImplDevice       *impl_device,
                                                        drmModeObjectProperties *props,
                                                        const char              *prop_name,
diff --git a/src/backends/native/meta-kms-impl-simple.c b/src/backends/native/meta-kms-impl-simple.c
index 6432cbb7a..ddbee6b81 100644
--- a/src/backends/native/meta-kms-impl-simple.c
+++ b/src/backends/native/meta-kms-impl-simple.c
@@ -21,16 +21,47 @@
 
 #include "backends/native/meta-kms-impl-simple.h"
 
+#include <errno.h>
 #include <gbm.h>
+#include <xf86drmMode.h>
+
+#include "backends/native/meta-kms-connector.h"
+#include "backends/native/meta-kms-crtc.h"
+#include "backends/native/meta-kms-device-private.h"
+#include "backends/native/meta-kms-page-flip-private.h"
+#include "backends/native/meta-kms-plane.h"
+#include "backends/native/meta-kms-private.h"
+#include "backends/native/meta-kms-update-private.h"
+#include "backends/native/meta-kms-utils.h"
+
+typedef struct _CachedModeSet
+{
+  GList *connectors;
+  drmModeModeInfo *drm_mode;
+} CachedModeSet;
 
 struct _MetaKmsImplSimple
 {
   MetaKmsImpl parent;
+
+  GSource *mode_set_fallback_feedback_source;
+  GList *mode_set_fallback_page_flip_datas;
+
+  GList *pending_page_flip_retries;
+  GSource *retry_page_flips_source;
+
+  GList *postponed_page_flip_datas;
+  GList *postponed_mode_set_fallback_datas;
+
+  GHashTable *cached_mode_sets;
 };
 
 G_DEFINE_TYPE (MetaKmsImplSimple, meta_kms_impl_simple,
                META_TYPE_KMS_IMPL)
 
+static void
+flush_postponed_page_flip_datas (MetaKmsImplSimple *impl_simple);
+
 MetaKmsImplSimple *
 meta_kms_impl_simple_new (MetaKms  *kms,
                           GError  **error)
@@ -40,12 +71,755 @@ meta_kms_impl_simple_new (MetaKms  *kms,
                        NULL);
 }
 
+static gboolean
+process_connector_property (MetaKmsImpl               *impl,
+                            MetaKmsUpdate             *update,
+                            MetaKmsConnectorProperty  *connector_property,
+                            GError                   **error)
+{
+  MetaKmsConnector *connector = connector_property->connector;
+  MetaKmsDevice *device = meta_kms_connector_get_device (connector);
+  MetaKmsImplDevice *impl_device = meta_kms_device_get_impl_device (device);
+  int fd;
+  int ret;
+
+  fd = meta_kms_impl_device_get_fd (impl_device);
+
+  ret = drmModeObjectSetProperty (fd,
+                                  meta_kms_connector_get_id (connector),
+                                  DRM_MODE_OBJECT_CONNECTOR,
+                                  connector_property->prop_id,
+                                  connector_property->value);
+  if (ret != 0)
+    {
+      g_set_error (error, G_IO_ERROR, g_io_error_from_errno (-ret),
+                   "Failed to set connector %u property %u: %s",
+                   meta_kms_connector_get_id (connector),
+                   connector_property->prop_id,
+                   g_strerror (-ret));
+      return FALSE;
+    }
+
+  return TRUE;
+}
+
+static gboolean
+process_plane_property (MetaKmsImpl      *impl,
+                        MetaKmsPlane     *plane,
+                        MetaKmsProperty  *prop,
+                        GError          **error)
+{
+  MetaKmsDevice *device = meta_kms_plane_get_device (plane);
+  MetaKmsImplDevice *impl_device = meta_kms_device_get_impl_device (device);
+  int fd;
+  int ret;
+
+  fd = meta_kms_impl_device_get_fd (impl_device);
+
+  ret = drmModeObjectSetProperty (fd,
+                                  meta_kms_plane_get_id (plane),
+                                  DRM_MODE_OBJECT_PLANE,
+                                  prop->prop_id,
+                                  prop->value);
+  if (ret != 0)
+    {
+      g_set_error (error, G_IO_ERROR, g_io_error_from_errno (-ret),
+                   "Failed to set plane %u property %u: %s",
+                   meta_kms_plane_get_id (plane),
+                   prop->prop_id,
+                   g_strerror (-ret));
+      return FALSE;
+    }
+
+  return TRUE;
+}
+
+static MetaKmsPlaneAssignment *
+get_primary_plane_assignment (MetaKmsImpl   *impl,
+                              MetaKmsUpdate *update,
+                              MetaKmsCrtc   *crtc)
+{
+  GList *l;
+
+  for (l = meta_kms_update_get_plane_assignments (update); l; l = l->next)
+    {
+      MetaKmsPlaneAssignment *plane_assignment = l->data;
+
+      if (plane_assignment->crtc == crtc)
+        return plane_assignment;
+    }
+
+  return NULL;
+}
+
+static CachedModeSet *
+cached_mode_set_new (GList                 *connectors,
+                     const drmModeModeInfo *drm_mode)
+{
+  CachedModeSet *cached_mode_set;
+
+  cached_mode_set = g_new0 (CachedModeSet, 1);
+  *cached_mode_set = (CachedModeSet) {
+    .connectors = g_list_copy (connectors),
+    .drm_mode = g_memdup (drm_mode, sizeof *drm_mode),
+  };
+
+  return cached_mode_set;
+}
+
+static void
+cached_mode_set_free (CachedModeSet *cached_mode_set)
+{
+  g_list_free (cached_mode_set->connectors);
+  g_free (cached_mode_set->drm_mode);
+  g_free (cached_mode_set);
+}
+
+static void
+fill_connector_ids_array (GList     *connectors,
+                          uint32_t **out_connectors,
+                          int       *out_n_connectors)
+{
+  GList *l;
+  int i;
+
+  *out_n_connectors = g_list_length (connectors);
+  *out_connectors = g_new0 (uint32_t, *out_n_connectors);
+  i = 0;
+  for (l = connectors; l; l = l->next)
+    {
+      MetaKmsConnector *connector = l->data;
+
+      (*out_connectors)[i++] = meta_kms_connector_get_id (connector);
+    }
+}
+
+static gboolean
+process_mode_set (MetaKmsImpl     *impl,
+                  MetaKmsUpdate   *update,
+                  MetaKmsModeSet  *mode_set,
+                  GError         **error)
+{
+  MetaKmsImplSimple *impl_simple = META_KMS_IMPL_SIMPLE (impl);
+  MetaKmsCrtc *crtc = mode_set->crtc;;
+  MetaKmsDevice *device = meta_kms_crtc_get_device (crtc);
+  MetaKmsImplDevice *impl_device = meta_kms_device_get_impl_device (device);
+  g_autofree uint32_t *connectors = NULL;
+  int n_connectors;
+  MetaKmsPlaneAssignment *plane_assignment;
+  uint32_t x, y;
+  uint32_t fb_id;
+  int fd;
+  int ret;
+
+  crtc = mode_set->crtc;
+
+  if (mode_set->drm_mode)
+    {
+      GList *l;
+
+      fill_connector_ids_array (mode_set->connectors,
+                                &connectors,
+                                &n_connectors);
+
+      plane_assignment = get_primary_plane_assignment (impl, update, crtc);
+      if (!plane_assignment)
+        {
+          g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                       "Missing primary plane assignment for legacy mode set on CRTC %u",
+                       meta_kms_crtc_get_id (crtc));
+          return FALSE;
+        }
+
+      x = meta_fixed_16_to_int (plane_assignment->src_rect.x);
+      y = meta_fixed_16_to_int (plane_assignment->src_rect.y);
+
+      for (l = plane_assignment->plane_properties; l; l = l->next)
+        {
+          MetaKmsProperty *prop = l->data;
+
+          if (!process_plane_property (impl, plane_assignment->plane,
+                                       prop, error))
+            return FALSE;
+        }
+
+      fb_id = plane_assignment->fb_id;
+    }
+  else
+    {
+      x = y = 0;
+      n_connectors = 0;
+      connectors = NULL;
+      fb_id = 0;
+    }
+
+  fd = meta_kms_impl_device_get_fd (impl_device);
+  ret = drmModeSetCrtc (fd,
+                        meta_kms_crtc_get_id (crtc),
+                        fb_id,
+                        x, y,
+                        connectors, n_connectors,
+                        mode_set->drm_mode);
+  if (ret != 0)
+    {
+      g_set_error (error, G_IO_ERROR, g_io_error_from_errno (-ret),
+                   "Failed to set mode on CRTC %u: %s",
+                   meta_kms_crtc_get_id (crtc),
+                   g_strerror (-ret));
+      return FALSE;
+    }
+
+  if (mode_set->drm_mode)
+    {
+      g_hash_table_replace (impl_simple->cached_mode_sets,
+                            crtc,
+                            cached_mode_set_new (mode_set->connectors,
+                                                 mode_set->drm_mode));
+    }
+  else
+    {
+      g_hash_table_remove (impl_simple->cached_mode_sets, crtc);
+    }
+
+  return TRUE;
+}
+
+static gboolean
+is_timestamp_earlier_than (uint64_t ts1,
+                           uint64_t ts2)
+{
+  if (ts1 == ts2)
+    return FALSE;
+  else
+    return ts2 - ts1 < UINT64_MAX / 2;
+}
+
+typedef struct _RetryPageFlipData
+{
+  MetaKmsCrtc *crtc;
+  uint32_t fb_id;
+  MetaKmsPageFlipData *page_flip_data;
+  float refresh_rate;
+  uint64_t retry_time_us;
+} RetryPageFlipData;
+
+static void
+retry_page_flip_data_free (RetryPageFlipData *retry_page_flip_data)
+{
+  g_assert (!retry_page_flip_data->page_flip_data);
+  g_free (retry_page_flip_data);
+}
+
+static float
+get_cached_crtc_refresh_rate (MetaKmsImplSimple *impl_simple,
+                              MetaKmsCrtc       *crtc)
+{
+  CachedModeSet *cached_mode_set;
+
+  cached_mode_set = g_hash_table_lookup (impl_simple->cached_mode_sets,
+                                         crtc);
+  g_assert (cached_mode_set);
+
+  return meta_calculate_drm_mode_refresh_rate (cached_mode_set->drm_mode);
+}
+
+static gboolean
+retry_page_flips (gpointer user_data)
+{
+  MetaKmsImplSimple *impl_simple = META_KMS_IMPL_SIMPLE (user_data);
+  uint64_t now_us;
+  GList *l;
+
+  meta_assert_in_kms_impl (meta_kms_impl_get_kms (META_KMS_IMPL (impl_simple)));
+
+  now_us = g_source_get_time (impl_simple->retry_page_flips_source);
+
+  l = impl_simple->pending_page_flip_retries;
+  while (l)
+    {
+      RetryPageFlipData *retry_page_flip_data = l->data;
+      MetaKmsCrtc *crtc = retry_page_flip_data->crtc;
+      MetaKmsDevice *device = meta_kms_crtc_get_device (crtc);
+      MetaKmsImplDevice *impl_device = meta_kms_device_get_impl_device (device);
+      GList *l_next = l->next;
+      int fd;
+      int ret;
+      MetaKmsPageFlipData *page_flip_data;
+
+      if (is_timestamp_earlier_than (now_us,
+                                     retry_page_flip_data->retry_time_us))
+        {
+          l = l_next;
+          continue;
+        }
+
+      fd = meta_kms_impl_device_get_fd (impl_device);
+      ret = drmModePageFlip (fd,
+                             meta_kms_crtc_get_id (crtc),
+                             retry_page_flip_data->fb_id,
+                             DRM_MODE_PAGE_FLIP_EVENT,
+                             retry_page_flip_data->page_flip_data);
+      if (ret == -EBUSY)
+        {
+          float refresh_rate;
+
+          refresh_rate = get_cached_crtc_refresh_rate (impl_simple, crtc);
+          retry_page_flip_data->retry_time_us +=
+            (uint64_t) (G_USEC_PER_SEC / refresh_rate);
+          l = l_next;
+          continue;
+        }
+
+      impl_simple->pending_page_flip_retries =
+        g_list_remove_link (impl_simple->pending_page_flip_retries, l);
+
+      page_flip_data = g_steal_pointer (&retry_page_flip_data->page_flip_data);
+      if (ret != 0)
+        {
+          g_autoptr (GError) error = NULL;
+
+          g_set_error (&error, G_IO_ERROR, g_io_error_from_errno (-ret),
+                       "drmModePageFlip on CRTC %u failed: %s",
+                       meta_kms_crtc_get_id (crtc),
+                       g_strerror (-ret));
+          if (!g_error_matches (error,
+                                G_IO_ERROR,
+                                G_IO_ERROR_PERMISSION_DENIED))
+            g_critical ("Failed to page flip: %s", error->message);
+
+          meta_kms_page_flip_data_discard_in_impl (page_flip_data, error);
+        }
+
+      retry_page_flip_data_free (retry_page_flip_data);
+
+      l = l_next;
+    }
+
+  if (impl_simple->pending_page_flip_retries)
+    {
+      GList *l;
+      uint64_t earliest_retry_time_us = 0;
+
+      for (l = impl_simple->pending_page_flip_retries; l; l = l->next)
+        {
+          RetryPageFlipData *retry_page_flip_data = l->data;
+
+          if (l == impl_simple->pending_page_flip_retries ||
+              is_timestamp_earlier_than (retry_page_flip_data->retry_time_us,
+                                         earliest_retry_time_us))
+            earliest_retry_time_us = retry_page_flip_data->retry_time_us;
+        }
+
+      g_source_set_ready_time (impl_simple->retry_page_flips_source,
+                               earliest_retry_time_us);
+      return G_SOURCE_CONTINUE;
+    }
+  else
+    {
+      g_clear_pointer (&impl_simple->retry_page_flips_source,
+                       g_source_unref);
+
+      flush_postponed_page_flip_datas (impl_simple);
+
+      return G_SOURCE_REMOVE;
+    }
+}
+
+static void
+schedule_retry_page_flip (MetaKmsImplSimple   *impl_simple,
+                          MetaKmsCrtc         *crtc,
+                          uint32_t             fb_id,
+                          float                refresh_rate,
+                          MetaKmsPageFlipData *page_flip_data)
+{
+  RetryPageFlipData *retry_page_flip_data;
+  uint64_t now_us;
+  uint64_t retry_time_us;
+
+  now_us = g_get_monotonic_time ();
+  retry_time_us = now_us + (uint64_t) (G_USEC_PER_SEC / refresh_rate);
+
+  retry_page_flip_data = g_new0 (RetryPageFlipData, 1);
+  *retry_page_flip_data = (RetryPageFlipData) {
+    .crtc = crtc,
+    .fb_id = fb_id,
+    .page_flip_data = meta_kms_page_flip_data_ref (page_flip_data),
+    .refresh_rate = refresh_rate,
+    .retry_time_us = retry_time_us,
+  };
+
+  if (!impl_simple->retry_page_flips_source)
+    {
+      MetaKms *kms = meta_kms_impl_get_kms (META_KMS_IMPL (impl_simple));
+      GSource *source;
+
+      source = meta_kms_add_source_in_impl (kms, retry_page_flips,
+                                            impl_simple, NULL);
+      g_source_set_ready_time (source, retry_time_us);
+
+      impl_simple->retry_page_flips_source = source;
+    }
+  else
+    {
+      GList *l;
+
+      for (l = impl_simple->pending_page_flip_retries; l; l = l->next)
+        {
+          RetryPageFlipData *pending_retry_page_flip_data = l->data;
+          uint64_t pending_retry_time_us =
+            pending_retry_page_flip_data->retry_time_us;
+
+          if (is_timestamp_earlier_than (retry_time_us, pending_retry_time_us))
+            {
+              g_source_set_ready_time (impl_simple->retry_page_flips_source,
+                                       retry_time_us);
+              break;
+            }
+        }
+    }
+
+  impl_simple->pending_page_flip_retries =
+    g_list_append (impl_simple->pending_page_flip_retries,
+                   retry_page_flip_data);
+}
+
+static void
+invoke_page_flip_datas (GList                        *page_flip_datas,
+                        MetaPageFlipDataFeedbackFunc  func)
+{
+  g_list_foreach (page_flip_datas, (GFunc) func, NULL);
+}
+
+static void
+clear_page_flip_datas (GList **page_flip_datas)
+{
+  g_list_free_full (*page_flip_datas,
+                    (GDestroyNotify) meta_kms_page_flip_data_unref);
+  *page_flip_datas = NULL;
+}
+
+static gboolean
+mode_set_fallback_feedback_idle (gpointer user_data)
+{
+  MetaKmsImplSimple *impl_simple = user_data;
+
+  g_clear_pointer (&impl_simple->mode_set_fallback_feedback_source,
+                   g_source_unref);
+
+  if (!impl_simple->pending_page_flip_retries)
+    {
+      impl_simple->postponed_mode_set_fallback_datas =
+        g_steal_pointer (&impl_simple->mode_set_fallback_page_flip_datas);
+    }
+  else
+    {
+      invoke_page_flip_datas (impl_simple->mode_set_fallback_page_flip_datas,
+                              meta_kms_page_flip_data_mode_set_fallback_in_impl);
+      clear_page_flip_datas (&impl_simple->mode_set_fallback_page_flip_datas);
+    }
+
+  return G_SOURCE_REMOVE;
+}
+
+static gboolean
+mode_set_fallback (MetaKmsImplSimple       *impl_simple,
+                   MetaKmsUpdate           *update,
+                   MetaKmsPageFlip         *page_flip,
+                   MetaKmsPlaneAssignment  *plane_assignment,
+                   MetaKmsPageFlipData     *page_flip_data,
+                   GError                 **error)
+{
+  MetaKms *kms = meta_kms_impl_get_kms (META_KMS_IMPL (impl_simple));
+  MetaKmsCrtc *crtc = page_flip->crtc;
+  MetaKmsDevice *device = meta_kms_crtc_get_device (crtc);
+  MetaKmsImplDevice *impl_device = meta_kms_device_get_impl_device (device);
+  CachedModeSet *cached_mode_set;
+  g_autofree uint32_t *connectors = NULL;
+  int n_connectors;
+  uint32_t x, y;
+  int fd;
+  int ret;
+
+  cached_mode_set = g_hash_table_lookup (impl_simple->cached_mode_sets,
+                                         crtc);
+  g_assert (cached_mode_set);
+
+  fill_connector_ids_array (cached_mode_set->connectors,
+                            &connectors,
+                            &n_connectors);
+
+  x = meta_fixed_16_to_int (plane_assignment->src_rect.x);
+  y = meta_fixed_16_to_int (plane_assignment->src_rect.y);
+
+  fd = meta_kms_impl_device_get_fd (impl_device);
+  ret = drmModeSetCrtc (fd,
+                        meta_kms_crtc_get_id (crtc),
+                        plane_assignment->fb_id,
+                        x, y,
+                        connectors, n_connectors,
+                        cached_mode_set->drm_mode);
+  if (ret != 0)
+    {
+      g_set_error (error, G_IO_ERROR, g_io_error_from_errno (-ret),
+                   "drmModeSetCrtc mode '%s' on CRTC %u failed: %s",
+                   cached_mode_set->drm_mode->name,
+                   meta_kms_crtc_get_id (crtc),
+                   g_strerror (-ret));
+      return FALSE;
+    }
+
+  if (!impl_simple->mode_set_fallback_feedback_source)
+    {
+      GSource *source;
+
+      source = meta_kms_add_source_in_impl (kms,
+                                            mode_set_fallback_feedback_idle,
+                                            impl_simple,
+                                            NULL);
+      impl_simple->mode_set_fallback_feedback_source = source;
+    }
+
+  impl_simple->mode_set_fallback_page_flip_datas =
+    g_list_prepend (impl_simple->mode_set_fallback_page_flip_datas,
+                    meta_kms_page_flip_data_ref (page_flip_data));
+
+  return TRUE;
+}
+
+static gboolean
+process_page_flip (MetaKmsImpl      *impl,
+                   MetaKmsUpdate    *update,
+                   MetaKmsPageFlip  *page_flip,
+                   GError          **error)
+{
+  MetaKmsImplSimple *impl_simple = META_KMS_IMPL_SIMPLE (impl);
+  MetaKmsCrtc *crtc;
+  MetaKmsDevice *device;
+  MetaKmsImplDevice *impl_device;
+  MetaKmsPlaneAssignment *plane_assignment;
+  MetaKmsPageFlipData *page_flip_data;
+  MetaKmsCustomPageFlipFunc custom_page_flip_func;
+  int fd;
+  int ret;
+
+  crtc = page_flip->crtc;
+  plane_assignment = get_primary_plane_assignment (impl, update, crtc);
+
+  page_flip_data = meta_kms_page_flip_data_new (impl,
+                                                crtc,
+                                                page_flip->feedback,
+                                                page_flip->user_data);
+
+  device = meta_kms_crtc_get_device (crtc);
+  impl_device = meta_kms_device_get_impl_device (device);
+  fd = meta_kms_impl_device_get_fd (impl_device);
+  custom_page_flip_func = page_flip->custom_page_flip_func;
+  if (custom_page_flip_func)
+    {
+      ret = custom_page_flip_func (page_flip->custom_page_flip_user_data,
+                                   meta_kms_page_flip_data_ref (page_flip_data));
+    }
+  else
+    {
+      ret = drmModePageFlip (fd,
+                             meta_kms_crtc_get_id (crtc),
+                             plane_assignment->fb_id,
+                             DRM_MODE_PAGE_FLIP_EVENT,
+                             meta_kms_page_flip_data_ref (page_flip_data));
+    }
+
+  if (ret == -EBUSY)
+    {
+      float refresh_rate;
+
+      refresh_rate = get_cached_crtc_refresh_rate (impl_simple, crtc);
+      schedule_retry_page_flip (impl_simple,
+                                crtc,
+                                plane_assignment->fb_id,
+                                refresh_rate,
+                                page_flip_data);
+    }
+  else if (ret == -EINVAL)
+    {
+      if (!mode_set_fallback (impl_simple,
+                              update,
+                              page_flip,
+                              plane_assignment,
+                              page_flip_data,
+                              error))
+        {
+          meta_kms_page_flip_data_unref (page_flip_data);
+          return FALSE;
+        }
+    }
+  else if (ret != 0)
+    {
+      g_set_error (error, G_IO_ERROR, g_io_error_from_errno (-ret),
+                   "drmModePageFlip on CRTC %u failed: %s",
+                   meta_kms_crtc_get_id (crtc),
+                   g_strerror (-ret));
+      meta_kms_page_flip_data_unref (page_flip_data);
+      return FALSE;
+    }
+
+  meta_kms_page_flip_data_unref (page_flip_data);
+  return TRUE;
+}
+
+static void
+discard_page_flip (MetaKmsImpl     *impl,
+                   MetaKmsUpdate   *update,
+                   MetaKmsPageFlip *page_flip)
+{
+  MetaKmsCrtc *crtc;
+  MetaKmsPageFlipData *page_flip_data;
+
+  crtc = page_flip->crtc;
+  page_flip_data = meta_kms_page_flip_data_new (impl,
+                                                crtc,
+                                                page_flip->feedback,
+                                                page_flip->user_data);
+  meta_kms_page_flip_data_discard_in_impl (page_flip_data, NULL);
+  meta_kms_page_flip_data_unref (page_flip_data);
+}
+
+static gboolean
+meta_kms_impl_simple_process_update (MetaKmsImpl    *impl,
+                                     MetaKmsUpdate  *update,
+                                     GError        **error)
+{
+  GList *l;
+
+  meta_assert_in_kms_impl (meta_kms_impl_get_kms (impl));
+
+  for (l = meta_kms_update_get_connector_properties (update); l; l = l->next)
+    {
+      MetaKmsConnectorProperty *connector_property = l->data;
+
+      if (!process_connector_property (impl, update, connector_property, error))
+        goto discard_page_flips;
+    }
+
+  for (l = meta_kms_update_get_mode_sets (update); l; l = l->next)
+    {
+      MetaKmsModeSet *mode_set = l->data;
+
+      if (!process_mode_set (impl, update, mode_set, error))
+        goto discard_page_flips;
+    }
+
+  for (l = meta_kms_update_get_page_flips (update); l; l = l->next)
+    {
+      MetaKmsPageFlip *page_flip = l->data;
+
+      if (!process_page_flip (impl, update, page_flip, error))
+        goto discard_page_flips;
+    }
+
+  return TRUE;
+
+discard_page_flips:
+  for (l = meta_kms_update_get_page_flips (update); l; l = l->next)
+    {
+      MetaKmsPageFlip *page_flip = l->data;
+
+      discard_page_flip (impl, update, page_flip);
+    }
+
+  return FALSE;
+}
+
+static void
+flush_postponed_page_flip_datas (MetaKmsImplSimple *impl_simple)
+{
+  invoke_page_flip_datas (impl_simple->postponed_page_flip_datas,
+                          meta_kms_page_flip_data_flipped_in_impl);
+  clear_page_flip_datas (&impl_simple->postponed_page_flip_datas);
+
+  invoke_page_flip_datas (impl_simple->postponed_mode_set_fallback_datas,
+                          meta_kms_page_flip_data_mode_set_fallback_in_impl);
+  clear_page_flip_datas (&impl_simple->postponed_mode_set_fallback_datas);
+}
+
+static void
+meta_kms_impl_simple_handle_page_flip_callback (MetaKmsImpl         *impl,
+                                                MetaKmsPageFlipData *page_flip_data)
+{
+  MetaKmsImplSimple *impl_simple = META_KMS_IMPL_SIMPLE (impl);
+
+  if (impl_simple->pending_page_flip_retries)
+    {
+      impl_simple->postponed_page_flip_datas =
+        g_list_append (impl_simple->postponed_page_flip_datas,
+                       page_flip_data);
+    }
+  else
+    {
+      meta_kms_page_flip_data_flipped_in_impl (page_flip_data);
+    }
+}
+
+static void
+meta_kms_impl_simple_discard_pending_page_flips (MetaKmsImpl *impl)
+{
+  MetaKmsImplSimple *impl_simple = META_KMS_IMPL_SIMPLE (impl);
+  GList *l;
+
+  if (!impl_simple->pending_page_flip_retries)
+    return;
+
+  for (l = impl_simple->pending_page_flip_retries; l; l = l->next)
+    {
+      RetryPageFlipData *retry_page_flip_data = l->data;
+      MetaKmsPageFlipData *page_flip_data;
+
+      page_flip_data = g_steal_pointer (&retry_page_flip_data->page_flip_data);
+      meta_kms_page_flip_data_discard_in_impl (page_flip_data, NULL);
+      retry_page_flip_data_free (retry_page_flip_data);
+    }
+  g_clear_pointer (&impl_simple->pending_page_flip_retries, g_list_free);
+
+  g_clear_pointer (&impl_simple->retry_page_flips_source,
+                   g_source_destroy);
+}
+
+static void
+meta_kms_impl_simple_finalize (GObject *object)
+{
+  MetaKmsImplSimple *impl_simple = META_KMS_IMPL_SIMPLE (object);
+
+  g_list_free_full (impl_simple->pending_page_flip_retries,
+                    (GDestroyNotify) retry_page_flip_data_free);
+  g_list_free_full (impl_simple->postponed_page_flip_datas,
+                    (GDestroyNotify) meta_kms_page_flip_data_unref);
+  g_list_free_full (impl_simple->postponed_mode_set_fallback_datas,
+                    (GDestroyNotify) meta_kms_page_flip_data_unref);
+  g_clear_pointer (&impl_simple->mode_set_fallback_feedback_source,
+                   g_source_destroy);
+  g_hash_table_destroy (impl_simple->cached_mode_sets);
+
+  G_OBJECT_CLASS (meta_kms_impl_simple_parent_class)->finalize (object);
+}
+
 static void
 meta_kms_impl_simple_init (MetaKmsImplSimple *impl_simple)
 {
+  impl_simple->cached_mode_sets =
+    g_hash_table_new_full (NULL,
+                           NULL,
+                           NULL,
+                           (GDestroyNotify) cached_mode_set_free);
 }
 
 static void
 meta_kms_impl_simple_class_init (MetaKmsImplSimpleClass *klass)
 {
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  MetaKmsImplClass *impl_class = META_KMS_IMPL_CLASS (klass);
+
+  object_class->finalize = meta_kms_impl_simple_finalize;
+
+  impl_class->process_update = meta_kms_impl_simple_process_update;
+  impl_class->handle_page_flip_callback = meta_kms_impl_simple_handle_page_flip_callback;
+  impl_class->discard_pending_page_flips = meta_kms_impl_simple_discard_pending_page_flips;
 }
diff --git a/src/backends/native/meta-kms-impl.c b/src/backends/native/meta-kms-impl.c
index b6f9c2509..1934d21ae 100644
--- a/src/backends/native/meta-kms-impl.c
+++ b/src/backends/native/meta-kms-impl.c
@@ -43,6 +43,28 @@ meta_kms_impl_get_kms (MetaKmsImpl *impl)
   return priv->kms;
 }
 
+gboolean
+meta_kms_impl_process_update (MetaKmsImpl    *impl,
+                              MetaKmsUpdate  *update,
+                              GError        **error)
+{
+  return META_KMS_IMPL_GET_CLASS (impl)->process_update (impl, update, error);
+}
+
+void
+meta_kms_impl_handle_page_flip_callback (MetaKmsImpl         *impl,
+                                         MetaKmsPageFlipData *page_flip_data)
+{
+  META_KMS_IMPL_GET_CLASS (impl)->handle_page_flip_callback (impl,
+                                                             page_flip_data);
+}
+
+void
+meta_kms_impl_discard_pending_page_flips (MetaKmsImpl *impl)
+{
+  META_KMS_IMPL_GET_CLASS (impl)->discard_pending_page_flips (impl);
+}
+
 static void
 meta_kms_impl_set_property (GObject      *object,
                             guint         prop_id,
diff --git a/src/backends/native/meta-kms-impl.h b/src/backends/native/meta-kms-impl.h
index fa774af17..7816253d9 100644
--- a/src/backends/native/meta-kms-impl.h
+++ b/src/backends/native/meta-kms-impl.h
@@ -21,6 +21,7 @@
 #define META_KMS_IMPL_H
 
 #include "backends/native/meta-kms-impl-device.h"
+#include "backends/native/meta-kms-page-flip-private.h"
 #include "backends/native/meta-kms.h"
 
 #define META_TYPE_KMS_IMPL (meta_kms_impl_get_type ())
@@ -30,8 +31,24 @@ G_DECLARE_DERIVABLE_TYPE (MetaKmsImpl, meta_kms_impl,
 struct _MetaKmsImplClass
 {
   GObjectClass parent_class;
+
+  gboolean (* process_update) (MetaKmsImpl    *impl,
+                               MetaKmsUpdate  *update,
+                               GError        **error);
+  void (* handle_page_flip_callback) (MetaKmsImpl         *impl,
+                                      MetaKmsPageFlipData *page_flip_data);
+  void (* discard_pending_page_flips) (MetaKmsImpl *impl);
 };
 
 MetaKms * meta_kms_impl_get_kms (MetaKmsImpl *impl);
 
+gboolean meta_kms_impl_process_update (MetaKmsImpl    *impl,
+                                       MetaKmsUpdate  *update,
+                                       GError        **error);
+
+void meta_kms_impl_handle_page_flip_callback (MetaKmsImpl         *impl,
+                                              MetaKmsPageFlipData *page_flip_data);
+
+void meta_kms_impl_discard_pending_page_flips (MetaKmsImpl *impl);
+
 #endif /* META_KMS_IMPL_H */
diff --git a/src/backends/native/meta-kms-page-flip-private.h 
b/src/backends/native/meta-kms-page-flip-private.h
new file mode 100644
index 000000000..ef8faf7ad
--- /dev/null
+++ b/src/backends/native/meta-kms-page-flip-private.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2019 Red Hat
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef META_KMS_PAGE_FLIP_H
+#define META_KMS_PAGE_FLIP_H
+
+#include <glib.h>
+
+#include "backends/native/meta-kms-types.h"
+
+typedef struct _MetaKmsPageFlipData MetaKmsPageFlipData;
+
+typedef void (* MetaPageFlipDataFeedbackFunc) (MetaKmsPageFlipData *page_flip_data);
+
+MetaKmsPageFlipData * meta_kms_page_flip_data_new (MetaKmsImpl                   *impl,
+                                                   MetaKmsCrtc                   *crtc,
+                                                   const MetaKmsPageFlipFeedback *feedback,
+                                                   gpointer                       user_data);
+
+MetaKmsPageFlipData * meta_kms_page_flip_data_ref (MetaKmsPageFlipData *page_flip_data);
+
+void meta_kms_page_flip_data_unref (MetaKmsPageFlipData *page_flip_data);
+
+MetaKmsImpl * meta_kms_page_flip_data_get_kms_impl (MetaKmsPageFlipData *page_flip_data);
+
+void meta_kms_page_flip_data_set_timings_in_impl (MetaKmsPageFlipData *page_flip_data,
+                                                  unsigned int         sequence,
+                                                  unsigned int         sec,
+                                                  unsigned int         usec);
+
+void meta_kms_page_flip_data_flipped_in_impl (MetaKmsPageFlipData *page_flip_data);
+
+void meta_kms_page_flip_data_mode_set_fallback_in_impl (MetaKmsPageFlipData *page_flip_data);
+
+void meta_kms_page_flip_data_discard_in_impl (MetaKmsPageFlipData *page_flip_data,
+                                              const GError        *error);
+
+void meta_kms_page_flip_data_take_error (MetaKmsPageFlipData *page_flip_data,
+                                         GError              *error);
+
+#endif /* META_KMS_PAGE_FLIP_H */
diff --git a/src/backends/native/meta-kms-page-flip.c b/src/backends/native/meta-kms-page-flip.c
new file mode 100644
index 000000000..997c3fca5
--- /dev/null
+++ b/src/backends/native/meta-kms-page-flip.c
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2019 Red Hat
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include "backends/native/meta-kms-page-flip-private.h"
+
+#include "backends/native/meta-kms-impl.h"
+#include "backends/native/meta-kms-private.h"
+#include "backends/native/meta-kms-update.h"
+
+struct _MetaKmsPageFlipData
+{
+  int ref_count;
+
+  MetaKmsImpl *impl;
+  MetaKmsCrtc *crtc;
+
+  const MetaKmsPageFlipFeedback *feedback;
+  gpointer user_data;
+
+  unsigned int sequence;
+  unsigned int sec;
+  unsigned int usec;
+
+  GError *error;
+};
+
+MetaKmsPageFlipData *
+meta_kms_page_flip_data_new (MetaKmsImpl                   *impl,
+                             MetaKmsCrtc                   *crtc,
+                             const MetaKmsPageFlipFeedback *feedback,
+                             gpointer                       user_data)
+{
+  MetaKmsPageFlipData *page_flip_data;
+
+  page_flip_data = g_new0 (MetaKmsPageFlipData , 1);
+  *page_flip_data = (MetaKmsPageFlipData) {
+    .ref_count = 1,
+    .impl = impl,
+    .crtc = crtc,
+    .feedback = feedback,
+    .user_data = user_data,
+  };
+
+  return page_flip_data;
+}
+
+MetaKmsPageFlipData *
+meta_kms_page_flip_data_ref (MetaKmsPageFlipData *page_flip_data)
+{
+  page_flip_data->ref_count++;
+
+  return page_flip_data;
+}
+
+void
+meta_kms_page_flip_data_unref (MetaKmsPageFlipData *page_flip_data)
+{
+  page_flip_data->ref_count--;
+
+  if (page_flip_data->ref_count == 0)
+    {
+      g_clear_error (&page_flip_data->error);
+      g_free (page_flip_data);
+    }
+}
+
+MetaKmsImpl *
+meta_kms_page_flip_data_get_kms_impl (MetaKmsPageFlipData *page_flip_data)
+{
+  return page_flip_data->impl;
+}
+
+static void
+meta_kms_page_flip_data_flipped (MetaKms  *kms,
+                                 gpointer  user_data)
+{
+  MetaKmsPageFlipData *page_flip_data = user_data;
+
+  meta_assert_not_in_kms_impl (kms);
+
+  page_flip_data->feedback->flipped (page_flip_data->crtc,
+                                     page_flip_data->sequence,
+                                     page_flip_data->sec,
+                                     page_flip_data->usec,
+                                     page_flip_data->user_data);
+}
+
+void
+meta_kms_page_flip_data_set_timings_in_impl (MetaKmsPageFlipData *page_flip_data,
+                                             unsigned int         sequence,
+                                             unsigned int         sec,
+                                             unsigned int         usec)
+{
+  MetaKms *kms = meta_kms_impl_get_kms (page_flip_data->impl);
+
+  meta_assert_in_kms_impl (kms);
+
+  page_flip_data->sequence = sequence;
+  page_flip_data->sec = sec;
+  page_flip_data->usec = usec;
+}
+
+void
+meta_kms_page_flip_data_flipped_in_impl (MetaKmsPageFlipData *page_flip_data)
+{
+  MetaKms *kms = meta_kms_impl_get_kms (page_flip_data->impl);
+
+  meta_assert_in_kms_impl (kms);
+
+  meta_kms_queue_callback (kms,
+                           meta_kms_page_flip_data_flipped,
+                           meta_kms_page_flip_data_ref (page_flip_data),
+                           (GDestroyNotify) meta_kms_page_flip_data_unref);
+}
+
+static void
+meta_kms_page_flip_data_mode_set_fallback (MetaKms  *kms,
+                                           gpointer  user_data)
+{
+  MetaKmsPageFlipData *page_flip_data = user_data;
+
+  meta_assert_not_in_kms_impl (kms);
+
+  page_flip_data->feedback->mode_set_fallback (page_flip_data->crtc,
+                                               page_flip_data->user_data);
+}
+
+void
+meta_kms_page_flip_data_mode_set_fallback_in_impl (MetaKmsPageFlipData *page_flip_data)
+{
+  MetaKms *kms = meta_kms_impl_get_kms (page_flip_data->impl);
+
+  meta_assert_in_kms_impl (kms);
+
+  meta_kms_queue_callback (kms,
+                           meta_kms_page_flip_data_mode_set_fallback,
+                           meta_kms_page_flip_data_ref (page_flip_data),
+                           (GDestroyNotify) meta_kms_page_flip_data_unref);
+}
+
+static void
+meta_kms_page_flip_data_discard (MetaKms  *kms,
+                                 gpointer  user_data)
+{
+  MetaKmsPageFlipData *page_flip_data = user_data;
+
+  meta_assert_not_in_kms_impl (kms);
+
+  page_flip_data->feedback->discarded (page_flip_data->crtc,
+                                       page_flip_data->user_data,
+                                       page_flip_data->error);
+}
+
+void
+meta_kms_page_flip_data_take_error (MetaKmsPageFlipData *page_flip_data,
+                                    GError              *error)
+{
+  g_assert (!page_flip_data->error);
+
+  page_flip_data->error = error;
+}
+
+void
+meta_kms_page_flip_data_discard_in_impl (MetaKmsPageFlipData *page_flip_data,
+                                         const GError        *error)
+{
+  MetaKms *kms = meta_kms_impl_get_kms (page_flip_data->impl);
+
+  meta_assert_in_kms_impl (kms);
+
+  if (error)
+    meta_kms_page_flip_data_take_error (page_flip_data, g_error_copy (error));
+
+  meta_kms_queue_callback (kms,
+                           meta_kms_page_flip_data_discard,
+                           meta_kms_page_flip_data_ref (page_flip_data),
+                           (GDestroyNotify) meta_kms_page_flip_data_unref);
+}
diff --git a/src/backends/native/meta-kms-plane.c b/src/backends/native/meta-kms-plane.c
index 94a407bd8..d34d8c194 100644
--- a/src/backends/native/meta-kms-plane.c
+++ b/src/backends/native/meta-kms-plane.c
@@ -27,6 +27,7 @@
 #include "backends/meta-monitor-transform.h"
 #include "backends/native/meta-kms-crtc.h"
 #include "backends/native/meta-kms-impl-device.h"
+#include "backends/native/meta-kms-update-private.h"
 
 struct _MetaKmsPlane
 {
@@ -53,6 +54,12 @@ struct _MetaKmsPlane
 
 G_DEFINE_TYPE (MetaKmsPlane, meta_kms_plane, G_TYPE_OBJECT)
 
+MetaKmsDevice *
+meta_kms_plane_get_device (MetaKmsPlane *plane)
+{
+  return plane->device;
+}
+
 uint32_t
 meta_kms_plane_get_id (MetaKmsPlane *plane)
 {
@@ -65,6 +72,18 @@ meta_kms_plane_get_plane_type (MetaKmsPlane *plane)
   return plane->type;
 }
 
+void
+meta_kms_plane_update_set_rotation (MetaKmsPlane           *plane,
+                                    MetaKmsPlaneAssignment *plane_assignment,
+                                    MetaMonitorTransform    transform)
+{
+  g_return_if_fail (meta_kms_plane_is_transform_handled (plane, transform));
+
+  meta_kms_plane_assignment_set_plane_property (plane_assignment,
+                                                plane->rotation_prop_id,
+                                                plane->rotation_map[transform]);
+}
+
 gboolean
 meta_kms_plane_is_transform_handled (MetaKmsPlane         *plane,
                                      MetaMonitorTransform  transform)
diff --git a/src/backends/native/meta-kms-plane.h b/src/backends/native/meta-kms-plane.h
index 11cb3ad2f..e360f83ca 100644
--- a/src/backends/native/meta-kms-plane.h
+++ b/src/backends/native/meta-kms-plane.h
@@ -43,6 +43,8 @@ MetaKmsPlane * meta_kms_plane_new (MetaKmsPlaneType         type,
                                    drmModePlane            *drm_plane,
                                    drmModeObjectProperties *drm_plane_props);
 
+MetaKmsDevice * meta_kms_plane_get_device (MetaKmsPlane *plane);
+
 uint32_t meta_kms_plane_get_id (MetaKmsPlane *plane);
 
 MetaKmsPlaneType meta_kms_plane_get_plane_type (MetaKmsPlane *plane);
@@ -61,4 +63,8 @@ gboolean meta_kms_plane_is_format_supported (MetaKmsPlane *plane,
 gboolean meta_kms_plane_is_usable_with (MetaKmsPlane *plane,
                                         MetaKmsCrtc  *crtc);
 
+void meta_kms_plane_update_set_rotation (MetaKmsPlane           *plane,
+                                         MetaKmsPlaneAssignment *plane_assignment,
+                                         MetaMonitorTransform    transform);
+
 #endif /* META_KMS_PLANE_H */
diff --git a/src/backends/native/meta-kms-private.h b/src/backends/native/meta-kms-private.h
index 910779ea7..fa632e1f7 100644
--- a/src/backends/native/meta-kms-private.h
+++ b/src/backends/native/meta-kms-private.h
@@ -36,14 +36,17 @@ void meta_kms_queue_callback (MetaKms         *kms,
                               gpointer         user_data,
                               GDestroyNotify   user_data_destroy);
 
+int meta_kms_flush_callbacks (MetaKms *kms);
+
 gboolean meta_kms_run_impl_task_sync (MetaKms              *kms,
                                       MetaKmsImplTaskFunc   func,
                                       gpointer              user_data,
                                       GError              **error);
 
-GSource * meta_kms_add_source_in_impl (MetaKms     *kms,
-                                       GSourceFunc  func,
-                                       gpointer     user_data);
+GSource * meta_kms_add_source_in_impl (MetaKms        *kms,
+                                       GSourceFunc     func,
+                                       gpointer        user_data,
+                                       GDestroyNotify  user_data_destroy);
 
 GSource * meta_kms_register_fd_in_impl (MetaKms             *kms,
                                         int                  fd,
diff --git a/src/backends/native/meta-kms-types.h b/src/backends/native/meta-kms-types.h
index 7a9264934..dd14a7be7 100644
--- a/src/backends/native/meta-kms-types.h
+++ b/src/backends/native/meta-kms-types.h
@@ -20,6 +20,8 @@
 #ifndef META_KMS_IMPL_TYPES_H
 #define META_KMS_IMPL_TYPES_H
 
+#include <stdint.h>
+
 typedef struct _MetaKms MetaKms;
 typedef struct _MetaKmsDevice MetaKmsDevice;
 
@@ -27,9 +29,26 @@ typedef struct _MetaKmsPlane MetaKmsPlane;
 typedef struct _MetaKmsCrtc MetaKmsCrtc;
 typedef struct _MetaKmsConnector MetaKmsConnector;
 
+typedef struct _MetaKmsUpdate MetaKmsUpdate;
+typedef struct _MetaKmsPlaneAssignment MetaKmsPlaneAssignment;
+typedef struct _MetaKmsModeSet MetaKmsModeSet;
+
+typedef struct _MetaKmsPageFlipFeedback MetaKmsPageFlipFeedback;
+
 typedef struct _MetaKmsImpl MetaKmsImpl;
 typedef struct _MetaKmsImplDevice MetaKmsImplDevice;
 
+/* 16:16 fixed point */
+typedef int32_t MetaFixed16;
+
+typedef struct _MetaFixed16Rectangle
+{
+  MetaFixed16 x;
+  MetaFixed16 y;
+  MetaFixed16 width;
+  MetaFixed16 height;
+} MetaFixed16Rectangle;
+
 typedef enum _MetaKmsDeviceFlag
 {
   META_KMS_DEVICE_FLAG_NONE = 0,
diff --git a/src/backends/native/meta-kms-update-private.h b/src/backends/native/meta-kms-update-private.h
new file mode 100644
index 000000000..bf3326ac4
--- /dev/null
+++ b/src/backends/native/meta-kms-update-private.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2019 Red Hat
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef META_KMS_UPDATE_PRIVATE_H
+#define META_KMS_UPDATE_PRIVATE_H
+
+#include <glib.h>
+#include <stdint.h>
+
+#include "backends/native/meta-kms-types.h"
+#include "backends/native/meta-kms-update.h"
+
+typedef struct _MetaKmsProperty
+{
+  uint32_t prop_id;
+  uint64_t value;
+} MetaKmsProperty;
+
+typedef struct _MetaKmsPlaneAssignment
+{
+  MetaKmsCrtc *crtc;
+  MetaKmsPlane *plane;
+  uint32_t fb_id;
+  MetaFixed16Rectangle src_rect;
+  MetaFixed16Rectangle dst_rect;
+
+  GList *plane_properties;
+} MetaKmsPlaneAssignment;
+
+typedef struct _MetaKmsModeSet
+{
+  MetaKmsCrtc *crtc;
+  GList *connectors;
+  drmModeModeInfo *drm_mode;
+} MetaKmsModeSet;
+
+typedef struct _MetaKmsConnectorProperty
+{
+  MetaKmsDevice *device;
+  MetaKmsConnector *connector;
+  uint32_t prop_id;
+  uint64_t value;
+} MetaKmsConnectorProperty;
+
+typedef struct _MetaKmsPageFlip
+{
+  MetaKmsCrtc *crtc;
+  const MetaKmsPageFlipFeedback *feedback;
+  gpointer user_data;
+  MetaKmsCustomPageFlipFunc custom_page_flip_func;
+  gpointer custom_page_flip_user_data;
+} MetaKmsPageFlip;
+
+void meta_kms_update_set_connector_property (MetaKmsUpdate    *update,
+                                             MetaKmsConnector *connector,
+                                             uint32_t          prop_id,
+                                             uint64_t          value);
+
+void meta_kms_plane_assignment_set_plane_property (MetaKmsPlaneAssignment *plane_assignment,
+                                                   uint32_t                prop_id,
+                                                   uint64_t                value);
+
+GList * meta_kms_update_get_plane_assignments (MetaKmsUpdate *update);
+
+GList * meta_kms_update_get_mode_sets (MetaKmsUpdate *update);
+
+GList * meta_kms_update_get_page_flips (MetaKmsUpdate *update);
+
+GList * meta_kms_update_get_connector_properties (MetaKmsUpdate *update);
+
+gboolean meta_kms_update_has_mode_set (MetaKmsUpdate *update);
+
+#endif /* META_KMS_UPDATE_PRIVATE_H */
diff --git a/src/backends/native/meta-kms-update.c b/src/backends/native/meta-kms-update.c
new file mode 100644
index 000000000..439e917ba
--- /dev/null
+++ b/src/backends/native/meta-kms-update.c
@@ -0,0 +1,236 @@
+/*
+ * Copyright (C) 2018 Red Hat
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include "backends/native/meta-kms-update.h"
+#include "backends/native/meta-kms-update-private.h"
+
+#include "backends/meta-display-config-shared.h"
+#include "backends/native/meta-kms-plane.h"
+
+struct _MetaKmsUpdate
+{
+  MetaPowerSave power_save;
+  GList *mode_sets;
+  GList *plane_assignments;
+  GList *page_flips;
+  GList *connector_properties;
+};
+
+static MetaKmsProperty *
+meta_kms_property_new (uint32_t prop_id,
+                       uint64_t value)
+{
+  MetaKmsProperty *prop;
+
+  prop = g_new0 (MetaKmsProperty, 1);
+  *prop = (MetaKmsProperty) {
+    .prop_id = prop_id,
+    .value = value,
+  };
+
+  return prop;
+}
+
+static void
+meta_kms_property_free (MetaKmsProperty *prop)
+{
+  g_free (prop);
+}
+
+static void
+meta_kms_plane_assignment_free (MetaKmsPlaneAssignment *plane_assignment)
+{
+  g_list_free_full (plane_assignment->plane_properties,
+                    (GDestroyNotify) meta_kms_property_free);
+  g_free (plane_assignment);
+}
+
+static void
+meta_kms_mode_set_free (MetaKmsModeSet *mode_set)
+{
+  g_free (mode_set->drm_mode);
+  g_list_free (mode_set->connectors);
+  g_free (mode_set);
+}
+
+MetaKmsPlaneAssignment *
+meta_kms_update_assign_plane (MetaKmsUpdate        *update,
+                              MetaKmsCrtc          *crtc,
+                              MetaKmsPlane         *plane,
+                              uint32_t              fb_id,
+                              MetaFixed16Rectangle  src_rect,
+                              MetaFixed16Rectangle  dst_rect)
+{
+  MetaKmsPlaneAssignment *plane_assignment;
+
+  plane_assignment = g_new0 (MetaKmsPlaneAssignment, 1);
+  *plane_assignment = (MetaKmsPlaneAssignment) {
+    .crtc = crtc,
+    .plane = plane,
+    .fb_id = fb_id,
+    .src_rect = src_rect,
+    .dst_rect = dst_rect,
+  };
+
+  update->plane_assignments = g_list_prepend (update->plane_assignments,
+                                              plane_assignment);
+
+  return plane_assignment;
+}
+
+void
+meta_kms_update_mode_set (MetaKmsUpdate   *update,
+                          MetaKmsCrtc     *crtc,
+                          GList           *connectors,
+                          drmModeModeInfo *drm_mode)
+{
+  MetaKmsModeSet *mode_set;
+
+  mode_set = g_new0 (MetaKmsModeSet, 1);
+  *mode_set = (MetaKmsModeSet) {
+    .crtc = crtc,
+    .connectors = connectors,
+    .drm_mode = drm_mode ? g_memdup (drm_mode, sizeof *drm_mode) : NULL,
+  };
+
+  update->mode_sets = g_list_prepend (update->mode_sets, mode_set);
+}
+
+void
+meta_kms_update_set_connector_property (MetaKmsUpdate    *update,
+                                        MetaKmsConnector *connector,
+                                        uint32_t          prop_id,
+                                        uint64_t          value)
+{
+  MetaKmsConnectorProperty *prop;
+
+  prop = g_new0 (MetaKmsConnectorProperty, 1);
+  *prop = (MetaKmsConnectorProperty) {
+    .connector = connector,
+    .prop_id = prop_id,
+    .value = value,
+  };
+
+  update->connector_properties = g_list_prepend (update->connector_properties,
+                                                 prop);
+}
+
+void
+meta_kms_update_page_flip (MetaKmsUpdate                 *update,
+                           MetaKmsCrtc                   *crtc,
+                           const MetaKmsPageFlipFeedback *feedback,
+                           gpointer                       user_data)
+{
+  MetaKmsPageFlip *page_flip;
+
+  page_flip = g_new0 (MetaKmsPageFlip, 1);
+  *page_flip = (MetaKmsPageFlip) {
+    .crtc = crtc,
+    .feedback = feedback,
+    .user_data = user_data,
+  };
+
+  update->page_flips = g_list_prepend (update->page_flips, page_flip);
+}
+
+void
+meta_kms_update_custom_page_flip (MetaKmsUpdate                 *update,
+                                  MetaKmsCrtc                   *crtc,
+                                  const MetaKmsPageFlipFeedback *feedback,
+                                  gpointer                       user_data,
+                                  MetaKmsCustomPageFlipFunc      custom_page_flip_func,
+                                  gpointer                       custom_page_flip_user_data)
+{
+  MetaKmsPageFlip *page_flip;
+
+  page_flip = g_new0 (MetaKmsPageFlip, 1);
+  *page_flip = (MetaKmsPageFlip) {
+    .crtc = crtc,
+    .feedback = feedback,
+    .user_data = user_data,
+    .custom_page_flip_func = custom_page_flip_func,
+    .custom_page_flip_user_data = custom_page_flip_user_data,
+  };
+
+  update->page_flips = g_list_prepend (update->page_flips, page_flip);
+}
+
+void
+meta_kms_plane_assignment_set_plane_property (MetaKmsPlaneAssignment *plane_assignment,
+                                              uint32_t                prop_id,
+                                              uint64_t                value)
+{
+  MetaKmsProperty *plane_prop;
+
+  plane_prop = meta_kms_property_new (prop_id, value);
+
+  plane_assignment->plane_properties =
+    g_list_prepend (plane_assignment->plane_properties, plane_prop);
+}
+
+GList *
+meta_kms_update_get_plane_assignments (MetaKmsUpdate *update)
+{
+  return update->plane_assignments;
+}
+
+GList *
+meta_kms_update_get_mode_sets (MetaKmsUpdate *update)
+{
+  return update->mode_sets;
+}
+
+GList *
+meta_kms_update_get_page_flips (MetaKmsUpdate *update)
+{
+  return update->page_flips;
+}
+
+GList *
+meta_kms_update_get_connector_properties (MetaKmsUpdate *update)
+{
+  return update->connector_properties;
+}
+
+gboolean
+meta_kms_update_has_mode_set (MetaKmsUpdate *update)
+{
+  return !!update->mode_sets;
+}
+
+MetaKmsUpdate *
+meta_kms_update_new (void)
+{
+  return g_new0 (MetaKmsUpdate, 1);
+}
+
+void
+meta_kms_update_free (MetaKmsUpdate *update)
+{
+  g_list_free_full (update->plane_assignments,
+                    (GDestroyNotify) meta_kms_plane_assignment_free);
+  g_list_free_full (update->mode_sets,
+                    (GDestroyNotify) meta_kms_mode_set_free);
+  g_list_free_full (update->page_flips, g_free);
+  g_list_free_full (update->connector_properties, g_free);
+
+  g_free (update);
+}
diff --git a/src/backends/native/meta-kms-update.h b/src/backends/native/meta-kms-update.h
new file mode 100644
index 000000000..fc7d83f33
--- /dev/null
+++ b/src/backends/native/meta-kms-update.h
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2018 Red Hat
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef META_KMS_UPDATE_H
+#define META_KMS_UPDATE_H
+
+#include <glib-object.h>
+#include <glib.h>
+#include <stdint.h>
+#include <xf86drmMode.h>
+
+#include "backends/meta-monitor-transform.h"
+#include "backends/native/meta-kms-types.h"
+
+struct _MetaKmsPageFlipFeedback
+{
+  void (* flipped) (MetaKmsCrtc  *crtc,
+                    unsigned int  sequence,
+                    unsigned int  tv_sec,
+                    unsigned int  tv_usec,
+                    gpointer      user_data);
+
+  void (* mode_set_fallback) (MetaKmsCrtc *crtc,
+                              gpointer     user_data);
+
+  void (* discarded) (MetaKmsCrtc  *crtc,
+                      gpointer      user_data,
+                      const GError *error);
+};
+
+typedef int (* MetaKmsCustomPageFlipFunc) (gpointer custom_page_flip_data,
+                                           gpointer user_data);
+
+MetaKmsUpdate * meta_kms_update_new (void);
+
+void meta_kms_update_free (MetaKmsUpdate *update);
+
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (MetaKmsUpdate, meta_kms_update_free)
+
+void meta_kms_update_mode_set (MetaKmsUpdate   *update,
+                               MetaKmsCrtc     *crtc,
+                               GList           *connectors,
+                               drmModeModeInfo *drm_mode);
+
+MetaKmsPlaneAssignment * meta_kms_update_assign_plane (MetaKmsUpdate        *update,
+                                                       MetaKmsCrtc          *crtc,
+                                                       MetaKmsPlane         *plane,
+                                                       uint32_t              fb_id,
+                                                       MetaFixed16Rectangle  src_rect,
+                                                       MetaFixed16Rectangle  dst_rect);
+
+void meta_kms_update_page_flip (MetaKmsUpdate                 *update,
+                                MetaKmsCrtc                   *crtc,
+                                const MetaKmsPageFlipFeedback *feedback,
+                                gpointer                       user_data);
+
+void meta_kms_update_custom_page_flip (MetaKmsUpdate                 *update,
+                                       MetaKmsCrtc                   *crtc,
+                                       const MetaKmsPageFlipFeedback *feedback,
+                                       gpointer                       user_data,
+                                       MetaKmsCustomPageFlipFunc      custom_page_flip_func,
+                                       gpointer                       custom_page_flip_user_data);
+
+static inline MetaFixed16
+meta_fixed_16_from_int (int16_t d)
+{
+  return d * 65536;
+}
+
+static inline int16_t
+meta_fixed_16_to_int (MetaFixed16 fixed)
+{
+  return fixed / 65536;
+}
+
+#endif /* META_KMS_UPDATE_H */
diff --git a/src/backends/native/meta-kms.c b/src/backends/native/meta-kms.c
index 31bafd53b..c13360a1d 100644
--- a/src/backends/native/meta-kms.c
+++ b/src/backends/native/meta-kms.c
@@ -25,6 +25,7 @@
 #include "backends/native/meta-kms-device-private.h"
 #include "backends/native/meta-kms-impl.h"
 #include "backends/native/meta-kms-impl-simple.h"
+#include "backends/native/meta-kms-update-private.h"
 #include "backends/native/meta-udev.h"
 
 typedef struct _MetaKmsCallbackData
@@ -64,12 +65,86 @@ struct _MetaKms
 
   GList *devices;
 
+  MetaKmsUpdate *pending_update;
+
   GList *pending_callbacks;
   guint callback_source_id;
 };
 
 G_DEFINE_TYPE (MetaKms, meta_kms, G_TYPE_OBJECT)
 
+static void
+meta_kms_update_states_in_impl (MetaKms *kms);
+
+MetaKmsUpdate *
+meta_kms_ensure_pending_update (MetaKms *kms)
+{
+  if (!kms->pending_update)
+    kms->pending_update = meta_kms_update_new ();
+
+  return meta_kms_get_pending_update (kms);
+}
+
+MetaKmsUpdate *
+meta_kms_get_pending_update (MetaKms *kms)
+{
+  return kms->pending_update;
+}
+
+static gboolean
+meta_kms_update_process_in_impl (MetaKmsImpl  *impl,
+                                 gpointer      user_data,
+                                 GError      **error)
+{
+  g_autoptr (MetaKmsUpdate) update = user_data;
+  gboolean ret;
+
+  ret = meta_kms_impl_process_update (impl, update, error);
+
+  if (meta_kms_update_has_mode_set (update))
+    meta_kms_update_states_in_impl (meta_kms_impl_get_kms (impl));
+
+  return ret;
+}
+
+static gboolean
+meta_kms_post_update_sync (MetaKms        *kms,
+                           MetaKmsUpdate  *update,
+                           GError        **error)
+{
+  return meta_kms_run_impl_task_sync (kms,
+                                      meta_kms_update_process_in_impl,
+                                      update,
+                                      error);
+}
+
+gboolean
+meta_kms_post_pending_update_sync (MetaKms  *kms,
+                                   GError  **error)
+{
+  return meta_kms_post_update_sync (kms,
+                                    g_steal_pointer (&kms->pending_update),
+                                    error);
+}
+
+static gboolean
+meta_kms_discard_pending_page_flips_in_impl (MetaKmsImpl  *impl,
+                                             gpointer      user_data,
+                                             GError      **error)
+{
+  meta_kms_impl_discard_pending_page_flips (impl);
+  return TRUE;
+}
+
+void
+meta_kms_discard_pending_page_flips (MetaKms *kms)
+{
+  meta_kms_run_impl_task_sync (kms,
+                               meta_kms_discard_pending_page_flips_in_impl,
+                               NULL,
+                               NULL);
+}
+
 static void
 meta_kms_callback_data_free (MetaKmsCallbackData *callback_data)
 {
@@ -78,11 +153,11 @@ meta_kms_callback_data_free (MetaKmsCallbackData *callback_data)
   g_slice_free (MetaKmsCallbackData, callback_data);
 }
 
-static gboolean
-callback_idle (gpointer user_data)
+static int
+flush_callbacks (MetaKms *kms)
 {
-  MetaKms *kms = user_data;
   GList *l;
+  int callback_count = 0;
 
   for (l = kms->pending_callbacks; l; l = l->next)
     {
@@ -90,11 +165,22 @@ callback_idle (gpointer user_data)
 
       callback_data->callback (kms, callback_data->user_data);
       meta_kms_callback_data_free (callback_data);
+      callback_count++;
     }
 
   g_list_free (kms->pending_callbacks);
   kms->pending_callbacks = NULL;
 
+  return callback_count;
+}
+
+static gboolean
+callback_idle (gpointer user_data)
+{
+  MetaKms *kms = user_data;
+
+  flush_callbacks (kms);
+
   kms->callback_source_id = 0;
   return G_SOURCE_REMOVE;
 }
@@ -119,6 +205,17 @@ meta_kms_queue_callback (MetaKms         *kms,
     kms->callback_source_id = g_idle_add (callback_idle, kms);
 }
 
+int
+meta_kms_flush_callbacks (MetaKms *kms)
+{
+  int callback_count;
+
+  callback_count = flush_callbacks (kms);
+  g_clear_handle_id (&kms->callback_source_id, g_source_remove);
+
+  return callback_count;
+}
+
 gboolean
 meta_kms_run_impl_task_sync (MetaKms              *kms,
                              MetaKmsImplTaskFunc   func,
@@ -156,9 +253,10 @@ static GSourceFuncs simple_impl_source_funcs = {
 };
 
 GSource *
-meta_kms_add_source_in_impl (MetaKms     *kms,
-                             GSourceFunc  func,
-                             gpointer     user_data)
+meta_kms_add_source_in_impl (MetaKms        *kms,
+                             GSourceFunc     func,
+                             gpointer        user_data,
+                             GDestroyNotify  user_data_destroy)
 {
   GSource *source;
   MetaKmsSimpleImplSource *simple_impl_source;
@@ -170,7 +268,7 @@ meta_kms_add_source_in_impl (MetaKms     *kms,
   simple_impl_source = (MetaKmsSimpleImplSource *) source;
   simple_impl_source->kms = kms;
 
-  g_source_set_callback (source, func, user_data, NULL);
+  g_source_set_callback (source, func, user_data, user_data_destroy);
   g_source_attach (source, g_main_context_get_thread_default ());
 
   return source;
@@ -245,14 +343,13 @@ meta_kms_in_impl_task (MetaKms *kms)
   return kms->in_impl_task;
 }
 
-static gboolean
-update_states_in_impl (MetaKmsImpl  *impl,
-                       gpointer      user_data,
-                       GError      **error)
+static void
+meta_kms_update_states_in_impl (MetaKms *kms)
 {
-  MetaKms *kms = user_data;
   GList *l;
 
+  meta_assert_in_kms_impl (kms);
+
   for (l = kms->devices; l; l = l->next)
     {
       MetaKmsDevice *device = l->data;
@@ -260,6 +357,16 @@ update_states_in_impl (MetaKmsImpl  *impl,
 
       meta_kms_impl_device_update_states (impl_device);
     }
+}
+
+static gboolean
+update_states_in_impl (MetaKmsImpl  *impl,
+                       gpointer      user_data,
+                       GError      **error)
+{
+  MetaKms *kms = user_data;
+
+  meta_kms_update_states_in_impl (kms);
 
   return TRUE;
 }
diff --git a/src/backends/native/meta-kms.h b/src/backends/native/meta-kms.h
index 7c4073256..cfe4e9105 100644
--- a/src/backends/native/meta-kms.h
+++ b/src/backends/native/meta-kms.h
@@ -28,6 +28,15 @@
 #define META_TYPE_KMS (meta_kms_get_type ())
 G_DECLARE_FINAL_TYPE (MetaKms, meta_kms, META, KMS, GObject)
 
+MetaKmsUpdate * meta_kms_ensure_pending_update (MetaKms *kms);
+
+MetaKmsUpdate * meta_kms_get_pending_update (MetaKms *kms);
+
+gboolean meta_kms_post_pending_update_sync (MetaKms  *kms,
+                                            GError  **error);
+
+void meta_kms_discard_pending_page_flips (MetaKms *kms);
+
 MetaBackend * meta_kms_get_backend (MetaKms *kms);
 
 MetaKmsDevice * meta_kms_create_device (MetaKms            *kms,
diff --git a/src/backends/native/meta-monitor-manager-kms.c b/src/backends/native/meta-monitor-manager-kms.c
index 6fa230020..58fd93fcc 100644
--- a/src/backends/native/meta-monitor-manager-kms.c
+++ b/src/backends/native/meta-monitor-manager-kms.c
@@ -54,6 +54,8 @@
 #include "backends/native/meta-backend-native.h"
 #include "backends/native/meta-crtc-kms.h"
 #include "backends/native/meta-gpu-kms.h"
+#include "backends/native/meta-kms-update.h"
+#include "backends/native/meta-kms.h"
 #include "backends/native/meta-launcher.h"
 #include "backends/native/meta-output-kms.h"
 #include "backends/native/meta-renderer-native.h"
@@ -115,6 +117,11 @@ static void
 meta_monitor_manager_kms_set_power_save_mode (MetaMonitorManager *manager,
                                               MetaPowerSave       mode)
 {
+  MetaBackend *backend = meta_monitor_manager_get_backend (manager);
+  MetaBackendNative *backend_native = META_BACKEND_NATIVE (backend);
+  MetaKms *kms = meta_backend_native_get_kms (backend_native);
+  MetaKmsUpdate *kms_update;
+  g_autoptr (GError) error = NULL;
   uint64_t state;
   GList *l;
 
@@ -135,12 +142,16 @@ meta_monitor_manager_kms_set_power_save_mode (MetaMonitorManager *manager,
     return;
   }
 
-  for (l = meta_backend_get_gpus (manager->backend); l; l = l->next)
+  kms_update = meta_kms_ensure_pending_update (kms);
+  for (l = meta_backend_get_gpus (backend); l; l = l->next)
     {
       MetaGpuKms *gpu_kms = l->data;
 
-      meta_gpu_kms_set_power_save_mode (gpu_kms, state);
+      meta_gpu_kms_set_power_save_mode (gpu_kms, state, kms_update);
     }
+
+  if (!meta_kms_post_pending_update_sync (kms, &error))
+    g_warning ("Failed to DPMS: %s", error->message);
 }
 
 static void
@@ -214,8 +225,6 @@ apply_crtc_assignments (MetaMonitorManager *manager,
               meta_output_assign_crtc (output, crtc);
             }
         }
-
-      meta_crtc_kms_apply_transform (crtc);
     }
   /* Disable CRTCs not mentioned in the list (they have is_dirty == FALSE,
      because they weren't seen in the first loop) */
@@ -253,8 +262,6 @@ apply_crtc_assignments (MetaMonitorManager *manager,
       output->is_primary = output_info->is_primary;
       output->is_presentation = output_info->is_presentation;
       output->is_underscanning = output_info->is_underscanning;
-
-      meta_output_kms_set_underscan (output);
     }
 
   /* Disable outputs not mentioned in the list */
diff --git a/src/backends/native/meta-output-kms.c b/src/backends/native/meta-output-kms.c
index 1d3386387..42633702c 100644
--- a/src/backends/native/meta-output-kms.c
+++ b/src/backends/native/meta-output-kms.c
@@ -42,67 +42,42 @@ typedef struct _MetaOutputKms
   MetaOutput parent;
 
   MetaKmsConnector *kms_connector;
+} MetaOutputKms;
 
-  drmModeConnector *connector;
-
-  uint32_t dpms_prop_id;
+MetaKmsConnector *
+meta_output_kms_get_kms_connector (MetaOutput *output)
+{
+  MetaOutputKms *output_kms = output->driver_private;
 
-  uint32_t underscan_prop_id;
-  uint32_t underscan_hborder_prop_id;
-  uint32_t underscan_vborder_prop_id;
-} MetaOutputKms;
+  return output_kms->kms_connector;
+}
 
 void
-meta_output_kms_set_underscan (MetaOutput *output)
+meta_output_kms_set_underscan (MetaOutput    *output,
+                               MetaKmsUpdate *kms_update)
 {
   MetaOutputKms *output_kms = output->driver_private;
-  MetaGpu *gpu = meta_output_get_gpu (output);
-  MetaGpuKms *gpu_kms = META_GPU_KMS (gpu);
-  MetaCrtc *crtc;
-  int kms_fd;
-  uint32_t connector_id;
 
-  if (!output_kms->underscan_prop_id)
+  if (!output->supports_underscanning)
     return;
 
-  crtc = meta_output_get_assigned_crtc (output);
-  kms_fd = meta_gpu_kms_get_fd (gpu_kms);
-  connector_id = output_kms->connector->connector_id;
-
-  if (output->is_underscanning && crtc && crtc->current_mode)
+  if (output->is_underscanning)
     {
-      drmModeObjectSetProperty (kms_fd, connector_id,
-                                DRM_MODE_OBJECT_CONNECTOR,
-                                output_kms->underscan_prop_id,
-                                (uint64_t) 1);
-
-      if (output_kms->underscan_hborder_prop_id)
-        {
-          uint64_t value;
-
-          value = MIN (128, crtc->current_mode->width * 0.05);
-          drmModeObjectSetProperty (kms_fd, connector_id,
-                                    DRM_MODE_OBJECT_CONNECTOR,
-                                    output_kms->underscan_hborder_prop_id,
-                                    value);
-        }
-      if (output_kms->underscan_vborder_prop_id)
-        {
-          uint64_t value;
-
-          value = MIN (128, crtc->current_mode->height * 0.05);
-          drmModeObjectSetProperty (kms_fd, connector_id,
-                                    DRM_MODE_OBJECT_CONNECTOR,
-                                    output_kms->underscan_vborder_prop_id,
-                                    value);
-        }
+      MetaCrtc *crtc;
+      uint64_t hborder, vborder;
+
+      crtc = meta_output_get_assigned_crtc (output);
+      hborder = MIN (128, (uint64_t) round (crtc->current_mode->width * 0.05));
+      vborder = MIN (128, (uint64_t) round (crtc->current_mode->height * 0.05));
+      meta_kms_connector_set_underscanning (output_kms->kms_connector,
+                                            kms_update,
+                                            hborder,
+                                            vborder);
     }
   else
     {
-      drmModeObjectSetProperty (kms_fd, connector_id,
-                                DRM_MODE_OBJECT_CONNECTOR,
-                                output_kms->underscan_prop_id,
-                                (uint64_t) 0);
+      meta_kms_connector_unset_underscanning (output_kms->kms_connector,
+                                              kms_update);
     }
 }
 
@@ -115,24 +90,15 @@ meta_output_kms_get_connector_id (MetaOutput *output)
 }
 
 void
-meta_output_kms_set_power_save_mode (MetaOutput *output,
-                                     uint64_t    state)
+meta_output_kms_set_power_save_mode (MetaOutput    *output,
+                                     uint64_t       dpms_state,
+                                     MetaKmsUpdate *kms_update)
 {
   MetaOutputKms *output_kms = output->driver_private;
-  MetaGpu *gpu = meta_output_get_gpu (output);
-  MetaGpuKms *gpu_kms = META_GPU_KMS (gpu);
 
-  if (output_kms->dpms_prop_id != 0)
-    {
-      int fd;
-
-      fd = meta_gpu_kms_get_fd (gpu_kms);
-      if (drmModeObjectSetProperty (fd, output_kms->connector->connector_id,
-                                    DRM_MODE_OBJECT_CONNECTOR,
-                                    output_kms->dpms_prop_id, state) < 0)
-        g_warning ("Failed to set power save mode for output %s: %s",
-                   output->name, strerror (errno));
-    }
+  meta_kms_connector_update_set_dpms_state (output_kms->kms_connector,
+                                            kms_update,
+                                            dpms_state);
 }
 
 gboolean
@@ -162,40 +128,6 @@ meta_output_kms_read_edid (MetaOutput *output)
   return g_bytes_new_from_bytes (edid_data, 0, g_bytes_get_size (edid_data));
 }
 
-static void
-find_connector_properties (MetaGpuKms       *gpu_kms,
-                           MetaOutput       *output,
-                           drmModeConnector *connector)
-{
-  MetaOutputKms *output_kms = output->driver_private;
-  int fd;
-  int i;
-
-  fd = meta_gpu_kms_get_fd (gpu_kms);
-
-  for (i = 0; i < connector->count_props; i++)
-    {
-      drmModePropertyPtr prop = drmModeGetProperty (fd, connector->props[i]);
-      if (!prop)
-        continue;
-
-      if ((prop->flags & DRM_MODE_PROP_ENUM) &&
-          strcmp (prop->name, "DPMS") == 0)
-        output_kms->dpms_prop_id = prop->prop_id;
-      else if ((prop->flags & DRM_MODE_PROP_ENUM) &&
-               strcmp (prop->name, "underscan") == 0)
-        output_kms->underscan_prop_id = prop->prop_id;
-      else if ((prop->flags & DRM_MODE_PROP_RANGE) &&
-               strcmp (prop->name, "underscan hborder") == 0)
-        output_kms->underscan_hborder_prop_id = prop->prop_id;
-      else if ((prop->flags & DRM_MODE_PROP_RANGE) &&
-               strcmp (prop->name, "underscan vborder") == 0)
-        output_kms->underscan_vborder_prop_id = prop->prop_id;
-
-      drmModeFreeProperty (prop);
-    }
-}
-
 static void
 meta_output_destroy_notify (MetaOutput *output)
 {
@@ -344,7 +276,6 @@ init_output_modes (MetaOutput  *output,
 MetaOutput *
 meta_create_kms_output (MetaGpuKms        *gpu_kms,
                         MetaKmsConnector  *kms_connector,
-                        drmModeConnector  *connector,
                         MetaOutput        *old_output,
                         GError           **error)
 {
@@ -373,8 +304,6 @@ meta_create_kms_output (MetaGpuKms        *gpu_kms,
 
   output_kms->kms_connector = kms_connector;
 
-  find_connector_properties (gpu_kms, output, connector);
-
   connector_state = meta_kms_connector_get_current_state (kms_connector);
 
   panel_orientation_transform = connector_state->panel_orientation_transform;
@@ -443,7 +372,8 @@ meta_create_kms_output (MetaGpuKms        *gpu_kms,
   output->suggested_x = connector_state->suggested_x;
   output->suggested_y = connector_state->suggested_y;
   output->hotplug_mode_update = connector_state->hotplug_mode_update;
-  output->supports_underscanning = output_kms->underscan_prop_id != 0;
+  output->supports_underscanning =
+    meta_kms_connector_is_underscanning_supported (kms_connector);
 
   meta_output_parse_edid (output, connector_state->edid_data);
 
diff --git a/src/backends/native/meta-output-kms.h b/src/backends/native/meta-output-kms.h
index f35aa130f..47ce68a3c 100644
--- a/src/backends/native/meta-output-kms.h
+++ b/src/backends/native/meta-output-kms.h
@@ -27,21 +27,24 @@
 #include "backends/native/meta-gpu-kms.h"
 #include "backends/native/meta-kms-types.h"
 
-void meta_output_kms_set_underscan (MetaOutput *output);
+void meta_output_kms_set_power_save_mode (MetaOutput    *output,
+                                          uint64_t       dpms_state,
+                                          MetaKmsUpdate *kms_update);
 
-void meta_output_kms_set_power_save_mode (MetaOutput *output,
-                                          uint64_t    state);
+void meta_output_kms_set_underscan (MetaOutput    *output,
+                                    MetaKmsUpdate *kms_update);
 
 gboolean meta_output_kms_can_clone (MetaOutput *output,
                                     MetaOutput *other_output);
 
+MetaKmsConnector * meta_output_kms_get_kms_connector (MetaOutput *output);
+
 uint32_t meta_output_kms_get_connector_id (MetaOutput *output);
 
 GBytes * meta_output_kms_read_edid (MetaOutput *output);
 
 MetaOutput * meta_create_kms_output (MetaGpuKms        *gpu_kms,
                                      MetaKmsConnector  *kms_connector,
-                                     drmModeConnector  *connector,
                                      MetaOutput        *old_output,
                                      GError           **error);
 
diff --git a/src/backends/native/meta-renderer-native.c b/src/backends/native/meta-renderer-native.c
index 2cb3f1cf7..5228310fc 100644
--- a/src/backends/native/meta-renderer-native.c
+++ b/src/backends/native/meta-renderer-native.c
@@ -62,11 +62,12 @@
 #include "backends/native/meta-drm-buffer-gbm.h"
 #include "backends/native/meta-drm-buffer.h"
 #include "backends/native/meta-gpu-kms.h"
+#include "backends/native/meta-kms-update.h"
 #include "backends/native/meta-kms-utils.h"
-#include "backends/native/meta-monitor-manager-kms.h"
+#include "backends/native/meta-kms.h"
+#include "backends/native/meta-output-kms.h"
 #include "backends/native/meta-renderer-native-gles3.h"
 #include "backends/native/meta-renderer-native.h"
-#include "meta-marshal.h"
 #include "cogl/cogl.h"
 #include "core/boxes-private.h"
 
@@ -182,7 +183,6 @@ typedef struct _MetaOnscreenNative
   } egl;
 #endif
 
-  gboolean pending_queue_swap_notify;
   gboolean pending_swap_notify;
 
   gboolean pending_set_crtc;
@@ -190,9 +190,6 @@ typedef struct _MetaOnscreenNative
   int64_t pending_queue_swap_notify_frame_count;
   int64_t pending_swap_notify_frame_count;
 
-  GList *pending_page_flip_retries;
-  GSource *retry_page_flips_source;
-
   MetaRendererView *view;
   int total_pending_flips;
 } MetaOnscreenNative;
@@ -215,7 +212,7 @@ struct _MetaRendererNative
   int64_t frame_counter;
   gboolean pending_unset_disabled_crtcs;
 
-  GList *power_save_page_flip_closures;
+  GList *power_save_page_flip_onscreens;
   guint power_save_page_flip_source_id;
 };
 
@@ -1292,11 +1289,9 @@ meta_onscreen_native_swap_drm_fb (CoglOnscreen *onscreen)
 }
 
 static void
-on_crtc_flipped (GClosure         *closure,
-                 MetaGpuKms       *gpu_kms,
-                 MetaCrtc         *crtc,
-                 int64_t           page_flip_time_ns,
-                 MetaRendererView *view)
+notify_view_crtc_presented (MetaRendererView *view,
+                            MetaKmsCrtc      *kms_crtc,
+                            int64_t           time_ns)
 {
   ClutterStageView *stage_view = CLUTTER_STAGE_VIEW (view);
   CoglFramebuffer *framebuffer =
@@ -1307,24 +1302,28 @@ on_crtc_flipped (GClosure         *closure,
   MetaRendererNative *renderer_native = onscreen_native->renderer_native;
   MetaGpuKms *render_gpu = onscreen_native->render_gpu;
   CoglFrameInfo *frame_info;
+  MetaCrtc *crtc;
   float refresh_rate;
-
-  frame_info = g_queue_peek_tail (&onscreen->pending_frame_infos);
-  refresh_rate = crtc && crtc->current_mode ?
-                 crtc->current_mode->refresh_rate :
-                 0.0f;
+  MetaGpuKms *gpu_kms;
 
   /* 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
    * fastest monitor(s) we direct it to produce new frames fast enough to
    * satisfy all monitors.
    */
+  frame_info = g_queue_peek_tail (&onscreen->pending_frame_infos);
+
+  crtc = meta_crtc_kms_from_kms_crtc (kms_crtc);
+  refresh_rate = crtc && crtc->current_mode ?
+                 crtc->current_mode->refresh_rate :
+                 0.0f;
   if (refresh_rate >= frame_info->refresh_rate)
     {
-      frame_info->presentation_time = page_flip_time_ns;
+      frame_info->presentation_time = time_ns;
       frame_info->refresh_rate = refresh_rate;
     }
 
+  gpu_kms = META_GPU_KMS (meta_crtc_get_gpu (crtc));
   if (gpu_kms != render_gpu)
     {
       MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state;
@@ -1338,8 +1337,6 @@ on_crtc_flipped (GClosure         *closure,
     {
       MetaRendererNativeGpuData *renderer_gpu_data;
 
-      onscreen_native->pending_queue_swap_notify = FALSE;
-
       meta_onscreen_native_queue_swap_notify (onscreen);
 
       renderer_gpu_data =
@@ -1358,6 +1355,92 @@ on_crtc_flipped (GClosure         *closure,
     }
 }
 
+static int64_t
+timeval_to_nanoseconds (const struct timeval *tv)
+{
+  int64_t usec = ((int64_t) tv->tv_sec) * G_USEC_PER_SEC + tv->tv_usec;
+  int64_t nsec = usec * 1000;
+
+  return nsec;
+}
+
+static void
+page_flip_feedback_flipped (MetaKmsCrtc  *kms_crtc,
+                            unsigned int  sequence,
+                            unsigned int  tv_sec,
+                            unsigned int  tv_usec,
+                            gpointer      user_data)
+{
+  MetaRendererView *view = user_data;
+  struct timeval page_flip_time;
+
+  page_flip_time = (struct timeval) {
+    .tv_sec = tv_sec,
+    .tv_usec = tv_usec,
+  };
+
+  notify_view_crtc_presented (view, kms_crtc,
+                              timeval_to_nanoseconds (&page_flip_time));
+
+  g_object_unref (view);
+}
+
+static void
+page_flip_feedback_mode_set_fallback (MetaKmsCrtc *kms_crtc,
+                                      gpointer     user_data)
+{
+  MetaRendererView *view = user_data;
+  MetaCrtc *crtc;
+  MetaGpuKms *gpu_kms;
+  int64_t now_ns;
+
+  /*
+   * We ended up not page flipping, thus we don't have a presentation time to
+   * use. Lets use the next best thing: the current time.
+   */
+
+  crtc = meta_crtc_kms_from_kms_crtc (kms_crtc);
+  gpu_kms = META_GPU_KMS (meta_crtc_get_gpu (crtc));
+  now_ns = meta_gpu_kms_get_current_time_ns (gpu_kms);
+
+  notify_view_crtc_presented (view, kms_crtc, now_ns);
+
+  g_object_unref (view);
+}
+
+static void
+page_flip_feedback_discarded (MetaKmsCrtc  *kms_crtc,
+                              gpointer      user_data,
+                              const GError *error)
+{
+  MetaRendererView *view = user_data;
+  MetaCrtc *crtc;
+  MetaGpuKms *gpu_kms;
+  int64_t now_ns;
+
+  /*
+   * Page flipping failed, but we want to fail gracefully, so to avoid freezing
+   * the frame clack, pretend we flipped.
+   */
+
+  if (error)
+    g_warning ("Page flip discarded: %s", error->message);
+
+  crtc = meta_crtc_kms_from_kms_crtc (kms_crtc);
+  gpu_kms = META_GPU_KMS (meta_crtc_get_gpu (crtc));
+  now_ns = meta_gpu_kms_get_current_time_ns (gpu_kms);
+
+  notify_view_crtc_presented (view, kms_crtc, now_ns);
+
+  g_object_unref (view);
+}
+
+static const MetaKmsPageFlipFeedback page_flip_feedback = {
+  .flipped = page_flip_feedback_flipped,
+  .mode_set_fallback = page_flip_feedback_mode_set_fallback,
+  .discarded = page_flip_feedback_discarded,
+};
+
 static void
 free_next_secondary_bo (MetaGpuKms                          *gpu_kms,
                         MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state)
@@ -1375,73 +1458,29 @@ free_next_secondary_bo (MetaGpuKms                          *gpu_kms,
     }
 }
 
-static void
-flip_closure_destroyed (MetaRendererView *view)
-{
-  ClutterStageView *stage_view = CLUTTER_STAGE_VIEW (view);
-  CoglFramebuffer *framebuffer =
-    clutter_stage_view_get_onscreen (stage_view);
-  CoglOnscreen *onscreen = COGL_ONSCREEN (framebuffer);
-  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;
-  MetaRendererNativeGpuData *renderer_gpu_data;
-
-  renderer_gpu_data = meta_renderer_native_get_gpu_data (renderer_native,
-                                                         render_gpu);
-  switch (renderer_gpu_data->mode)
-    {
-    case META_RENDERER_NATIVE_MODE_GBM:
-      g_clear_object (&onscreen_native->gbm.next_fb);
-
-      g_hash_table_foreach (onscreen_native->secondary_gpu_states,
-                            (GHFunc) free_next_secondary_bo,
-                            NULL);
-
-      break;
-#ifdef HAVE_EGL_DEVICE
-    case META_RENDERER_NATIVE_MODE_EGL_DEVICE:
-      break;
-#endif
-    }
-
-  if (onscreen_native->pending_queue_swap_notify)
-    {
-      meta_onscreen_native_queue_swap_notify (onscreen);
-      onscreen_native->pending_queue_swap_notify = FALSE;
-    }
-
-  g_object_unref (view);
-}
-
 #ifdef HAVE_EGL_DEVICE
-static gboolean
-flip_egl_stream (MetaOnscreenNative *onscreen_native,
-                 GClosure           *flip_closure)
+static int
+custom_egl_stream_page_flip (gpointer custom_page_flip_data,
+                             gpointer user_data)
 {
+  MetaOnscreenNative *onscreen_native = custom_page_flip_data;
+  MetaRendererView *view = user_data;
+  MetaEgl *egl = meta_onscreen_native_get_egl (onscreen_native);
   MetaRendererNativeGpuData *renderer_gpu_data;
   EGLDisplay *egl_display;
-  MetaEgl *egl = meta_onscreen_native_get_egl (onscreen_native);
-  MetaGpuKmsFlipClosureContainer *closure_container;
   EGLAttrib *acquire_attribs;
-  GError *error = NULL;
-
-  renderer_gpu_data =
-    meta_renderer_native_get_gpu_data (onscreen_native->renderer_native,
-                                       onscreen_native->render_gpu);
-
-  closure_container =
-    meta_gpu_kms_wrap_flip_closure (onscreen_native->render_gpu,
-                                    NULL,
-                                    flip_closure);
+  g_autoptr (GError) error = NULL;
 
   acquire_attribs = (EGLAttrib[]) {
     EGL_DRM_FLIP_EVENT_DATA_NV,
-    (EGLAttrib) closure_container,
+    (EGLAttrib) view,
     EGL_NONE
   };
 
+  renderer_gpu_data =
+    meta_renderer_native_get_gpu_data (onscreen_native->renderer_native,
+                                       onscreen_native->render_gpu);
+
   egl_display = renderer_gpu_data->egl_display;
   if (!meta_egl_stream_consumer_acquire_attrib (egl,
                                                 egl_display,
@@ -1449,28 +1488,21 @@ flip_egl_stream (MetaOnscreenNative *onscreen_native,
                                                 acquire_attribs,
                                                 &error))
     {
-      if (error->domain != META_EGL_ERROR ||
-          error->code != EGL_RESOURCE_BUSY_EXT)
-        {
-          g_warning ("Failed to flip EGL stream: %s", error->message);
-        }
-      g_error_free (error);
-      meta_gpu_kms_flip_closure_container_free (closure_container);
-      return FALSE;
+      if (g_error_matches (error, META_EGL_ERROR, EGL_RESOURCE_BUSY_EXT))
+        return -EBUSY;
+      else
+        return -EINVAL;
     }
 
-  return TRUE;
+  return 0;
 }
 #endif /* HAVE_EGL_DEVICE */
 
-static gboolean
-is_timestamp_earlier_than (uint64_t ts1,
-                           uint64_t ts2)
+static void
+dummy_power_save_page_flip (CoglOnscreen *onscreen)
 {
-  if (ts1 == ts2)
-    return FALSE;
-  else
-    return ts2 - ts1 < UINT64_MAX / 2;
+  meta_onscreen_native_swap_drm_fb (onscreen);
+  meta_onscreen_native_queue_swap_notify (onscreen);
 }
 
 static gboolean
@@ -1478,18 +1510,22 @@ dummy_power_save_page_flip_cb (gpointer user_data)
 {
   MetaRendererNative *renderer_native = user_data;
 
-  g_list_free_full (renderer_native->power_save_page_flip_closures,
-                    (GDestroyNotify) g_closure_unref);
-  renderer_native->power_save_page_flip_closures = NULL;
+  g_list_foreach (renderer_native->power_save_page_flip_onscreens,
+                  (GFunc) dummy_power_save_page_flip, NULL);
+  g_list_free_full (renderer_native->power_save_page_flip_onscreens,
+                    (GDestroyNotify) cogl_object_unref);
+  renderer_native->power_save_page_flip_onscreens = NULL;
   renderer_native->power_save_page_flip_source_id = 0;
 
   return G_SOURCE_REMOVE;
 }
 
 static void
-queue_dummy_power_save_page_flip (MetaRendererNative *renderer_native,
-                                  GClosure           *flip_closure)
+queue_dummy_power_save_page_flip (CoglOnscreen *onscreen)
 {
+  CoglOnscreenEGL *onscreen_egl = onscreen->winsys;
+  MetaOnscreenNative *onscreen_native = onscreen_egl->platform;
+  MetaRendererNative *renderer_native = onscreen_native->renderer_native;
   const unsigned int timeout_ms = 100;
 
   if (!renderer_native->power_save_page_flip_source_id)
@@ -1500,225 +1536,16 @@ queue_dummy_power_save_page_flip (MetaRendererNative *renderer_native,
                        renderer_native);
     }
 
-  renderer_native->power_save_page_flip_closures =
-    g_list_prepend (renderer_native->power_save_page_flip_closures,
-                    g_closure_ref (flip_closure));
-}
-
-typedef struct _RetryPageFlipData
-{
-  MetaCrtc *crtc;
-  uint32_t fb_id;
-  GClosure *flip_closure;
-  uint64_t retry_time_us;
-} RetryPageFlipData;
-
-static void
-retry_page_flip_data_free (RetryPageFlipData *retry_page_flip_data)
-{
-  g_closure_unref (retry_page_flip_data->flip_closure);
-  g_free (retry_page_flip_data);
-}
-
-static void
-retry_page_flip_data_fake_flipped (RetryPageFlipData  *retry_page_flip_data,
-                                   MetaOnscreenNative *onscreen_native)
-{
-  MetaCrtc *crtc = retry_page_flip_data->crtc;
-  MetaGpuKms *gpu_kms = META_GPU_KMS (meta_crtc_get_gpu (crtc));
-
-  if (gpu_kms != onscreen_native->render_gpu)
-    {
-      MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state;
-
-      secondary_gpu_state =
-        meta_onscreen_native_get_secondary_gpu_state (onscreen_native,
-                                                      gpu_kms);
-      secondary_gpu_state->pending_flips--;
-    }
-
-  onscreen_native->total_pending_flips--;
-}
-
-static gboolean
-retry_page_flips (gpointer user_data)
-{
-  MetaOnscreenNative *onscreen_native = user_data;
-  MetaRendererNative *renderer_native = onscreen_native->renderer_native;
-  MetaMonitorManager *monitor_manager =
-    meta_backend_get_monitor_manager (renderer_native->backend);
-  uint64_t now_us;
-  MetaPowerSave power_save_mode;
-  GList *l;
-
-  now_us = g_source_get_time (onscreen_native->retry_page_flips_source);
-  power_save_mode = meta_monitor_manager_get_power_save_mode (monitor_manager);
-
-  l = onscreen_native->pending_page_flip_retries;
-  while (l)
-    {
-      RetryPageFlipData *retry_page_flip_data = l->data;
-      MetaCrtc *crtc = retry_page_flip_data->crtc;
-      MetaGpuKms *gpu_kms = META_GPU_KMS (meta_crtc_get_gpu (crtc));
-      GList *l_next = l->next;
-      g_autoptr (GError) error = NULL;
-      gboolean did_flip;
-
-      if (power_save_mode != META_POWER_SAVE_ON)
-        {
-          onscreen_native->pending_page_flip_retries =
-            g_list_remove_link (onscreen_native->pending_page_flip_retries, l);
-
-          retry_page_flip_data_fake_flipped (retry_page_flip_data,
-                                             onscreen_native);
-          retry_page_flip_data_free (retry_page_flip_data);
-
-          l = l_next;
-          continue;
-        }
-
-      if (is_timestamp_earlier_than (now_us,
-                                     retry_page_flip_data->retry_time_us))
-        {
-          l = l_next;
-          continue;
-        }
-
-      did_flip = meta_gpu_kms_flip_crtc (gpu_kms,
-                                         crtc,
-                                         retry_page_flip_data->fb_id,
-                                         retry_page_flip_data->flip_closure,
-                                         &error);
-      if (!did_flip &&
-          g_error_matches (error, G_IO_ERROR, G_IO_ERROR_BUSY))
-        {
-          retry_page_flip_data->retry_time_us +=
-            (uint64_t) (G_USEC_PER_SEC / crtc->current_mode->refresh_rate);
-          l = l_next;
-          continue;
-        }
-
-      onscreen_native->pending_page_flip_retries =
-        g_list_remove_link (onscreen_native->pending_page_flip_retries, l);
-
-      if (!did_flip)
-        {
-          if (!g_error_matches (error,
-                                G_IO_ERROR,
-                                G_IO_ERROR_PERMISSION_DENIED))
-            g_critical ("Failed to page flip: %s", error->message);
-
-          retry_page_flip_data_fake_flipped (retry_page_flip_data,
-                                             onscreen_native);
-        }
-
-      retry_page_flip_data_free (retry_page_flip_data);
-
-      l = l_next;
-    }
-
-  if (onscreen_native->pending_page_flip_retries)
-    {
-      GList *l;
-      uint64_t earliest_retry_time_us = 0;
-
-      for (l = onscreen_native->pending_page_flip_retries; l; l = l->next)
-        {
-          RetryPageFlipData *retry_page_flip_data = l->data;
-
-          if (l == onscreen_native->pending_page_flip_retries ||
-              is_timestamp_earlier_than (retry_page_flip_data->retry_time_us,
-                                         earliest_retry_time_us))
-            earliest_retry_time_us = retry_page_flip_data->retry_time_us;
-        }
-
-      g_source_set_ready_time (onscreen_native->retry_page_flips_source,
-                               earliest_retry_time_us);
-      return G_SOURCE_CONTINUE;
-    }
-  else
-    {
-      meta_backend_thaw_updates (renderer_native->backend);
-      g_clear_pointer (&onscreen_native->retry_page_flips_source,
-                       g_source_unref);
-      return G_SOURCE_REMOVE;
-    }
-}
-
-static gboolean
-retry_page_flips_source_dispatch (GSource     *source,
-                                  GSourceFunc  callback,
-                                  gpointer     user_data)
-{
-  return callback (user_data);
+  renderer_native->power_save_page_flip_onscreens =
+    g_list_prepend (renderer_native->power_save_page_flip_onscreens,
+                    cogl_object_ref (onscreen));
 }
 
-static GSourceFuncs retry_page_flips_source_funcs = {
-  .dispatch = retry_page_flips_source_dispatch,
-};
-
 static void
-schedule_retry_page_flip (MetaOnscreenNative *onscreen_native,
-                          MetaCrtc           *crtc,
-                          uint32_t            fb_id,
-                          GClosure           *flip_closure)
-{
-  MetaRendererNative *renderer_native = onscreen_native->renderer_native;
-  RetryPageFlipData *retry_page_flip_data;
-  uint64_t now_us;
-  uint64_t retry_time_us;
-
-  now_us = g_get_monotonic_time ();
-  retry_time_us =
-    now_us + (uint64_t) (G_USEC_PER_SEC / crtc->current_mode->refresh_rate);
-
-  retry_page_flip_data = g_new0 (RetryPageFlipData, 1);
-  retry_page_flip_data->crtc = crtc;
-  retry_page_flip_data->fb_id = fb_id;
-  retry_page_flip_data->flip_closure = g_closure_ref (flip_closure);
-  retry_page_flip_data->retry_time_us = retry_time_us;
-
-  if (!onscreen_native->retry_page_flips_source)
-    {
-      GSource *source;
-
-      source = g_source_new (&retry_page_flips_source_funcs, sizeof (GSource));
-      g_source_set_callback (source, retry_page_flips, onscreen_native, NULL);
-      g_source_set_ready_time (source, retry_time_us);
-      g_source_attach (source, NULL);
-
-      onscreen_native->retry_page_flips_source = source;
-      meta_backend_freeze_updates (renderer_native->backend);
-    }
-  else
-    {
-      GList *l;
-
-      for (l = onscreen_native->pending_page_flip_retries; l; l = l->next)
-        {
-          RetryPageFlipData *pending_retry_page_flip_data = l->data;
-          uint64_t pending_retry_time_us =
-            pending_retry_page_flip_data->retry_time_us;
-
-          if (is_timestamp_earlier_than (retry_time_us, pending_retry_time_us))
-            {
-              g_source_set_ready_time (onscreen_native->retry_page_flips_source,
-                                       retry_time_us);
-              break;
-            }
-        }
-    }
-
-  onscreen_native->pending_page_flip_retries =
-    g_list_append (onscreen_native->pending_page_flip_retries,
-                   retry_page_flip_data);
-}
-
-static gboolean
-meta_onscreen_native_flip_crtc (CoglOnscreen  *onscreen,
-                                GClosure      *flip_closure,
-                                MetaCrtc      *crtc,
-                                GError       **error)
+meta_onscreen_native_flip_crtc (CoglOnscreen     *onscreen,
+                                MetaRendererView *view,
+                                MetaCrtc         *crtc,
+                                MetaKmsUpdate    *kms_update)
 {
   CoglOnscreenEGL *onscreen_egl = onscreen->winsys;
   MetaOnscreenNative *onscreen_native = onscreen_egl->platform;
@@ -1748,25 +1575,11 @@ meta_onscreen_native_flip_crtc (CoglOnscreen  *onscreen,
           fb_id = meta_drm_buffer_get_fb_id (secondary_gpu_state->gbm.next_fb);
         }
 
-      if (!meta_gpu_kms_flip_crtc (gpu_kms,
-                                   crtc,
-                                   fb_id,
-                                   flip_closure,
-                                   error))
-        {
-          if (g_error_matches (*error,
-                               G_IO_ERROR,
-                               G_IO_ERROR_BUSY))
-            {
-              g_clear_error (error);
-              schedule_retry_page_flip (onscreen_native, crtc,
-                                        fb_id, flip_closure);
-            }
-          else
-            {
-              return FALSE;
-            }
-        }
+      meta_crtc_kms_assign_primary_plane (crtc, fb_id, kms_update);
+      meta_crtc_kms_page_flip (crtc,
+                               &page_flip_feedback,
+                               g_object_ref (view),
+                               kms_update);
 
       onscreen_native->total_pending_flips++;
       if (secondary_gpu_state)
@@ -1775,166 +1588,78 @@ meta_onscreen_native_flip_crtc (CoglOnscreen  *onscreen,
       break;
 #ifdef HAVE_EGL_DEVICE
     case META_RENDERER_NATIVE_MODE_EGL_DEVICE:
-      if (flip_egl_stream (onscreen_native,
-                           flip_closure))
-        onscreen_native->total_pending_flips++;
+      meta_kms_update_custom_page_flip (kms_update,
+                                        meta_crtc_kms_get_kms_crtc (crtc),
+                                        &page_flip_feedback,
+                                        g_object_ref (view),
+                                        custom_egl_stream_page_flip,
+                                        onscreen_native);
+      onscreen_native->total_pending_flips++;
       break;
 #endif
     }
-
-  return TRUE;
 }
 
-static void
-set_crtc_fb (CoglOnscreen       *onscreen,
-             MetaLogicalMonitor *logical_monitor,
-             MetaCrtc           *crtc,
-             uint32_t            render_fb_id)
+typedef struct _SetCrtcModeData
 {
-  CoglOnscreenEGL *onscreen_egl = onscreen->winsys;
-  MetaOnscreenNative *onscreen_native = onscreen_egl->platform;
-  MetaGpuKms *render_gpu = onscreen_native->render_gpu;
-  MetaGpuKms *gpu_kms;
-  int x, y;
-  uint32_t fb_id;
-
-  gpu_kms = META_GPU_KMS (meta_crtc_get_gpu (crtc));
-  if (gpu_kms == render_gpu)
-    {
-      fb_id = render_fb_id;
-    }
-  else
-    {
-      MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state;
-
-      secondary_gpu_state = get_secondary_gpu_state (onscreen, gpu_kms);
-      if (!secondary_gpu_state)
-        return;
-
-      fb_id = meta_drm_buffer_get_fb_id (secondary_gpu_state->gbm.next_fb);
-    }
-
-  x = crtc->rect.x - logical_monitor->rect.x;
-  y = crtc->rect.y - logical_monitor->rect.y;
-
-  meta_gpu_kms_apply_crtc_mode (gpu_kms, crtc, x, y, fb_id);
-}
-
-typedef struct _SetCrtcFbData
-{
-  CoglOnscreen *onscreen;
-  uint32_t fb_id;
-} SetCrtcFbData;
-
-static void
-set_crtc_fb_cb (MetaLogicalMonitor *logical_monitor,
-                MetaOutput         *output,
-                MetaCrtc           *crtc,
-                gpointer            user_data)
-{
-  SetCrtcFbData *data = user_data;
-  CoglOnscreen *onscreen = data->onscreen;
-
-  set_crtc_fb (onscreen, logical_monitor, crtc, data->fb_id);
-}
+  MetaRendererNativeGpuData *renderer_gpu_data;
+  MetaOnscreenNative *onscreen_native;
+  MetaKmsUpdate *kms_update;
+} SetCrtcModeData;
 
 static void
-meta_onscreen_native_set_crtc_modes (CoglOnscreen *onscreen)
+set_crtc_mode (MetaLogicalMonitor *logical_monitor,
+               MetaOutput         *output,
+               MetaCrtc           *crtc,
+               gpointer            user_data)
 {
-  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;
-  MetaRendererNativeGpuData *renderer_gpu_data;
-  MetaRendererView *view = onscreen_native->view;
-  uint32_t fb_id = 0;
-  MetaLogicalMonitor *logical_monitor;
+  SetCrtcModeData *data = user_data;
 
-  renderer_gpu_data = meta_renderer_native_get_gpu_data (renderer_native,
-                                                         render_gpu);
-  switch (renderer_gpu_data->mode)
+  switch (data->renderer_gpu_data->mode)
     {
     case META_RENDERER_NATIVE_MODE_GBM:
-      fb_id = meta_drm_buffer_get_fb_id (onscreen_native->gbm.next_fb);
       break;
 #ifdef HAVE_EGL_DEVICE
     case META_RENDERER_NATIVE_MODE_EGL_DEVICE:
-      fb_id = onscreen_native->egl.dumb_fb.fb_id;
-      break;
-#endif
-    }
-
-  g_assert (fb_id != 0);
-
-  logical_monitor = meta_renderer_view_get_logical_monitor (view);
-  if (logical_monitor)
-    {
-      SetCrtcFbData data = {
-        .onscreen = onscreen,
-        .fb_id = fb_id
-      };
+      {
+        uint32_t fb_id;
 
-      meta_logical_monitor_foreach_crtc (logical_monitor,
-                                         set_crtc_fb_cb,
-                                         &data);
+        fb_id = data->onscreen_native->egl.dumb_fb.fb_id;
+        meta_crtc_kms_assign_primary_plane (crtc, fb_id, data->kms_update);
+        break;
+      }
+#endif
     }
-  else
-    {
-      GList *l;
-
-      for (l = meta_gpu_get_crtcs (META_GPU (render_gpu)); l; l = l->next)
-        {
-          MetaCrtc *crtc = l->data;
 
-          meta_gpu_kms_apply_crtc_mode (render_gpu,
-                                        crtc,
-                                        crtc->rect.x, crtc->rect.y,
-                                        fb_id);
-        }
-    }
+  meta_crtc_kms_set_mode (crtc, data->kms_update);
+  meta_output_kms_set_underscan (output, data->kms_update);
 }
 
-static gboolean
-crtc_mode_set_fallback (CoglOnscreen       *onscreen,
-                        MetaLogicalMonitor *logical_monitor,
-                        MetaCrtc           *crtc)
+static void
+meta_onscreen_native_set_crtc_modes (CoglOnscreen              *onscreen,
+                                     MetaRendererNativeGpuData *renderer_gpu_data,
+                                     MetaKmsUpdate             *kms_update)
 {
   CoglOnscreenEGL *onscreen_egl = onscreen->winsys;
   MetaOnscreenNative *onscreen_native = onscreen_egl->platform;
-  MetaGpuKms *render_gpu = onscreen_native->render_gpu;
-  MetaRendererNative *renderer_native;
-  MetaRendererNativeGpuData *renderer_gpu_data;
-  uint32_t fb_id;
-  static gboolean warned_once = FALSE;
-
-  if (!warned_once)
-    {
-      g_warning ("Page flipping not supported by driver, "
-                 "relying on the clock from now on");
-      warned_once = TRUE;
-    }
-
-  renderer_native = meta_renderer_native_from_gpu (render_gpu);
-  renderer_gpu_data = meta_renderer_native_get_gpu_data (renderer_native,
-                                                         render_gpu);
-  if (renderer_gpu_data->mode != META_RENDERER_NATIVE_MODE_GBM)
-    {
-      g_warning ("Mode set fallback not handled for EGLStreams");
-      return FALSE;
-    }
+  MetaRendererView *view = onscreen_native->view;
+  MetaLogicalMonitor *logical_monitor;
+  SetCrtcModeData data;
 
-  fb_id = meta_drm_buffer_get_fb_id (onscreen_native->gbm.next_fb);
-  set_crtc_fb (onscreen, logical_monitor, crtc, fb_id);
-  return TRUE;
+  logical_monitor = meta_renderer_view_get_logical_monitor (view);
+  data = (SetCrtcModeData) {
+    .renderer_gpu_data = renderer_gpu_data,
+    .onscreen_native = onscreen_native,
+    .kms_update = kms_update,
+  };
+  meta_logical_monitor_foreach_crtc (logical_monitor, set_crtc_mode, &data);
 }
 
 typedef struct _FlipCrtcData
 {
+  MetaRendererView *view;
   CoglOnscreen *onscreen;
-  GClosure *flip_closure;
-
-  gboolean did_flip;
-  gboolean did_mode_set;
+  MetaKmsUpdate *kms_update;
 } FlipCrtcData;
 
 static void
@@ -1944,32 +1669,16 @@ flip_crtc (MetaLogicalMonitor *logical_monitor,
            gpointer            user_data)
 {
   FlipCrtcData *data = user_data;
-  GError *error = NULL;
 
-  if (!meta_onscreen_native_flip_crtc (data->onscreen,
-                                       data->flip_closure,
-                                       crtc,
-                                       &error))
-    {
-      if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT))
-        {
-          if (crtc_mode_set_fallback (data->onscreen, logical_monitor, crtc))
-            data->did_mode_set = TRUE;
-        }
-      else if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED))
-        {
-          g_warning ("Failed to flip onscreen: %s", error->message);
-        }
-      g_error_free (error);
-    }
-  else
-    {
-      data->did_flip = TRUE;
-    }
+  meta_onscreen_native_flip_crtc (data->onscreen,
+                                  data->view,
+                                  crtc,
+                                  data->kms_update);
 }
 
 static void
-meta_onscreen_native_flip_crtcs (CoglOnscreen *onscreen)
+meta_onscreen_native_flip_crtcs (CoglOnscreen  *onscreen,
+                                 MetaKmsUpdate *kms_update)
 {
   CoglOnscreenEGL *onscreen_egl = onscreen->winsys;
   MetaOnscreenNative *onscreen_native = onscreen_egl->platform;
@@ -1977,51 +1686,24 @@ meta_onscreen_native_flip_crtcs (CoglOnscreen *onscreen)
   MetaRendererNative *renderer_native = onscreen_native->renderer_native;
   MetaMonitorManager *monitor_manager =
     meta_backend_get_monitor_manager (renderer_native->backend);
-  GClosure *flip_closure;
   MetaPowerSave power_save_mode;
   MetaLogicalMonitor *logical_monitor;
 
-  /*
-   * Create a closure that either will be invoked or destructed.
-   * Invoking the closure represents a completed flip. If the closure
-   * is destructed before being invoked, the framebuffer references will be
-   * cleaned up accordingly.
-   *
-   * Each successful flip will each own one reference to the closure, thus keep
-   * it alive until either invoked or destructed. If flipping failed, the
-   * closure will be destructed before this function goes out of scope.
-   */
-  flip_closure = g_cclosure_new (G_CALLBACK (on_crtc_flipped),
-                                 g_object_ref (view),
-                                 (GClosureNotify) flip_closure_destroyed);
-  g_closure_set_marshal (flip_closure, meta_marshal_VOID__OBJECT_OBJECT_INT64);
-
   power_save_mode = meta_monitor_manager_get_power_save_mode (monitor_manager);
   if (power_save_mode == META_POWER_SAVE_ON)
     {
       FlipCrtcData data = {
         .onscreen = onscreen,
-        .flip_closure = flip_closure,
+        .view = view,
+        .kms_update = kms_update,
       };
       logical_monitor = meta_renderer_view_get_logical_monitor (view);
       meta_logical_monitor_foreach_crtc (logical_monitor, flip_crtc, &data);
-
-      /*
-       * If we didn't queue a page flip, but instead directly changed the mode
-       * due to the driver not supporting mode setting, we must swap the
-       * buffers directly as we won't get a page flip callback.
-       */
-      if (!data.did_flip && data.did_mode_set)
-        meta_onscreen_native_swap_drm_fb (onscreen);
     }
   else
     {
-      queue_dummy_power_save_page_flip (renderer_native, flip_closure);
+      queue_dummy_power_save_page_flip (onscreen);
     }
-
-  onscreen_native->pending_queue_swap_notify = TRUE;
-
-  g_closure_unref (flip_closure);
 }
 
 static void
@@ -2031,6 +1713,7 @@ wait_for_pending_flips (CoglOnscreen *onscreen)
   MetaOnscreenNative *onscreen_native = onscreen_egl->platform;
   MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state;
   GHashTableIter iter;
+  GError *error = NULL;
 
   g_hash_table_iter_init (&iter, onscreen_native->secondary_gpu_states);
   while (g_hash_table_iter_next (&iter,
@@ -2038,11 +1721,26 @@ wait_for_pending_flips (CoglOnscreen *onscreen)
                                  (gpointer *) &secondary_gpu_state))
     {
       while (secondary_gpu_state->pending_flips)
-        meta_gpu_kms_wait_for_flip (secondary_gpu_state->gpu_kms, NULL);
+        {
+          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)
-    meta_gpu_kms_wait_for_flip (onscreen_native->render_gpu, NULL);
+    {
+      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 void
@@ -2305,17 +2003,23 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen,
   CoglRendererEGL *cogl_renderer_egl = cogl_renderer->winsys;
   MetaRendererNativeGpuData *renderer_gpu_data = cogl_renderer_egl->platform;
   MetaRendererNative *renderer_native = renderer_gpu_data->renderer_native;
+  MetaBackend *backend = renderer_native->backend;
   MetaMonitorManager *monitor_manager =
-    meta_backend_get_monitor_manager (renderer_native->backend);
+    meta_backend_get_monitor_manager (backend);
+  MetaBackendNative *backend_native = META_BACKEND_NATIVE (backend);
+  MetaKms *kms = meta_backend_native_get_kms (backend_native);
   CoglOnscreenEGL *onscreen_egl = onscreen->winsys;
   MetaOnscreenNative *onscreen_native = onscreen_egl->platform;
   MetaGpuKms *render_gpu = onscreen_native->render_gpu;
   CoglFrameInfo *frame_info;
   gboolean egl_context_changed = FALSE;
+  MetaKmsUpdate *kms_update;
   MetaPowerSave power_save_mode;
   g_autoptr (GError) error = NULL;
   MetaDrmBufferGbm *buffer_gbm;
 
+  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.
@@ -2367,12 +2071,14 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen,
   if (onscreen_native->pending_set_crtc &&
       power_save_mode == META_POWER_SAVE_ON)
     {
-      meta_onscreen_native_set_crtc_modes (onscreen);
+      meta_onscreen_native_set_crtc_modes (onscreen,
+                                           renderer_gpu_data,
+                                           kms_update);
       onscreen_native->pending_set_crtc = FALSE;
     }
 
   onscreen_native->pending_queue_swap_notify_frame_count = renderer_native->frame_counter;
-  meta_onscreen_native_flip_crtcs (onscreen);
+  meta_onscreen_native_flip_crtcs (onscreen, kms_update);
 
   /*
    * If we changed EGL context, cogl will have the wrong idea about what is
@@ -2382,6 +2088,12 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen,
    */
   if (egl_context_changed)
     _cogl_winsys_egl_ensure_current (cogl_display);
+
+  if (!meta_kms_post_pending_update_sync (kms, &error))
+    {
+      if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED))
+        g_warning ("Failed to post KMS update: %s", error->message);
+    }
 }
 
 static gboolean
@@ -2946,15 +2658,6 @@ meta_renderer_native_release_onscreen (CoglOnscreen *onscreen)
         g_warning ("Failed to clear current context");
     }
 
-  g_list_free_full (onscreen_native->pending_page_flip_retries,
-                    (GDestroyNotify) retry_page_flip_data_free);
-  if (onscreen_native->retry_page_flips_source)
-    {
-      meta_backend_thaw_updates (renderer_native->backend);
-      g_clear_pointer (&onscreen_native->retry_page_flips_source,
-                       g_source_destroy);
-    }
-
   renderer_gpu_data =
     meta_renderer_native_get_gpu_data (renderer_native,
                                        onscreen_native->render_gpu);
@@ -3410,6 +3113,12 @@ meta_renderer_native_create_view (MetaRenderer       *renderer,
 void
 meta_renderer_native_finish_frame (MetaRendererNative *renderer_native)
 {
+  MetaBackend *backend = renderer_native->backend;
+  MetaBackendNative *backend_native = META_BACKEND_NATIVE (backend);
+  MetaKms *kms = meta_backend_native_get_kms (backend_native);
+  MetaKmsUpdate *kms_update = NULL;
+  GError *error = NULL;
+
   renderer_native->frame_counter++;
 
   if (renderer_native->pending_unset_disabled_crtcs)
@@ -3419,7 +3128,6 @@ meta_renderer_native_finish_frame (MetaRendererNative *renderer_native)
       for (l = meta_backend_get_gpus (renderer_native->backend); l; l = l->next)
         {
           MetaGpu *gpu = l->data;
-          MetaGpuKms *gpu_kms = META_GPU_KMS (gpu);
           GList *k;
 
           for (k = meta_gpu_get_crtcs (gpu); k; k = k->next)
@@ -3429,12 +3137,23 @@ meta_renderer_native_finish_frame (MetaRendererNative *renderer_native)
               if (crtc->current_mode)
                 continue;
 
-              meta_gpu_kms_apply_crtc_mode (gpu_kms, crtc, 0, 0, 0);
+              kms_update = meta_kms_ensure_pending_update (kms);
+              meta_crtc_kms_set_mode (crtc, kms_update);
             }
         }
 
       renderer_native->pending_unset_disabled_crtcs = FALSE;
     }
+
+  if (kms_update)
+    {
+      if (!meta_kms_post_pending_update_sync (kms, &error))
+        {
+          if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED))
+            g_warning ("Failed to post KMS update: %s", error->message);
+          g_error_free (error);
+        }
+    }
 }
 
 int64_t
@@ -4031,6 +3750,22 @@ on_gpu_added (MetaBackendNative  *backend_native,
   _cogl_winsys_egl_ensure_current (cogl_display);
 }
 
+static void
+on_power_save_mode_changed (MetaMonitorManager *monitor_manager,
+                            MetaRendererNative *renderer_native)
+{
+  MetaBackendNative *backend_native =
+    META_BACKEND_NATIVE (renderer_native->backend);
+  MetaKms *kms = meta_backend_native_get_kms (backend_native);
+  MetaPowerSave power_save_mode;
+
+  power_save_mode = meta_monitor_manager_get_power_save_mode (monitor_manager);
+  if (power_save_mode == META_POWER_SAVE_ON)
+    meta_renderer_native_queue_modes_reset (renderer_native);
+  else
+    meta_kms_discard_pending_page_flips (kms);
+}
+
 static MetaGpuKms *
 choose_primary_gpu_unchecked (MetaBackend        *backend,
                               MetaRendererNative *renderer_native)
@@ -4143,10 +3878,10 @@ meta_renderer_native_finalize (GObject *object)
 {
   MetaRendererNative *renderer_native = META_RENDERER_NATIVE (object);
 
-  if (renderer_native->power_save_page_flip_closures)
+  if (renderer_native->power_save_page_flip_onscreens)
     {
-      g_list_free_full (renderer_native->power_save_page_flip_closures,
-                        (GDestroyNotify) g_closure_unref);
+      g_list_free_full (renderer_native->power_save_page_flip_onscreens,
+                        (GDestroyNotify) cogl_object_unref);
       g_source_remove (renderer_native->power_save_page_flip_source_id);
     }
 
@@ -4162,6 +3897,8 @@ meta_renderer_native_constructed (GObject *object)
   MetaRendererNative *renderer_native = META_RENDERER_NATIVE (object);
   MetaBackend *backend = renderer_native->backend;
   MetaSettings *settings = meta_backend_get_settings (backend);
+  MetaMonitorManager *monitor_manager =
+    meta_backend_get_monitor_manager (backend);
 
   if (meta_settings_is_experimental_feature_enabled (
         settings, META_EXPERIMENTAL_FEATURE_KMS_MODIFIERS))
@@ -4169,6 +3906,8 @@ meta_renderer_native_constructed (GObject *object)
 
   g_signal_connect (backend, "gpu-added",
                     G_CALLBACK (on_gpu_added), renderer_native);
+  g_signal_connect (monitor_manager, "power-save-mode-changed",
+                    G_CALLBACK (on_power_save_mode_changed), renderer_native);
 
   G_OBJECT_CLASS (meta_renderer_native_parent_class)->constructed (object);
 }
diff --git a/src/meson.build b/src/meson.build
index acfd879ef..515638fe5 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -610,10 +610,15 @@ if have_native_backend
     'backends/native/meta-kms-impl-simple.h',
     'backends/native/meta-kms-impl.c',
     'backends/native/meta-kms-impl.h',
+    'backends/native/meta-kms-page-flip.c',
+    'backends/native/meta-kms-page-flip-private.h',
     'backends/native/meta-kms-plane.c',
     'backends/native/meta-kms-plane.h',
     'backends/native/meta-kms-private.h',
     'backends/native/meta-kms-types.h',
+    'backends/native/meta-kms-update-private.h',
+    'backends/native/meta-kms-update.c',
+    'backends/native/meta-kms-update.h',
     'backends/native/meta-kms-utils.c',
     'backends/native/meta-kms-utils.h',
     'backends/native/meta-kms.c',
@@ -801,14 +806,6 @@ endif
 
 subdir('meta')
 
-mutter_marshal = gnome.genmarshal('meta-marshal',
-  sources: ['meta-marshal.list'],
-  prefix: 'meta_marshal',
-  extra_args: ['--quiet'],
-  internal: true,
-)
-mutter_built_sources += mutter_marshal
-
 mutter_built_sources += mutter_enum_types
 mutter_built_sources += mutter_version
 


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