[cogl/wip/outputs: 52/52] stash: Expose api to configure outputs



commit 15d77251ffbcbd346f7103dc57e29f441ec58d5e
Author: Robert Bragg <robert linux intel com>
Date:   Sun Apr 21 13:05:04 2013 +0100

    stash: Expose api to configure outputs
    
    Previously it was only possible to read the state of outputs. This patch
    introduces apis for configuring outputs including setting new modes and
    setting up hardware overlays.
    
    This notably has a different style to the XRandR/KMS apis that expose
    a model of components that can be assembled into a display pipeline. For
    example the KMS api lets you make a pipeline like this:
    
      framebuffers -> crtc [+planes] -> encoder -> connector
    
    This Cogl api instead exposes an object (CoglOutput) that represents a
    full display/output pipeline and then lets you hook sources into that
    pipeline via CoglOverlay objects whereby a source may be scaled and
    positioned relative to the full output resolution and multiple overlays
    may be stacked to be composed before display.
    
    With this api all output changes are batched up until
    cogl_renderer_commit_outputs() is called and on failure we will role
    back to the previous state. This is also unlike the KMS api where errors
    can be reported while you may only be part way through configuring the
    outputs as desired and there is no role back mechanism.
    
    With this api we also hope to cover more capabilities with a portable
    api whereas the portable KMS api is very minimal and depends on device
    specific ioctls for more complete control.

 cogl/Makefile.am                  |    5 +
 cogl/cogl-frame-info.h            |    6 +-
 cogl/cogl-framebuffer.h           |    1 +
 cogl/cogl-mode-private.h          |   58 ++++
 cogl/cogl-mode.c                  |   73 +++++
 cogl/cogl-mode.h                  |   51 ++++
 cogl/cogl-onscreen.h              |    6 +-
 cogl/cogl-output-private.h        |   46 +++-
 cogl/cogl-output.c                |  288 +++++++++++++++++++--
 cogl/cogl-output.h                |  245 +++++++++++++++++-
 cogl/cogl-overlay-private.h       |   76 ++++++
 cogl/cogl-overlay.c               |  249 ++++++++++++++++++
 cogl/cogl-overlay.h               |  114 ++++++++
 cogl/cogl-renderer-private.h      |    3 +
 cogl/cogl-renderer.c              |   89 ++++++-
 cogl/cogl-renderer.h              |   27 ++-
 cogl/cogl-xlib-renderer.c         |  123 ++++------
 cogl/cogl.h                       |    1 +
 cogl/winsys/cogl-winsys-egl-kms.c |  523 +++++++++++++++++++++++++++++++++---
 cogl/winsys/cogl-winsys-private.h |   12 +
 20 files changed, 1840 insertions(+), 156 deletions(-)
---
diff --git a/cogl/Makefile.am b/cogl/Makefile.am
index 642e9c9..512b3c1 100644
--- a/cogl/Makefile.am
+++ b/cogl/Makefile.am
@@ -59,6 +59,8 @@ DISTCLEANFILES += $(pc_files)
 
 # public api headers
 cogl_public_h = \
+       $(srcdir)/cogl-mode.h                   \
+       $(srcdir)/cogl-overlay.h                \
        $(srcdir)/cogl-attribute-buffer.h       \
        $(srcdir)/cogl-attribute.h              \
        $(srcdir)/cogl-bitmap.h                 \
@@ -365,6 +367,9 @@ cogl_sources_c = \
        $(srcdir)/cogl-closure-list.c                   \
        $(srcdir)/cogl-fence.c                          \
        $(srcdir)/cogl-fence-private.h                  \
+       $(srcdir)/cogl-overlay-private.h                \
+       $(srcdir)/cogl-overlay.c                        \
+       $(srcdir)/cogl-mode.c                           \
        $(NULL)
 
 if USE_GLIB
diff --git a/cogl/cogl-frame-info.h b/cogl/cogl-frame-info.h
index 36f8a04..f20d085 100644
--- a/cogl/cogl-frame-info.h
+++ b/cogl/cogl-frame-info.h
@@ -31,13 +31,17 @@
 #ifndef __COGL_FRAME_INFO_H
 #define __COGL_FRAME_INFO_H
 
+/* We forward declare the CoglFrameInfo type here to avoid some
+ * circular dependency issues with the following headers.
+ */
+typedef struct _CoglFrameInfo CoglFrameInfo;
+
 #include <cogl/cogl-types.h>
 #include <cogl/cogl-output.h>
 #include <glib.h>
 
 G_BEGIN_DECLS
 
-typedef struct _CoglFrameInfo CoglFrameInfo;
 #define COGL_FRAME_INFO(X) ((CoglFrameInfo *)(X))
 
 /**
diff --git a/cogl/cogl-framebuffer.h b/cogl/cogl-framebuffer.h
index 2ad41f8..8106095 100644
--- a/cogl/cogl-framebuffer.h
+++ b/cogl/cogl-framebuffer.h
@@ -43,6 +43,7 @@ typedef struct _CoglFramebuffer CoglFramebuffer;
 #include <cogl/cogl-texture.h>
 #include <cogl/cogl-quaternion.h>
 #include <cogl/cogl-euler.h>
+#include <cogl/cogl-primitive.h>
 
 COGL_BEGIN_DECLS
 
diff --git a/cogl/cogl-mode-private.h b/cogl/cogl-mode-private.h
new file mode 100644
index 0000000..fddd1ca
--- /dev/null
+++ b/cogl/cogl-mode-private.h
@@ -0,0 +1,58 @@
+/*
+ * Cogl
+ *
+ * An object oriented GL/GLES Abstraction/Utility Layer
+ *
+ * Copyright (C) 2013 Intel Corporation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ *
+ */
+
+#ifndef _COGL_MODE_PRIVATE_H_
+#define _COGL_MODE_PRIVATE_H_
+
+#include "cogl-mode.h"
+#include "cogl-object-private.h"
+
+struct _CoglMode
+{
+  CoglObjectClass _parent;
+
+  char *name;
+
+  /* NB: _cogl_mode_equal() expects everything from the width member
+   * to the end of the struct to be comparable using memcmp().
+   */
+  int width;
+  int height;
+
+  float refresh_rate;
+
+#if 0
+  uint32_t clock;
+  uint16_t hdisplay, hsync_start, hsync_end, htotal, hskew;
+  uint16_t vdisplay, vsync_start, vsync_end, vtotal, vscan;
+
+  uint32_t flags;
+  uint32_t type;
+#endif
+};
+
+CoglMode *
+_cogl_mode_new (const char *name);
+
+#endif /* _COGL_MODE_PRIVATE_H_ */
diff --git a/cogl/cogl-mode.c b/cogl/cogl-mode.c
new file mode 100644
index 0000000..d742609
--- /dev/null
+++ b/cogl/cogl-mode.c
@@ -0,0 +1,73 @@
+/*
+ * Cogl
+ *
+ * An object oriented GL/GLES Abstraction/Utility Layer
+ *
+ * Copyright (C) 2013 Intel Corporation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ *
+ */
+
+#include <config.h>
+
+#include "cogl-mode-private.h"
+#include "cogl-object-private.h"
+
+static void _cogl_mode_free (CoglMode *mode);
+
+COGL_OBJECT_DEFINE (Mode, mode);
+
+static void
+_cogl_mode_free (CoglMode *mode)
+{
+  g_free (mode->name);
+  g_slice_free (CoglMode, mode);
+}
+
+CoglMode *
+_cogl_mode_new (const char *name)
+{
+  CoglMode *mode = g_slice_new0 (CoglMode);
+
+  mode->name = g_strdup (name);
+
+  return _cogl_mode_object_new (mode);
+}
+
+const char *
+cogl_mode_get_name (CoglMode *mode)
+{
+  return mode->name;
+}
+
+float
+cogl_mode_get_refresh_rate (CoglMode *mode)
+{
+  return mode->refresh_rate;
+}
+
+int
+cogl_mode_get_width (CoglMode *mode)
+{
+  return mode->width;
+}
+
+int
+cogl_mode_get_height (CoglMode *mode)
+{
+  return mode->height;
+}
diff --git a/cogl/cogl-mode.h b/cogl/cogl-mode.h
new file mode 100644
index 0000000..6d25f95
--- /dev/null
+++ b/cogl/cogl-mode.h
@@ -0,0 +1,51 @@
+/*
+ * Cogl
+ *
+ * An object oriented GL/GLES Abstraction/Utility Layer
+ *
+ * Copyright (C) 2013 Intel Corporation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ *
+ */
+
+#ifndef _COGL_MODE_H_
+#define _COGL_MODE_H_
+
+#include <cogl/cogl-types.h>
+
+COGL_BEGIN_DECLS
+
+typedef struct _CoglMode CoglMode;
+
+CoglBool
+cogl_is_mode (void *object);
+
+const char *
+cogl_mode_get_name (CoglMode *mode);
+
+float
+cogl_mode_get_refresh_rate (CoglMode *mode);
+
+int
+cogl_mode_get_width (CoglMode *mode);
+
+int
+cogl_mode_get_height (CoglMode *mode);
+
+COGL_END_DECLS
+
+#endif /* _COGL_MODE_H_ */
diff --git a/cogl/cogl-onscreen.h b/cogl/cogl-onscreen.h
index d9582fa..b023158 100644
--- a/cogl/cogl-onscreen.h
+++ b/cogl/cogl-onscreen.h
@@ -32,6 +32,11 @@
 #ifndef __COGL_ONSCREEN_H
 #define __COGL_ONSCREEN_H
 
+/* We forward declare the CoglOnscreen type here to avoid some
+ * circular dependency issues with the following headers.
+ */
+typedef struct _CoglOnscreen CoglOnscreen;
+
 #include <cogl/cogl-context.h>
 #include <cogl/cogl-framebuffer.h>
 #include <cogl/cogl-frame-info.h>
@@ -39,7 +44,6 @@
 
 COGL_BEGIN_DECLS
 
-typedef struct _CoglOnscreen CoglOnscreen;
 #define COGL_ONSCREEN(X) ((CoglOnscreen *)(X))
 
 /**
diff --git a/cogl/cogl-output-private.h b/cogl/cogl-output-private.h
index f0e8d57..82bb491 100644
--- a/cogl/cogl-output-private.h
+++ b/cogl/cogl-output-private.h
@@ -24,24 +24,51 @@
 #ifndef __COGL_OUTPUT_PRIVATE_H
 #define __COGL_OUTPUT_PRIVATE_H
 
+#include <glib.h>
+
 #include "cogl-output.h"
 #include "cogl-object-private.h"
 
-struct _CoglOutput
+#warning "TODO: remove if not used in the end"
+typedef enum _CoglOutputChange
 {
-  CoglObject _parent;
+  COGL_OUTPUT_CHANGE_OVERLAYS = 1<<0,
+  COGL_OUTPUT_CHANGE_MODE = 1<<1,
+} CoglOutputChange;
 
+typedef struct _CoglOutputState
+{
   char *name;
 
-  int x; /* Must be first field for _cogl_output_values_equal() */
+  GList *overlays;
+
+  CoglMode *mode;
+
+  /* x must be first field for _cogl_output_state_equal()
+   * and all following members should be comparable using
+   * memcmp() */
+  int x;
   int y;
-  int width;
-  int height;
   int mm_width;
   int mm_height;
-  float refresh_rate;
   CoglSubpixelOrder subpixel_order;
 
+  CoglDpmsMode dpms_mode;
+
+#warning "TODO: remove if not used in the end"
+  CoglOutputChange changes;
+
+} CoglOutputState;
+
+struct _CoglOutput
+{
+  CoglObject _parent;
+
+  GList *modes;
+
+  CoglOutputState *pending;
+  CoglOutputState *state;
+
   void *winsys;
   CoglUserDataDestroyCallback winsys_destroy_callback;
 };
@@ -54,8 +81,11 @@ _cogl_output_set_winsys_data (CoglOutput *output,
                               void *winsys,
                               CoglUserDataDestroyCallback destroy_callback);
 
+void
+_cogl_output_update_state (CoglOutput *output);
+
 CoglBool
-_cogl_output_values_equal (CoglOutput *output,
-                           CoglOutput *other);
+_cogl_output_equal (CoglOutput *output,
+                    CoglOutput *other);
 
 #endif /* __COGL_OUTPUT_PRIVATE_H */
diff --git a/cogl/cogl-output.c b/cogl/cogl-output.c
index bf2ba21..cd79f5a 100644
--- a/cogl/cogl-output.c
+++ b/cogl/cogl-output.c
@@ -24,6 +24,8 @@
 #include <config.h>
 
 #include "cogl-output-private.h"
+#include "cogl-overlay-private.h"
+#include "cogl-mode-private.h"
 
 #include <string.h>
 
@@ -37,18 +39,40 @@ _cogl_output_new (const char *name)
   CoglOutput *output;
 
   output = g_slice_new0 (CoglOutput);
-  output->name = g_strdup (name);
+
+  output->state = g_slice_new0 (CoglOutputState);
+  output->state->name = g_strdup (name);
+
+  output->pending = output->state;
 
   return _cogl_output_object_new (output);
 }
 
 static void
+free_output_state (CoglOutputState *state)
+{
+  GList *l;
+
+  for (l = state->overlays; l; l = l->next)
+    cogl_object_unref (l->data);
+  g_list_free (state->overlays);
+
+  g_free (state->name);
+
+  g_slice_free (CoglOutputState, state);
+}
+
+static void
 _cogl_output_free (CoglOutput *output)
 {
   if (output->winsys_destroy_callback)
     output->winsys_destroy_callback (output->winsys);
 
-  g_free (output->name);
+  if (output->pending != output->state)
+    free_output_state (output->pending);
+
+  free_output_state (output->state);
+
   g_slice_free (CoglOutput, output);
 }
 
@@ -61,59 +85,291 @@ _cogl_output_set_winsys_data (CoglOutput *output,
   output->winsys_destroy_callback = destroy_callback;
 }
 
-gboolean
-_cogl_output_values_equal (CoglOutput *output,
-                           CoglOutput *other)
+static CoglBool
+_cogl_mode_equal (CoglMode *mode0, CoglMode *mode1)
+{
+  if (memcmp ((const char *)mode0 + G_STRUCT_OFFSET (CoglMode, width),
+              (const char *)mode1 + G_STRUCT_OFFSET (CoglMode, width),
+              sizeof (CoglMode) - G_STRUCT_OFFSET (CoglMode, width)) != 0)
+    return FALSE;
+
+  return TRUE;
+}
+
+static CoglBool
+_cogl_mode_lists_equal (GList *modes0, GList *modes1)
 {
-  return memcmp ((const char *)output + G_STRUCT_OFFSET (CoglOutput, x),
-                 (const char *)other + G_STRUCT_OFFSET (CoglOutput, x),
-                 sizeof (CoglOutput) - G_STRUCT_OFFSET (CoglOutput, x)) == 0;
+  GList *l, *m;
+
+  for (l = modes0, m = modes1;
+       l && m;
+       l = l->next, m = m->next)
+    {
+      if (!_cogl_mode_equal (l->data, m->data))
+        return FALSE;
+    }
+
+  if (l || m)
+    return FALSE;
+
+  return TRUE;
+}
+
+CoglBool
+_cogl_output_equal (CoglOutput *output,
+                    CoglOutput *other)
+{
+  GList *l, *l2;
+
+  if (output == other)
+    return TRUE;
+
+  if (!_cogl_mode_lists_equal (output->modes, other->modes))
+    return FALSE;
+
+  if (!_cogl_mode_equal (output->pending->mode, other->pending->mode))
+    return FALSE;
+
+  if (memcmp ((const char *)output->pending +
+              G_STRUCT_OFFSET (CoglOutputState, x),
+              (const char *)other->pending +
+              G_STRUCT_OFFSET (CoglOutputState, x),
+              sizeof (CoglOutputState) -
+              G_STRUCT_OFFSET (CoglOutputState, x)) != 0)
+    return FALSE;
+
+  for (l = output->pending->overlays, l2 = other->pending->overlays;
+       l && l2;
+       l = l->next, l2 = l2->next)
+    {
+      if (!_cogl_overlay_equal (l->data, l2->data))
+        return FALSE;
+    }
+
+  if (l || l2)
+    return FALSE;
+
+  return TRUE;
 }
 
 int
 cogl_output_get_x (CoglOutput *output)
 {
-  return output->x;
+  return output->pending->x;
 }
 
 int
 cogl_output_get_y (CoglOutput *output)
 {
-  return output->y;
+  return output->pending->y;
 }
 
 int
 cogl_output_get_width (CoglOutput *output)
 {
-  return output->width;
+  return output->pending->mode->width;
 }
 
 int
 cogl_output_get_height (CoglOutput *output)
 {
-  return output->height;
+  return output->pending->mode->height;
 }
 
 int
 cogl_output_get_mm_width (CoglOutput *output)
 {
-  return output->mm_width;
+  return output->pending->mm_width;
 }
 
 int
 cogl_output_get_mm_height (CoglOutput *output)
 {
-  return output->mm_height;
+  return output->pending->mm_height;
 }
 
 CoglSubpixelOrder
 cogl_output_get_subpixel_order (CoglOutput *output)
 {
-  return output->subpixel_order;
+  return output->pending->subpixel_order;
 }
 
 float
 cogl_output_get_refresh_rate (CoglOutput *output)
 {
-  return output->refresh_rate;
+  return output->pending->mode->refresh_rate;
+}
+
+CoglOverlay *
+cogl_output_get_overlay0 (CoglOutput *output)
+{
+  return output->pending->overlays->data;
+}
+
+static void
+ensure_pending_state (CoglOutput *output)
+{
+  CoglOutputState *state = output->state;
+  CoglOutputState *pending;
+
+  if (output->pending != state)
+    return;
+
+  pending = g_slice_dup (CoglOutputState, state);
+  pending->name = g_strdup (state->name);
+  pending->overlays = g_list_copy (state->overlays);
+
+  output->pending = state;
+}
+
+void
+_cogl_output_update_state (CoglOutput *output)
+{
+  if (output->pending == output->state)
+    return;
+
+  free_output_state (output->state);
+
+  output->state = output->pending;
+}
+
+void
+cogl_output_append_overlay (CoglOutput *output,
+                            CoglOverlay *overlay)
+{
+  ensure_pending_state (output);
+
+  output->pending->overlays =
+    g_list_append (output->pending->overlays, overlay);
+}
+
+void
+cogl_output_put_overlay_above (CoglOutput *output,
+                               CoglOverlay *overlay,
+                               CoglOverlay *sibling)
+{
+  GList *l;
+
+  ensure_pending_state (output);
+
+  if (sibling)
+    {
+      l = g_list_find (output->pending->overlays, sibling);
+
+      _COGL_RETURN_IF_FAIL (l != NULL);
+
+      l = l->next;
+    }
+  else
+    l = NULL;
+
+  output->pending->overlays =
+    g_list_insert_before (output->pending->overlays,
+                          l,
+                          overlay);
+}
+
+void
+cogl_output_put_overlay_below (CoglOutput *output,
+                               CoglOverlay *overlay,
+                               CoglOverlay *sibling)
+{
+  GList *l;
+
+  ensure_pending_state (output);
+
+  if (sibling)
+    {
+      l = g_list_find (output->pending->overlays, sibling);
+
+      _COGL_RETURN_IF_FAIL (l != NULL);
+    }
+  else
+    l = NULL;
+
+  output->pending->overlays =
+    g_list_insert_before (output->pending->overlays,
+                          l,
+                          overlay);
+}
+
+void
+cogl_output_remove_overlay (CoglOutput *output,
+                            CoglOverlay *overlay)
+{
+  GList *l;
+
+  ensure_pending_state (output);
+
+  l = g_list_find (output->pending->overlays, overlay);
+
+  _COGL_RETURN_IF_FAIL (l != NULL);
+
+  cogl_object_unref (l->data);
+
+  output->pending->overlays =
+    g_list_delete_link (output->pending->overlays, l);
+}
+
+void
+cogl_output_foreach_mode (CoglOutput *output,
+                          CoglOutputModeCallback callback,
+                          void *user_data)
+{
+  GList *l;
+  for (l = output->modes; l; l = l->next)
+    callback (l->data, user_data);
+}
+
+void
+cogl_output_set_mode (CoglOutput *output,
+                      CoglMode *mode)
+{
+  _COGL_RETURN_IF_FAIL (mode);
+
+  if (_cogl_mode_equal (output->pending->mode, mode))
+    return;
+
+  ensure_pending_state (output);
+
+  if (output->pending->mode)
+    cogl_object_unref (output->pending->mode);
+
+  output->pending->mode = cogl_object_ref (mode);
+}
+
+CoglMode *
+cogl_output_get_mode (CoglOutput *output)
+{
+  _COGL_RETURN_VAL_IF_FAIL (output->pending->mode, NULL);
+
+  return output->pending->mode;
+}
+
+void
+cogl_output_set_dpms_mode (CoglOutput *output,
+                           CoglDpmsMode dpms_mode)
+{
+  if (output->pending->dpms_mode == dpms_mode)
+    return;
+
+  ensure_pending_state (output);
+
+  output->pending->dpms_mode = dpms_mode;
+}
+
+CoglDpmsMode
+cogl_output_get_dpms_mode (CoglOutput *output)
+{
+  return output->pending->dpms_mode;
+}
+
+void
+cogl_output_foreach_overlay (CoglOutput *output,
+                             CoglOutputOverlayCallback callback,
+                             void *user_data)
+{
+  GList *l;
+  for (l = output->pending->overlays; l; l = l->next)
+    callback (l->data, user_data);
 }
diff --git a/cogl/cogl-output.h b/cogl/cogl-output.h
index c0cc7cc..1f275a3 100644
--- a/cogl/cogl-output.h
+++ b/cogl/cogl-output.h
@@ -31,7 +31,14 @@
 #ifndef __COGL_OUTPUT_H
 #define __COGL_OUTPUT_H
 
+/* We forward declare the CoglOutput type here to avoid some circular
+ * dependency issues with the following headers.
+ */
+typedef struct _CoglOutput CoglOutput;
+
 #include <cogl/cogl-types.h>
+#include <cogl/cogl-overlay.h>
+#include <cogl/cogl-mode.h>
 
 COGL_BEGIN_DECLS
 
@@ -57,7 +64,6 @@ COGL_BEGIN_DECLS
  * to the #CoglOnscreen.
  */
 
-typedef struct _CoglOutput CoglOutput;
 #define COGL_OUTPUT(X) ((CoglOutput *)(X))
 
 /**
@@ -234,6 +240,243 @@ cogl_output_get_subpixel_order (CoglOutput *output);
 float
 cogl_output_get_refresh_rate (CoglOutput *output);
 
+/**
+ * cogl_output_get_overlay0:
+ * @output: a #CoglOutput
+ *
+ * Queries the first, base overlay associated with the given @output.
+ *
+ * Return value: A pointer to the first #CoglOverlay associated with
+ *               @output.
+ * Since: 1.16
+ * Stability: unstable
+ */
+CoglOverlay *
+cogl_output_get_overlay0 (CoglOutput *output);
+
+/**
+ * cogl_output_append_overlay:
+ * @output: a #CoglOutput
+ * @overlay: A #CoglOverlay to add
+ *
+ * Stacks @overlay above all the other overlays currently associated
+ * with the given @output.
+ *
+ * Since: 1.16
+ * Stability: unstable
+ */
+void
+cogl_output_append_overlay (CoglOutput *output,
+                            CoglOverlay *overlay);
+
+/**
+ * cogl_output_put_overlay_above:
+ * @output: a #CoglOutput
+ * @overlay: A #CoglOverlay to position
+ * @sibling: A #CoglOverlay to position @overlay above or %NULL
+ *
+ * Stacks @overlay above @sibling. If @sibling is %NULL then @overlay
+ * is stacked at the lowest position.
+ *
+ * Since: 1.16
+ * Stability: unstable
+ */
+void
+cogl_output_put_overlay_above (CoglOutput *output,
+                               CoglOverlay *overlay,
+                               CoglOverlay *sibling);
+
+/**
+ * cogl_output_put_overlay_below:
+ * @output: a #CoglOutput
+ * @overlay: A #CoglOverlay to position
+ * @sibling: A #CoglOverlay to position @overlay below or %NULL
+ *
+ * Stacks @overlay below @sibling. If @sibling is %NULL then @overlay
+ * is stacked at the highest position.
+ *
+ * Since: 1.16
+ * Stability: unstable
+ */
+void
+cogl_output_put_overlay_below (CoglOutput *output,
+                               CoglOverlay *overlay,
+                               CoglOverlay *sibling);
+
+/**
+ * cogl_output_remove_overlay:
+ * @output: a #CoglOutput
+ * @overlay: A #CoglOverlay to remove
+ *
+ * Removes @overlay from the given @output.
+ *
+ * Since: 1.16
+ * Stability: unstable
+ */
+void
+cogl_output_remove_overlay (CoglOutput *output,
+                            CoglOverlay *overlay);
+
+/**
+ * CoglOutputModeCallback:
+ * @overlay: The current overlay being iterated
+ * @user_data: The private data passed to
+ *             cogl_output_foreach_mode()
+ *
+ * A callback type for use with cogl_output_foreach_mode()
+ *
+ * Since: 1.16
+ * Stability: unstable
+ */
+typedef void (*CoglOutputModeCallback) (CoglMode *mode,
+                                        void *user_data);
+
+/**
+ * cogl_output_foreach_mode:
+ * @output: a #CoglOutput
+ * @callback: A #CoglOutputModeCallback to call for each mode.
+ * @user_data: A private pointer to pass to @callback
+ *
+ * Iterates through the possible #CoglMode<!-- -->'s usable with @output.
+ *
+ * Since: 1.16
+ * Stability: unstable
+ */
+void
+cogl_output_foreach_mode (CoglOutput *output,
+                          CoglOutputModeCallback callback,
+                          void *user_data);
+
+/**
+ * cogl_output_set_mode:
+ * @output: a #CoglOutput
+ * @mode: A #CoglMode
+ *
+ * Requests to set the given @mode on the given @output the next
+ * time output configurations are comitted via
+ * cogl_renderer_commit_outputs().
+ *
+ * <note>@mode should be a mode that was found via
+ * cogl_output_foreach_mode()</note>
+ *
+ * Since: 1.16
+ * Stability: unstable
+ */
+void
+cogl_output_set_mode (CoglOutput *output,
+                      CoglMode *mode);
+
+/**
+ * cogl_output_get_mode:
+ * @output: a #CoglOutput
+ *
+ * Queries the mode that's currently set on the given @output.
+ *
+ * <note>If the mode is set via cogl_output_set_mode() but
+ * cogl_renderer_commit_outputs() hasn't since been called then this
+ * will return the last set mode, which might not correspond to the
+ * outputs actual mode.</note>
+ *
+ * Since: 1.16
+ * Stability: unstable
+ */
+CoglMode *
+cogl_output_get_mode (CoglOutput *output);
+
+/**
+ * CoglDpmsMode:
+ * @COGL_DPMS_MODE_ON: Set when the display is use
+ * @COGL_DPMS_MODE_STANDBY: Uses less than 80% of the power compared
+ *                          to @COGL_DPMS_MODE_ON. Recovery time about
+ *                          1 second.
+ * @COGL_DPMS_MODE_SUSPEND: Uses less the 30W. Recovery time may be
+ *                          about 5 seconds.
+ * @COGL_DPMS_MODE_OFF: Uses less than 8W. Recovery time may be about
+ *                      20 seconds.
+ *
+ * Standard VESA Display Power Management modes that can be controlled
+ * for each #CoglOutput via cogl_output_set_dpms_mode().
+ *
+ * Since: 1.16
+ * Stability: unstable
+ */
+typedef enum _CoglDpmsMode
+{
+  COGL_DPMS_MODE_ON,
+  COGL_DPMS_MODE_STANDBY,
+  COGL_DPMS_MODE_SUSPEND,
+  COGL_DPMS_MODE_OFF,
+} CoglDpmsMode;
+
+/**
+ * cogl_output_set_dpms_mode:
+ * @output: a #CoglOutput
+ * @dpms_mode: A #CoglDpmsMode
+ *
+ * Requests to set the given @dpms_mode on the given @output the next
+ * time that cogl_renderer_commit_outputs() is called.
+ *
+ * This can be used to save power when a display is not in active
+ * use.
+ *
+ * Since: 1.16
+ * Stability: unstable
+ */
+void
+cogl_output_set_dpms_mode (CoglOutput *output,
+                           CoglDpmsMode dpms_mode);
+
+/**
+ * cogl_output_get_dpms_mode:
+ * @output: a #CoglOutput
+ *
+ * Queries the dpms mode last set on @output.
+ *
+ * <note>If the dpms mode has been set with
+ * cogl_output_set_dpms_mode() but cogl_renderer_commit_outputs()
+ * hasn't since been called then this will return the last set
+ * dpms mode that might not correspond to the actual state of
+ * @output.<note.
+ *
+ * Return value: The dpms mode set on @output.
+ *
+ * Since: 1.16
+ * Stability: unstable
+ */
+CoglDpmsMode
+cogl_output_get_dpms_mode (CoglOutput *output);
+
+/**
+ * CoglOutputOverlayCallback:
+ * @overlay: The current overlay being iterated
+ * @user_data: The private data passed to
+ *             cogl_output_foreach_overlay()
+ *
+ * A callback type for use with cogl_output_foreach_overlay()
+ *
+ * Since: 1.16
+ * Stability: unstable
+ */
+typedef void (*CoglOutputOverlayCallback) (CoglOverlay *overlay,
+                                           void *user_data);
+
+/**
+ * cogl_output_foreach_overlay:
+ * @output: a #CoglOutput
+ * @callback: A #CoglOutputOverlayCallback to call for each overlay.
+ * @user_data: A private pointer to pass to @callback
+ *
+ * Iterates through the current @output overlays, starting from
+ * overlay 0.
+ *
+ * Since: 1.16
+ * Stability: unstable
+ */
+void
+cogl_output_foreach_overlay (CoglOutput *output,
+                             CoglOutputOverlayCallback callback,
+                             void *user_data);
+
 COGL_END_DECLS
 
 #endif /* __COGL_OUTPUT_H */
diff --git a/cogl/cogl-overlay-private.h b/cogl/cogl-overlay-private.h
new file mode 100644
index 0000000..1943a9a
--- /dev/null
+++ b/cogl/cogl-overlay-private.h
@@ -0,0 +1,76 @@
+/*
+ * Cogl
+ *
+ * An object oriented GL/GLES Abstraction/Utility Layer
+ *
+ * Copyright (C) 2013 Intel Corporation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ *
+ */
+
+#ifndef _COGL_OVERLAY_PRIVATE_H_
+#define _COGL_OVERLAY_PRIVATE_H_
+
+#include <cogl/cogl-overlay.h>
+
+typedef enum _CoglOverlayChange
+{
+  COGL_OVERLAY_CHANGE_SOURCE = 1<<0,
+  COGL_OVERLAY_CHANGE_TRANSFORM = 1<<1,
+} CoglOverlayChange;
+
+typedef struct _CoglOverlayState
+{
+  /* NB: to avoid a circular reference we don't keep a reference
+   * here... */
+  CoglOutput *output;
+
+  /* Note: In the future we might support other sources, such as for
+   * video */
+  CoglOnscreen *onscreen_source;
+
+  /* XXX: src_x must be the first member for _cogl_overlay_equal() to
+   * work and all remaining members should be comparable via
+   * memcpy() */
+
+  /* What region of the source should be overlayed? */
+  int src_x;
+  int src_y;
+  int src_width;
+  int src_height;
+
+  int dst_x;
+  int dst_y;
+
+} CoglOverlayState;
+
+struct _CoglOverlay
+{
+  CoglObjectClass _parent;
+
+  CoglOverlayState *state;
+  CoglOverlayState *pending;
+};
+
+void
+_cogl_overlay_update_state (CoglOverlay *overlay);
+
+CoglBool
+_cogl_overlay_equal (CoglOverlay *overlay0,
+                     CoglOverlay *overlay1);
+
+#endif /* _COGL_OVERLAY_PRIVATE_H_ */
diff --git a/cogl/cogl-overlay.c b/cogl/cogl-overlay.c
new file mode 100644
index 0000000..0abe621
--- /dev/null
+++ b/cogl/cogl-overlay.c
@@ -0,0 +1,249 @@
+/*
+ * Cogl
+ *
+ * An object oriented GL/GLES Abstraction/Utility Layer
+ *
+ * Copyright (C) 2013 Intel Corporation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ *
+ */
+
+#include <config.h>
+
+#include <string.h>
+
+#include "cogl-object-private.h"
+#include "cogl-overlay-private.h"
+#include "cogl-onscreen.h"
+
+static void _cogl_overlay_free (CoglOverlay *overlay);
+
+COGL_OBJECT_DEFINE (Overlay, overlay);
+
+static void
+free_overlay_state (CoglOverlayState *state)
+{
+  if (state->onscreen_source)
+    cogl_object_unref (state->onscreen_source);
+
+  g_slice_free (CoglOverlayState, state);
+}
+
+static void
+ensure_pending_state (CoglOverlay *overlay)
+{
+  CoglOverlayState *state = overlay->state;
+  CoglOverlayState *pending;
+
+  if (state != overlay->pending)
+    return;
+
+  pending = g_slice_dup (CoglOverlayState, state);
+
+  pending->onscreen_source = state->onscreen_source;
+  if (pending->onscreen_source)
+    cogl_object_ref (pending->onscreen_source);
+
+  overlay->pending = pending;
+}
+
+static void
+_cogl_overlay_free (CoglOverlay *overlay)
+{
+  if (overlay->pending != overlay->state)
+    free_overlay_state (overlay->pending);
+
+  free_overlay_state (overlay->state);
+
+  g_slice_free (CoglOverlay, overlay);
+}
+
+CoglOverlay *
+cogl_overlay_new (CoglOutput *output)
+{
+  CoglOverlay *overlay = g_slice_new0 (CoglOverlay);
+
+  overlay->state = g_slice_new0 (CoglOverlayState);
+
+  /* Note: to avoid a circular reference we don't reference
+   * the output here. */
+  overlay->state->output = output;
+
+  overlay->pending = overlay->state;
+
+  overlay = _cogl_overlay_object_new (overlay);
+
+  cogl_output_append_overlay (output, overlay);
+
+  return overlay;
+}
+
+void
+_cogl_overlay_update_state (CoglOverlay *overlay)
+{
+  if (overlay->pending == overlay->state)
+    return;
+
+  free_overlay_state (overlay->state);
+
+  overlay->state = overlay->pending;
+}
+
+void
+cogl_overlay_set_onscreen_source (CoglOverlay *overlay,
+                                  CoglOnscreen *onscreen_source)
+{
+  if (overlay->pending->onscreen_source == onscreen_source)
+    return;
+
+  ensure_pending_state (overlay);
+
+  if (overlay->pending->onscreen_source)
+    cogl_object_unref (overlay->pending->onscreen_source);
+
+  overlay->pending->onscreen_source = cogl_object_ref (onscreen_source);
+}
+
+CoglOnscreen *
+cogl_overlay_get_onscreen_source (CoglOverlay *overlay)
+{
+  return overlay->pending->onscreen_source;
+}
+
+int
+cogl_overlay_get_source_x (CoglOverlay *overlay)
+{
+  return overlay->pending->src_x;
+}
+
+void
+cogl_overlay_set_source_x (CoglOverlay *overlay, int src_x)
+{
+  if (overlay->pending->src_x == src_x)
+    return;
+
+  ensure_pending_state (overlay);
+
+  overlay->pending->src_x = src_x;
+}
+
+int
+cogl_overlay_get_source_y (CoglOverlay *overlay)
+{
+  return overlay->pending->src_y;
+}
+
+void
+cogl_overlay_set_source_y (CoglOverlay *overlay, int src_y)
+{
+  if (overlay->pending->src_y == src_y)
+    return;
+
+  ensure_pending_state (overlay);
+
+  overlay->pending->src_y = src_y;
+}
+
+int
+cogl_overlay_get_source_width (CoglOverlay *overlay)
+{
+  return overlay->pending->src_width;
+}
+
+void
+cogl_overlay_set_source_width (CoglOverlay *overlay, int src_width)
+{
+  if (overlay->pending->src_width == src_width)
+    return;
+
+  ensure_pending_state (overlay);
+
+  overlay->pending->src_width = src_width;
+}
+
+int
+cogl_overlay_get_source_height (CoglOverlay *overlay)
+{
+  return overlay->pending->src_height;
+}
+
+void
+cogl_overlay_set_source_height (CoglOverlay *overlay, int src_height)
+{
+  if (overlay->pending->src_height == src_height)
+    return;
+
+  ensure_pending_state (overlay);
+
+  overlay->pending->src_height = src_height;
+}
+
+int
+cogl_overlay_get_x (CoglOverlay *overlay)
+{
+  return overlay->pending->dst_x;
+}
+
+void
+cogl_overlay_set_x (CoglOverlay *overlay, int dst_x)
+{
+  if (overlay->pending->dst_x == dst_x)
+    return;
+
+  ensure_pending_state (overlay);
+
+  overlay->pending->dst_x = dst_x;
+}
+
+int
+cogl_overlay_get_y (CoglOverlay *overlay)
+{
+  return overlay->pending->dst_y;
+}
+
+void
+cogl_overlay_set_y (CoglOverlay *overlay, int dst_y)
+{
+  if (overlay->pending->dst_y == dst_y)
+    return;
+
+  ensure_pending_state (overlay);
+
+  overlay->pending->dst_y = dst_y;
+}
+
+CoglBool
+_cogl_overlay_equal (CoglOverlay *overlay0,
+                     CoglOverlay *overlay1)
+{
+  if (overlay0 == overlay1)
+    return TRUE;
+
+  if (memcmp ((const char *)overlay0->pending +
+              G_STRUCT_OFFSET (CoglOverlayState, src_x),
+              (const char *)overlay1->pending +
+              G_STRUCT_OFFSET (CoglOverlayState, src_x),
+              sizeof (CoglOverlayState) -
+              G_STRUCT_OFFSET (CoglOverlayState, src_x)) != 0)
+    return FALSE;
+
+  if (overlay0->pending->onscreen_source !=
+      overlay1->pending->onscreen_source)
+    return FALSE;
+
+  return TRUE;
+}
diff --git a/cogl/cogl-overlay.h b/cogl/cogl-overlay.h
new file mode 100644
index 0000000..1a3c793
--- /dev/null
+++ b/cogl/cogl-overlay.h
@@ -0,0 +1,114 @@
+/*
+ * Cogl
+ *
+ * An object oriented GL/GLES Abstraction/Utility Layer
+ *
+ * Copyright (C) 2013 Intel Corporation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ *
+ */
+
+#ifndef _COGL_OVERLAY_H_
+#define _COGL_OVERLAY_H_
+
+/* We forward declare the CoglOverlay type here to avoid some
+ * circular dependency issues with the following headers.
+ */
+typedef struct _CoglOverlay CoglOverlay;
+
+#include <cogl/cogl-onscreen.h>
+#include <cogl/cogl-output.h>
+
+COGL_BEGIN_DECLS
+
+/**
+ * SECTION:cogl-overlay
+ * @short_description: A single overlay to composite on a #CoglOutput
+ *
+ * A #CoglOverlay represents a single image source to composite on a
+ * given #CoglOutput. Depending on the capabilities of the output an
+ * overlay can - for example - be offset and scaled within that
+ * output.
+ *
+ * If an output can be composited with overlyas using dedicated,
+ * fixed-function hardware then it can be more power effecient than
+ * compositing with the more general purpose GPU pipeline.
+ */
+
+CoglBool cogl_is_overlay (void *object);
+
+/**
+ * cogl_overlay_new:
+ * @output: A #CoglOutput pointer
+ *
+ * Creates a new #CoglOverlay that is associated with the given @output,
+ * positioned on top of any existing overlays.
+ *
+ * Return value: A newly allocated #CoglOverlay.
+ *
+ * Since: 1.16
+ * Stability: unstable
+ */
+CoglOverlay *
+cogl_overlay_new (CoglOutput *output);
+
+void
+cogl_overlay_set_onscreen_source (CoglOverlay *overlay,
+                                  CoglOnscreen *onscreen_source);
+
+CoglOnscreen *
+cogl_overlay_get_onscreen_source (CoglOverlay *overlay);
+
+int
+cogl_overlay_get_source_x (CoglOverlay *overlay);
+
+void
+cogl_overlay_set_source_x (CoglOverlay *overlay, int src_x);
+
+int
+cogl_overlay_get_source_y (CoglOverlay *overlay);
+
+void
+cogl_overlay_set_source_y (CoglOverlay *overlay, int src_y);
+
+int
+cogl_overlay_get_source_width (CoglOverlay *overlay);
+
+void
+cogl_overlay_set_source_width (CoglOverlay *overlay, int src_width);
+
+int
+cogl_overlay_get_source_height (CoglOverlay *overlay);
+
+void
+cogl_overlay_set_source_height (CoglOverlay *overlay, int src_height);
+
+int
+cogl_overlay_get_x (CoglOverlay *overlay);
+
+void
+cogl_overlay_set_x (CoglOverlay *overlay, int dst_x);
+
+int
+cogl_overlay_get_y (CoglOverlay *overlay);
+
+void
+cogl_overlay_set_y (CoglOverlay *overlay, int dst_y);
+
+COGL_END_DECLS
+
+#endif /* _COGL_OVERLAY_H_ */
diff --git a/cogl/cogl-renderer-private.h b/cogl/cogl-renderer-private.h
index 8b8d2bf..7bca274 100644
--- a/cogl/cogl-renderer-private.h
+++ b/cogl/cogl-renderer-private.h
@@ -117,4 +117,7 @@ _cogl_renderer_get_proc_address (CoglRenderer *renderer,
                                  const char *name,
                                  CoglBool in_core);
 
+void
+_cogl_renderer_notify_outputs_changed (CoglRenderer *renderer);
+
 #endif /* __COGL_RENDERER_PRIVATE_H */
diff --git a/cogl/cogl-renderer.c b/cogl/cogl-renderer.c
index 2564c9d..ab9b37b 100644
--- a/cogl/cogl-renderer.c
+++ b/cogl/cogl-renderer.c
@@ -44,6 +44,7 @@
 #include "cogl-winsys-stub-private.h"
 #include "cogl-config-private.h"
 #include "cogl-error-private.h"
+#include "cogl-output-private.h"
 
 #ifdef COGL_HAS_EGL_PLATFORM_XLIB_SUPPORT
 #include "cogl-winsys-egl-x11-private.h"
@@ -111,7 +112,7 @@ static CoglDriverDescription _cogl_drivers[] =
   {
     COGL_DRIVER_GL3,
     "gl3",
-    0,
+    COGL_RENDERER_CONSTRAINT_USES_KMS,
     COGL_PRIVATE_FEATURE_ANY_GL |
       COGL_PRIVATE_FEATURE_GL_PROGRAMMABLE,
     &_cogl_driver_gl,
@@ -121,7 +122,7 @@ static CoglDriverDescription _cogl_drivers[] =
   {
     COGL_DRIVER_GL,
     "gl",
-    0,
+    COGL_RENDERER_CONSTRAINT_USES_KMS,
     COGL_PRIVATE_FEATURE_ANY_GL |
       COGL_PRIVATE_FEATURE_GL_FIXED |
       COGL_PRIVATE_FEATURE_GL_PROGRAMMABLE,
@@ -134,7 +135,8 @@ static CoglDriverDescription _cogl_drivers[] =
   {
     COGL_DRIVER_GLES2,
     "gles2",
-    COGL_RENDERER_CONSTRAINT_SUPPORTS_COGL_GLES2,
+    COGL_RENDERER_CONSTRAINT_SUPPORTS_COGL_GLES2 |
+      COGL_RENDERER_CONSTRAINT_USES_KMS,
     COGL_PRIVATE_FEATURE_ANY_GL |
       COGL_PRIVATE_FEATURE_GL_EMBEDDED |
       COGL_PRIVATE_FEATURE_GL_PROGRAMMABLE,
@@ -147,7 +149,7 @@ static CoglDriverDescription _cogl_drivers[] =
   {
     COGL_DRIVER_GLES1,
     "gles1",
-    0,
+    COGL_RENDERER_CONSTRAINT_USES_KMS,
     COGL_PRIVATE_FEATURE_ANY_GL |
       COGL_PRIVATE_FEATURE_GL_EMBEDDED |
       COGL_PRIVATE_FEATURE_GL_FIXED,
@@ -173,7 +175,7 @@ static CoglDriverDescription _cogl_drivers[] =
   {
     COGL_DRIVER_NOP,
     "nop",
-    0, /* constraints satisfied */
+    COGL_RENDERER_CONSTRAINT_USES_KMS, /* constraints satisfied */
     0, /* flags */
     &_cogl_driver_nop,
     NULL, /* texture driver */
@@ -843,3 +845,80 @@ cogl_renderer_foreach_output (CoglRenderer *renderer,
   for (l = renderer->outputs; l; l = l->next)
     callback (l->data, user_data);
 }
+
+CoglBool
+cogl_renderer_commit_outputs (CoglRenderer *renderer,
+                              CoglError **error)
+{
+  CoglWinsysVtable *winsys;
+
+  _COGL_RETURN_IF_FAIL (!renderer->connected);
+
+  winsys = renderer->winsys;
+
+  if (winsys->commit_outputs)
+    return winsys->commit_outputs (renderer, error);
+  else
+    {
+      _cogl_set_error (error,
+                       COGL_SYSTEM_ERROR,
+                       COGL_SYSTEM_ERROR_UNSUPPORTED,
+                       "The current Cogl window system doesn't support "
+                       "display configuration");
+      return FALSE;
+    }
+}
+
+void
+_cogl_renderer_notify_outputs_changed (CoglRenderer *renderer)
+{
+  const CoglWinsysVtable *winsys = renderer->winsys_vtable;
+  GList *l;
+
+  COGL_NOTE (WINSYS, "Outputs changed:");
+
+  for (l = renderer->outputs; l; l = l->next)
+    {
+      CoglOutput *output = l->data;
+      const char *subpixel_string;
+
+      switch (output->state->subpixel_order)
+        {
+        case COGL_SUBPIXEL_ORDER_UNKNOWN:
+        default:
+          subpixel_string = "unknown";
+          break;
+        case COGL_SUBPIXEL_ORDER_NONE:
+          subpixel_string = "none";
+          break;
+        case COGL_SUBPIXEL_ORDER_HORIZONTAL_RGB:
+          subpixel_string = "horizontal_rgb";
+          break;
+        case COGL_SUBPIXEL_ORDER_HORIZONTAL_BGR:
+          subpixel_string = "horizontal_bgr";
+          break;
+        case COGL_SUBPIXEL_ORDER_VERTICAL_RGB:
+          subpixel_string = "vertical_rgb";
+          break;
+        case COGL_SUBPIXEL_ORDER_VERTICAL_BGR:
+          subpixel_string = "vertical_bgr";
+          break;
+        }
+
+      COGL_NOTE (WINSYS,
+                 " %10s: +%d+%dx%dx%d mm=%dx%d dpi=%.1fx%.1f "
+                 "subpixel_order=%s refresh_rate=%.3f",
+                 output->state->name,
+                 output->state->x, output->state->y,
+                 cogl_output_get_width (output),
+                 cogl_output_get_height (output),
+                 output->state->mm_width, output->state->mm_height,
+                 cogl_output_get_width (output) / (output->state->mm_width / 25.4),
+                 cogl_output_get_height (output) / (output->state->mm_height / 25.4),
+                 subpixel_string,
+                 cogl_output_get_refresh_rate (output));
+    }
+
+  if (winsys->renderer_outputs_changed)
+    winsys->renderer_outputs_changed (renderer);
+}
diff --git a/cogl/cogl-renderer.h b/cogl/cogl-renderer.h
index c7e6a5a..1f81faa 100644
--- a/cogl/cogl-renderer.h
+++ b/cogl/cogl-renderer.h
@@ -28,6 +28,11 @@
 #ifndef __COGL_RENDERER_H__
 #define __COGL_RENDERER_H__
 
+/* We forward declare the CoglRenderer type here to avoid some
+ * circular dependency issues with the following headers.
+ */
+typedef struct _CoglRenderer CoglRenderer;
+
 #include <cogl/cogl-types.h>
 #include <cogl/cogl-onscreen-template.h>
 #include <cogl/cogl-error.h>
@@ -78,8 +83,6 @@ COGL_BEGIN_DECLS
 uint32_t
 cogl_renderer_error_domain (void);
 
-typedef struct _CoglRenderer CoglRenderer;
-
 /**
  * cogl_is_renderer:
  * @object: A #CoglObject pointer
@@ -421,6 +424,26 @@ cogl_renderer_foreach_output (CoglRenderer *renderer,
                               CoglOutputCallback callback,
                               void *user_data);
 
+/**
+ * cogl_renderer_commit_outputs:
+ * @renderer: A connected #CoglRenderer
+ *
+ * Attempts to apply all outstanding #CoglOutput changes to the
+ * underlying hardware. If for some reason it is not possible to
+ * support the whole configuration then all outstanding changes
+ * will be reverted and a #CoglError will be returned.
+ *
+ * XXX: Instead of reverting the outstanding changes should we
+ * instead have a separate api for manually reverting outstanding
+ * changes?
+ *
+ * Since: 1.16
+ * Stability: Unstable
+ */
+CoglBool
+cogl_renderer_commit_outputs (CoglRenderer *renderer,
+                              CoglError **error);
+
 COGL_END_DECLS
 
 #endif /* __COGL_RENDERER_H__ */
diff --git a/cogl/cogl-xlib-renderer.c b/cogl/cogl-xlib-renderer.c
index 4d332fa..99f1f89 100644
--- a/cogl/cogl-xlib-renderer.c
+++ b/cogl/cogl-xlib-renderer.c
@@ -39,6 +39,7 @@
 #include "cogl-winsys-private.h"
 #include "cogl-error-private.h"
 #include "cogl-poll-private.h"
+#include "cogl-mode-private.h"
 
 #include <X11/Xlib.h>
 #include <X11/extensions/Xdamage.h>
@@ -198,7 +199,7 @@ static int
 compare_outputs (CoglOutput *a,
                  CoglOutput *b)
 {
-  return strcmp (a->name, b->name);
+  return strcmp (a->pending->name, b->pending->name);
 }
 
 #define CSO(X) COGL_SUBPIXEL_ORDER_ ## X
@@ -243,8 +244,9 @@ update_outputs (CoglRenderer *renderer,
     {
       XRRCrtcInfo *crtc_info = NULL;
       XRROutputInfo *output_info = NULL;
+      GList *new_modes = NULL;
+      CoglMode *current_mode = NULL;
       CoglOutput *output;
-      float refresh_rate = 0;
       int j;
 
       crtc_info = XRRGetCrtcInfo (xlib_renderer->xdpy,
@@ -260,10 +262,17 @@ update_outputs (CoglRenderer *renderer,
 
       for (j = 0; j < resources->nmode; j++)
         {
-          if (resources->modes[j].id == crtc_info->mode)
-            refresh_rate = (resources->modes[j].dotClock /
-                            ((float)resources->modes[j].hTotal *
-                             resources->modes[j].vTotal));
+          XRRModeInfo *info = &resources->modes[j];
+
+          CoglMode *mode = _cogl_mode_new (info->name);
+          mode->width = info->width;
+          mode->height = info->height;
+          mode->refresh_rate =
+            (info->dotClock / ((float)info->hTotal * info->vTotal));
+          new_modes = g_list_prepend (new_modes, mode);
+
+          if (info->id == crtc_info->mode)
+            current_mode = mode;
         }
 
       output_info = XRRGetOutputInfo (xlib_renderer->xdpy,
@@ -276,53 +285,51 @@ update_outputs (CoglRenderer *renderer,
         }
 
       output = _cogl_output_new (output_info->name);
-      output->x = crtc_info->x;
-      output->y = crtc_info->y;
-      output->width = crtc_info->width;
-      output->height = crtc_info->height;
+      output->modes = g_list_reverse (new_modes);
+      output->state->mode = cogl_object_ref (current_mode);
+      output->state->x = crtc_info->x;
+      output->state->y = crtc_info->y;
       if ((crtc_info->rotation & (RR_Rotate_90 | RR_Rotate_270)) != 0)
         {
-          output->mm_width = output_info->mm_height;
-          output->mm_height = output_info->mm_width;
+          output->state->mm_width = output_info->mm_height;
+          output->state->mm_height = output_info->mm_width;
         }
       else
         {
-          output->mm_width = output_info->mm_width;
-          output->mm_height = output_info->mm_height;
+          output->state->mm_width = output_info->mm_width;
+          output->state->mm_height = output_info->mm_height;
         }
 
-      output->refresh_rate = refresh_rate;
-
       switch (output_info->subpixel_order)
         {
         case SubPixelUnknown:
         default:
-          output->subpixel_order = COGL_SUBPIXEL_ORDER_UNKNOWN;
+          output->state->subpixel_order = COGL_SUBPIXEL_ORDER_UNKNOWN;
           break;
         case SubPixelNone:
-          output->subpixel_order = COGL_SUBPIXEL_ORDER_NONE;
+          output->state->subpixel_order = COGL_SUBPIXEL_ORDER_NONE;
           break;
         case SubPixelHorizontalRGB:
-          output->subpixel_order = COGL_SUBPIXEL_ORDER_HORIZONTAL_RGB;
+          output->state->subpixel_order = COGL_SUBPIXEL_ORDER_HORIZONTAL_RGB;
           break;
         case SubPixelHorizontalBGR:
-          output->subpixel_order = COGL_SUBPIXEL_ORDER_HORIZONTAL_BGR;
+          output->state->subpixel_order = COGL_SUBPIXEL_ORDER_HORIZONTAL_BGR;
           break;
         case SubPixelVerticalRGB:
-          output->subpixel_order = COGL_SUBPIXEL_ORDER_VERTICAL_RGB;
+          output->state->subpixel_order = COGL_SUBPIXEL_ORDER_VERTICAL_RGB;
           break;
         case SubPixelVerticalBGR:
-          output->subpixel_order = COGL_SUBPIXEL_ORDER_VERTICAL_BGR;
+          output->state->subpixel_order = COGL_SUBPIXEL_ORDER_VERTICAL_BGR;
           break;
         }
 
-      output->subpixel_order = COGL_SUBPIXEL_ORDER_HORIZONTAL_RGB;
+      output->state->subpixel_order = COGL_SUBPIXEL_ORDER_HORIZONTAL_RGB;
 
       /* Handle the effect of rotation and reflection on subpixel order (ugh) */
       for (j = 0; j < 6; j++)
         {
           if ((crtc_info->rotation & (1 << j)) != 0)
-            output->subpixel_order = subpixel_map[j][output->subpixel_order];
+            output->state->subpixel_order = subpixel_map[j][output->state->subpixel_order];
         }
 
       new_outputs = g_list_prepend (new_outputs, output);
@@ -350,6 +357,14 @@ update_outputs (CoglRenderer *renderer,
           CoglOutput *output_l = l ? (CoglOutput *)l->data : NULL;
           CoglOutput *output_m = m ? (CoglOutput *)m->data : NULL;
 
+          if (output_m && output_m->pending != output_m->state)
+            {
+              g_warning ("Unexpected pending state associated with CoglOutput "
+                         "%s while processing events. Pending output state "
+                         "shouldn't be maintained between mainloop "
+                         "iterations\n", output_m->state->name);
+            }
+
           if (l && m)
             cmp = compare_outputs (output_l, output_m);
           else if (l)
@@ -361,7 +376,7 @@ update_outputs (CoglRenderer *renderer,
             {
               GList *m_next = m->next;
 
-              if (!_cogl_output_values_equal (output_l, output_m))
+              if (!_cogl_output_equal (output_l, output_m))
                 {
                   renderer->outputs = g_list_remove_link (renderer->outputs, m);
                   renderer->outputs = g_list_insert_before (renderer->outputs,
@@ -396,57 +411,7 @@ update_outputs (CoglRenderer *renderer,
   _cogl_xlib_renderer_untrap_errors (renderer, &state);
 
   if (changed)
-    {
-      const CoglWinsysVtable *winsys = renderer->winsys_vtable;
-
-      if (notify)
-        COGL_NOTE (WINSYS, "Outputs changed:");
-      else
-        COGL_NOTE (WINSYS, "Outputs:");
-
-      for (l = renderer->outputs; l; l = l->next)
-        {
-          CoglOutput *output = l->data;
-          const char *subpixel_string;
-
-          switch (output->subpixel_order)
-            {
-            case COGL_SUBPIXEL_ORDER_UNKNOWN:
-            default:
-              subpixel_string = "unknown";
-              break;
-            case COGL_SUBPIXEL_ORDER_NONE:
-              subpixel_string = "none";
-              break;
-            case COGL_SUBPIXEL_ORDER_HORIZONTAL_RGB:
-              subpixel_string = "horizontal_rgb";
-              break;
-            case COGL_SUBPIXEL_ORDER_HORIZONTAL_BGR:
-              subpixel_string = "horizontal_bgr";
-              break;
-            case COGL_SUBPIXEL_ORDER_VERTICAL_RGB:
-              subpixel_string = "vertical_rgb";
-              break;
-            case COGL_SUBPIXEL_ORDER_VERTICAL_BGR:
-              subpixel_string = "vertical_bgr";
-              break;
-            }
-
-          COGL_NOTE (WINSYS,
-                     " %10s: +%d+%dx%dx%d mm=%dx%d dpi=%.1fx%.1f "
-                     "subpixel_order=%s refresh_rate=%.3f",
-                     output->name,
-                     output->x, output->y, output->width, output->height,
-                     output->mm_width, output->mm_height,
-                     output->width / (output->mm_width / 25.4),
-                     output->height / (output->mm_height / 25.4),
-                     subpixel_string,
-                     output->refresh_rate);
-        }
-
-      if (notify && winsys->renderer_outputs_changed != NULL)
-        winsys->renderer_outputs_changed (renderer);
-    }
+    _cogl_renderer_notify_outputs_changed (renderer);
 }
 
 static CoglFilterReturn
@@ -634,8 +599,10 @@ _cogl_xlib_renderer_output_for_rectangle (CoglRenderer *renderer,
   for (l = renderer->outputs; l; l = l->next)
     {
       CoglOutput *output = l->data;
-      int xb1 = output->x, xb2 = output->x + output->width;
-      int yb1 = output->y, yb2 = output->y + output->height;
+      int xb1 = output->state->x;
+      int xb2 = output->state->x + cogl_output_get_width (output);
+      int yb1 = output->state->y;
+      int yb2 = output->state->y + cogl_output_get_height (output);
 
       int overlap_x = MIN(xa2, xb2) - MAX(xa1, xb1);
       int overlap_y = MIN(ya2, yb2) - MAX(ya1, yb1);
diff --git a/cogl/cogl.h b/cogl/cogl.h
index c5ad661..5ec316b 100644
--- a/cogl/cogl.h
+++ b/cogl/cogl.h
@@ -46,6 +46,7 @@
 #endif
 
 #include <cogl/cogl-renderer.h>
+#include <cogl/cogl-overlay.h>
 #include <cogl/cogl-output.h>
 #include <cogl/cogl-display.h>
 #include <cogl/cogl-context.h>
diff --git a/cogl/winsys/cogl-winsys-egl-kms.c b/cogl/winsys/cogl-winsys-egl-kms.c
index a81237d..260342f 100644
--- a/cogl/winsys/cogl-winsys-egl-kms.c
+++ b/cogl/winsys/cogl-winsys-egl-kms.c
@@ -334,7 +334,6 @@ update_outputs (CoglRenderer *renderer)
     {
       CoglOutput *output = NULL;
       CoglOutputKMS *kms_output;
-      const char *type_name;
       GList *l;
 
       drmModeConnector *connector =
@@ -356,7 +355,7 @@ update_outputs (CoglRenderer *renderer)
         }
 
       /* If we already have a CoglOutput corresponding to this
-       * connector id then we can simply keep it and move on... */
+       * connector id then we keep it... */
       for (l = renderer->outputs; l; l = l->next)
         {
           CoglOutput *existing_output = l->data;
@@ -366,53 +365,82 @@ update_outputs (CoglRenderer *renderer)
             {
               renderer->outputs = g_list_delete_link (renderer->outputs, l);
               output = existing_output;
+              kms_output = output->winsys;
+
+              if (output->pending != output->state)
+                {
+                  g_warning ("Unexpected pending state associated with CoglOutput "
+                             "%s while processing events. Pending output state "
+                             "shouldn't be maintained between mainloop "
+                             "iterations\n", output->state->name);
+                }
+
+              break;
             }
         }
 
-      if (output)
+      if (!output)
         {
-          new_outputs = g_list_prepend (new_outputs, output);
-          drmModeFreeConnector (connector);
-          continue;
-        }
+          const char *type_name;
 
-      if (connector->connector_type < G_N_ELEMENTS (kms_connector_types))
-        type_name = kms_connector_types[connector->connector_type];
-      else
-        type_name = kms_connector_types[0];
+          if (connector->connector_type < G_N_ELEMENTS (kms_connector_types))
+            type_name = kms_connector_types[connector->connector_type];
+          else
+            type_name = kms_connector_types[0];
 
-      output = _cogl_output_new (type_name);
+          output = _cogl_output_new (type_name);
 
-      kms_output = g_slice_new0 (CoglOutputKMS);
-      kms_output->connector_id = connector->connector_id;
-      kms_output->connector = connector;
+          kms_output = g_slice_new0 (CoglOutputKMS);
+          kms_output->connector_id = connector->connector_id;
+          kms_output->connector = connector;
+
+          _cogl_output_set_winsys_data (output,
+                                        kms_output,
+                                        kms_output_destroy_cb);
+        }
+
+      for (j = 0; j < connector->count_modes; j++)
+        {
+          drmModeModeInfo *info = &connector->modes[j];
+
+          CoglMode *mode = _cogl_mode_new (info->name);
+          mode->width = info->hdisplay;
+          mode->height = info->vdisplay;
+          mode->refresh_rate =
+            (info->clock / ((float)info->htotal * info->vtotal));
+          new_modes = g_list_prepend (new_modes, mode);
+        }
+
+      if (output->modes)
+        g_list_free_full (output->modes, cogl_object_unref);
+      output->modes = g_list_reverse (new_modes);
 
       /* We can't determinine anything about the relative position
        * of the outputs... */
-      output->x = output->y = 0;
+      output->state->x = output->state->y = 0;
 
-      output->mm_width = connector->mmWidth;
-      output->mm_height = connector->mmHeight;
+      output->state->mm_width = connector->mmWidth;
+      output->state->mm_height = connector->mmHeight;
 
       switch (connector->subpixel)
         {
         case DRM_MODE_SUBPIXEL_UNKNOWN:
-          output->subpixel_order = COGL_SUBPIXEL_ORDER_UNKNOWN;
+          output->state->subpixel_order = COGL_SUBPIXEL_ORDER_UNKNOWN;
           break;
         case DRM_MODE_SUBPIXEL_HORIZONTAL_RGB:
-          output->subpixel_order = COGL_SUBPIXEL_ORDER_HORIZONTAL_RGB;
+          output->state->subpixel_order = COGL_SUBPIXEL_ORDER_HORIZONTAL_RGB;
           break;
         case DRM_MODE_SUBPIXEL_HORIZONTAL_BGR:
-          output->subpixel_order = COGL_SUBPIXEL_ORDER_HORIZONTAL_BGR;
+          output->state->subpixel_order = COGL_SUBPIXEL_ORDER_HORIZONTAL_BGR;
           break;
         case DRM_MODE_SUBPIXEL_VERTICAL_RGB:
-          output->subpixel_order = COGL_SUBPIXEL_ORDER_VERTICAL_RGB;
+          output->state->subpixel_order = COGL_SUBPIXEL_ORDER_VERTICAL_RGB;
           break;
         case DRM_MODE_SUBPIXEL_VERTICAL_BGR:
-          output->subpixel_order = COGL_SUBPIXEL_ORDER_VERTICAL_BGR;
+          output->state->subpixel_order = COGL_SUBPIXEL_ORDER_VERTICAL_BGR;
           break;
         case DRM_MODE_SUBPIXEL_NONE:
-          output->subpixel_order = COGL_SUBPIXEL_ORDER_NONE;
+          output->state->subpixel_order = COGL_SUBPIXEL_ORDER_NONE;
           break;
         }
 
@@ -439,29 +467,19 @@ update_outputs (CoglRenderer *renderer)
             }
         }
 
-      if (kms_output->saved_crtc)
-        {
-          output->width = kms_output->saved_crtc->width;
-          output->height = kms_output->saved_crtc->height;
+      if (output->state->mode)
+        cogl_object_unref (output->state->mode);
+      output->state->mode = NULL;
 
-          if (kms_output->saved_crtc->mode_valid)
-            {
-              drmModeModeInfo *mode = &kms_output->saved_crtc->mode;
-              output->refresh_rate = mode->vrefresh;
-            }
-        }
-      else
+      if (kms_output->saved_crtc &&
+          kms_output->saved_crtc->mode_valid)
         {
-          /* If there is no encoder associated with the connector then
-           * there is no crtc mode and so there's currently no basis
-           * to specify a width/height */
-          output->width = 0;
-          output->height = 0;
-        }
+          output->state->mode =
+            find_mode (output->modes, kms_output->saved_crtc->mode.name);
+          cogl_object_ref (output->state->mode);
 
-      _cogl_output_set_winsys_data (output,
-                                    kms_output,
-                                    kms_output_destroy_cb);
+          g_warn_if_fail (output->state->mode);
+        }
 
       new_outputs = g_list_prepend (new_outputs, output);
     }
@@ -476,6 +494,8 @@ update_outputs (CoglRenderer *renderer)
   renderer->outputs = new_outputs;
 
   drmModeFreeResources (resources);
+
+  _cogl_renderer_notify_outputs_changed (renderer);
 }
 
 static void
@@ -1242,6 +1262,419 @@ _cogl_winsys_onscreen_deinit (CoglOnscreen *onscreen)
   onscreen->winsys = NULL;
 }
 
+static drmModeProperty *
+get_connector_property (int fd,
+                        drmModeConnector *connector,
+                        const char *name)
+{
+  drmModeProperty *property;
+  int i;
+
+  for (i = 0; i < connector->count_props; i++)
+    {
+      property = drmModeGetProperty (fd, connector->props[i]);
+      if (!property)
+        continue;
+
+      if (strcmp (property->name, name) == 0)
+        return props;
+
+      drmModeFreeProperty (property);
+    }
+
+  return NULL;
+}
+
+/* XXX: NB: Don't assume that the output->state is a reliable cache
+ * of the real hardware state such that state changes can be avoided
+ * by looking for NOP changes between ->state and ->pending since we
+ * sometimes have to deal with the display being changed behind our
+ * back.
+ *
+ * TODO: Support incremental updates in certain cases
+ *
+ * XXX: The corner cases to consider...
+ *
+ * Q: How do we _commit an overlay configuration?
+ * A: During a _commit we always check that each overlay has
+ *    an associated framebuffer source that has a valid
+ *    ->next_bo or ->current_bo (otherwise we report an error)
+ *    since we can't set a mode without a framebuffer and we
+ *    don't want to be automatically allocating place holder
+ *    framebuffers in corner cases or displaying undefined
+ *    buffer contents.
+ *
+ *    If we need to call drmModeSetCrtc() we will pass ->next_bo
+ *    if available, otherwise ->current_bo.
+ *
+ *    We always assume there could be outstanding rendering
+ *    associated with a bo when calling drmModeSetCrtc() and so
+ *    we explicitly synchronize with the GPU to make sure all
+ *    rendering to the buffer is complete first.
+ *
+ *    If we are using ->next_bo that implies there is currently
+ *    a pending flip that hasn't completed. We should mark the
+ *    pending flip as a "stale" flip by incrementing
+ *    ->stale_flips++. Later when we reach page_flip_handler()
+ *    we will check the ->stale_flips counter before
+ *    decrementing it and whenever it is set we should only make
+ *    sure to issue any necessary _FRAME_SYNC events but we
+ *    should avoid making any updates to ->current_bo/->next_bo
+ *    pointers.
+ *
+ *XXX: why does stale_flips need to be a counter instead of just
+ *     a boolean
+ *
+ *    After successfully calling drmModeSetCrtc() we insert a
+ *    reference to the overlay in kms_onscreen->overlays.
+ *
+ * Q: what if there is an error when calling drmModeSetCrtc?
+ * A: We roll-back the whole configuration
+ *
+ * Q: What are the semantics for calling drmModeSetCrtc while
+ *    there are pending page flips?
+ * A: Looking at the intel drm driver it looks like setting
+ *    a mode starts by disabling the crtc, which involves
+ *    waiting for pending flips so I think we can assume
+ *    page flip events will *always* be delivered by drm.
+ *
+ * Q: How do we deal with roll-back when this is the first
+ *    configuration to be committed?
+ * A: We should be assuming that update_outputs has been
+ *    called before any configuration so we should know the
+ *    previous state. XXX: what about previous framebuffer
+ *    state? It seems likely that we'll need to special case
+ *    restoring a saved state where the saved drmModeFB doesn't
+ *    correspond to a CoglFramebuffer.
+ *
+ * Q: What if we disassociate a framebuffer from an overlay
+ *    while it still has a pending flip? (considering what
+ *    might go wrong if we then immediately tried to associate
+ *    the framebuffer with a different overlay)
+ * A: XXX
+ *
+ * Q: When do we remove references from the
+ *    kms_onscreen->overlays list?
+ * A: XXX
+ *
+ * Q: How do we handle _swap_buffers
+ * A: We iterate through each output that is associated with
+ *    the onscreen framebuffer and for the first output
+ *    we use drmModePageFlip (passing DRM_MODE_PAGE_FLIP_EVENT)
+ *    to post the framebuffer for overlay0 and use
+ *    drmModeSetPlane() for any additional overlays. For
+ *    all other outputs we use drmModeSetCrtc to post the
+ *    framebuffer for overlay0 while also using
+ *    drmModeSetPlane() for additional overlays.
+ *
+ *    Note: until we have the atomic page flipping api we
+ *    don't have a very sane way of synchronizing changes
+ *    to overlay planes.
+ *
+ *    Note: we are intentionally only trying to synchronize
+ *    with one output but we will probably want to provide
+ *    api for controlling which output is synchronized.
+ *
+ * Q: Would it be better when handling multiple outputs to
+ *    call drmModeSetCrtc() after being notified of the flip
+ *    for the first output?
+ * A: Since using drmModeSetCrtc() implies needing to
+ *    synchronize with the GPU to make sure rendering is
+ *    complete, then waiting until the flip should minimize
+ *    blocking on the CPU, especially in the case where the same
+ *    buffer is being posted to multiple outputs since the flip
+ *    should end up waiting for the GPU to finish for us so we
+ *    won't have to.
+ *
+ *    We should create a queue_swap_outputs utility for us to
+ *    queue the swapping of all unsynchronized outputs.
+ *
+ *
+ * Q: What if we call _swap_buffers() before we have committed
+ *    an output configuration associating the framebuffer with
+ *    a hardware overlay?
+ * A: In this case we'll see that kms_output->overlays is NULL.
+ *    We lock the front buffer, set it on ->next_bo and directly
+ *    call page_flip_handler() to behave as if the flip
+ *    completed immediately. This will make sure we issue a
+ *    _FRAME_SYNC event for the swap and move the bo to
+ *    ->current_bo.
+ *
+ *
+ * Q: How do we deal with an error in drmModePageFlip() also
+ *    considering that after the error we might commit a
+ *    new display configuration which will want to find
+ *    a next/current_bo to reference.
+ * A: We can pretend there was no error and that the page
+ *    flip completed immediately by calling page_flip_handler()
+ *    directly in this case. This will make sure cogl
+ *    dispatches a _FRAME_SYNC event for the frame otherwise
+ *    applications may freeze. The main problem here is that
+ *    we don't have meaningful timestamp data to pass to
+ *    page_flip_handler(). XXX: wont this potentially break the
+ *    invariable that we should be able to assume ->current_bo
+ *    has no outstanding rendering?
+ *
+ * Q: How do we handle an onscreen framebuffer resize?
+ * A: We don't, you just have to create a new framebuffer
+ */
+static void
+_cogl_winsys_commit_outputs (CoglRenderer *renderer,
+                             CoglError **error)
+{
+  CoglRendererEGL *egl_renderer = renderer->winsys;
+  CoglRendererKMS *kms_renderer = egl_renderer->platform;
+  GList *l;
+  CoglBool roll_back = FALSE;
+
+
+  /* If we hit an error at any point while committing the new
+   * configuration then we revert back to here with roll_back
+   * set to TRUE and instead re-commit the previous
+   * configuration */
+ROLL_BACK:
+
+
+  for (l = renderer->outputs; l; l = l->next)
+    {
+      CoglOutput *output = l->data;
+      CoglOutputKMS *kms_output = output->winsys;
+      CoglOutputState *state = roll_back ? output->state : output->pending;
+      CoglOverlay *overlay0;
+      drmModeConnector *connector;
+      drmModeProperty *property;
+      int i;
+
+      connector = drmModeGetConnector (kms_renderer->fd,
+                                       kms_output->connector_id);
+      if (!connector)
+        {
+          g_warn_if_reached ();
+          continue;
+        }
+
+      if (state->overlays)
+        overlay0 = state->overlays->data;
+      else
+        overlay0 = NULL;
+
+      /* If the output has no associated overlays then we assume
+       * it's ok to try and put it into DPMS_MODE_OFF */
+      dpms_mode = overlay0_new ? state_new->dpms_mode : DRM_MODE_DPMS_OFF;
+
+      property = get_connector_property (kms_renderer->fd,
+                                         connector,
+                                         "DPMS");
+      if (property)
+        {
+          int status = drmModeConnectorSetProperty (kms_renderer->fd,
+                                                    connector->connector_id,
+                                                    property->prop_id,
+                                                    dpms_mode);
+          drmModeFreeProperty (property);
+
+          if (status < 0)
+            {
+              const char *dpms_mode_names[] = {
+                  "ON",
+                  "STANDBY",
+                  "SUSPEND",
+                  "OFF"
+              };
+
+              g_return_if_fail (roll_back == FALSE);
+
+              roll_back = TRUE;
+              roll_back_error =
+                g_strdup_printf ("Failed to set DPMS state for "
+                                 "connector %d to %s",
+                                 kms_output->connector_id,
+                                 dpms_mode_names[dpms_mode]);
+              goto ROLL_BACK;
+            }
+        }
+
+      if (!overlay0)
+        {
+          drmModeFreeConnector (connector);
+          continue;
+        }
+
+      if (!overlay0->onscreen_source)
+        {
+          g_return_if_fail (roll_back == FALSE);
+
+          roll_back = TRUE;
+          roll_back_error =
+            g_strdup_printf ("An overlay must be associated with an "
+                             "onscreen framebuffer as a color source");
+          goto ROLL_BACK;
+        }
+
+      for (i = 0; i < connector->count_modes; i++)
+        {
+          drmModeInfo *mode_info = &connector->modes[i];
+          int n_planes;
+
+          if (strcmp (mode_info->name, state->mode->name) == 0)
+            {
+              CoglOnscreen *onscreen = overlay0->onscreen_source;
+              CoglOnscreenEGL *egl_onscreen = onscreen->winsys;
+              CoglOnscreenKMS *kms_onscreen = egl_onscreen->platform;
+              uint32_t fb_id;
+              int status;
+
+              if (overlay0->src_x != 0 ||
+                  overlay0->src_y != 0 ||
+                  overlay0->src_width != mode_info->width ||
+                  overlay0->src_height != mode_info->height)
+                {
+                  g_return_if_fail (roll_back == FALSE);
+
+                  roll_back = TRUE;
+                  roll_back_error =
+                    g_strdup_printf ("On KMS the first overlay's source can "
+                                     "not be offset and its size must match "
+                                     "the mode resolution");
+                  goto ROLL_BACK;
+                }
+
+              if (kms_onscreen->next_fb_id)
+                fb_id = kms_onscreen->next_fb_id;
+              else if (kms_onscreen->current_fb_id)
+                fb_id = kms_onscreen->current_fb_id;
+              else
+                {
+                  g_return_if_fail (roll_back == FALSE);
+
+                  roll_back = TRUE;
+                  roll_back_error =
+                    g_strdup_printf ("Can't commit output changes involving "
+                                     "an uninitialized (un-swapped) onscreen "
+                                     "framebuffer since it has no actual "
+                                     "data to scan out yet");
+                  goto ROLL_BACK;
+                }
+
+              /* drmModeSetCrtc isn't synchronized with the GPU
+               * pipeline so we explicitly wait for any out standing
+               * rendering to complete before setting the new
+               * mode... */
+              cogl_framebuffer_finish (overlay0->onscreen_source);
+
+              status = drmModeSetCrtc (kms_renderer->fd,
+                                       kms_output->encoder->crtc_id,
+                                       fb_id, 0, 0,
+                                       &kms_output->connector_id, 1,
+                                       &kms_mode_info);
+              if (status < 0)
+                {
+                  g_return_if_fail (roll_back == FALSE);
+
+                  roll_back = TRUE;
+                  roll_back_error =
+                    g_strdup_printf ("KMS was unable to set the requested "
+                                     "mode (%s) on connector %d, possibly "
+                                     "due to a hardware limitation",
+                                     dpms_mode_names[dpms_mode],
+                                     kms_output->connector_id);
+                  goto ROLL_BACK;
+                }
+            }
+
+          /*
+           * The remaining overlays should be setup via drmModeSetPlane()
+           */
+
+          for (n_planes = 0, l = state->overlays->next;
+               l;
+               n_planes++, l = l->next)
+            ;
+
+          if (n_planes)
+            {
+              drmModePlaneRes *planes =
+                drmModeGetPlaneResources (kms_renderer->fd);
+
+              if (!planes)
+                g_warn_if_reached ();
+
+              if (planes == NULL || planes->count_planes < n_planes)
+                {
+                  g_return_if_fail (roll_back == FALSE);
+
+                  roll_back = TRUE;
+                  roll_back_error =
+                    g_strdup_printf ("Hardware doesn't support enough "
+                                     "overlays to support configuration.");
+                  goto ROLL_BACK;
+                }
+
+              for (n = 0, l = state->overlays->next; l; n++, l = l->next)
+                {
+                  CoglOverlay *overlay = l->data;
+                  CoglOnscreen *onscreen = overlay0->onscreen_source;
+                  CoglOnscreenEGL *egl_onscreen = onscreen->winsys;
+                  CoglOnscreenKMS *kms_onscreen = egl_onscreen->platform;
+                  uint32_t fb_id;
+
+                  if (!overlay->onscreen_source)
+                    {
+                      g_return_if_fail (roll_back == FALSE);
+
+                      roll_back = TRUE;
+                      roll_back_error =
+                        g_strdup_printf ("An overlay must be associated with an "
+                                         "onscreen framebuffer as a color source");
+                      goto ROLL_BACK;
+                    }
+
+                  if (kms_onscreen->next_fb_id)
+                    fb_id = kms_onscreen->next_fb_id;
+                  else if (kms_onscreen->current_fb_id)
+                    fb_id = kms_onscreen->current_fb_id;
+                  else
+                    {
+                      g_return_if_fail (roll_back == FALSE);
+
+                      roll_back = TRUE;
+                      roll_back_error =
+                        g_strdup_printf ("Can't commit output changes involving "
+                                         "an uninitialized (un-swapped) onscreen "
+                                         "framebuffer since it has no actual "
+                                         "data to scan out yet");
+                      goto ROLL_BACK;
+                    }
+
+                  if (drmModeSetPlane (kms_renderer->fd,
+                                       planes->planes[n],
+                                       kms_output->encoder->crtc_id,
+                                       fb_id,
+                                       /* FIXME: flags? */,
+                                       /* FIXME: ctc_x/y/w/h,
+                                        * src_x/y/w/h */) < 0)
+                    {
+                      g_return_if_fail (roll_back == FALSE);
+
+                      roll_back = TRUE;
+                      roll_back_error =
+                        g_strdup_printf ("KMS was unable to setup the "
+                                         "overlays as requested, possibly "
+                                         "due to a hardware limitation.");
+                      goto ROLL_BACK;
+                    }
+                }
+
+              drmModeFreePlaneResources (planes);
+            }
+        }
+
+      drmModeFreeConnector (connector);
+
+      _cogl_output_update_state (output);
+    }
+}
+
 static const CoglWinsysEGLVtable
 _cogl_winsys_egl_vtable =
   {
@@ -1282,6 +1715,8 @@ _cogl_winsys_egl_kms_get_vtable (void)
       vtable.onscreen_swap_buffers_with_damage =
         _cogl_winsys_onscreen_swap_buffers_with_damage;
 
+      vtable.commit_outputs = _cogl_winsys_commit_outputs;
+
       vtable_inited = TRUE;
     }
 
diff --git a/cogl/winsys/cogl-winsys-private.h b/cogl/winsys/cogl-winsys-private.h
index 123419e..43f3463 100644
--- a/cogl/winsys/cogl-winsys-private.h
+++ b/cogl/winsys/cogl-winsys-private.h
@@ -194,6 +194,18 @@ typedef struct _CoglWinsysVtable
   void
   (*fence_destroy) (CoglContext *ctx, void *fence);
 
+  /* optional
+   *
+   * This iterates the current renderer->outputs and for each output
+   * with pending changes they should be applied to the window system
+   * or hardware.
+   *
+   * If it's not possible to apply the pending changes then the
+   * previous state should be restored and an error returned.
+   */
+  CoglBool
+  (*commit_outputs) (CoglRenderer *renderer, CoglError **error);
+
 } CoglWinsysVtable;
 
 CoglBool


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