[mutter/wip/nielsdg/add-yuv-support: 9/11] WIP: wayland: add basic support for non-RGBA textures



commit 1c84156ea3581dda0ba7c986190c8a12118df01f
Author: Niels De Graef <Niels DeGraef barco com>
Date:   Wed Nov 14 12:22:02 2018 +0100

    WIP: wayland: add basic support for non-RGBA textures
    
    Up until now, we didn't support sending YUV textures to the Wayland
    server. This was for several reasons:
    
    * We draw onto an RGBA framebuffer, so any other color format needs to
    be converted to that color space. Since we don't want to lose a lot of
    performance, this is ideally done on the GPU (using shaders).
    * YUV formats can consist of several planes (for example NV12, a common
    format in decoded video frames consists of a Y-plane and a subsampled
    UV-plane). Mutter always assumed that any texture it got was
    representable by a `CoglTexture`, which does not have this kind of
    concept.
    
    To deal with this, we introduce a new "texture": a
    `CoglMultiPlaneTexture` which consists of multiple CoglTextures, each
    representing a plane in the texture we got. It also provides support
    for CoglSnippets which can convert the colorspace if necessary.
    
    What changes are in this commit:
    * Introduce a new CoglMultiPlaneTexture object. Right now it is not
    implemented as a CoglTexture to prevent any confusion (but it is
    somewhat related to CoglMetaTexture)
    * Added some extra values to the CoglPixelFormat enum that deal with YUV
    * Make the necessary changes in MetaWaylandBuffer, so that it knows how
    to deal with incoming buffers (for example EGLImages and shm buffers)
    * This also introduces some changes in MetaDmaBuf, as that is also a
    different kind of buffer we can receive from wayland.
    
    Acknowledgements
    * There was a lot of prior art already done by the authors of Weston,
    CoglGstVideoSink and ClutterGstSink
    * My employer Barco for allowing me to work on this

 cogl/cogl/cogl-bitmap-conversion.c              |  30 ++
 cogl/cogl/cogl-bitmap-packing.h                 |  60 +++
 cogl/cogl/cogl-multi-plane-texture.c            | 209 ++++++++
 cogl/cogl/cogl-multi-plane-texture.h            | 205 ++++++++
 cogl/cogl/cogl-pixel-format-conversion.c        | 163 ++++++
 cogl/cogl/cogl-pixel-format-conversion.h        |  92 ++++
 cogl/cogl/cogl-pixel-format.c                   | 668 +++++++++++++++++++++---
 cogl/cogl/cogl-pixel-format.h                   | 116 +++-
 cogl/cogl/cogl-texture-2d.c                     |   5 +
 cogl/cogl/cogl-texture-2d.h                     |   1 +
 cogl/cogl/cogl.h                                |   2 +
 cogl/cogl/driver/gl/cogl-texture-2d-gl.c        |   4 +
 cogl/cogl/driver/gl/gl/cogl-driver-gl.c         |  30 ++
 cogl/cogl/driver/gl/gl/cogl-texture-driver-gl.c |   6 +
 cogl/cogl/driver/gl/gles/cogl-driver-gles.c     |  30 ++
 cogl/cogl/meson.build                           |   8 +-
 cogl/cogl/winsys/cogl-winsys-egl-x11.c          |   1 +
 src/compositor/meta-shaped-texture-private.h    |   2 +-
 src/compositor/meta-shaped-texture.c            | 176 ++++---
 src/compositor/meta-surface-actor-x11.c         |  11 +-
 src/compositor/meta-surface-actor.c             |  10 +-
 src/compositor/meta-texture-tower.c             | 158 +++---
 src/compositor/meta-texture-tower.h             |   6 +-
 src/compositor/meta-window-actor.c              |   7 +-
 src/meta/meta-shaped-texture.h                  |   2 +-
 src/wayland/meta-wayland-buffer.c               | 333 ++++++++----
 src/wayland/meta-wayland-buffer.h               |  16 +-
 src/wayland/meta-wayland-cursor-surface.c       |   9 +-
 src/wayland/meta-wayland-dma-buf.c              | 233 ++++++---
 src/wayland/meta-wayland-dma-buf.h              |   8 +-
 src/wayland/meta-wayland-egl-stream.c           |  21 +-
 src/wayland/meta-wayland-egl-stream.h           |   4 +-
 src/wayland/meta-wayland-shell-surface.c        |   6 +-
 src/wayland/meta-wayland-surface.c              |   8 +-
 src/wayland/meta-wayland-surface.h              |   4 +-
 src/wayland/meta-wayland.c                      |  22 +
 36 files changed, 2238 insertions(+), 428 deletions(-)
---
diff --git a/cogl/cogl/cogl-bitmap-conversion.c b/cogl/cogl/cogl-bitmap-conversion.c
index d407980a3..ab7a4593c 100644
--- a/cogl/cogl/cogl-bitmap-conversion.c
+++ b/cogl/cogl/cogl-bitmap-conversion.c
@@ -320,7 +320,37 @@ _cogl_bitmap_needs_short_temp_buffer (CoglPixelFormat format)
     case COGL_PIXEL_FORMAT_DEPTH_32:
     case COGL_PIXEL_FORMAT_DEPTH_24_STENCIL_8:
     case COGL_PIXEL_FORMAT_ANY:
+    /* No support for YUV or multi-plane formats */
     case COGL_PIXEL_FORMAT_YUV:
+    case COGL_PIXEL_FORMAT_YUYV:
+    case COGL_PIXEL_FORMAT_YVYU:
+    case COGL_PIXEL_FORMAT_UYVY:
+    case COGL_PIXEL_FORMAT_VYUY:
+    case COGL_PIXEL_FORMAT_AYUV:
+    case COGL_PIXEL_FORMAT_XRGB8888_A8:
+    case COGL_PIXEL_FORMAT_XBGR8888_A8:
+    case COGL_PIXEL_FORMAT_RGBX8888_A8:
+    case COGL_PIXEL_FORMAT_BGRX8888_A8:
+    case COGL_PIXEL_FORMAT_RGB888_A8:
+    case COGL_PIXEL_FORMAT_BGR888_A8:
+    case COGL_PIXEL_FORMAT_RGB565_A8:
+    case COGL_PIXEL_FORMAT_BGR565_A8:
+    case COGL_PIXEL_FORMAT_NV12:
+    case COGL_PIXEL_FORMAT_NV21:
+    case COGL_PIXEL_FORMAT_NV16:
+    case COGL_PIXEL_FORMAT_NV61:
+    case COGL_PIXEL_FORMAT_NV24:
+    case COGL_PIXEL_FORMAT_NV42:
+    case COGL_PIXEL_FORMAT_YUV410:
+    case COGL_PIXEL_FORMAT_YVU410:
+    case COGL_PIXEL_FORMAT_YUV411:
+    case COGL_PIXEL_FORMAT_YVU411:
+    case COGL_PIXEL_FORMAT_YUV420:
+    case COGL_PIXEL_FORMAT_YVU420:
+    case COGL_PIXEL_FORMAT_YUV422:
+    case COGL_PIXEL_FORMAT_YVU422:
+    case COGL_PIXEL_FORMAT_YUV444:
+    case COGL_PIXEL_FORMAT_YVU444:
       g_assert_not_reached ();
 
     case COGL_PIXEL_FORMAT_A_8:
diff --git a/cogl/cogl/cogl-bitmap-packing.h b/cogl/cogl/cogl-bitmap-packing.h
index 3a1646fbf..1c8bbb5fc 100644
--- a/cogl/cogl/cogl-bitmap-packing.h
+++ b/cogl/cogl/cogl-bitmap-packing.h
@@ -395,7 +395,37 @@ G_PASTE (_cogl_unpack_, component_size) (CoglPixelFormat format,
     case COGL_PIXEL_FORMAT_DEPTH_32:
     case COGL_PIXEL_FORMAT_DEPTH_24_STENCIL_8:
     case COGL_PIXEL_FORMAT_ANY:
+    /* No support for YUV or multi-plane formats */
     case COGL_PIXEL_FORMAT_YUV:
+    case COGL_PIXEL_FORMAT_YUYV:
+    case COGL_PIXEL_FORMAT_YVYU:
+    case COGL_PIXEL_FORMAT_UYVY:
+    case COGL_PIXEL_FORMAT_VYUY:
+    case COGL_PIXEL_FORMAT_AYUV:
+    case COGL_PIXEL_FORMAT_XRGB8888_A8:
+    case COGL_PIXEL_FORMAT_XBGR8888_A8:
+    case COGL_PIXEL_FORMAT_RGBX8888_A8:
+    case COGL_PIXEL_FORMAT_BGRX8888_A8:
+    case COGL_PIXEL_FORMAT_RGB888_A8:
+    case COGL_PIXEL_FORMAT_BGR888_A8:
+    case COGL_PIXEL_FORMAT_RGB565_A8:
+    case COGL_PIXEL_FORMAT_BGR565_A8:
+    case COGL_PIXEL_FORMAT_NV12:
+    case COGL_PIXEL_FORMAT_NV21:
+    case COGL_PIXEL_FORMAT_NV16:
+    case COGL_PIXEL_FORMAT_NV61:
+    case COGL_PIXEL_FORMAT_NV24:
+    case COGL_PIXEL_FORMAT_NV42:
+    case COGL_PIXEL_FORMAT_YUV410:
+    case COGL_PIXEL_FORMAT_YVU410:
+    case COGL_PIXEL_FORMAT_YUV411:
+    case COGL_PIXEL_FORMAT_YVU411:
+    case COGL_PIXEL_FORMAT_YUV420:
+    case COGL_PIXEL_FORMAT_YVU420:
+    case COGL_PIXEL_FORMAT_YUV422:
+    case COGL_PIXEL_FORMAT_YVU422:
+    case COGL_PIXEL_FORMAT_YUV444:
+    case COGL_PIXEL_FORMAT_YVU444:
       g_assert_not_reached ();
     }
 }
@@ -751,7 +781,37 @@ G_PASTE (_cogl_pack_, component_size) (CoglPixelFormat format,
     case COGL_PIXEL_FORMAT_DEPTH_32:
     case COGL_PIXEL_FORMAT_DEPTH_24_STENCIL_8:
     case COGL_PIXEL_FORMAT_ANY:
+    /* No support for YUV or multi-plane formats */
     case COGL_PIXEL_FORMAT_YUV:
+    case COGL_PIXEL_FORMAT_YUYV:
+    case COGL_PIXEL_FORMAT_YVYU:
+    case COGL_PIXEL_FORMAT_UYVY:
+    case COGL_PIXEL_FORMAT_VYUY:
+    case COGL_PIXEL_FORMAT_AYUV:
+    case COGL_PIXEL_FORMAT_XRGB8888_A8:
+    case COGL_PIXEL_FORMAT_XBGR8888_A8:
+    case COGL_PIXEL_FORMAT_RGBX8888_A8:
+    case COGL_PIXEL_FORMAT_BGRX8888_A8:
+    case COGL_PIXEL_FORMAT_RGB888_A8:
+    case COGL_PIXEL_FORMAT_BGR888_A8:
+    case COGL_PIXEL_FORMAT_RGB565_A8:
+    case COGL_PIXEL_FORMAT_BGR565_A8:
+    case COGL_PIXEL_FORMAT_NV12:
+    case COGL_PIXEL_FORMAT_NV21:
+    case COGL_PIXEL_FORMAT_NV16:
+    case COGL_PIXEL_FORMAT_NV61:
+    case COGL_PIXEL_FORMAT_NV24:
+    case COGL_PIXEL_FORMAT_NV42:
+    case COGL_PIXEL_FORMAT_YUV410:
+    case COGL_PIXEL_FORMAT_YVU410:
+    case COGL_PIXEL_FORMAT_YUV411:
+    case COGL_PIXEL_FORMAT_YVU411:
+    case COGL_PIXEL_FORMAT_YUV420:
+    case COGL_PIXEL_FORMAT_YVU420:
+    case COGL_PIXEL_FORMAT_YUV422:
+    case COGL_PIXEL_FORMAT_YVU422:
+    case COGL_PIXEL_FORMAT_YUV444:
+    case COGL_PIXEL_FORMAT_YVU444:
       g_assert_not_reached ();
     }
 }
diff --git a/cogl/cogl/cogl-multi-plane-texture.c b/cogl/cogl/cogl-multi-plane-texture.c
new file mode 100644
index 000000000..e9cb916ab
--- /dev/null
+++ b/cogl/cogl/cogl-multi-plane-texture.c
@@ -0,0 +1,209 @@
+/*
+ * Authored By Niels De Graef <niels degraef barco com>
+ *
+ * Copyright (C) 2018 Barco NV
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "cogl-config.h"
+
+#include "cogl-object-private.h"
+#include "cogl-multi-plane-texture.h"
+#include "cogl-gtype-private.h"
+#include "cogl-texture-private.h"
+#include "cogl-texture-2d-sliced.h"
+
+struct _CoglMultiPlaneTexture
+{
+  CoglObject _parent;
+
+  CoglPixelFormat format;
+
+  guint n_planes;
+  CoglTexture **planes;
+};
+
+static void
+_cogl_multi_plane_texture_free (CoglMultiPlaneTexture *self);
+
+COGL_OBJECT_DEFINE (MultiPlaneTexture, multi_plane_texture);
+COGL_GTYPE_DEFINE_CLASS (MultiPlaneTexture, multi_plane_texture);
+
+
+CoglPixelFormat
+cogl_multi_plane_texture_get_format (CoglMultiPlaneTexture *self)
+{
+  return self->format;
+}
+
+guint
+cogl_multi_plane_texture_get_n_planes (CoglMultiPlaneTexture *self)
+{
+  return self->n_planes;
+}
+
+CoglTexture *
+cogl_multi_plane_texture_get_plane (CoglMultiPlaneTexture *self, guint index)
+{
+  g_return_val_if_fail (self->n_planes > 0, NULL);
+  g_return_val_if_fail (index < self->n_planes, NULL);
+
+  return self->planes[index];
+}
+
+CoglTexture **
+cogl_multi_plane_texture_get_planes (CoglMultiPlaneTexture *self)
+{
+  return self->planes;
+}
+
+guint
+cogl_multi_plane_texture_get_width (CoglMultiPlaneTexture *self)
+{
+  g_return_val_if_fail (self->n_planes > 0, 0);
+
+  return cogl_texture_get_width (self->planes[0]);
+}
+
+guint
+cogl_multi_plane_texture_get_height (CoglMultiPlaneTexture *self)
+{
+  g_return_val_if_fail (self->n_planes > 0, 0);
+
+  return cogl_texture_get_height (self->planes[0]);
+}
+
+static void
+_cogl_multi_plane_texture_free (CoglMultiPlaneTexture *self)
+{
+  guint i = 0;
+
+  for (i = 0; i < self->n_planes; i++)
+    cogl_object_unref (self->planes[i]);
+
+  g_free (self->planes);
+}
+
+CoglMultiPlaneTexture *
+cogl_multi_plane_texture_new (CoglPixelFormat format,
+                              CoglTexture **planes, guint n_planes)
+{
+  CoglMultiPlaneTexture *self = g_slice_new0 (CoglMultiPlaneTexture);
+
+  _cogl_multi_plane_texture_object_new (self);
+
+  self->format = format;
+  self->n_planes = n_planes;
+  self->planes = planes;
+
+  return self;
+}
+
+CoglMultiPlaneTexture *
+cogl_multi_plane_texture_new_single_plane (CoglPixelFormat format,
+                                           CoglTexture *plane)
+{
+  CoglMultiPlaneTexture *self = g_slice_new0 (CoglMultiPlaneTexture);
+
+  _cogl_multi_plane_texture_object_new (self);
+
+  self->format = format;
+  self->n_planes = 1;
+  self->planes = g_malloc (sizeof (CoglTexture *));
+  self->planes[0] = plane;
+
+  return self;
+}
+
+CoglMultiPlaneTexture *
+cogl_multi_plane_texture_new_from_bitmaps (CoglPixelFormat format,
+                                           CoglBitmap **bitmaps, guint n_planes,
+                                           GError **error)
+{
+  guint i = 0;
+  CoglMultiPlaneTexture *self = g_slice_new0 (CoglMultiPlaneTexture);
+
+  _cogl_multi_plane_texture_object_new (self);
+
+  self->format = format;
+  self->n_planes = n_planes;
+  self->planes = g_malloc (sizeof (CoglTexture *) * n_planes);
+
+  for (i = 0; i < n_planes; i++)
+    {
+      CoglTexture *plane;
+
+      plane = COGL_TEXTURE (cogl_texture_2d_new_from_bitmap (bitmaps[i]));
+
+      if (!cogl_texture_allocate (plane, error))
+        {
+          g_clear_pointer (&plane, cogl_object_unref);
+
+          /* There's a chance we failed due to the buffer being NPOT size.
+           * If so, try again with CoglTexture2DSliced (which does support this) */
+          if (g_error_matches (*error,
+                               COGL_TEXTURE_ERROR,
+                               COGL_TEXTURE_ERROR_SIZE))
+            {
+              CoglTexture2DSliced *plane_sliced;
+
+              g_clear_error (error);
+
+              plane_sliced =
+                cogl_texture_2d_sliced_new_from_bitmap (bitmaps[i],
+                                                        COGL_TEXTURE_MAX_WASTE);
+              plane = COGL_TEXTURE (plane_sliced);
+
+              if (!cogl_texture_allocate (plane, error))
+                cogl_clear_object (&plane);
+            }
+        }
+
+      cogl_object_unref (bitmaps[i]);
+      self->planes[i] = plane;
+    }
+
+
+  return self;
+}
+
+gchar *
+cogl_multi_plane_texture_to_string (CoglMultiPlaneTexture *self)
+{
+    g_autoptr(GString) str = NULL;
+    g_autofree gchar *ret = NULL;
+    guint i;
+
+    str = g_string_new ("");
+    g_string_append_printf (str, "CoglMultiPlaneTexture (%p) {\n", self);
+    g_string_append_printf (str, "  .format   =  %s;\n", cogl_pixel_format_to_string (self->format));
+    g_string_append_printf (str, "  .n_planes =  %u;\n", self->n_planes);
+    g_string_append (str, "  .planes   =  {\n");
+
+    for (i = 0; i < self->n_planes; i++)
+      {
+        CoglTexture *plane = self->planes[i];
+
+        g_string_append_printf (str, "    (%p) { .format = %s },\n",
+                                plane,
+                                cogl_pixel_format_to_string (_cogl_texture_get_format (plane)));
+      }
+
+    g_string_append (str, "  }\n");
+    g_string_append (str, "}");
+
+    ret = g_string_free (g_steal_pointer (&str), FALSE);
+    return g_steal_pointer (&ret);
+}
diff --git a/cogl/cogl/cogl-multi-plane-texture.h b/cogl/cogl/cogl-multi-plane-texture.h
new file mode 100644
index 000000000..c0d8d89b9
--- /dev/null
+++ b/cogl/cogl/cogl-multi-plane-texture.h
@@ -0,0 +1,205 @@
+/*
+ * Authored By Niels De Graef <niels degraef barco com>
+ *
+ * Copyright (C) 2018 Barco NV
+ *
+ * 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 __COGL_MULTI_PLANE_TEXTURE_H__
+#define __COGL_MULTI_PLANE_TEXTURE_H__
+
+#include "cogl/cogl-texture.h"
+
+G_BEGIN_DECLS
+
+/**
+ * SECTION:cogl-multi-plane-texture
+ * @title: CoglMultiPlaneTexture
+ * @short_description: A non-primitive texture that can have multiple planes.
+ *
+ * #CoglMultiPlaneTexture allows one to deal with non-trivial formats that
+ * have multiple planes, requires subsampling and/or aren't in RGB. A common
+ * example of this are decoded video frames, which often use something in the
+ * YUV colorspace, combined with subsampling.
+ *
+ * The basic idea of a #CoglMultiPlaneTexture is the following:
+ * - Each plane is represented by a separate #CoglTexture. That means that you
+ *   should add each of these planes as a layer to your CoglPipeline.
+ * - When dealing with a color space that is not RGB, you can ask the
+ *   #CoglMultiPlaneTexture to create a shader for you that does the conversion
+ *   in the GPU.
+ * - In case you need to deal with memory access in a format with subsampling,
+ *   you can use cogl_multi_plane_texture_get_width() and its analogous version
+ *   for the height to get the correct size of the texture.
+ */
+
+typedef struct _CoglMultiPlaneTexture CoglMultiPlaneTexture;
+#define COGL_MULTI_PLANE_TEXTURE(tex) ((CoglMultiPlaneTexture *) tex)
+
+
+/**
+ * cogl_multi_plane_texture_get_gtype:
+ *
+ * Returns: a #GType that can be used with the GLib type system.
+ */
+GType cogl_multi_plane_texture_get_gtype (void);
+
+/**
+ * cogl_is_multi_plane_texture:
+ * @object: A #CoglObject pointer
+ *
+ * Gets whether the given @object references an existing CoglMultiPlaneTexture.
+ *
+ * Return value: %TRUE if the @object references a #CoglMultiPlaneTexture,
+ *   %FALSE otherwise
+ */
+gboolean
+cogl_is_multi_plane_texture (void *object);
+
+/**
+ * cogl_multi_plane_texture_new:
+ * @format: The format of the #CoglMultiPlaneTexture
+ * @planes: (transfer full): The actual planes of the texture
+ * @n_planes: The number of planes
+ *
+ * Creates a #CoglMultiPlaneTexture with the given @format. Each of the
+ * #CoglTexture<!-- -->s represents a plane.
+ *
+ * Returns: (transfer full): A new #CoglMultiPlaneTexture. Use
+ * cogl_object_unref() when you're done with it.
+ */
+CoglMultiPlaneTexture * cogl_multi_plane_texture_new  (CoglPixelFormat format,
+                                                       CoglTexture **planes,
+                                                       guint n_planes);
+
+/**
+ * cogl_multi_plane_texture_new_single_plane:
+ * @format: The format of the #CoglMultiPlaneTexture
+ * @plane: (transfer full): The actual planes of the texture
+ *
+ * Creates a #CoglMultiPlaneTexture for a "simple" texture, i.e. with only one
+ * plane.
+ *
+ * Returns: (transfer full): A new #CoglMultiPlaneTexture. Use
+ * cogl_object_unref() when you're done with it.
+ */
+CoglMultiPlaneTexture * cogl_multi_plane_texture_new_single_plane (CoglPixelFormat format,
+                                                                   CoglTexture *plane);
+
+/**
+ * cogl_multi_plane_texture_new_from_bitmaps:
+ * @format: The format of the new #CoglMultiPlaneTexture
+ * @bitmaps: (transfer full): The planes of the texture, each as a #CoglBitmap
+ * @n_planes: the number of planes the texture contains
+ * @error: (out): Will be set if an error occurred
+ *
+ * Creates a #CoglMultiPlaneTexture from the given bitmaps and makes sure the
+ * planes are uploaded to the GPU.
+ *
+ * Returns: (transfer full): A new #CoglMultiPlaneTexture. Use
+ * cogl_object_unref() when you're done with it.
+ */
+CoglMultiPlaneTexture *
+cogl_multi_plane_texture_new_from_bitmaps (CoglPixelFormat format,
+                                           CoglBitmap **bitmaps, guint n_planes,
+                                           GError **error);
+
+/**
+ * cogl_multi_plane_texture_get_format:
+ * @self: a #CoglMultiPlaneTexture
+ *
+ * Returns the pixel format that is used by this texture.
+ *
+ * Returns: The pixel format that is used by this #CoglMultiPlaneTexture.
+ */
+CoglPixelFormat cogl_multi_plane_texture_get_format   (CoglMultiPlaneTexture *self);
+
+/**
+ * cogl_multi_plane_texture_get_format:
+ * @self: a #CoglMultiPlaneTexture
+ *
+ * Returns the number of planes for this texture. Note that this is entirely
+ * dependent on the #CoglPixelFormat that is used. For example, simple RGB
+ * textures will have a single plane, while some more convoluted formats like
+ * NV12 and YUV 4:4:4 can have 2 and 3 planes respectively.
+ *
+ * Returns: The number of planes in this #CoglMultiPlaneTexture.
+ */
+guint           cogl_multi_plane_texture_get_n_planes (CoglMultiPlaneTexture *self);
+
+/**
+ * cogl_multi_plane_texture_get_plane:
+ * @self: a #CoglMultiPlaneTexture
+ * @index: the index of the plane
+ *
+ * Returns the n'th plane of the #CoglMultiPlaneTexture. Note that it is a
+ * programming error to use with an index larger than
+ * cogl_multi_plane_texture_get_n_planes().
+ *
+ * Returns: The plane at the given @index.
+ */
+CoglTexture *   cogl_multi_plane_texture_get_plane    (CoglMultiPlaneTexture *self,
+                                                       guint index);
+
+/**
+ * cogl_multi_plane_texture_get_planes:
+ * @self: a #CoglMultiPlaneTexture
+ *
+ * Returns all planes of the #CoglMultiPlaneTexture.
+ *
+ * Returns: (transfer none): The planes of this texture.
+ */
+CoglTexture **  cogl_multi_plane_texture_get_planes   (CoglMultiPlaneTexture *self);
+
+/**
+ * cogl_multi_plane_texture_get_width:
+ * @self: a #CoglMultiPlaneTexture
+ *
+ * Returns the width of the #CoglMultiPlaneTexture. Prefer this over calling
+ * cogl_texture_get_width() on one of the textures, as that might give a
+ * different size when dealing with subsampling.
+ *
+ * Returns: The width of the texture.
+ */
+guint           cogl_multi_plane_texture_get_width    (CoglMultiPlaneTexture *self);
+
+/**
+ * cogl_multi_plane_texture_get_height:
+ * @self: a #CoglMultiPlaneTexture
+ *
+ * Returns the height of the #CoglMultiPlaneTexture. Prefer this over calling
+ * cogl_texture_get_height() on one of the textures, as that might give a
+ * different size when dealing with subsampling.
+ *
+ * Returns: The height of the texture.
+ */
+guint           cogl_multi_plane_texture_get_height   (CoglMultiPlaneTexture *self);
+
+/**
+ * cogl_multi_plane_texture_to_string:
+ * @self: a #CoglMultiPlaneTexture
+ *
+ * Returns a string representation of @self, useful for debugging purposes.
+ *
+ * Returns: (transfer full): A string representation of @self. Use g_free() when
+ * done with it.
+ */
+gchar *         cogl_multi_plane_texture_to_string    (CoglMultiPlaneTexture *self);
+
+G_END_DECLS
+
+#endif
diff --git a/cogl/cogl/cogl-pixel-format-conversion.c b/cogl/cogl/cogl-pixel-format-conversion.c
new file mode 100644
index 000000000..6b505705e
--- /dev/null
+++ b/cogl/cogl/cogl-pixel-format-conversion.c
@@ -0,0 +1,163 @@
+/*
+ * Authored By Niels De Graef <niels degraef barco com>
+ *
+ * Copyright (C) 2018 Barco NV
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "cogl-config.h"
+
+#include "cogl-object-private.h"
+#include "cogl-gtype-private.h"
+#include "cogl-pixel-format-conversion.h"
+#include "cogl-snippet.h"
+#include "cogl-pipeline-layer-state.h"
+#include "cogl-pipeline-state.h"
+
+#define _COGL_YUV_TO_RGBA(res, y, u, v)                     \
+    res ".r = " y " + 1.59765625 * " v ";\n"                \
+    res ".g = " y " - 0.390625 * " u " - 0.8125 * " v ";\n" \
+    res ".b = " y " + 2.015625 * " u ";\n"                  \
+    res ".a = 1.0;\n"
+
+static const gchar nv12_to_rgba_shader[] =
+    "vec4\n"
+    "cogl_nv12_to_rgba (vec2 UV)\n"
+    "{\n"
+    "  vec4 color;\n"
+    "  float y = 1.1640625 * (texture2D (cogl_sampler0, UV).x - 0.0625);\n"
+    "  vec2 uv = texture2D (cogl_sampler1, UV).rg;\n"
+    "  uv -= 0.5;\n"
+    "  float u = uv.x;\n"
+    "  float v = uv.y;\n"
+       _COGL_YUV_TO_RGBA ("color", "y", "u", "v")
+    "  return color;\n"
+    "}\n";
+
+static const gchar yuv_to_rgba_shader[] =
+    "vec4\n"
+    "cogl_yuv_to_rgba (vec2 UV)\n"
+    "{\n"
+    "  vec4 color;\n"
+    "  float y = 1.16438356 * (texture2D(cogl_sampler0, UV).x - 0.0625);\n"
+    "  float u = texture2D(cogl_sampler1, UV).x - 0.5;\n"
+    "  float v = texture2D(cogl_sampler2, UV).x - 0.5;\n"
+       _COGL_YUV_TO_RGBA ("color", "y", "u", "v")
+    "  return color;\n"
+    "}\n";
+
+struct _CoglPixelFormatConversion
+{
+  CoglObject _parent;
+
+  CoglSnippet *vertex_declaration_snippet;
+  CoglSnippet *fragment_declaration_snippet;
+
+  CoglSnippet *fragment_execution_snippet;
+};
+
+static void
+_cogl_pixel_format_conversion_free (CoglPixelFormatConversion *self);
+
+COGL_OBJECT_DEFINE (PixelFormatConversion, pixel_format_conversion);
+COGL_GTYPE_DEFINE_CLASS (PixelFormatConversion, pixel_format_conversion);
+
+
+void
+cogl_pixel_format_conversion_attach_to_pipeline (CoglPixelFormatConversion *self,
+                                                 CoglPipeline *pipeline,
+                                                 gint layer)
+{
+  cogl_pipeline_add_snippet (pipeline, self->fragment_declaration_snippet);
+  cogl_pipeline_add_snippet (pipeline, self->vertex_declaration_snippet);
+
+  cogl_pipeline_add_layer_snippet (pipeline,
+                                   layer,
+                                   self->fragment_execution_snippet);
+}
+
+static gboolean
+get_cogl_snippets (CoglPixelFormat format,
+                   CoglSnippet **vertex_snippet_out,
+                   CoglSnippet **fragment_snippet_out,
+                   CoglSnippet **layer_snippet_out)
+{
+  const gchar *global_hook;
+  const gchar *layer_hook;
+
+  switch (format)
+    {
+    case COGL_PIXEL_FORMAT_YUV444:
+      global_hook = yuv_to_rgba_shader;
+      layer_hook =  "cogl_layer = cogl_yuv_to_rgba(cogl_tex_coord0_in.st);\n";
+      break;
+    case COGL_PIXEL_FORMAT_NV12:
+      /* XXX are we using Y_UV or Y_xUxV? Maybe check for RG support? */
+      global_hook = nv12_to_rgba_shader;
+      layer_hook =  "cogl_layer = cogl_nv12_to_rgba(cogl_tex_coord0_in.st);\n";
+      break;
+    default:
+      *vertex_snippet_out = NULL;
+      *fragment_snippet_out = NULL;
+      *layer_snippet_out = NULL;
+      return FALSE;
+    }
+
+  *vertex_snippet_out = cogl_snippet_new (COGL_SNIPPET_HOOK_VERTEX_GLOBALS,
+                                          global_hook,
+                                          NULL);
+
+  *fragment_snippet_out = cogl_snippet_new (COGL_SNIPPET_HOOK_FRAGMENT_GLOBALS,
+                                            global_hook,
+                                            NULL);
+
+  *layer_snippet_out = cogl_snippet_new (COGL_SNIPPET_HOOK_LAYER_FRAGMENT,
+                                         NULL,
+                                         layer_hook);
+
+  return TRUE;
+}
+
+static void
+_cogl_pixel_format_conversion_free (CoglPixelFormatConversion *self)
+{
+  cogl_clear_object (&self->vertex_declaration_snippet);
+  cogl_clear_object (&self->fragment_declaration_snippet);
+  cogl_clear_object (&self->fragment_execution_snippet);
+}
+
+CoglPixelFormatConversion *
+cogl_pixel_format_conversion_new (CoglPixelFormat format)
+{
+  CoglPixelFormatConversion *self;
+  CoglSnippet *vertex_declaration_snippet;
+  CoglSnippet *fragment_declaration_snippet;
+  CoglSnippet *fragment_execution_snippet;
+
+  if (!get_cogl_snippets (format,
+                          &vertex_declaration_snippet,
+                          &fragment_declaration_snippet,
+                          &fragment_execution_snippet))
+    return NULL;
+
+  self = g_slice_new0 (CoglPixelFormatConversion);
+  _cogl_pixel_format_conversion_object_new (self);
+
+  self->vertex_declaration_snippet = vertex_declaration_snippet;
+  self->fragment_declaration_snippet = fragment_declaration_snippet;
+  self->fragment_execution_snippet = fragment_execution_snippet;
+
+  return self;
+}
diff --git a/cogl/cogl/cogl-pixel-format-conversion.h b/cogl/cogl/cogl-pixel-format-conversion.h
new file mode 100644
index 000000000..f93107a33
--- /dev/null
+++ b/cogl/cogl/cogl-pixel-format-conversion.h
@@ -0,0 +1,92 @@
+/*
+ * Authored By Niels De Graef <niels degraef barco com>
+ *
+ * Copyright (C) 2018 Barco NV
+ *
+ * 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 __COGL_PIXEL_FORMAT_CONVERSION_H__
+#define __COGL_PIXEL_FORMAT_CONVERSION_H__
+
+#include "cogl/cogl-types.h"
+#include "cogl/cogl-pipeline.h"
+
+G_BEGIN_DECLS
+
+/**
+ * SECTION:cogl-color-space-conversion
+ * @title: CoglPixelFormatConversion
+ * @short_description: A collection of snippets to handle pixel_format conversion
+ *
+ * In some use cases, one might generate non-RGBA textures (e.g. YUV), which is
+ * problematic if you then have to composite them in to an RGBA framebuffer. In
+ * comes #CoglPixelFormatConversion, which you can attach to a #CoglPipeline to
+ * do this all for you. Internally, it consists of nothing more than a
+ * collection of #CoglSnippets which do the right thing for you.
+ */
+
+typedef struct _CoglPixelFormatConversion CoglPixelFormatConversion;
+#define COGL_PIXEL_FORMAT_CONVERSION(ptr) ((CoglPixelFormatConversion *) ptr)
+
+
+/**
+ * cogl_multiplane_texture_get_gtype:
+ *
+ * Returns: a #GType that can be used with the GLib type system.
+ */
+GType cogl_pixel_format_conversion_get_gtype (void);
+
+/*
+ * cogl_is_pixel_format_conversion:
+ * @object: A #CoglObject pointer
+ *
+ * Gets whether the given @object references an existing
+ * CoglPixelFormatConversion.
+ *
+ * Return value: %TRUE if the @object references a #CoglPixelFormatConversion,
+ *   %FALSE otherwise
+ */
+gboolean
+cogl_is_pixel_format_conversion (void *object);
+
+/**
+ * cogl_pixel_format_conversion_new:
+ * @format: The input format
+ *
+ * Creates a #CoglPixelFormatConversion to convert the given @formatro RGBA. If
+ * no such conversion is needed, it will return %NULL.
+ *
+ * Returns: (transfer full) (nullable): A new #CoglPixelFormatConversion, or
+ * %NULL if none is needed.
+ */
+CoglPixelFormatConversion * cogl_pixel_format_conversion_new  (CoglPixelFormat format);
+
+/**
+ * cogl_pixel_format_conversion_attach_to_pipeline:
+ * @self: The #CoglPixelFormatConversion you want to add
+ * @pipeline: The #CoglPipeline which needs the color conversion
+ * @layer: The layer you want to perform the color space conversion at
+ *
+ * Adds color conversion to the given @pipeline at the given @layer.
+ */
+void cogl_pixel_format_conversion_attach_to_pipeline (CoglPixelFormatConversion *self,
+                                                      CoglPipeline *pipeline,
+                                                      int layer);
+
+G_END_DECLS
+
+#endif
diff --git a/cogl/cogl/cogl-pixel-format.c b/cogl/cogl/cogl-pixel-format.c
index a62388469..ee03a1c90 100644
--- a/cogl/cogl/cogl-pixel-format.c
+++ b/cogl/cogl/cogl-pixel-format.c
@@ -35,202 +35,627 @@
 #include <stdlib.h>
 
 #include "cogl-pixel-format.h"
+#include "cogl-texture.h"
 
 /* An entry to map CoglPixelFormats to their respective properties */
 typedef struct _CoglPixelFormatInfo
 {
   CoglPixelFormat cogl_format;
   const char *format_str;
-  int bpp;                         /* Bytes per pixel                 */
-  int aligned;                     /* Aligned components? (-1 if n/a) */
+  int aligned;                               /* Is aligned? (bool; -1 if n/a) */
+  uint8_t n_planes;
+
+  /* Per plane-information */
+  uint8_t bpp[COGL_PIXEL_FORMAT_MAX_PLANES];        /* Bytes per pixel        */
+  uint8_t hsub[COGL_PIXEL_FORMAT_MAX_PLANES];       /* horizontal subsampling */
+  uint8_t vsub[COGL_PIXEL_FORMAT_MAX_PLANES];       /* vertical subsampling   */
+  CoglPixelFormat subformats[COGL_PIXEL_FORMAT_MAX_PLANES]; /* how to upload to GL    */
 } CoglPixelFormatInfo;
 
 static const CoglPixelFormatInfo format_info_table[] = {
   {
     .cogl_format = COGL_PIXEL_FORMAT_ANY,
     .format_str = "ANY",
-    .bpp = 0,
-    .aligned = -1
+    .n_planes = 1,
+    .aligned = -1,
+    .bpp = { 0 },
+    .hsub = { 1, 0, 0, 0 },
+    .vsub = { 1, 0, 0, 0 },
+    .subformats = { COGL_PIXEL_FORMAT_ANY }
   },
   {
     .cogl_format = COGL_PIXEL_FORMAT_A_8,
     .format_str = "A_8",
-    .bpp = 1,
-    .aligned = 1
+    .n_planes = 1,
+    .aligned = 1,
+    .bpp = { 1 },
+    .hsub = { 1, 0, 0, 0 },
+    .vsub = { 1, 0, 0, 0 },
+    .subformats = { COGL_PIXEL_FORMAT_A_8 }
   },
   {
     .cogl_format = COGL_PIXEL_FORMAT_RGB_565,
     .format_str = "RGB_565",
-    .bpp = 2,
-    .aligned = 0
+    .n_planes = 1,
+    .aligned = 0,
+    .bpp = { 2 },
+    .hsub = { 1, 0, 0, 0 },
+    .vsub = { 1, 0, 0, 0 },
+    .subformats = { COGL_PIXEL_FORMAT_RGB_565 }
   },
   {
     .cogl_format = COGL_PIXEL_FORMAT_RGBA_4444,
     .format_str = "RGBA_4444",
-    .bpp = 2,
-    .aligned = 0
+    .n_planes = 1,
+    .aligned = 0,
+    .bpp = { 2 },
+    .hsub = { 1, 0, 0, 0 },
+    .vsub = { 1, 0, 0, 0 },
+    .subformats = { COGL_PIXEL_FORMAT_RGBA_4444 }
   },
   {
     .cogl_format = COGL_PIXEL_FORMAT_RGBA_5551,
     .format_str = "RGBA_5551",
-    .bpp = 2,
-    .aligned = 0
+    .n_planes = 1,
+    .aligned = 0,
+    .bpp = { 2 },
+    .hsub = { 1, 0, 0, 0 },
+    .vsub = { 1, 0, 0, 0 },
+    .subformats = { COGL_PIXEL_FORMAT_RGBA_5551 }
   },
   {
     .cogl_format = COGL_PIXEL_FORMAT_YUV,
     .format_str = "YUV",
-    .bpp = 0,
-    .aligned = -1
+    .n_planes = 1,
+    .aligned = -1,
+    .bpp = { 0 },
+    .hsub = { 1, 0, 0, 0 },
+    .vsub = { 1, 0, 0, 0 },
+    .subformats = { COGL_PIXEL_FORMAT_YUV }
   },
   {
     .cogl_format = COGL_PIXEL_FORMAT_R_8,
     .format_str = "R_8",
-    .bpp = 1,
-    .aligned = 1
+    .n_planes = 1,
+    .aligned = 1,
+    .bpp = { 1 },
+    .hsub = { 1, 0, 0, 0 },
+    .vsub = { 1, 0, 0, 0 },
+    .subformats = { COGL_PIXEL_FORMAT_R_8 }
   },
   {
     .cogl_format = COGL_PIXEL_FORMAT_RG_88,
     .format_str = "RG_88",
-    .bpp = 2,
-    .aligned = 1
+    .n_planes = 1,
+    .aligned = 1,
+    .bpp = { 2 },
+    .hsub = { 1, 0, 0, 0 },
+    .vsub = { 1, 0, 0, 0 },
+    .subformats = { COGL_PIXEL_FORMAT_RG_88 }
   },
   {
     .cogl_format = COGL_PIXEL_FORMAT_RGB_888,
     .format_str = "RGB_888",
-    .bpp = 3,
-    .aligned = 1
+    .n_planes = 1,
+    .aligned = 1,
+    .bpp = { 3 },
+    .hsub = { 1, 0, 0, 0 },
+    .vsub = { 1, 0, 0, 0 },
+    .subformats = { COGL_PIXEL_FORMAT_RGB_888 }
   },
   {
     .cogl_format = COGL_PIXEL_FORMAT_BGR_888,
     .format_str = "BGR_888",
-    .bpp = 3,
-    .aligned = 1
+    .n_planes = 1,
+    .aligned = 1,
+    .bpp = { 3 },
+    .hsub = { 1, 0, 0, 0 },
+    .vsub = { 1, 0, 0, 0 },
+    .subformats = { COGL_PIXEL_FORMAT_BGR_888 }
   },
   {
     .cogl_format = COGL_PIXEL_FORMAT_RGBA_8888,
     .format_str = "RGBA_8888",
-    .bpp = 4,
-    .aligned = 1
+    .n_planes = 1,
+    .aligned = 1,
+    .bpp = { 4 },
+    .hsub = { 1, 0, 0, 0 },
+    .vsub = { 1, 0, 0, 0 },
+    .subformats = { COGL_PIXEL_FORMAT_RGBA_8888 }
   },
   {
     .cogl_format = COGL_PIXEL_FORMAT_BGRA_8888,
     .format_str = "BGRA_8888",
-    .bpp = 4,
-    .aligned = 1
+    .n_planes = 1,
+    .aligned = 1,
+    .bpp = { 4 },
+    .hsub = { 1, 0, 0, 0 },
+    .vsub = { 1, 0, 0, 0 },
+    .subformats = { COGL_PIXEL_FORMAT_BGRA_8888 }
   },
   {
     .cogl_format = COGL_PIXEL_FORMAT_ARGB_8888,
     .format_str = "ARGB_8888",
-    .bpp = 4,
-    .aligned = 1
+    .n_planes = 1,
+    .aligned = 1,
+    .bpp = { 4 },
+    .hsub = { 1, 0, 0, 0 },
+    .vsub = { 1, 0, 0, 0 },
+    .subformats = { COGL_PIXEL_FORMAT_ARGB_8888 }
   },
   {
     .cogl_format = COGL_PIXEL_FORMAT_ABGR_8888,
     .format_str = "ABGR_8888",
-    .bpp = 4,
-    .aligned = 1
+    .n_planes = 1,
+    .aligned = 1,
+    .bpp = { 4 },
+    .hsub = { 1, 0, 0, 0 },
+    .vsub = { 1, 0, 0, 0 },
+    .subformats = { COGL_PIXEL_FORMAT_ABGR_8888 }
   },
   {
     .cogl_format = COGL_PIXEL_FORMAT_RGBA_1010102,
     .format_str = "RGBA_1010102",
-    .bpp = 4,
-    .aligned = 0
+    .n_planes = 1,
+    .aligned = 0,
+    .bpp = { 4 },
+    .hsub = { 1, 0, 0, 0 },
+    .vsub = { 1, 0, 0, 0 },
+    .subformats = { COGL_PIXEL_FORMAT_RGBA_1010102 }
   },
   {
     .cogl_format = COGL_PIXEL_FORMAT_BGRA_1010102,
     .format_str = "BGRA_1010102",
-    .bpp = 4,
-    .aligned = 0
+    .n_planes = 1,
+    .aligned = 0,
+    .bpp = { 4 },
+    .hsub = { 1, 0, 0, 0 },
+    .vsub = { 1, 0, 0, 0 },
+    .subformats = { COGL_PIXEL_FORMAT_BGRA_1010102 }
   },
   {
     .cogl_format = COGL_PIXEL_FORMAT_ARGB_2101010,
     .format_str = "ARGB_2101010",
-    .bpp = 4,
-    .aligned = 0
+    .n_planes = 1,
+    .aligned = 0,
+    .bpp = { 4 },
+    .hsub = { 1, 0, 0, 0 },
+    .vsub = { 1, 0, 0, 0 },
+    .subformats = { COGL_PIXEL_FORMAT_ARGB_2101010 }
   },
   {
     .cogl_format = COGL_PIXEL_FORMAT_ABGR_2101010,
     .format_str = "ABGR_2101010",
-    .bpp = 4,
-    .aligned = 0
+    .n_planes = 1,
+    .aligned = 0,
+    .bpp = { 4 },
+    .hsub = { 1, 0, 0, 0 },
+    .vsub = { 1, 0, 0, 0 },
+    .subformats = { COGL_PIXEL_FORMAT_ABGR_2101010 }
   },
   {
     .cogl_format = COGL_PIXEL_FORMAT_RGBA_8888_PRE,
     .format_str = "RGBA_8888_PRE",
-    .bpp = 4,
-    .aligned = 1
+    .n_planes = 1,
+    .aligned = 1,
+    .bpp = { 4 },
+    .hsub = { 1, 0, 0, 0 },
+    .vsub = { 1, 0, 0, 0 },
+    .subformats = { COGL_PIXEL_FORMAT_RGBA_8888_PRE }
   },
   {
     .cogl_format = COGL_PIXEL_FORMAT_BGRA_8888_PRE,
     .format_str = "BGRA_8888_PRE",
-    .bpp = 4,
-    .aligned = 1
+    .n_planes = 1,
+    .aligned = 1,
+    .bpp = { 4 },
+    .hsub = { 1, 0, 0, 0 },
+    .vsub = { 1, 0, 0, 0 },
+    .subformats = { COGL_PIXEL_FORMAT_BGRA_8888_PRE }
   },
   {
     .cogl_format = COGL_PIXEL_FORMAT_ARGB_8888_PRE,
     .format_str = "ARGB_8888_PRE",
-    .bpp = 4,
-    .aligned = 1
+    .n_planes = 1,
+    .aligned = 1,
+    .bpp = { 4 },
+    .hsub = { 1, 0, 0, 0 },
+    .vsub = { 1, 0, 0, 0 },
+    .subformats = { COGL_PIXEL_FORMAT_ARGB_8888_PRE }
   },
   {
     .cogl_format = COGL_PIXEL_FORMAT_ABGR_8888_PRE,
     .format_str = "ABGR_8888_PRE",
-    .bpp = 4,
-    .aligned = 1
+    .n_planes = 1,
+    .aligned = 1,
+    .bpp = { 4 },
+    .hsub = { 1, 0, 0, 0 },
+    .vsub = { 1, 0, 0, 0 },
+    .subformats = { COGL_PIXEL_FORMAT_ABGR_8888_PRE }
   },
   {
     .cogl_format = COGL_PIXEL_FORMAT_RGBA_4444_PRE,
     .format_str = "RGBA_4444_PRE",
-    .bpp = 2,
-    .aligned = 0
+    .n_planes = 1,
+    .aligned = 0,
+    .bpp = { 2 },
+    .hsub = { 1, 0, 0, 0 },
+    .vsub = { 1, 0, 0, 0 },
+    .subformats = { COGL_PIXEL_FORMAT_RGBA_4444_PRE }
   },
   {
     .cogl_format = COGL_PIXEL_FORMAT_RGBA_5551_PRE,
     .format_str = "RGBA_5551_PRE",
-    .bpp = 2,
-    .aligned = 0
+    .n_planes = 1,
+    .aligned = 0,
+    .bpp = { 2 },
+    .hsub = { 1, 0, 0, 0 },
+    .vsub = { 1, 0, 0, 0 },
+    .subformats = { COGL_PIXEL_FORMAT_RGBA_5551_PRE }
   },
   {
     .cogl_format = COGL_PIXEL_FORMAT_RGBA_1010102_PRE,
     .format_str = "RGBA_1010102_PRE",
-    .bpp = 4,
-    .aligned = 0
+    .n_planes = 1,
+    .aligned = 0,
+    .bpp = { 4 },
+    .hsub = { 1, 0, 0, 0 },
+    .vsub = { 1, 0, 0, 0 },
+    .subformats = { COGL_PIXEL_FORMAT_RGBA_1010102_PRE }
   },
   {
     .cogl_format = COGL_PIXEL_FORMAT_BGRA_1010102_PRE,
     .format_str = "BGRA_1010102_PRE",
-    .bpp = 4,
-    .aligned = 0
+    .n_planes = 1,
+    .aligned = 0,
+    .bpp = { 4 },
+    .hsub = { 1, 0, 0, 0 },
+    .vsub = { 1, 0, 0, 0 },
+    .subformats = { COGL_PIXEL_FORMAT_BGRA_1010102_PRE }
   },
   {
     .cogl_format = COGL_PIXEL_FORMAT_ARGB_2101010_PRE,
     .format_str = "ARGB_2101010_PRE",
-    .bpp = 4,
-    .aligned = 0
+    .n_planes = 1,
+    .aligned = 0,
+    .bpp = { 4 },
+    .hsub = { 1, 0, 0, 0 },
+    .vsub = { 1, 0, 0, 0 },
+    .subformats = { COGL_PIXEL_FORMAT_ARGB_2101010_PRE }
   },
   {
     .cogl_format = COGL_PIXEL_FORMAT_ABGR_2101010_PRE,
     .format_str = "ABGR_2101010_PRE",
-    .bpp = 4,
-    .aligned = 0
+    .n_planes = 1,
+    .aligned = 0,
+    .bpp = { 4 },
+    .hsub = { 1, 0, 0, 0 },
+    .vsub = { 1, 0, 0, 0 },
+    .subformats = { COGL_PIXEL_FORMAT_ABGR_2101010_PRE }
   },
   {
     .cogl_format = COGL_PIXEL_FORMAT_DEPTH_16,
     .format_str = "DEPTH_16",
-    .bpp = 2,
-    .aligned = 1
+    .n_planes = 1,
+    .aligned = 1,
+    .bpp = { 2 },
+    .hsub = { 1, 0, 0, 0 },
+    .vsub = { 1, 0, 0, 0 },
+    .subformats = { COGL_PIXEL_FORMAT_DEPTH_16 }
   },
   {
     .cogl_format = COGL_PIXEL_FORMAT_DEPTH_32,
     .format_str = "DEPTH_32",
-    .bpp = 4,
-    .aligned = 1
+    .n_planes = 1,
+    .aligned = 1,
+    .bpp = { 4 },
+    .hsub = { 1, 0, 0, 0 },
+    .vsub = { 1, 0, 0, 0 },
+    .subformats = { COGL_PIXEL_FORMAT_DEPTH_32 }
   },
   {
     .cogl_format = COGL_PIXEL_FORMAT_DEPTH_24_STENCIL_8,
     .format_str = "DEPTH_24_STENCIL_8",
-    .bpp = 4,
-    .aligned = 1
+    .n_planes = 1,
+    .aligned = 1,
+    .bpp = { 4 },
+    .hsub = { 1, 0, 0, 0 },
+    .vsub = { 1, 0, 0, 0 },
+    .subformats = { COGL_PIXEL_FORMAT_DEPTH_24_STENCIL_8 }
+  },
+  /* Packed YUV */
+  {
+    .cogl_format = COGL_PIXEL_FORMAT_YUYV,
+    .format_str = "YUYV",
+    .n_planes = 2,
+    .aligned = 0,
+    .bpp = { 4 },
+    .hsub = { 1, 0, 0, 0 },
+    .vsub = { 1, 0, 0, 0 },
+    .subformats = { COGL_PIXEL_FORMAT_YUYV, COGL_PIXEL_FORMAT_YUYV }
+  },
+  {
+    .cogl_format = COGL_PIXEL_FORMAT_YVYU,
+    .format_str = "YVYU",
+    .n_planes = 2,
+    .aligned = 0,
+    .bpp = { 4 },
+    .hsub = { 1, 0, 0, 0 },
+    .vsub = { 1, 0, 0, 0 },
+    .subformats = { COGL_PIXEL_FORMAT_YVYU, COGL_PIXEL_FORMAT_YVYU }
+  },
+  {
+    .cogl_format = COGL_PIXEL_FORMAT_UYVY,
+    .format_str = "UYVY",
+    .n_planes = 2,
+    .aligned = 0,
+    .bpp = { 4 },
+    .hsub = { 1, 0, 0, 0 },
+    .vsub = { 1, 0, 0, 0 },
+    .subformats = { COGL_PIXEL_FORMAT_UYVY, COGL_PIXEL_FORMAT_UYVY }
+  },
+  {
+    .cogl_format = COGL_PIXEL_FORMAT_VYUY,
+    .format_str = "VYUY",
+    .n_planes = 2,
+    .aligned = 0,
+    .bpp = { 4 },
+    .hsub = { 1, 0, 0, 0 },
+    .vsub = { 1, 0, 0, 0 },
+    .subformats = { COGL_PIXEL_FORMAT_VYUY, COGL_PIXEL_FORMAT_VYUY }
+  },
+  {
+    .cogl_format = COGL_PIXEL_FORMAT_AYUV,
+    .format_str = "AYUV",
+    .n_planes = 2,
+    .aligned = 0,
+    .bpp = { 4 },
+    .hsub = { 1, 0, 0, 0 },
+    .vsub = { 1, 0, 0, 0 },
+    .subformats = { COGL_PIXEL_FORMAT_AYUV, COGL_PIXEL_FORMAT_AYUV }
+  },
+  /* 2 plane RGB + A */
+  {
+    .cogl_format = COGL_PIXEL_FORMAT_XRGB8888_A8,
+    .format_str = "XRGB8888_A8",
+    .n_planes = 2,
+    .aligned = 0,
+    .bpp = { 4, 1 },
+    .hsub = { 1, 1, 0, 0 },
+    .vsub = { 1, 1, 0, 0 },
+    .subformats = { COGL_PIXEL_FORMAT_ARGB_8888, COGL_PIXEL_FORMAT_A_8 }
+  },
+  {
+    .cogl_format = COGL_PIXEL_FORMAT_XBGR8888_A8,
+    .format_str = "XBGR8888_A8",
+    .n_planes = 2,
+    .aligned = 0,
+    .bpp = { 4, 1 },
+    .hsub = { 1, 1, 0, 0 },
+    .vsub = { 1, 1, 0, 0 },
+    .subformats = { COGL_PIXEL_FORMAT_ABGR_8888, COGL_PIXEL_FORMAT_A_8 }
+  },
+  {
+    .cogl_format = COGL_PIXEL_FORMAT_RGBX8888_A8,
+    .format_str = "RGBX8888_A8",
+    .n_planes = 2,
+    .aligned = 0,
+    .bpp = { 4, 1 },
+    .hsub = { 1, 1, 0, 0 },
+    .vsub = { 1, 1, 0, 0 },
+    .subformats = { COGL_PIXEL_FORMAT_RGBA_8888, COGL_PIXEL_FORMAT_A_8 }
+  },
+  {
+    .cogl_format = COGL_PIXEL_FORMAT_BGRX8888_A8,
+    .format_str = "BGRX8888_A8",
+    .n_planes = 2,
+    .aligned = 0,
+    .bpp = { 4, 1 },
+    .hsub = { 1, 1, 0, 0 },
+    .vsub = { 1, 1, 0, 0 },
+    .subformats = { COGL_PIXEL_FORMAT_BGRA_8888, COGL_PIXEL_FORMAT_A_8 }
+  },
+  {
+    .cogl_format = COGL_PIXEL_FORMAT_RGB888_A8,
+    .format_str = "RGB888_A8",
+    .n_planes = 2,
+    .aligned = 0,
+    .bpp = { 3, 1 },
+    .hsub = { 1, 1, 0, 0 },
+    .vsub = { 1, 1, 0, 0 },
+    .subformats = { COGL_PIXEL_FORMAT_RGB_888, COGL_PIXEL_FORMAT_A_8 }
+  },
+  {
+    .cogl_format = COGL_PIXEL_FORMAT_BGR888_A8,
+    .format_str = "BGR888_A8",
+    .n_planes = 2,
+    .aligned = 0,
+    .bpp = { 3, 1 },
+    .hsub = { 1, 1, 0, 0 },
+    .vsub = { 1, 1, 0, 0 },
+    .subformats = { COGL_PIXEL_FORMAT_BGR_888, COGL_PIXEL_FORMAT_A_8 }
+  },
+  {
+    .cogl_format = COGL_PIXEL_FORMAT_RGB565_A8,
+    .format_str = "RGB565_A8",
+    .n_planes = 2,
+    .aligned = 0,
+    .bpp = { 2, 1 },
+    .hsub = { 1, 1, 0, 0 },
+    .vsub = { 1, 1, 0, 0 },
+    .subformats = { COGL_PIXEL_FORMAT_RGB_565, COGL_PIXEL_FORMAT_A_8 }
+  },
+  {
+    .cogl_format = COGL_PIXEL_FORMAT_BGR565_A8,
+    .format_str = "BGR565_A8",
+    .n_planes = 2,
+    .aligned = 0,
+    .bpp = { 2, 1 },
+    .hsub = { 1, 1, 0, 0 },
+    .vsub = { 1, 1, 0, 0 },
+    .subformats = { COGL_PIXEL_FORMAT_RGB_565, COGL_PIXEL_FORMAT_A_8 }
+  },
+  /* 2 plane YUV */
+  {
+    .cogl_format = COGL_PIXEL_FORMAT_NV12,
+    .format_str = "NV12",
+    .n_planes = 2,
+    .aligned = 0,
+    .bpp = { 1, 2 },
+    .hsub = { 1, 2, 0, 0 },
+    .vsub = { 1, 2, 0, 0 },
+    .subformats = { COGL_PIXEL_FORMAT_R_8, COGL_PIXEL_FORMAT_RG_88 }
+  },
+  {
+    .cogl_format = COGL_PIXEL_FORMAT_NV21,
+    .format_str = "NV21",
+    .n_planes = 2,
+    .aligned = 0,
+    .bpp = { 1, 2 },
+    .hsub = { 1, 2, 0, 0 },
+    .vsub = { 1, 2, 0, 0 },
+    .subformats = { COGL_PIXEL_FORMAT_R_8, COGL_PIXEL_FORMAT_RG_88 }
+  },
+  {
+    .cogl_format = COGL_PIXEL_FORMAT_NV16,
+    .format_str = "NV16",
+    .n_planes = 2,
+    .aligned = 0,
+    .bpp = { 1, 2 },
+    .hsub = { 1, 2, 0, 0 },
+    .vsub = { 1, 1, 0, 0 },
+    .subformats = { COGL_PIXEL_FORMAT_R_8, COGL_PIXEL_FORMAT_RG_88 }
+  },
+  {
+    .cogl_format = COGL_PIXEL_FORMAT_NV61,
+    .format_str = "NV61",
+    .n_planes = 2,
+    .aligned = 0,
+    .bpp = { 1, 2 },
+    .hsub = { 1, 2, 0, 0 },
+    .vsub = { 1, 1, 0, 0 },
+    .subformats = { COGL_PIXEL_FORMAT_R_8, COGL_PIXEL_FORMAT_RG_88 }
+  },
+  {
+    .cogl_format = COGL_PIXEL_FORMAT_NV24,
+    .format_str = "NV24",
+    .n_planes = 2,
+    .aligned = 0,
+    .bpp = { 1, 2 },
+    .hsub = { 1, 1, 0, 0 },
+    .vsub = { 1, 1, 0, 0 },
+    .subformats = { COGL_PIXEL_FORMAT_R_8, COGL_PIXEL_FORMAT_RG_88 }
+  },
+  {
+    .cogl_format = COGL_PIXEL_FORMAT_NV42,
+    .format_str = "NV42",
+    .n_planes = 2,
+    .aligned = 0,
+    .bpp = { 1, 2 },
+    .hsub = { 1, 1, 0, 0 },
+    .vsub = { 1, 1, 0, 0 },
+    .subformats = { COGL_PIXEL_FORMAT_R_8, COGL_PIXEL_FORMAT_RG_88 }
+  },
+  /* 3 plane YUV */
+  {
+    .cogl_format = COGL_PIXEL_FORMAT_YUV410,
+    .format_str = "YUV410",
+    .n_planes = 3,
+    .aligned = 0,
+    .bpp = { 1, 1, 1 },
+    .hsub = { 1, 4, 4, 0 },
+    .vsub = { 1, 4, 4, 0 },
+    .subformats = { COGL_PIXEL_FORMAT_R_8, COGL_PIXEL_FORMAT_R_8, COGL_PIXEL_FORMAT_R_8 }
+  },
+  {
+    .cogl_format = COGL_PIXEL_FORMAT_YVU410,
+    .format_str = "YVU410",
+    .n_planes = 3,
+    .aligned = 0,
+    .bpp = { 1, 1, 1 },
+    .hsub = { 1, 4, 4, 0 },
+    .vsub = { 1, 4, 4, 0 },
+    .subformats = { COGL_PIXEL_FORMAT_R_8, COGL_PIXEL_FORMAT_R_8, COGL_PIXEL_FORMAT_R_8 }
+  },
+  {
+    .cogl_format = COGL_PIXEL_FORMAT_YUV411,
+    .format_str = "YUV411",
+    .n_planes = 3,
+    .aligned = 0,
+    .bpp = { 1, 1, 1 },
+    .hsub = { 1, 4, 4, 0 },
+    .vsub = { 1, 1, 1, 0 },
+    .subformats = { COGL_PIXEL_FORMAT_R_8, COGL_PIXEL_FORMAT_R_8, COGL_PIXEL_FORMAT_R_8 }
+  },
+  {
+    .cogl_format = COGL_PIXEL_FORMAT_YVU411,
+    .format_str = "YVU411",
+    .n_planes = 3,
+    .aligned = 0,
+    .bpp = { 1, 1, 1 },
+    .hsub = { 1, 4, 4, 0 },
+    .vsub = { 1, 1, 1, 0 },
+    .subformats = { COGL_PIXEL_FORMAT_R_8, COGL_PIXEL_FORMAT_R_8, COGL_PIXEL_FORMAT_R_8 }
+  },
+  {
+    .cogl_format = COGL_PIXEL_FORMAT_YUV420,
+    .format_str = "YUV420",
+    .n_planes = 3,
+    .aligned = 0,
+    .bpp = { 1, 1, 1 },
+    .hsub = { 1, 2, 2, 0 },
+    .vsub = { 1, 2, 2, 0 },
+    .subformats = { COGL_PIXEL_FORMAT_R_8, COGL_PIXEL_FORMAT_R_8, COGL_PIXEL_FORMAT_R_8 }
+  },
+  {
+    .cogl_format = COGL_PIXEL_FORMAT_YVU420,
+    .format_str = "YVU420",
+    .n_planes = 3,
+    .aligned = 0,
+    .bpp = { 1, 1, 1 },
+    .hsub = { 1, 2, 2, 0 },
+    .vsub = { 1, 2, 2, 0 },
+    .subformats = { COGL_PIXEL_FORMAT_R_8, COGL_PIXEL_FORMAT_R_8, COGL_PIXEL_FORMAT_R_8 }
+  },
+  {
+    .cogl_format = COGL_PIXEL_FORMAT_YUV422,
+    .format_str = "YUV422",
+    .n_planes = 3,
+    .aligned = 0,
+    .bpp = { 1, 1, 1 },
+    .hsub = { 1, 2, 2, 0 },
+    .vsub = { 1, 1, 1, 0 },
+    .subformats = { COGL_PIXEL_FORMAT_R_8, COGL_PIXEL_FORMAT_R_8, COGL_PIXEL_FORMAT_R_8 }
+  },
+  {
+    .cogl_format = COGL_PIXEL_FORMAT_YVU422,
+    .format_str = "YVU422",
+    .n_planes = 3,
+    .aligned = 0,
+    .bpp = { 1, 1, 1 },
+    .hsub = { 1, 2, 2, 0 },
+    .vsub = { 1, 1, 1, 0 },
+    .subformats = { COGL_PIXEL_FORMAT_R_8, COGL_PIXEL_FORMAT_R_8, COGL_PIXEL_FORMAT_R_8 }
+  },
+  {
+    .cogl_format = COGL_PIXEL_FORMAT_YUV444,
+    .format_str = "YUV444",
+    .n_planes = 3,
+    .aligned = 0,
+    .bpp = { 1, 1, 1 },
+    .hsub = { 1, 1, 1, 0 },
+    .vsub = { 1, 1, 1, 0 },
+    .subformats = { COGL_PIXEL_FORMAT_R_8, COGL_PIXEL_FORMAT_R_8, COGL_PIXEL_FORMAT_R_8 }
+  },
+  {
+    .cogl_format = COGL_PIXEL_FORMAT_YVU444,
+    .format_str = "YVU444",
+    .n_planes = 3,
+    .aligned = 0,
+    .bpp = { 1, 1, 1 },
+    .hsub = { 1, 1, 1, 0 },
+    .vsub = { 1, 1, 1, 0 },
+    .subformats = { COGL_PIXEL_FORMAT_R_8, COGL_PIXEL_FORMAT_R_8, COGL_PIXEL_FORMAT_R_8 }
   },
 };
 
@@ -254,7 +679,8 @@ static const CoglPixelFormatInfo format_info_table[] = {
  * 13    = 4 bpp, not aligned (e.g. 2101010)
  * 14-15 = undefined
  */
-int
+/* FIXME: this needs to be changed in all places this is called */
+uint8_t
 _cogl_pixel_format_get_bytes_per_pixel (CoglPixelFormat format)
 {
   size_t i;
@@ -262,7 +688,31 @@ _cogl_pixel_format_get_bytes_per_pixel (CoglPixelFormat format)
   for (i = 0; i < G_N_ELEMENTS (format_info_table); i++)
     {
       if (format_info_table[i].cogl_format == format)
-        return format_info_table[i].bpp;
+        return format_info_table[i].bpp[0];
+    }
+
+  g_assert_not_reached ();
+}
+
+/*
+ * XXX document.
+ *
+ * XXX lol, this is even per macropixel, not per pixel :D
+ */
+/* FIXME: this needs to be merged with get_bytes_per_pixel */
+void
+cogl_pixel_format_get_bytes_per_pixel_ (CoglPixelFormat format, uint8_t *bpp_out)
+{
+  size_t i, j;
+
+  for (i = 0; i < G_N_ELEMENTS (format_info_table); i++)
+    {
+      if (format_info_table[i].cogl_format != format)
+        continue;
+
+      for (j = 0; j < format_info_table[i].n_planes; j++)
+        bpp_out[j] = format_info_table[i].bpp[j];
+      return;
     }
 
   g_assert_not_reached ();
@@ -295,6 +745,44 @@ _cogl_pixel_format_is_endian_dependant (CoglPixelFormat format)
   return aligned;
 }
 
+guint
+cogl_pixel_format_get_n_planes (CoglPixelFormat format)
+{
+  guint i;
+
+  for (i = 0; i < G_N_ELEMENTS (format_info_table); i++)
+    {
+      if (format_info_table[i].cogl_format == format)
+        return format_info_table[i].n_planes;
+    }
+
+  g_assert_not_reached ();
+}
+
+void
+cogl_pixel_format_get_subsampling_factors (CoglPixelFormat format,
+                                           uint8_t *horizontal_factors,
+                                           uint8_t *vertical_factors)
+{
+  guint i, j;
+
+  for (i = 0; i < G_N_ELEMENTS (format_info_table); i++)
+    {
+      if (format_info_table[i].cogl_format != format)
+        continue;
+
+      for (j = 0; j < format_info_table[i].n_planes; j++)
+        {
+          horizontal_factors[j] = format_info_table[i].hsub[j];
+          vertical_factors[j] = format_info_table[i].vsub[j];
+        }
+
+      return;
+    }
+
+  g_assert_not_reached ();
+}
+
 const char *
 cogl_pixel_format_to_string (CoglPixelFormat format)
 {
@@ -308,3 +796,51 @@ cogl_pixel_format_to_string (CoglPixelFormat format)
 
   g_assert_not_reached ();
 }
+
+void
+cogl_pixel_format_get_subformats (CoglPixelFormat  format,
+                                  CoglPixelFormat *formats_out)
+{
+  size_t i, j;
+
+  for (i = 0; i < G_N_ELEMENTS (format_info_table); i++)
+    {
+      if (format_info_table[i].cogl_format != format)
+        continue;
+
+      for (j = 0; j < format_info_table[i].n_planes; j++)
+        formats_out[j] = format_info_table[i].subformats[j];
+      return;
+    }
+
+  g_assert_not_reached ();
+}
+
+void
+cogl_pixel_format_get_cogl_components (CoglPixelFormat  format,
+                                       guint           *components_out)
+{
+  switch (format)
+    {
+    case COGL_PIXEL_FORMAT_NV12:
+      components_out[0] = COGL_TEXTURE_COMPONENTS_A;
+      components_out[1] = COGL_TEXTURE_COMPONENTS_RG;
+      break;
+    case COGL_PIXEL_FORMAT_NV21:
+      components_out[0] = COGL_TEXTURE_COMPONENTS_A;
+      components_out[1] = COGL_TEXTURE_COMPONENTS_RG;
+      break;
+    case COGL_PIXEL_FORMAT_YUV422:
+      components_out[0] = COGL_TEXTURE_COMPONENTS_A;
+      components_out[1] = COGL_TEXTURE_COMPONENTS_A;
+      components_out[2] = COGL_TEXTURE_COMPONENTS_A;
+      break;
+    case COGL_PIXEL_FORMAT_YUV444:
+      components_out[0] = COGL_TEXTURE_COMPONENTS_A;
+      components_out[1] = COGL_TEXTURE_COMPONENTS_A;
+      components_out[2] = COGL_TEXTURE_COMPONENTS_A;
+      break;
+    default:
+      components_out[0] = COGL_TEXTURE_COMPONENTS_RGBA;
+    }
+}
diff --git a/cogl/cogl/cogl-pixel-format.h b/cogl/cogl/cogl-pixel-format.h
index 6b015f9e2..2c9471e5e 100644
--- a/cogl/cogl/cogl-pixel-format.h
+++ b/cogl/cogl/cogl-pixel-format.h
@@ -145,7 +145,7 @@ G_BEGIN_DECLS
  * @COGL_PIXEL_FORMAT_RGB_565: RGB, 16 bits
  * @COGL_PIXEL_FORMAT_RGBA_4444: RGBA, 16 bits
  * @COGL_PIXEL_FORMAT_RGBA_5551: RGBA, 16 bits
- * @COGL_PIXEL_FORMAT_YUV: Not currently supported
+ * @COGL_PIXEL_FORMAT_YUV: Obsolete. See the other YUV-based formats.
  * @COGL_PIXEL_FORMAT_R_8: Single red component, 8 bits
  * @COGL_PIXEL_FORMAT_RGB_888: RGB, 24 bits
  * @COGL_PIXEL_FORMAT_BGR_888: BGR, 24 bits
@@ -167,6 +167,35 @@ G_BEGIN_DECLS
  * @COGL_PIXEL_FORMAT_BGRA_1010102_PRE: Premultiplied BGRA, 32 bits, 10 bpc
  * @COGL_PIXEL_FORMAT_ARGB_2101010_PRE: Premultiplied ARGB, 32 bits, 10 bpc
  * @COGL_PIXEL_FORMAT_ABGR_2101010_PRE: Premultiplied ABGR, 32 bits, 10 bpc
+ * @COGL_PIXEL_FORMAT_YUYV: YUYV, 32 bits, 16 bpc (Y), 8 bpc (U & V)
+ * @COGL_PIXEL_FORMAT_YVYU: YVYU, 32 bits, 16 bpc (Y), 8 bpc (V & U)
+ * @COGL_PIXEL_FORMAT_UYVY: UYVY, 32 bits, 16 bpc (Y), 8 bpc (V & U)
+ * @COGL_PIXEL_FORMAT_VYUY: VYUV, 32 bits, 16 bpc (Y), 8 bpc (V & U)
+ * @COGL_PIXEL_FORMAT_AYUV: AYUV, 32 bits, 8 bpc
+ * @COGL_PIXEL_FORMAT_XRGB8888_A8: 2 planes: 1 RGB-plane (64-bit), 1 alpha-plane
+ * @COGL_PIXEL_FORMAT_XBGR8888_A8: 2 planes: 1 BGR-plane (64-bit), 1 alpha-plane
+ * @COGL_PIXEL_FORMAT_RGBX8888_A8: 2 planes: 1 RGB-plane (64-bit), 1 alpha-plane
+ * @COGL_PIXEL_FORMAT_BGRX8888_A8: 2 planes: 1 BGR-plane (64-bit), 1 alpha-plane
+ * @COGL_PIXEL_FORMAT_RGB888_A8: 2 planes: 1 RGB-plane (32-bit), 1 alpha-plane
+ * @COGL_PIXEL_FORMAT_BGR888_A8: 2 planes: 1 BGR-plane (32-bit), 1 alpha-plane
+ * @COGL_PIXEL_FORMAT_RGB565_A8: 2 planes: 1 RGB-plane (16-bit), 1 alpha-plane
+ * @COGL_PIXEL_FORMAT_BGR565_A8: 2 planes: 1 BGR-plane (16-bit), 1 alpha-plane
+ * @COGL_PIXEL_FORMAT_NV12: 2 planes: 1 Y-plane, 1 UV-plane (2x2 subsampled)
+ * @COGL_PIXEL_FORMAT_NV21: 2 planes: 1 Y-plane, 1 VU-plane (2x2 subsampled)
+ * @COGL_PIXEL_FORMAT_NV16: 2 planes: 1 Y-plane, 1 UV-plane (2x1 subsampled)
+ * @COGL_PIXEL_FORMAT_NV61: 2 planes: 1 Y-plane, 1 VU-plane (2x1 subsampled)
+ * @COGL_PIXEL_FORMAT_NV24: 2 planes: 1 Y-plane, 1 UV-plane
+ * @COGL_PIXEL_FORMAT_NV42: 2 planes: 1 Y-plane, 1 VU-plane
+ * @COGL_PIXEL_FORMAT_YUV410: 3 planes: 1 Y-plane, 1 U-plane (4x4 subsampled), 1 V-plane (4x4 subsampled)
+ * @COGL_PIXEL_FORMAT_YVU410: 3 planes: 1 Y-plane, 1 V-plane (4x4 subsampled), 1 U-plane (4x4 subsampled)
+ * @COGL_PIXEL_FORMAT_YUV411: 3 planes: 1 Y-plane, 1 U-plane (4x1 subsampled), 1 V-plane (4x1 subsampled)
+ * @COGL_PIXEL_FORMAT_YVU411: 3 planes: 1 Y-plane, 1 V-plane (4x1 subsampled), 1 U-plane (4x1 subsampled)
+ * @COGL_PIXEL_FORMAT_YUV420: 3 planes: 1 Y-plane, 1 U-plane (2x2 subsampled), 1 V-plane (2x2 subsampled)
+ * @COGL_PIXEL_FORMAT_YVU420: 3 planes: 1 Y-plane, 1 V-plane (2x2 subsampled), 1 U-plane (2x2 subsampled)
+ * @COGL_PIXEL_FORMAT_YUV422: 3 planes: 1 Y-plane, 1 U-plane (2x1 subsampled), 1 V-plane (2x1 subsampled)
+ * @COGL_PIXEL_FORMAT_YVU422: 3 planes: 1 Y-plane, 1 V-plane (2x1 subsampled), 1 U-plane (2x1 subsampled)
+ * @COGL_PIXEL_FORMAT_YUV444: 3 planes: 1 Y-plane, 1 U-plane, 1 V-plane
+ * @COGL_PIXEL_FORMAT_YVU444: 3 planes: 1 Y-plane, 1 V-plane, 1 U-plane
  *
  * Pixel formats used by Cogl. For the formats with a byte per
  * component, the order of the components specify the order in
@@ -230,9 +259,59 @@ typedef enum /*< prefix=COGL_PIXEL_FORMAT >*/
   COGL_PIXEL_FORMAT_DEPTH_16  = (9 | COGL_DEPTH_BIT),
   COGL_PIXEL_FORMAT_DEPTH_32  = (3 | COGL_DEPTH_BIT),
 
-  COGL_PIXEL_FORMAT_DEPTH_24_STENCIL_8 = (3 | COGL_DEPTH_BIT | COGL_STENCIL_BIT)
+  COGL_PIXEL_FORMAT_DEPTH_24_STENCIL_8 = (3 | COGL_DEPTH_BIT | COGL_STENCIL_BIT),
+
+ /* From here on out, we simply enumerate with sequential values in the most
+  * significant enum byte. See the comments above if you want to know why. */
+
+  /* The following list is basically synced with Linux's <drm_fourcc.h> */
+
+  /* Packed YUV */
+  COGL_PIXEL_FORMAT_YUYV = (1 << 24),
+  COGL_PIXEL_FORMAT_YVYU = (2 << 24),
+  COGL_PIXEL_FORMAT_UYVY = (3 << 24),
+  COGL_PIXEL_FORMAT_VYUY = (4 << 24),
+
+  COGL_PIXEL_FORMAT_AYUV = (5 << 24),
+
+  /* 2 plane RGB + A */
+  COGL_PIXEL_FORMAT_XRGB8888_A8 = ( 6 << 24),
+  COGL_PIXEL_FORMAT_XBGR8888_A8 = ( 7 << 24),
+  COGL_PIXEL_FORMAT_RGBX8888_A8 = ( 8 << 24),
+  COGL_PIXEL_FORMAT_BGRX8888_A8 = ( 9 << 24),
+  COGL_PIXEL_FORMAT_RGB888_A8    = (10 << 24),
+  COGL_PIXEL_FORMAT_BGR888_A8    = (11 << 24),
+  COGL_PIXEL_FORMAT_RGB565_A8    = (12 << 24),
+  COGL_PIXEL_FORMAT_BGR565_A8    = (13 << 24),
+
+  /* 2 plane YUV */
+  COGL_PIXEL_FORMAT_NV12 = (14 << 24),
+  COGL_PIXEL_FORMAT_NV21 = (15 << 24),
+  COGL_PIXEL_FORMAT_NV16 = (16 << 24),
+  COGL_PIXEL_FORMAT_NV61 = (17 << 24),
+  COGL_PIXEL_FORMAT_NV24 = (18 << 24),
+  COGL_PIXEL_FORMAT_NV42 = (19 << 24),
+
+  /* 3 plane YUV */
+  COGL_PIXEL_FORMAT_YUV410 = (20 << 24),
+  COGL_PIXEL_FORMAT_YVU410 = (21 << 24),
+  COGL_PIXEL_FORMAT_YUV411 = (22 << 24),
+  COGL_PIXEL_FORMAT_YVU411 = (23 << 24),
+  COGL_PIXEL_FORMAT_YUV420 = (24 << 24),
+  COGL_PIXEL_FORMAT_YVU420 = (25 << 24),
+  COGL_PIXEL_FORMAT_YUV422 = (26 << 24),
+  COGL_PIXEL_FORMAT_YVU422 = (27 << 24),
+  COGL_PIXEL_FORMAT_YUV444 = (28 << 24),
+  COGL_PIXEL_FORMAT_YVU444 = (29 << 24)
 } CoglPixelFormat;
 
+/**
+ * COGL_PIXEL_FORMAT_MAX_PLANES:
+ *
+ * The maximum number of planes
+ */
+#define COGL_PIXEL_FORMAT_MAX_PLANES (4)
+
 /*
  * _cogl_pixel_format_get_bytes_per_pixel:
  * @format: a #CoglPixelFormat
@@ -242,7 +321,7 @@ typedef enum /*< prefix=COGL_PIXEL_FORMAT >*/
  * Return value: The number of bytes taken for a pixel of the given
  *               @format.
  */
-int
+uint8_t
 _cogl_pixel_format_get_bytes_per_pixel (CoglPixelFormat format);
 
 /*
@@ -284,6 +363,29 @@ _cogl_pixel_format_is_endian_dependant (CoglPixelFormat format);
 #define COGL_PIXEL_FORMAT_CAN_HAVE_PREMULT(format) \
   (((format) & COGL_A_BIT) && (format) != COGL_PIXEL_FORMAT_A_8)
 
+/**
+ * cogl_pixel_format_get_n_planes:
+ * @format: The format for which to get the number of planes
+ *
+ * Returns the number of planes the given CoglPixelFormat specifies.
+ */
+guint
+cogl_pixel_format_get_n_planes (CoglPixelFormat format);
+
+/**
+ * cogl_pixel_format_get_subsampling_factors:
+ * @format: The format to get the subsampling factors from.
+ *
+ * Returns the subsampling in both the horizontal as the vertical direction.
+ */
+void
+cogl_pixel_format_get_subsampling_factors (CoglPixelFormat format,
+                                           uint8_t *horizontal_factors,
+                                           uint8_t *vertical_factors);
+
+void
+cogl_pixel_format_get_bytes_per_pixel_ (CoglPixelFormat format, uint8_t *bpp_out);
+
 /**
  * cogl_pixel_format_to_string:
  * @format: a #CoglPixelFormat
@@ -295,6 +397,14 @@ _cogl_pixel_format_is_endian_dependant (CoglPixelFormat format);
 const char *
 cogl_pixel_format_to_string (CoglPixelFormat format);
 
+void
+cogl_pixel_format_get_subformats (CoglPixelFormat  format,
+                                  CoglPixelFormat *formats_out);
+
+void
+cogl_pixel_format_get_cogl_components (CoglPixelFormat format,
+                                       guint *components_out);
+
 G_END_DECLS
 
 #endif /* __COGL_PIXEL_FORMAT_H__ */
diff --git a/cogl/cogl/cogl-texture-2d.c b/cogl/cogl/cogl-texture-2d.c
index 24fc426a7..7b559d60d 100644
--- a/cogl/cogl/cogl-texture-2d.c
+++ b/cogl/cogl/cogl-texture-2d.c
@@ -240,6 +240,7 @@ cogl_egl_texture_2d_new_from_image (CoglContext *ctx,
                                     int width,
                                     int height,
                                     CoglPixelFormat format,
+                                    CoglTextureComponents components,
                                     EGLImageKHR image,
                                     CoglError **error)
 {
@@ -264,6 +265,9 @@ cogl_egl_texture_2d_new_from_image (CoglContext *ctx,
 
   tex = _cogl_texture_2d_create_base (ctx, width, height, format, loader);
 
+  /* Make sure we've set the right components before allocating */
+  cogl_texture_set_components (COGL_TEXTURE (tex), components);
+
   if (!cogl_texture_allocate (COGL_TEXTURE (tex), error))
     {
       cogl_object_unref (tex);
@@ -434,6 +438,7 @@ cogl_wayland_texture_2d_new_from_buffer (CoglContext *ctx,
           tex = cogl_egl_texture_2d_new_from_image (ctx,
                                                     width, height,
                                                     internal_format,
+                                                    COGL_TEXTURE_COMPONENTS_RGBA,
                                                     image,
                                                     error);
           _cogl_egl_destroy_image (ctx, image);
diff --git a/cogl/cogl/cogl-texture-2d.h b/cogl/cogl/cogl-texture-2d.h
index e649f24f7..f96e715b1 100644
--- a/cogl/cogl/cogl-texture-2d.h
+++ b/cogl/cogl/cogl-texture-2d.h
@@ -218,6 +218,7 @@ cogl_egl_texture_2d_new_from_image (CoglContext *ctx,
                                     int width,
                                     int height,
                                     CoglPixelFormat format,
+                                    CoglTextureComponents components,
                                     EGLImageKHR image,
                                     CoglError **error);
 
diff --git a/cogl/cogl/cogl.h b/cogl/cogl/cogl.h
index 14ccf5748..9b36d7593 100644
--- a/cogl/cogl/cogl.h
+++ b/cogl/cogl/cogl.h
@@ -61,6 +61,7 @@
 #include <cogl/cogl1-context.h>
 #include <cogl/cogl-bitmap.h>
 #include <cogl/cogl-color.h>
+#include <cogl/cogl-pixel-format-conversion.h>
 #include <cogl/cogl-matrix.h>
 #include <cogl/cogl-matrix-stack.h>
 #include <cogl/cogl-offscreen.h>
@@ -110,6 +111,7 @@
 #include <cogl/cogl-sub-texture.h>
 #include <cogl/cogl-atlas-texture.h>
 #include <cogl/cogl-meta-texture.h>
+#include <cogl/cogl-multi-plane-texture.h>
 #include <cogl/cogl-primitive-texture.h>
 #include <cogl/cogl-index-buffer.h>
 #include <cogl/cogl-attribute-buffer.h>
diff --git a/cogl/cogl/driver/gl/cogl-texture-2d-gl.c b/cogl/cogl/driver/gl/cogl-texture-2d-gl.c
index 2f9e3424c..fe4a53dfc 100644
--- a/cogl/cogl/driver/gl/cogl-texture-2d-gl.c
+++ b/cogl/cogl/driver/gl/cogl-texture-2d-gl.c
@@ -202,6 +202,8 @@ allocate_from_bitmap (CoglTexture2D *tex_2d,
   GLenum gl_format;
   GLenum gl_type;
 
+  g_warning ("allocate_from_bitmap()");
+
   internal_format =
     _cogl_texture_determine_internal_format (tex, cogl_bitmap_get_format (bmp));
 
@@ -342,6 +344,8 @@ allocate_from_gl_foreign (CoglTexture2D *tex_2d,
   GLint gl_compressed = GL_FALSE;
   GLenum gl_int_format = 0;
 
+  g_warning ("allocate_from_egl_image_foreign()");
+
   if (!ctx->texture_driver->allows_foreign_gl_target (ctx, GL_TEXTURE_2D))
     {
       _cogl_set_error (error,
diff --git a/cogl/cogl/driver/gl/gl/cogl-driver-gl.c b/cogl/cogl/driver/gl/gl/cogl-driver-gl.c
index a04540341..d9e95748c 100644
--- a/cogl/cogl/driver/gl/gl/cogl-driver-gl.c
+++ b/cogl/cogl/driver/gl/gl/cogl-driver-gl.c
@@ -261,7 +261,37 @@ _cogl_driver_pixel_format_to_gl (CoglContext     *context,
       break;
 
     case COGL_PIXEL_FORMAT_ANY:
+    /* No support for YUV or multi-plane formats */
     case COGL_PIXEL_FORMAT_YUV:
+    case COGL_PIXEL_FORMAT_YUYV:
+    case COGL_PIXEL_FORMAT_YVYU:
+    case COGL_PIXEL_FORMAT_UYVY:
+    case COGL_PIXEL_FORMAT_VYUY:
+    case COGL_PIXEL_FORMAT_AYUV:
+    case COGL_PIXEL_FORMAT_XRGB8888_A8:
+    case COGL_PIXEL_FORMAT_XBGR8888_A8:
+    case COGL_PIXEL_FORMAT_RGBX8888_A8:
+    case COGL_PIXEL_FORMAT_BGRX8888_A8:
+    case COGL_PIXEL_FORMAT_RGB888_A8:
+    case COGL_PIXEL_FORMAT_BGR888_A8:
+    case COGL_PIXEL_FORMAT_RGB565_A8:
+    case COGL_PIXEL_FORMAT_BGR565_A8:
+    case COGL_PIXEL_FORMAT_NV12:
+    case COGL_PIXEL_FORMAT_NV21:
+    case COGL_PIXEL_FORMAT_NV16:
+    case COGL_PIXEL_FORMAT_NV61:
+    case COGL_PIXEL_FORMAT_NV24:
+    case COGL_PIXEL_FORMAT_NV42:
+    case COGL_PIXEL_FORMAT_YUV410:
+    case COGL_PIXEL_FORMAT_YVU410:
+    case COGL_PIXEL_FORMAT_YUV411:
+    case COGL_PIXEL_FORMAT_YVU411:
+    case COGL_PIXEL_FORMAT_YUV420:
+    case COGL_PIXEL_FORMAT_YVU420:
+    case COGL_PIXEL_FORMAT_YUV422:
+    case COGL_PIXEL_FORMAT_YVU422:
+    case COGL_PIXEL_FORMAT_YUV444:
+    case COGL_PIXEL_FORMAT_YVU444:
       g_assert_not_reached ();
       break;
     }
diff --git a/cogl/cogl/driver/gl/gl/cogl-texture-driver-gl.c b/cogl/cogl/driver/gl/gl/cogl-texture-driver-gl.c
index c2270124f..84d87138c 100644
--- a/cogl/cogl/driver/gl/gl/cogl-texture-driver-gl.c
+++ b/cogl/cogl/driver/gl/gl/cogl-texture-driver-gl.c
@@ -98,6 +98,8 @@ _cogl_texture_driver_gen (CoglContext *ctx,
       g_assert_not_reached();
     }
 
+  g_warning ("_cogl_texture_driver_gen %s", cogl_pixel_format_to_string (internal_format));
+
   /* If the driver doesn't support alpha textures directly then we'll
    * fake them by setting the swizzle parameters */
   if (internal_format == COGL_PIXEL_FORMAT_A_8 &&
@@ -203,6 +205,8 @@ _cogl_texture_driver_upload_subregion_to_gl (CoglContext *ctx,
   int level_width;
   int level_height;
 
+  g_warning ("uploading subregion to gl");
+
   cogl_texture_get_gl_texture (texture, &gl_handle, &gl_target);
 
   data = _cogl_bitmap_gl_bind (source_bmp, COGL_BUFFER_ACCESS_READ, 0, &internal_error);
@@ -307,6 +311,8 @@ _cogl_texture_driver_upload_to_gl (CoglContext *ctx,
   gboolean status = TRUE;
   CoglError *internal_error = NULL;
 
+  g_warning ("uploading to gl");
+
   data = _cogl_bitmap_gl_bind (source_bmp,
                                COGL_BUFFER_ACCESS_READ,
                                0, /* hints */
diff --git a/cogl/cogl/driver/gl/gles/cogl-driver-gles.c b/cogl/cogl/driver/gl/gles/cogl-driver-gles.c
index 29137c367..0823558c9 100644
--- a/cogl/cogl/driver/gl/gles/cogl-driver-gles.c
+++ b/cogl/cogl/driver/gl/gles/cogl-driver-gles.c
@@ -199,7 +199,37 @@ _cogl_driver_pixel_format_to_gl (CoglContext     *context,
       break;
 
     case COGL_PIXEL_FORMAT_ANY:
+    /* No support for YUV or multi-plane formats */
     case COGL_PIXEL_FORMAT_YUV:
+    case COGL_PIXEL_FORMAT_YUYV:
+    case COGL_PIXEL_FORMAT_YVYU:
+    case COGL_PIXEL_FORMAT_UYVY:
+    case COGL_PIXEL_FORMAT_VYUY:
+    case COGL_PIXEL_FORMAT_AYUV:
+    case COGL_PIXEL_FORMAT_XRGB8888_A8:
+    case COGL_PIXEL_FORMAT_XBGR8888_A8:
+    case COGL_PIXEL_FORMAT_RGBX8888_A8:
+    case COGL_PIXEL_FORMAT_BGRX8888_A8:
+    case COGL_PIXEL_FORMAT_RGB888_A8:
+    case COGL_PIXEL_FORMAT_BGR888_A8:
+    case COGL_PIXEL_FORMAT_RGB565_A8:
+    case COGL_PIXEL_FORMAT_BGR565_A8:
+    case COGL_PIXEL_FORMAT_NV12:
+    case COGL_PIXEL_FORMAT_NV21:
+    case COGL_PIXEL_FORMAT_NV16:
+    case COGL_PIXEL_FORMAT_NV61:
+    case COGL_PIXEL_FORMAT_NV24:
+    case COGL_PIXEL_FORMAT_NV42:
+    case COGL_PIXEL_FORMAT_YUV410:
+    case COGL_PIXEL_FORMAT_YVU410:
+    case COGL_PIXEL_FORMAT_YUV411:
+    case COGL_PIXEL_FORMAT_YVU411:
+    case COGL_PIXEL_FORMAT_YUV420:
+    case COGL_PIXEL_FORMAT_YVU420:
+    case COGL_PIXEL_FORMAT_YUV422:
+    case COGL_PIXEL_FORMAT_YVU422:
+    case COGL_PIXEL_FORMAT_YUV444:
+    case COGL_PIXEL_FORMAT_YVU444:
       g_assert_not_reached ();
       break;
     }
diff --git a/cogl/cogl/meson.build b/cogl/cogl/meson.build
index f89b4fb7d..8064ab875 100644
--- a/cogl/cogl/meson.build
+++ b/cogl/cogl/meson.build
@@ -89,6 +89,7 @@ cogl_headers = [
   'cogl-pipeline-state.h',
   'cogl-pipeline-layer-state.h',
   'cogl-pixel-format.h',
+  'cogl-pixel-format-conversion.h',
   'cogl-primitives.h',
   'cogl-texture.h',
   'cogl-texture-2d.h',
@@ -124,6 +125,7 @@ cogl_nonintrospected_headers = [
   'cogl-sub-texture.h',
   'cogl-atlas-texture.h',
   'cogl-meta-texture.h',
+  'cogl-multi-plane-texture.h',
   'cogl-primitive-texture.h',
   'cogl-depth-state.h',
   'cogl-buffer.h',
@@ -250,10 +252,11 @@ cogl_sources = [
   'cogl-bitmap-pixbuf.c',
   'cogl-clip-stack.h',
   'cogl-clip-stack.c',
-  'cogl-feature-private.h',
-  'cogl-feature-private.c',
   'cogl-color-private.h',
   'cogl-color.c',
+  'cogl-pixel-format-conversion.c',
+  'cogl-feature-private.c',
+  'cogl-feature-private.h',
   'cogl-buffer-private.h',
   'cogl-buffer.c',
   'cogl-pixel-buffer-private.h',
@@ -321,6 +324,7 @@ cogl_sources = [
   'cogl-atlas-texture-private.h',
   'cogl-atlas-texture.c',
   'cogl-meta-texture.c',
+  'cogl-multi-plane-texture.c',
   'cogl-primitive-texture.c',
   'cogl-blit.h',
   'cogl-blit.c',
diff --git a/cogl/cogl/winsys/cogl-winsys-egl-x11.c b/cogl/cogl/winsys/cogl-winsys-egl-x11.c
index 4f0a12543..ed07c7091 100644
--- a/cogl/cogl/winsys/cogl-winsys-egl-x11.c
+++ b/cogl/cogl/winsys/cogl-winsys-egl-x11.c
@@ -801,6 +801,7 @@ _cogl_winsys_texture_pixmap_x11_create (CoglTexturePixmapX11 *tex_pixmap)
                                         tex->width,
                                         tex->height,
                                         texture_format,
+                                        COGL_TEXTURE_COMPONENTS_RGBA,
                                         egl_tex_pixmap->image,
                                         NULL));
 
diff --git a/src/compositor/meta-shaped-texture-private.h b/src/compositor/meta-shaped-texture-private.h
index a86a2bff0..43be08127 100644
--- a/src/compositor/meta-shaped-texture-private.h
+++ b/src/compositor/meta-shaped-texture-private.h
@@ -32,7 +32,7 @@
 
 ClutterActor *meta_shaped_texture_new (void);
 void meta_shaped_texture_set_texture (MetaShapedTexture *stex,
-                                      CoglTexture       *texture);
+                                      CoglMultiPlaneTexture *texture);
 void meta_shaped_texture_set_is_y_inverted (MetaShapedTexture *stex,
                                             gboolean           is_y_inverted);
 void meta_shaped_texture_set_snippet (MetaShapedTexture *stex,
diff --git a/src/compositor/meta-shaped-texture.c b/src/compositor/meta-shaped-texture.c
index 90a02210d..c6dfa76e7 100644
--- a/src/compositor/meta-shaped-texture.c
+++ b/src/compositor/meta-shaped-texture.c
@@ -89,7 +89,7 @@ struct _MetaShapedTexture
 
   MetaTextureTower *paint_tower;
 
-  CoglTexture *texture;
+  CoglMultiPlaneTexture *texture;
   CoglTexture *mask_texture;
   CoglSnippet *snippet;
 
@@ -97,6 +97,8 @@ struct _MetaShapedTexture
   CoglPipeline *masked_pipeline;
   CoglPipeline *unblended_pipeline;
 
+  CoglPixelFormatConversion *pixel_format_conversion;
+
   gboolean is_y_inverted;
 
   /* The region containing only fully opaque pixels */
@@ -277,9 +279,13 @@ set_clip_region (MetaShapedTexture *stex,
 static void
 meta_shaped_texture_reset_pipelines (MetaShapedTexture *stex)
 {
-  g_clear_pointer (&stex->base_pipeline, cogl_object_unref);
-  g_clear_pointer (&stex->masked_pipeline, cogl_object_unref);
-  g_clear_pointer (&stex->unblended_pipeline, cogl_object_unref);
+  g_warning ("resetting pipelines!");
+
+  cogl_clear_object (&stex->pixel_format_conversion);
+
+  cogl_clear_object (&stex->base_pipeline);
+  cogl_clear_object (&stex->masked_pipeline);
+  cogl_clear_object (&stex->unblended_pipeline);
 }
 
 static void
@@ -297,7 +303,7 @@ meta_shaped_texture_dispose (GObject *object)
     meta_texture_tower_free (stex->paint_tower);
   stex->paint_tower = NULL;
 
-  g_clear_pointer (&stex->texture, cogl_object_unref);
+  cogl_clear_object (&stex->texture);
   g_clear_pointer (&stex->opaque_region, cairo_region_destroy);
 
   meta_shaped_texture_set_mask_texture (stex, NULL);
@@ -306,7 +312,7 @@ meta_shaped_texture_dispose (GObject *object)
 
   meta_shaped_texture_reset_pipelines (stex);
 
-  g_clear_pointer (&stex->snippet, cogl_object_unref);
+  cogl_clear_object (&stex->snippet);
 
   G_OBJECT_CLASS (meta_shaped_texture_parent_class)->dispose (object);
 }
@@ -317,19 +323,24 @@ get_base_pipeline (MetaShapedTexture *stex,
 {
   CoglPipeline *pipeline;
   CoglMatrix matrix;
+  CoglPixelFormat format;
+  guint n_planes;
+  guint i = 0;
 
   if (stex->base_pipeline)
     return stex->base_pipeline;
 
   pipeline = cogl_pipeline_new (ctx);
-  cogl_pipeline_set_layer_wrap_mode_s (pipeline, 0,
-                                       COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE);
-  cogl_pipeline_set_layer_wrap_mode_t (pipeline, 0,
-                                       COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE);
-  cogl_pipeline_set_layer_wrap_mode_s (pipeline, 1,
-                                       COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE);
-  cogl_pipeline_set_layer_wrap_mode_t (pipeline, 1,
-                                       COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE);
+
+  format = cogl_multi_plane_texture_get_format (stex->texture);
+  n_planes = cogl_multi_plane_texture_get_n_planes (stex->texture);
+  for (i = 0; i < n_planes; i++)
+    {
+      cogl_pipeline_set_layer_wrap_mode_s (pipeline, i,
+                                           COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE);
+      cogl_pipeline_set_layer_wrap_mode_t (pipeline, i,
+                                           COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE);
+    }
 
   cogl_matrix_init_identity (&matrix);
 
@@ -409,11 +420,24 @@ get_base_pipeline (MetaShapedTexture *stex,
                              0);
     }
 
-  cogl_pipeline_set_layer_matrix (pipeline, 0, &matrix);
-  cogl_pipeline_set_layer_matrix (pipeline, 1, &matrix);
+  for (i = 0; i < n_planes + 1; i++)
+    cogl_pipeline_set_layer_matrix (pipeline, i, &matrix);
+
+  cogl_clear_object (&stex->pixel_format_conversion);
+  stex->pixel_format_conversion = cogl_pixel_format_conversion_new (format);
+
+  if (stex->pixel_format_conversion)
+    {
+      cogl_pixel_format_conversion_attach_to_pipeline (stex->pixel_format_conversion,
+                                                       pipeline,
+                                                       n_planes - 1);
+    }
 
   if (stex->snippet)
-    cogl_pipeline_add_layer_snippet (pipeline, 0, stex->snippet);
+    {
+      for (i = 0; i < n_planes; i++)
+        cogl_pipeline_add_layer_snippet (pipeline, i, stex->snippet);
+    }
 
   stex->base_pipeline = pipeline;
 
@@ -432,12 +456,14 @@ get_masked_pipeline (MetaShapedTexture *stex,
                      CoglContext       *ctx)
 {
   CoglPipeline *pipeline;
+  guint n_layers;
 
   if (stex->masked_pipeline)
     return stex->masked_pipeline;
 
   pipeline = cogl_pipeline_copy (get_base_pipeline (stex, ctx));
-  cogl_pipeline_set_layer_combine (pipeline, 1,
+  n_layers = cogl_multi_plane_texture_get_n_planes (stex->texture);
+  cogl_pipeline_set_layer_combine (pipeline, n_layers,
                                    "RGBA = MODULATE (PREVIOUS, TEXTURE[A])",
                                    NULL);
 
@@ -451,17 +477,15 @@ get_unblended_pipeline (MetaShapedTexture *stex,
                         CoglContext       *ctx)
 {
   CoglPipeline *pipeline;
-  CoglColor color;
 
   if (stex->unblended_pipeline)
     return stex->unblended_pipeline;
 
   pipeline = cogl_pipeline_copy (get_base_pipeline (stex, ctx));
-  cogl_color_init_from_4ub (&color, 255, 255, 255, 255);
   cogl_pipeline_set_blend (pipeline,
                            "RGBA = ADD (SRC_COLOR, 0)",
                            NULL);
-  cogl_pipeline_set_color (pipeline, &color);
+  cogl_pipeline_set_color4ub (pipeline, 255, 255, 255, 255);
 
   stex->unblended_pipeline = pipeline;
 
@@ -503,23 +527,21 @@ paint_clipped_rectangle (MetaShapedTexture     *stex,
 }
 
 static void
-set_cogl_texture (MetaShapedTexture *stex,
-                  CoglTexture       *cogl_tex)
+set_planar_texture (MetaShapedTexture *stex,
+                    CoglMultiPlaneTexture *planar_tex)
 {
   int width, height;
 
   g_return_if_fail (META_IS_SHAPED_TEXTURE (stex));
 
-  if (stex->texture)
-    cogl_object_unref (stex->texture);
-
-  stex->texture = cogl_tex;
+  cogl_clear_object (&stex->texture);
+  stex->texture = planar_tex;
 
-  if (cogl_tex != NULL)
+  if (planar_tex != NULL)
     {
-      cogl_object_ref (cogl_tex);
-      width = cogl_texture_get_width (COGL_TEXTURE (cogl_tex));
-      height = cogl_texture_get_height (COGL_TEXTURE (cogl_tex));
+      cogl_object_ref (planar_tex);
+      width = cogl_multi_plane_texture_get_width (planar_tex);
+      height = cogl_multi_plane_texture_get_height (planar_tex);
     }
   else
     {
@@ -540,8 +562,11 @@ set_cogl_texture (MetaShapedTexture *stex,
    * previous buffer. We only queue a redraw in response to surface
    * damage. */
 
+  /* if (FALSE) */
   if (stex->create_mipmaps)
-    meta_texture_tower_set_base_texture (stex->paint_tower, cogl_tex);
+    {
+      meta_texture_tower_set_base_texture (stex->paint_tower, planar_tex);
+    }
 }
 
 static gboolean
@@ -559,10 +584,10 @@ texture_is_idle_and_not_mipmapped (gpointer user_data)
 }
 
 static void
-do_paint (MetaShapedTexture *stex,
-          CoglFramebuffer   *fb,
-          CoglTexture       *paint_tex,
-          cairo_region_t    *clip_region)
+do_paint (MetaShapedTexture      *stex,
+          CoglFramebuffer        *fb,
+          CoglMultiPlaneTexture  *paint_tex,
+          cairo_region_t         *clip_region)
 {
   double tex_scale;
   int dst_width, dst_height;
@@ -575,8 +600,12 @@ do_paint (MetaShapedTexture *stex,
   CoglContext *ctx;
   ClutterActorBox alloc;
   CoglPipelineFilter filter;
+  guint n_planes;
 
   clutter_actor_get_scale (CLUTTER_ACTOR (stex), &tex_scale, NULL);
+
+  n_planes = cogl_multi_plane_texture_get_n_planes (paint_tex);
+
   ensure_size_valid (stex);
   dst_width = stex->dst_width;
 
@@ -682,8 +711,15 @@ do_paint (MetaShapedTexture *stex,
       if (!cairo_region_is_empty (region))
         {
           opaque_pipeline = get_unblended_pipeline (stex, ctx);
-          cogl_pipeline_set_layer_texture (opaque_pipeline, 0, paint_tex);
-          cogl_pipeline_set_layer_filters (opaque_pipeline, 0, filter, filter);
+
+          for (i = 0; i < n_planes; i++)
+            {
+              CoglTexture *plane = cogl_multi_plane_texture_get_plane (paint_tex, i);
+
+
+              cogl_pipeline_set_layer_texture (opaque_pipeline, i, plane);
+              cogl_pipeline_set_layer_filters (opaque_pipeline, i, filter, filter);
+            }
 
           n_rects = cairo_region_num_rectangles (region);
           for (i = 0; i < n_rects; i++)
@@ -714,6 +750,7 @@ do_paint (MetaShapedTexture *stex,
   if (!blended_tex_region || !cairo_region_is_empty (blended_tex_region))
     {
       CoglPipeline *blended_pipeline;
+      guint i;
 
       if (stex->mask_texture == NULL)
         {
@@ -722,16 +759,21 @@ do_paint (MetaShapedTexture *stex,
       else
         {
           blended_pipeline = get_masked_pipeline (stex, ctx);
-          cogl_pipeline_set_layer_texture (blended_pipeline, 1, stex->mask_texture);
-          cogl_pipeline_set_layer_filters (blended_pipeline, 1, filter, filter);
+          cogl_pipeline_set_layer_texture (blended_pipeline, n_planes, stex->mask_texture);
+          cogl_pipeline_set_layer_filters (blended_pipeline, n_planes, filter, filter);
         }
 
-      cogl_pipeline_set_layer_texture (blended_pipeline, 0, paint_tex);
-      cogl_pipeline_set_layer_filters (blended_pipeline, 0, filter, filter);
+      for (i = 0; i < n_planes; i++)
+        {
+          CoglTexture *plane = cogl_multi_plane_texture_get_plane (paint_tex, i);
+          /* g_warning ("Blended pipeline: adding layer %d, %p", i, plane); */
+
+          cogl_pipeline_set_layer_texture (blended_pipeline, i, plane);
+          cogl_pipeline_set_layer_filters (blended_pipeline, i, filter, filter);
+        }
 
-      CoglColor color;
-      cogl_color_init_from_4ub (&color, opacity, opacity, opacity, opacity);
-      cogl_pipeline_set_color (blended_pipeline, &color);
+      cogl_pipeline_set_color4ub (blended_pipeline,
+                                  opacity, opacity, opacity, opacity);
 
       if (blended_tex_region)
         {
@@ -773,7 +815,7 @@ static void
 meta_shaped_texture_paint (ClutterActor *actor)
 {
   MetaShapedTexture *stex = META_SHAPED_TEXTURE (actor);
-  CoglTexture *paint_tex;
+  CoglMultiPlaneTexture *paint_tex;
   CoglFramebuffer *fb;
 
   if (!stex->texture)
@@ -828,11 +870,11 @@ meta_shaped_texture_paint (ClutterActor *actor)
     }
   else
     {
-      paint_tex = COGL_TEXTURE (stex->texture);
+      paint_tex = stex->texture;
     }
 
-  if (cogl_texture_get_width (paint_tex) == 0 ||
-      cogl_texture_get_height (paint_tex) == 0)
+  if (cogl_multi_plane_texture_get_width (paint_tex) == 0 ||
+      cogl_multi_plane_texture_get_height (paint_tex) == 0)
     return;
 
   fb = cogl_get_draw_framebuffer ();
@@ -906,7 +948,7 @@ meta_shaped_texture_set_create_mipmaps (MetaShapedTexture *stex,
 
   if (create_mipmaps != stex->create_mipmaps)
     {
-      CoglTexture *base_texture;
+      CoglMultiPlaneTexture *base_texture;
       stex->create_mipmaps = create_mipmaps;
       base_texture = create_mipmaps ? stex->texture : NULL;
       meta_texture_tower_set_base_texture (stex->paint_tower, base_texture);
@@ -1089,15 +1131,15 @@ meta_shaped_texture_update_area (MetaShapedTexture *stex,
 /**
  * meta_shaped_texture_set_texture:
  * @stex: The #MetaShapedTexture
- * @pixmap: The #CoglTexture to display
+ * @pixmap: The #CoglMultiPlaneTexture to display
  */
 void
 meta_shaped_texture_set_texture (MetaShapedTexture *stex,
-                                 CoglTexture       *texture)
+                                 CoglMultiPlaneTexture *texture)
 {
   g_return_if_fail (META_IS_SHAPED_TEXTURE (stex));
 
-  set_cogl_texture (stex, texture);
+  set_planar_texture (stex, texture);
 }
 
 /**
@@ -1127,7 +1169,7 @@ meta_shaped_texture_set_snippet (MetaShapedTexture *stex,
 
   meta_shaped_texture_reset_pipelines (stex);
 
-  g_clear_pointer (&stex->snippet, cogl_object_unref);
+  cogl_clear_object (&stex->snippet);
   if (snippet)
     stex->snippet = cogl_object_ref (snippet);
 }
@@ -1138,11 +1180,12 @@ meta_shaped_texture_set_snippet (MetaShapedTexture *stex,
  *
  * Returns: (transfer none): the unshaped texture
  */
-CoglTexture *
+CoglMultiPlaneTexture *
 meta_shaped_texture_get_texture (MetaShapedTexture *stex)
 {
   g_return_val_if_fail (META_IS_SHAPED_TEXTURE (stex), NULL);
-  return COGL_TEXTURE (stex->texture);
+
+  return stex->texture;
 }
 
 /**
@@ -1247,7 +1290,15 @@ meta_shaped_texture_reset_viewport_dst_size (MetaShapedTexture *stex)
 static gboolean
 should_get_via_offscreen (MetaShapedTexture *stex)
 {
-  if (!cogl_texture_is_get_data_supported (stex->texture))
+  CoglTexture *texture;
+
+  /* If we have more than 1 plane, we can't access the data */
+  if (cogl_multi_plane_texture_get_n_planes (stex->texture) > 1)
+    return TRUE;
+
+  /* We have only 1 plane -> access it directly */
+  texture = cogl_multi_plane_texture_get_plane (stex->texture, 0);
+  if (!cogl_texture_is_get_data_supported (texture))
     return TRUE;
 
   if (stex->has_viewport_src_rect || stex->has_viewport_dst_size)
@@ -1364,19 +1415,19 @@ get_image_via_offscreen (MetaShapedTexture     *stex,
  * Returns: (transfer full): a new cairo surface to be freed with
  * cairo_surface_destroy().
  */
+  /* XXX Still need to fix this, but apparently only used for screenshot */
 cairo_surface_t *
 meta_shaped_texture_get_image (MetaShapedTexture     *stex,
                                cairo_rectangle_int_t *clip)
 {
   cairo_rectangle_int_t *transformed_clip = NULL;
-  CoglTexture *texture, *mask_texture;
+  CoglTexture *texture;
+  CoglTexture *mask_texture;
   cairo_surface_t *surface;
 
   g_return_val_if_fail (META_IS_SHAPED_TEXTURE (stex), NULL);
 
-  texture = COGL_TEXTURE (stex->texture);
-
-  if (texture == NULL)
+  if (stex->texture == NULL)
     return NULL;
 
   ensure_size_valid (stex);
@@ -1409,6 +1460,9 @@ meta_shaped_texture_get_image (MetaShapedTexture     *stex,
   if (should_get_via_offscreen (stex))
     return get_image_via_offscreen (stex, transformed_clip);
 
+  /* We know that we only have 1 plane at this point */
+  texture = cogl_multi_plane_texture_get_plane (stex->texture, 0);
+
   if (transformed_clip)
     texture = cogl_texture_new_from_sub_texture (texture,
                                                  transformed_clip->x,
diff --git a/src/compositor/meta-surface-actor-x11.c b/src/compositor/meta-surface-actor-x11.c
index 244b1e885..446772d4b 100644
--- a/src/compositor/meta-surface-actor-x11.c
+++ b/src/compositor/meta-surface-actor-x11.c
@@ -45,7 +45,7 @@ struct _MetaSurfaceActorX11
 
   MetaDisplay *display;
 
-  CoglTexture *texture;
+  CoglMultiPlaneTexture *texture;
   Pixmap pixmap;
   Damage damage;
 
@@ -129,8 +129,9 @@ set_pixmap (MetaSurfaceActorX11 *self,
   else if (G_UNLIKELY (!cogl_texture_pixmap_x11_is_using_tfp_extension (COGL_TEXTURE_PIXMAP_X11 (texture))))
     g_warning ("NOTE: Not using GLX TFP!\n");
 
-  self->texture = texture;
-  meta_shaped_texture_set_texture (stex, texture);
+  /* FIXME: we need to find out the format here */
+  self->texture = cogl_multi_plane_texture_new_single_plane (COGL_PIXEL_FORMAT_ANY, texture);
+  meta_shaped_texture_set_texture (stex, self->texture);
 }
 
 static void
@@ -187,6 +188,7 @@ meta_surface_actor_x11_process_damage (MetaSurfaceActor *actor,
                                        int x, int y, int width, int height)
 {
   MetaSurfaceActorX11 *self = META_SURFACE_ACTOR_X11 (actor);
+  CoglTexture *texture;
 
   self->received_damage = TRUE;
 
@@ -210,7 +212,8 @@ meta_surface_actor_x11_process_damage (MetaSurfaceActor *actor,
   if (!is_visible (self))
     return;
 
-  cogl_texture_pixmap_x11_update_area (COGL_TEXTURE_PIXMAP_X11 (self->texture),
+  texture = cogl_multi_plane_texture_get_plane (self->texture, 0);
+  cogl_texture_pixmap_x11_update_area (COGL_TEXTURE_PIXMAP_X11 (texture),
                                        x, y, width, height);
 }
 
diff --git a/src/compositor/meta-surface-actor.c b/src/compositor/meta-surface-actor.c
index ca4ca19a9..e50ca06f6 100644
--- a/src/compositor/meta-surface-actor.c
+++ b/src/compositor/meta-surface-actor.c
@@ -325,7 +325,8 @@ gboolean
 meta_surface_actor_is_argb32 (MetaSurfaceActor *self)
 {
   MetaShapedTexture *stex = meta_surface_actor_get_texture (self);
-  CoglTexture *texture = meta_shaped_texture_get_texture (stex);
+  CoglMultiPlaneTexture *mtex = meta_shaped_texture_get_texture (stex);
+  CoglTexture *texture;
 
   /* If we don't have a texture, like during initialization, assume
    * that we're ARGB32.
@@ -335,9 +336,14 @@ meta_surface_actor_is_argb32 (MetaSurfaceActor *self)
    * place. This prevents us from continually redirecting and
    * unredirecting on every paint.
    */
-  if (!texture)
+  if (!mtex)
     return !meta_surface_actor_is_unredirected (self);
 
+  /* Are we dealing with multiple planes? Then it can't be argb32 either */
+  if (cogl_multi_plane_texture_get_n_planes (mtex) != 1)
+    return FALSE;
+
+  texture = cogl_multi_plane_texture_get_plane (mtex, 0);
   switch (cogl_texture_get_components (texture))
     {
     case COGL_TEXTURE_COMPONENTS_A:
diff --git a/src/compositor/meta-texture-tower.c b/src/compositor/meta-texture-tower.c
index a95ee5ccf..ea67219f4 100644
--- a/src/compositor/meta-texture-tower.c
+++ b/src/compositor/meta-texture-tower.c
@@ -58,8 +58,8 @@ typedef struct
 struct _MetaTextureTower
 {
   int n_levels;
-  CoglTexture *textures[MAX_TEXTURE_LEVELS];
-  CoglOffscreen *fbos[MAX_TEXTURE_LEVELS];
+  CoglMultiPlaneTexture *textures[MAX_TEXTURE_LEVELS];
+  GList *fbos[MAX_TEXTURE_LEVELS];
   Box invalid[MAX_TEXTURE_LEVELS];
   CoglPipeline *pipeline_template;
 };
@@ -112,8 +112,8 @@ meta_texture_tower_free (MetaTextureTower *tower)
  * unset or until the tower is freed.
  */
 void
-meta_texture_tower_set_base_texture (MetaTextureTower *tower,
-                                     CoglTexture      *texture)
+meta_texture_tower_set_base_texture (MetaTextureTower      *tower,
+                                     CoglMultiPlaneTexture *texture)
 {
   int i;
 
@@ -126,22 +126,14 @@ meta_texture_tower_set_base_texture (MetaTextureTower *tower,
     {
       for (i = 1; i < tower->n_levels; i++)
         {
-          if (tower->textures[i] != NULL)
-            {
-              cogl_object_unref (tower->textures[i]);
-              tower->textures[i] = NULL;
-            }
-
-          if (tower->fbos[i] != NULL)
-            {
-              cogl_object_unref (tower->fbos[i]);
-              tower->fbos[i] = NULL;
-            }
-        }
+          cogl_clear_object (&tower->textures[i]);
 
-      cogl_object_unref (tower->textures[0]);
+          g_list_free_full (tower->fbos[i], cogl_object_unref);
+          tower->fbos[i] = NULL;
+        }
     }
 
+  cogl_clear_object (&tower->textures[0]);
   tower->textures[0] = texture;
 
   if (tower->textures[0] != NULL)
@@ -150,8 +142,8 @@ meta_texture_tower_set_base_texture (MetaTextureTower *tower,
 
       cogl_object_ref (tower->textures[0]);
 
-      width = cogl_texture_get_width (tower->textures[0]);
-      height = cogl_texture_get_height (tower->textures[0]);
+      width = cogl_multi_plane_texture_get_width (tower->textures[0]);
+      height = cogl_multi_plane_texture_get_height (tower->textures[0]);
 
       tower->n_levels = 1 + MAX ((int)(M_LOG2E * log (width)), (int)(M_LOG2E * log (height)));
       tower->n_levels = MIN(tower->n_levels, MAX_TEXTURE_LEVELS);
@@ -192,8 +184,8 @@ meta_texture_tower_update_area (MetaTextureTower *tower,
   if (tower->textures[0] == NULL)
     return;
 
-  texture_width = cogl_texture_get_width (tower->textures[0]);
-  texture_height = cogl_texture_get_height (tower->textures[0]);
+  texture_width = cogl_multi_plane_texture_get_width (tower->textures[0]);
+  texture_height = cogl_multi_plane_texture_get_height (tower->textures[0]);
 
   invalid.x1 = x;
   invalid.y1 = y;
@@ -351,9 +343,27 @@ texture_tower_create_texture (MetaTextureTower *tower,
                               int               width,
                               int               height)
 {
-  tower->textures[level] = cogl_texture_new_with_size (width, height,
-                                                       COGL_TEXTURE_NO_AUTO_MIPMAP,
-                                                       TEXTURE_FORMAT);
+  CoglMultiPlaneTexture *base_tex = tower->textures[0];
+  GPtrArray *planes;
+  guint n_planes, i;
+
+  n_planes = cogl_multi_plane_texture_get_n_planes (base_tex);
+  planes = g_ptr_array_new_full (n_planes, cogl_object_unref);
+
+  for (i = 0; i < n_planes; i++)
+    {
+      CoglTexture *texture;
+
+      texture = cogl_texture_new_with_size (width, height,
+                                            COGL_TEXTURE_NO_AUTO_MIPMAP,
+                                            TEXTURE_FORMAT);
+      g_ptr_array_add (planes, texture);
+    }
+
+  tower->textures[level] = cogl_multi_plane_texture_new (
+      cogl_multi_plane_texture_get_format (base_tex),
+      (CoglTexture **) g_ptr_array_free (planes, FALSE),
+      n_planes);
 
   tower->invalid[level].x1 = 0;
   tower->invalid[level].y1 = 0;
@@ -365,50 +375,68 @@ static void
 texture_tower_revalidate (MetaTextureTower *tower,
                           int               level)
 {
-  CoglTexture *source_texture = tower->textures[level - 1];
-  int source_texture_width = cogl_texture_get_width (source_texture);
-  int source_texture_height = cogl_texture_get_height (source_texture);
-  CoglTexture *dest_texture = tower->textures[level];
-  int dest_texture_width = cogl_texture_get_width (dest_texture);
-  int dest_texture_height = cogl_texture_get_height (dest_texture);
-  Box *invalid = &tower->invalid[level];
-  CoglFramebuffer *fb;
-  CoglError *catch_error = NULL;
-  CoglPipeline *pipeline;
-
-  if (tower->fbos[level] == NULL)
-    tower->fbos[level] = cogl_offscreen_new_with_texture (dest_texture);
-
-  fb = COGL_FRAMEBUFFER (tower->fbos[level]);
-
-  if (!cogl_framebuffer_allocate (fb, &catch_error))
+  CoglMultiPlaneTexture *src_tex = tower->textures[level - 1];
+  int src_width = cogl_multi_plane_texture_get_width (src_tex);
+  int src_height = cogl_multi_plane_texture_get_height (src_tex);
+  guint src_tex_n_planes = cogl_multi_plane_texture_get_n_planes (src_tex);
+  CoglMultiPlaneTexture *dest_tex = tower->textures[level];
+  int dest_width = cogl_multi_plane_texture_get_width (dest_tex);
+  int dest_height = cogl_multi_plane_texture_get_height (dest_tex);
+  guint i;
+
+
+  /* FIXME: cogl_offscreen_texture_new_with_texture doesn't work for
+   * multi-plane textures, so we have to make an FBO for each layer */
+  for (i = 0; i < src_tex_n_planes; i++)
     {
-      cogl_error_free (catch_error);
-      return;
-    }
+      Box *invalid = &tower->invalid[level];
+      CoglTexture *src_plane, *dest_plane;
+      CoglFramebuffer *fb;
+      CoglError *catch_error = NULL;
+      CoglPipeline *pipeline;
 
-  cogl_framebuffer_orthographic (fb, 0, 0, dest_texture_width, dest_texture_height, -1., 1.);
+      src_plane = cogl_multi_plane_texture_get_plane (src_tex, i);
+      dest_plane = cogl_multi_plane_texture_get_plane (dest_tex, i);
 
-  if (!tower->pipeline_template)
-    {
-      CoglContext *ctx =
-        clutter_backend_get_cogl_context (clutter_get_default_backend ());
-      tower->pipeline_template = cogl_pipeline_new (ctx);
-      cogl_pipeline_set_blend (tower->pipeline_template, "RGBA = ADD (SRC_COLOR, 0)", NULL);
-    }
+      if (g_list_nth (tower->fbos[level], i) != NULL)
+        {
+          fb = COGL_FRAMEBUFFER (g_list_nth (tower->fbos[level], i)->data);
+        }
+      else
+        {
+          fb = COGL_FRAMEBUFFER (cogl_offscreen_new_with_texture (dest_plane));
+          tower->fbos[level] = g_list_append (tower->fbos[level], fb);
+        }
+
+      if (!cogl_framebuffer_allocate (fb, &catch_error))
+        {
+          cogl_error_free (catch_error);
+          return;
+        }
+
+      cogl_framebuffer_orthographic (fb, 0, 0, dest_width, dest_height, -1., 1.);
+
+      if (!tower->pipeline_template)
+        {
+          CoglContext *ctx =
+            clutter_backend_get_cogl_context (clutter_get_default_backend ());
+          tower->pipeline_template = cogl_pipeline_new (ctx);
+          cogl_pipeline_set_blend (tower->pipeline_template, "RGBA = ADD (SRC_COLOR, 0)", NULL);
+        }
 
-  pipeline = cogl_pipeline_copy (tower->pipeline_template);
-  cogl_pipeline_set_layer_texture (pipeline, 0, tower->textures[level - 1]);
+      pipeline = cogl_pipeline_copy (tower->pipeline_template);
+      cogl_pipeline_set_layer_texture (pipeline, 0, src_plane);
 
-  cogl_framebuffer_draw_textured_rectangle (fb, pipeline,
-                                            invalid->x1, invalid->y1,
-                                            invalid->x2, invalid->y2,
-                                            (2. * invalid->x1) / source_texture_width,
-                                            (2. * invalid->y1) / source_texture_height,
-                                            (2. * invalid->x2) / source_texture_width,
-                                            (2. * invalid->y2) / source_texture_height);
+      cogl_framebuffer_draw_textured_rectangle (fb, pipeline,
+                                                invalid->x1, invalid->y1,
+                                                invalid->x2, invalid->y2,
+                                                (2. * invalid->x1) / src_width,
+                                                (2. * invalid->y1) / src_height,
+                                                (2. * invalid->x2) / src_width,
+                                                (2. * invalid->y2) / src_height);
 
-  cogl_object_unref (pipeline);
+      cogl_object_unref (pipeline);
+    }
 
   tower->invalid[level].x1 = tower->invalid[level].x2 = 0;
   tower->invalid[level].y1 = tower->invalid[level].y2 = 0;
@@ -427,7 +455,7 @@ texture_tower_revalidate (MetaTextureTower *tower,
  * Return value: the COGL texture handle to use for painting, or
  *  %NULL if no base texture has yet been set.
  */
-CoglTexture *
+CoglMultiPlaneTexture *
 meta_texture_tower_get_paint_texture (MetaTextureTower *tower)
 {
   int texture_width, texture_height;
@@ -438,8 +466,8 @@ meta_texture_tower_get_paint_texture (MetaTextureTower *tower)
   if (tower->textures[0] == NULL)
     return NULL;
 
-  texture_width = cogl_texture_get_width (tower->textures[0]);
-  texture_height = cogl_texture_get_height (tower->textures[0]);
+  texture_width = cogl_multi_plane_texture_get_width (tower->textures[0]);
+  texture_height = cogl_multi_plane_texture_get_height (tower->textures[0]);
 
   level = get_paint_level(texture_width, texture_height);
   if (level < 0) /* singular paint matrix, scaled to nothing */
diff --git a/src/compositor/meta-texture-tower.h b/src/compositor/meta-texture-tower.h
index 6a39e4184..9897d93b3 100644
--- a/src/compositor/meta-texture-tower.h
+++ b/src/compositor/meta-texture-tower.h
@@ -53,14 +53,14 @@ typedef struct _MetaTextureTower MetaTextureTower;
 
 MetaTextureTower *meta_texture_tower_new               (void);
 void              meta_texture_tower_free              (MetaTextureTower *tower);
-void              meta_texture_tower_set_base_texture  (MetaTextureTower *tower,
-                                                        CoglTexture      *texture);
+void              meta_texture_tower_set_base_texture  (MetaTextureTower      *tower,
+                                                        CoglMultiPlaneTexture *texture);
 void              meta_texture_tower_update_area       (MetaTextureTower *tower,
                                                         int               x,
                                                         int               y,
                                                         int               width,
                                                         int               height);
-CoglTexture      *meta_texture_tower_get_paint_texture (MetaTextureTower *tower);
+CoglMultiPlaneTexture *meta_texture_tower_get_paint_texture (MetaTextureTower *tower);
 
 G_END_DECLS
 
diff --git a/src/compositor/meta-window-actor.c b/src/compositor/meta-window-actor.c
index 837a4c3e9..db64663ce 100644
--- a/src/compositor/meta-window-actor.c
+++ b/src/compositor/meta-window-actor.c
@@ -1502,7 +1502,8 @@ build_and_scan_frame_mask (MetaWindowActor       *self,
   guchar *mask_data;
   guint tex_width, tex_height;
   MetaShapedTexture *stex;
-  CoglTexture *paint_tex, *mask_texture;
+  CoglMultiPlaneTexture *paint_tex;
+  CoglTexture *mask_texture;
   int stride;
   cairo_t *cr;
   cairo_surface_t *surface;
@@ -1517,8 +1518,8 @@ build_and_scan_frame_mask (MetaWindowActor       *self,
   if (paint_tex == NULL)
     return;
 
-  tex_width = cogl_texture_get_width (paint_tex);
-  tex_height = cogl_texture_get_height (paint_tex);
+  tex_width = cogl_multi_plane_texture_get_width (paint_tex);
+  tex_height = cogl_multi_plane_texture_get_height (paint_tex);
 
   stride = cairo_format_stride_for_width (CAIRO_FORMAT_A8, tex_width);
 
diff --git a/src/meta/meta-shaped-texture.h b/src/meta/meta-shaped-texture.h
index c36b8547f..dd4548cb5 100644
--- a/src/meta/meta-shaped-texture.h
+++ b/src/meta/meta-shaped-texture.h
@@ -52,7 +52,7 @@ gboolean meta_shaped_texture_update_area (MetaShapedTexture *stex,
                                           int                height);
 
 META_EXPORT
-CoglTexture * meta_shaped_texture_get_texture (MetaShapedTexture *stex);
+CoglMultiPlaneTexture * meta_shaped_texture_get_texture (MetaShapedTexture *stex);
 
 META_EXPORT
 void meta_shaped_texture_set_mask_texture (MetaShapedTexture *stex,
diff --git a/src/wayland/meta-wayland-buffer.c b/src/wayland/meta-wayland-buffer.c
index f45679d3a..80e981f43 100644
--- a/src/wayland/meta-wayland-buffer.c
+++ b/src/wayland/meta-wayland-buffer.c
@@ -123,7 +123,7 @@ meta_wayland_buffer_realize (MetaWaylandBuffer *buffer)
   stream = meta_wayland_egl_stream_new (buffer, NULL);
   if (stream)
     {
-      CoglTexture2D *texture;
+      CoglMultiPlaneTexture *texture;
 
       texture = meta_wayland_egl_stream_create_texture (stream, NULL);
       if (!texture)
@@ -131,7 +131,7 @@ meta_wayland_buffer_realize (MetaWaylandBuffer *buffer)
 
       buffer->egl_stream.stream = stream;
       buffer->type = META_WAYLAND_BUFFER_TYPE_EGL_STREAM;
-      buffer->egl_stream.texture = COGL_TEXTURE (texture);
+      buffer->egl_stream.texture = texture;
       buffer->is_y_inverted = meta_wayland_egl_stream_is_y_inverted (stream);
 
       return TRUE;
@@ -163,17 +163,17 @@ shm_buffer_get_cogl_pixel_format (struct wl_shm_buffer  *shm_buffer,
                                   CoglTextureComponents *components_out)
 {
   CoglPixelFormat format;
-  CoglTextureComponents components = COGL_TEXTURE_COMPONENTS_RGBA;
 
   switch (wl_shm_buffer_get_format (shm_buffer))
     {
 #if G_BYTE_ORDER == G_BIG_ENDIAN
     case WL_SHM_FORMAT_ARGB8888:
       format = COGL_PIXEL_FORMAT_ARGB_8888_PRE;
+      components_out[0] = COGL_TEXTURE_COMPONENTS_RGBA;
       break;
     case WL_SHM_FORMAT_XRGB8888:
       format = COGL_PIXEL_FORMAT_ARGB_8888;
-      components = COGL_TEXTURE_COMPONENTS_RGB;
+      components_out[0] = COGL_TEXTURE_COMPONENTS_RGB;
       break;
 #elif G_BYTE_ORDER == G_LITTLE_ENDIAN
     case WL_SHM_FORMAT_ARGB8888:
@@ -181,25 +181,59 @@ shm_buffer_get_cogl_pixel_format (struct wl_shm_buffer  *shm_buffer,
       break;
     case WL_SHM_FORMAT_XRGB8888:
       format = COGL_PIXEL_FORMAT_BGRA_8888;
-      components = COGL_TEXTURE_COMPONENTS_RGB;
+      components_out[0] = COGL_TEXTURE_COMPONENTS_RGB;
       break;
 #endif
+    case WL_SHM_FORMAT_NV12:
+      format = COGL_PIXEL_FORMAT_NV12;
+      components_out[0] = COGL_TEXTURE_COMPONENTS_A;
+      components_out[1] = COGL_TEXTURE_COMPONENTS_RG;
+      break;
+    case WL_SHM_FORMAT_NV21:
+      format = COGL_PIXEL_FORMAT_NV21;
+      components_out[0] = COGL_TEXTURE_COMPONENTS_A;
+      components_out[1] = COGL_TEXTURE_COMPONENTS_RG;
+      break;
+    case WL_SHM_FORMAT_YUV422:
+      format = COGL_PIXEL_FORMAT_YUV422;
+      components_out[0] = COGL_TEXTURE_COMPONENTS_A;
+      components_out[1] = COGL_TEXTURE_COMPONENTS_A;
+      components_out[2] = COGL_TEXTURE_COMPONENTS_A;
+      break;
+    case WL_SHM_FORMAT_YVU422:
+      format = COGL_PIXEL_FORMAT_YVU422;
+      components_out[0] = COGL_TEXTURE_COMPONENTS_A;
+      components_out[1] = COGL_TEXTURE_COMPONENTS_A;
+      components_out[2] = COGL_TEXTURE_COMPONENTS_A;
+      break;
+    case WL_SHM_FORMAT_YUV444:
+      format = COGL_PIXEL_FORMAT_YUV444;
+      components_out[0] = COGL_TEXTURE_COMPONENTS_A;
+      components_out[1] = COGL_TEXTURE_COMPONENTS_A;
+      components_out[2] = COGL_TEXTURE_COMPONENTS_A;
+      break;
+    case WL_SHM_FORMAT_YVU444:
+      format = COGL_PIXEL_FORMAT_YVU444;
+      components_out[0] = COGL_TEXTURE_COMPONENTS_A;
+      components_out[1] = COGL_TEXTURE_COMPONENTS_A;
+      components_out[2] = COGL_TEXTURE_COMPONENTS_A;
+      break;
+
     default:
       g_warn_if_reached ();
       format = COGL_PIXEL_FORMAT_ARGB_8888;
+      components_out[0] = COGL_TEXTURE_COMPONENTS_RGBA;
     }
 
   if (format_out)
     *format_out = format;
-  if (components_out)
-    *components_out = components;
 }
 
 static gboolean
-shm_buffer_attach (MetaWaylandBuffer  *buffer,
-                   CoglTexture       **texture,
-                   gboolean           *changed_texture,
-                   GError            **error)
+shm_buffer_attach (MetaWaylandBuffer      *buffer,
+                   CoglMultiPlaneTexture **texture,
+                   gboolean               *changed_texture,
+                   GError                **error)
 {
   MetaBackend *backend = meta_get_backend ();
   ClutterBackend *clutter_backend = meta_backend_get_clutter_backend (backend);
@@ -207,21 +241,31 @@ shm_buffer_attach (MetaWaylandBuffer  *buffer,
   struct wl_shm_buffer *shm_buffer;
   int stride, width, height;
   CoglPixelFormat format;
-  CoglTextureComponents components;
-  CoglBitmap *bitmap;
-  CoglTexture *new_texture;
-
+  CoglTextureComponents components[3];
+  guint i, n_planes;
+  uint8_t h_factors[3], v_factors[3];
+  CoglPixelFormat subformats[3];
+  gsize plane_offset = 0;
+  guint8 *data;
+  GPtrArray *bitmaps;
+
+  /* Query the necessary parameters */
   shm_buffer = wl_shm_buffer_get (buffer->resource);
   stride = wl_shm_buffer_get_stride (shm_buffer);
   width = wl_shm_buffer_get_width (shm_buffer);
   height = wl_shm_buffer_get_height (shm_buffer);
-  shm_buffer_get_cogl_pixel_format (shm_buffer, &format, &components);
+  shm_buffer_get_cogl_pixel_format (shm_buffer, &format, components);
+  n_planes = cogl_pixel_format_get_n_planes (format);
+  cogl_pixel_format_get_subsampling_factors (format, h_factors, v_factors);
+  cogl_pixel_format_get_subformats (format, subformats);
+
+  g_warning ("Got SHM buffer, format %s", cogl_pixel_format_to_string (format));
 
   if (*texture &&
-      cogl_texture_get_width (*texture) == width &&
-      cogl_texture_get_height (*texture) == height &&
-      cogl_texture_get_components (*texture) == components &&
-      _cogl_texture_get_format (*texture) == format)
+      cogl_multi_plane_texture_get_width (*texture) == width &&
+      cogl_multi_plane_texture_get_height (*texture) == height &&
+      /*XXX cogl_texture_get_components (*texture) == components && */
+      cogl_multi_plane_texture_get_format (*texture) == format)
     {
       buffer->is_y_inverted = TRUE;
       *changed_texture = FALSE;
@@ -230,56 +274,52 @@ shm_buffer_attach (MetaWaylandBuffer  *buffer,
 
   cogl_clear_object (texture);
 
+  /* Safely access the data inside the buffer */
   wl_shm_buffer_begin_access (shm_buffer);
+  data = wl_shm_buffer_get_data (shm_buffer);
 
-  bitmap = cogl_bitmap_new_for_data (cogl_context,
-                                     width, height,
-                                     format,
-                                     stride,
-                                     wl_shm_buffer_get_data (shm_buffer));
-
-  new_texture = COGL_TEXTURE (cogl_texture_2d_new_from_bitmap (bitmap));
-  cogl_texture_set_components (new_texture, components);
-
-  if (!cogl_texture_allocate (new_texture, error))
+  bitmaps = g_ptr_array_new_full (n_planes, cogl_object_unref);
+  for (i = 0; i < n_planes; i++)
     {
-      g_clear_pointer (&new_texture, cogl_object_unref);
-      if (g_error_matches (*error, COGL_TEXTURE_ERROR, COGL_TEXTURE_ERROR_SIZE))
-        {
-          CoglTexture2DSliced *texture_sliced;
+      CoglBitmap *bitmap;
 
-          g_clear_error (error);
+      /* Calculate the plane start in the buffer (consider subsampling) */
+      if (i > 0)
+        plane_offset += (stride / h_factors[i-1]) * (height / v_factors[i-1]);
 
-          texture_sliced =
-            cogl_texture_2d_sliced_new_from_bitmap (bitmap,
-                                                    COGL_TEXTURE_MAX_WASTE);
-          new_texture = COGL_TEXTURE (texture_sliced);
-          cogl_texture_set_components (new_texture, components);
+      g_warning ("Creating plane %u, h_factor = %u, v_factor = %u, plane_offset = %lu",
+                 i, h_factors[i], v_factors[i], plane_offset);
 
-          if (!cogl_texture_allocate (new_texture, error))
-            g_clear_pointer (&new_texture, cogl_object_unref);
-        }
+      bitmap = cogl_bitmap_new_for_data (cogl_context,
+                                         width / h_factors[i],
+                                         height / v_factors[i],
+                                         subformats[i],
+                                         stride / h_factors[i],
+                                         data + plane_offset);
+      g_assert (bitmap);
+
+      g_ptr_array_add (bitmaps, bitmap);
     }
 
-  cogl_object_unref (bitmap);
+  *texture = cogl_multi_plane_texture_new_from_bitmaps (format,
+                                          (CoglBitmap **) g_ptr_array_free (bitmaps, FALSE),
+                                          n_planes, error);
 
   wl_shm_buffer_end_access (shm_buffer);
 
-  if (!new_texture)
-    return FALSE;
-
-  *texture = new_texture;
   *changed_texture = TRUE;
   buffer->is_y_inverted = TRUE;
 
+  g_warning ("Got the following multiplane texture:\n%s", cogl_multi_plane_texture_to_string (*texture));
+
   return TRUE;
 }
 
 static gboolean
-egl_image_buffer_attach (MetaWaylandBuffer  *buffer,
-                         CoglTexture       **texture,
-                         gboolean           *changed_texture,
-                         GError            **error)
+egl_image_buffer_attach (MetaWaylandBuffer      *buffer,
+                         CoglMultiPlaneTexture **texture,
+                         gboolean               *changed_texture,
+                         GError                **error)
 {
   MetaBackend *backend = meta_get_backend ();
   MetaEgl *egl = meta_backend_get_egl (backend);
@@ -288,8 +328,9 @@ egl_image_buffer_attach (MetaWaylandBuffer  *buffer,
   EGLDisplay egl_display = cogl_egl_context_get_egl_display (cogl_context);
   int format, width, height, y_inverted;
   CoglPixelFormat cogl_format;
-  EGLImageKHR egl_image;
-  CoglTexture2D *texture_2d;
+  CoglTextureComponents components[3];
+  guint i, n_planes;
+  GPtrArray *planes;
 
   if (buffer->egl_image.texture)
     {
@@ -299,6 +340,7 @@ egl_image_buffer_attach (MetaWaylandBuffer  *buffer,
       return TRUE;
     }
 
+  /* Query the necessary properties */
   if (!meta_egl_query_wayland_buffer (egl, egl_display, buffer->resource,
                                       EGL_TEXTURE_FORMAT, &format,
                                       error))
@@ -319,6 +361,7 @@ egl_image_buffer_attach (MetaWaylandBuffer  *buffer,
                                       NULL))
     y_inverted = EGL_TRUE;
 
+  /* Map the EGL texture format to CoglPixelFormat, if possible */
   switch (format)
     {
     case EGL_TEXTURE_RGB:
@@ -327,6 +370,14 @@ egl_image_buffer_attach (MetaWaylandBuffer  *buffer,
     case EGL_TEXTURE_RGBA:
       cogl_format = COGL_PIXEL_FORMAT_RGBA_8888_PRE;
       break;
+    case EGL_TEXTURE_Y_UV_WL:
+      g_warning ("Got a NV12 color format texture!!");
+      cogl_format = COGL_PIXEL_FORMAT_NV12;
+      break;
+    case EGL_TEXTURE_Y_U_V_WL:
+      g_warning ("Got a YUV 4:4:4 color format texture!!");
+      cogl_format = COGL_PIXEL_FORMAT_YUV444;
+      break;
     default:
       g_set_error (error, G_IO_ERROR,
                    G_IO_ERROR_FAILED,
@@ -334,27 +385,51 @@ egl_image_buffer_attach (MetaWaylandBuffer  *buffer,
       return FALSE;
     }
 
-  /* The WL_bind_wayland_display spec states that EGL_NO_CONTEXT is to be used
-   * in conjunction with the EGL_WAYLAND_BUFFER_WL target. */
-  egl_image = meta_egl_create_image (egl, egl_display, EGL_NO_CONTEXT,
-                                     EGL_WAYLAND_BUFFER_WL, buffer->resource,
-                                     NULL,
-                                     error);
-  if (egl_image == EGL_NO_IMAGE_KHR)
-    return FALSE;
+  n_planes = cogl_pixel_format_get_n_planes (cogl_format);
+  cogl_pixel_format_get_cogl_components (cogl_format, components);
+  planes = g_ptr_array_new_full (n_planes, cogl_object_unref);
 
-  texture_2d = cogl_egl_texture_2d_new_from_image (cogl_context,
-                                                   width, height,
-                                                   cogl_format,
-                                                   egl_image,
-                                                   error);
+  /* Each EGLImage is a plane in the final texture */
+  for (i = 0; i < n_planes; i++)
+    {
+      EGLint egl_attribs[3];
+      EGLImageKHR egl_img;
+      CoglTexture2D *texture_2d;
+
+      /* Specify that we want the i'th plane */
+      egl_attribs[0] = EGL_WAYLAND_PLANE_WL;
+      egl_attribs[1] = i;
+      egl_attribs[2] = EGL_NONE;
+
+      /* The WL_bind_wayland_display spec states that EGL_NO_CONTEXT is to be
+       * used in conjunction with the EGL_WAYLAND_BUFFER_WL target. */
+      egl_img = meta_egl_create_image (egl, egl_display, EGL_NO_CONTEXT,
+                                       EGL_WAYLAND_BUFFER_WL, buffer->resource,
+                                       egl_attribs,
+                                       error);
+
+      if (G_UNLIKELY (egl_img == EGL_NO_IMAGE_KHR))
+        goto on_error;
+
+      texture_2d = cogl_egl_texture_2d_new_from_image (cogl_context,
+                                                    width, height,
+                                                    cogl_format,
+                                                    components[i],
+                                                    egl_img,
+                                                    error);
+
+      meta_egl_destroy_image (egl, egl_display, egl_img, NULL);
+
+      if (G_UNLIKELY (!texture_2d))
+        goto on_error;
+
+      g_ptr_array_add (planes, texture_2d);
+    }
 
-  meta_egl_destroy_image (egl, egl_display, egl_image, NULL);
 
-  if (!texture_2d)
-    return FALSE;
-
-  buffer->egl_image.texture = COGL_TEXTURE (texture_2d);
+  buffer->egl_image.texture = cogl_multi_plane_texture_new (cogl_format,
+                                             (CoglTexture **) g_ptr_array_free (planes, FALSE),
+                                             n_planes);
   buffer->is_y_inverted = !!y_inverted;
 
   cogl_clear_object (texture);
@@ -362,14 +437,19 @@ egl_image_buffer_attach (MetaWaylandBuffer  *buffer,
   *changed_texture = TRUE;
 
   return TRUE;
+
+on_error:
+  g_ptr_array_free (planes, TRUE);
+
+  return FALSE;
 }
 
 #ifdef HAVE_WAYLAND_EGLSTREAM
 static gboolean
-egl_stream_buffer_attach (MetaWaylandBuffer  *buffer,
-                          CoglTexture       **texture,
-                          gboolean           *changed_texture,
-                          GError            **error)
+egl_stream_buffer_attach (MetaWaylandBuffer      *buffer,
+                          CoglMultiPlaneTexture **texture,
+                          gboolean               *changed_texture,
+                          GError                **error)
 {
   MetaWaylandEglStream *stream = buffer->egl_stream.stream;
 
@@ -406,10 +486,10 @@ egl_stream_buffer_attach (MetaWaylandBuffer  *buffer,
  * meta_wayland_buffer_attach(), which also might free it, as described above.
  */
 gboolean
-meta_wayland_buffer_attach (MetaWaylandBuffer  *buffer,
-                            CoglTexture       **texture,
-                            gboolean           *changed_texture,
-                            GError            **error)
+meta_wayland_buffer_attach (MetaWaylandBuffer      *buffer,
+                            CoglMultiPlaneTexture **texture,
+                            gboolean               *changed_texture,
+                            GError                **error)
 {
   g_return_val_if_fail (buffer->resource, FALSE);
 
@@ -427,12 +507,15 @@ meta_wayland_buffer_attach (MetaWaylandBuffer  *buffer,
     case META_WAYLAND_BUFFER_TYPE_SHM:
       return shm_buffer_attach (buffer, texture, changed_texture, error);
     case META_WAYLAND_BUFFER_TYPE_EGL_IMAGE:
+      g_warning ("Got EGL IMAGE buffer");
       return egl_image_buffer_attach (buffer, texture, changed_texture, error);
 #ifdef HAVE_WAYLAND_EGLSTREAM
     case META_WAYLAND_BUFFER_TYPE_EGL_STREAM:
+      g_warning ("Got EGL STREAM buffer");
       return egl_stream_buffer_attach (buffer, texture, changed_texture, error);
 #endif
     case META_WAYLAND_BUFFER_TYPE_DMA_BUF:
+      g_warning ("Got DMABUF buffer");
       return meta_wayland_dma_buf_buffer_attach (buffer,
                                                  texture,
                                                  changed_texture,
@@ -466,55 +549,87 @@ meta_wayland_buffer_is_y_inverted (MetaWaylandBuffer *buffer)
 }
 
 static gboolean
-process_shm_buffer_damage (MetaWaylandBuffer *buffer,
-                           CoglTexture       *texture,
-                           cairo_region_t    *region,
-                           GError           **error)
+process_shm_buffer_damage (MetaWaylandBuffer      *buffer,
+                           CoglMultiPlaneTexture  *texture,
+                           cairo_region_t         *region,
+                           GError                **error)
 {
   struct wl_shm_buffer *shm_buffer;
-  int i, n_rectangles;
+  gint j, n_rectangles;
   gboolean set_texture_failed = FALSE;
+  CoglPixelFormat format;
+  CoglTextureComponents components[3];
+  uint8_t h_factors[3], v_factors[3], bpp[3];
+  CoglPixelFormat subformats[3];
+  gsize plane_offset = 0;
+  const uint8_t *data;
+  int32_t stride, height;
+  guint i, n_planes = cogl_multi_plane_texture_get_n_planes (texture);
 
   n_rectangles = cairo_region_num_rectangles (region);
 
   shm_buffer = wl_shm_buffer_get (buffer->resource);
+
+  /* Get the data */
   wl_shm_buffer_begin_access (shm_buffer);
+  data = wl_shm_buffer_get_data (shm_buffer);
+
+  /* Query the necessary properties */
+  stride = wl_shm_buffer_get_stride (shm_buffer);
+  height = wl_shm_buffer_get_height (shm_buffer);
+  shm_buffer_get_cogl_pixel_format (shm_buffer, &format, components);
+  cogl_pixel_format_get_subformats (format, subformats);
+  cogl_pixel_format_get_subsampling_factors (format, h_factors, v_factors);
+  cogl_pixel_format_get_bytes_per_pixel_ (format, bpp);
 
-  for (i = 0; i < n_rectangles; i++)
+  g_warning ("Processing damage on SHM %s buffer", cogl_pixel_format_to_string (format));
+
+  for (i = 0; i < n_planes; i++)
     {
-      const uint8_t *data = wl_shm_buffer_get_data (shm_buffer);
-      int32_t stride = wl_shm_buffer_get_stride (shm_buffer);
-      CoglPixelFormat format;
-      int bpp;
-      cairo_rectangle_int_t rect;
-
-      shm_buffer_get_cogl_pixel_format (shm_buffer, &format, NULL);
-      bpp = _cogl_pixel_format_get_bytes_per_pixel (format);
-      cairo_region_get_rectangle (region, i, &rect);
-
-      if (!_cogl_texture_set_region (texture,
-                                     rect.width, rect.height,
-                                     format,
-                                     stride,
-                                     data + rect.x * bpp + rect.y * stride,
-                                     rect.x, rect.y,
-                                     0,
-                                     error))
+      CoglTexture *plane;
+
+      plane = cogl_multi_plane_texture_get_plane (texture, i);
+
+      /* Calculate the plane start in the buffer (consider subsampling) */
+      if (i > 0)
+        plane_offset += (stride / h_factors[i-1]) * (height / v_factors[i-1]);
+
+      for (j = 0; j < n_rectangles; j++)
         {
-          set_texture_failed = TRUE;
-          break;
+          cairo_rectangle_int_t rect;
+
+          cairo_region_get_rectangle (region, j, &rect);
+
+          /* It's possible we get a faulty rectangle of size zero: ignore */
+          if (rect.height == 0 || rect.width == 0)
+            continue;
+
+          if (!_cogl_texture_set_region (plane,
+                                         rect.width / h_factors[i],
+                                         rect.height / v_factors[i],
+                                         subformats[i],
+                                         stride,
+                                         data + plane_offset + rect.x * bpp[i] + rect.y * stride,
+                                         rect.x, rect.y,
+                                         0,
+                                         error))
+            {
+              set_texture_failed = TRUE;
+              goto out;
+            }
         }
     }
 
+out:
   wl_shm_buffer_end_access (shm_buffer);
 
   return !set_texture_failed;
 }
 
 void
-meta_wayland_buffer_process_damage (MetaWaylandBuffer *buffer,
-                                    CoglTexture       *texture,
-                                    cairo_region_t    *region)
+meta_wayland_buffer_process_damage (MetaWaylandBuffer     *buffer,
+                                    CoglMultiPlaneTexture *texture,
+                                    cairo_region_t        *region)
 {
   gboolean res = FALSE;
   GError *error = NULL;
diff --git a/src/wayland/meta-wayland-buffer.h b/src/wayland/meta-wayland-buffer.h
index 5d75a3451..07522f27b 100644
--- a/src/wayland/meta-wayland-buffer.h
+++ b/src/wayland/meta-wayland-buffer.h
@@ -56,19 +56,19 @@ struct _MetaWaylandBuffer
   MetaWaylandBufferType type;
 
   struct {
-    CoglTexture *texture;
+    CoglMultiPlaneTexture *texture;
   } egl_image;
 
 #ifdef HAVE_WAYLAND_EGLSTREAM
   struct {
     MetaWaylandEglStream *stream;
-    CoglTexture *texture;
+    CoglMultiPlaneTexture *texture;
   } egl_stream;
 #endif
 
   struct {
     MetaWaylandDmaBufBuffer *dma_buf;
-    CoglTexture *texture;
+    CoglMultiPlaneTexture *texture;
   } dma_buf;
 };
 
@@ -80,14 +80,14 @@ MetaWaylandBuffer *     meta_wayland_buffer_from_resource       (struct wl_resou
 struct wl_resource *    meta_wayland_buffer_get_resource        (MetaWaylandBuffer     *buffer);
 gboolean                meta_wayland_buffer_is_realized         (MetaWaylandBuffer     *buffer);
 gboolean                meta_wayland_buffer_realize             (MetaWaylandBuffer     *buffer);
-gboolean                meta_wayland_buffer_attach              (MetaWaylandBuffer     *buffer,
-                                                                 CoglTexture          **texture,
-                                                                 gboolean              *changed_texture,
-                                                                 GError               **error);
+gboolean                meta_wayland_buffer_attach              (MetaWaylandBuffer      *buffer,
+                                                                 CoglMultiPlaneTexture **texture,
+                                                                 gboolean               *changed_texture,
+                                                                 GError                **error);
 CoglSnippet *           meta_wayland_buffer_create_snippet      (MetaWaylandBuffer     *buffer);
 gboolean                meta_wayland_buffer_is_y_inverted       (MetaWaylandBuffer     *buffer);
 void                    meta_wayland_buffer_process_damage      (MetaWaylandBuffer     *buffer,
-                                                                 CoglTexture           *texture,
+                                                                 CoglMultiPlaneTexture *texture,
                                                                  cairo_region_t        *region);
 
 #endif /* META_WAYLAND_BUFFER_H */
diff --git a/src/wayland/meta-wayland-cursor-surface.c b/src/wayland/meta-wayland-cursor-surface.c
index 76268d5f2..5e31db239 100644
--- a/src/wayland/meta-wayland-cursor-surface.c
+++ b/src/wayland/meta-wayland-cursor-surface.c
@@ -58,7 +58,7 @@ update_cursor_sprite_texture (MetaWaylandCursorSurface *cursor_surface)
   MetaWaylandSurface *surface =
     meta_wayland_surface_role_get_surface (META_WAYLAND_SURFACE_ROLE (cursor_surface));
   MetaCursorSprite *cursor_sprite = META_CURSOR_SPRITE (priv->cursor_sprite);
-  CoglTexture *texture;
+  CoglMultiPlaneTexture *texture;
 
   if (!priv->cursor_renderer)
     return;
@@ -66,8 +66,13 @@ update_cursor_sprite_texture (MetaWaylandCursorSurface *cursor_surface)
   texture = meta_wayland_surface_get_texture (surface);
   if (texture)
     {
+      CoglTexture *plane;
+
+      /* XXX We assume that we get a simple (single-plane) texture here */
+      plane = cogl_multi_plane_texture_get_plane (texture, 0);
+
       meta_cursor_sprite_set_texture (cursor_sprite,
-                                      texture,
+                                      plane,
                                       priv->hot_x * surface->scale,
                                       priv->hot_y * surface->scale);
     }
diff --git a/src/wayland/meta-wayland-dma-buf.c b/src/wayland/meta-wayland-dma-buf.c
index e49fba9cf..839fa1e4d 100644
--- a/src/wayland/meta-wayland-dma-buf.c
+++ b/src/wayland/meta-wayland-dma-buf.c
@@ -56,8 +56,9 @@ struct _MetaWaylandDmaBufBuffer
   int width;
   int height;
   uint32_t drm_format;
-  uint64_t drm_modifier;
   bool is_y_inverted;
+
+  uint64_t drm_modifier[META_WAYLAND_DMA_BUF_MAX_FDS];
   int fds[META_WAYLAND_DMA_BUF_MAX_FDS];
   int offsets[META_WAYLAND_DMA_BUF_MAX_FDS];
   unsigned int strides[META_WAYLAND_DMA_BUF_MAX_FDS];
@@ -65,25 +66,10 @@ struct _MetaWaylandDmaBufBuffer
 
 G_DEFINE_TYPE (MetaWaylandDmaBufBuffer, meta_wayland_dma_buf_buffer, G_TYPE_OBJECT);
 
-static gboolean
-meta_wayland_dma_buf_realize_texture (MetaWaylandBuffer  *buffer,
-                                      GError            **error)
+static CoglPixelFormat
+drm_buffer_get_cogl_pixel_format (MetaWaylandDmaBufBuffer *dma_buf)
 {
-  MetaBackend *backend = meta_get_backend ();
-  MetaEgl *egl = meta_backend_get_egl (backend);
-  ClutterBackend *clutter_backend = meta_backend_get_clutter_backend (backend);
-  CoglContext *cogl_context = clutter_backend_get_cogl_context (clutter_backend);
-  EGLDisplay egl_display = cogl_egl_context_get_egl_display (cogl_context);
-  MetaWaylandDmaBufBuffer *dma_buf = buffer->dma_buf.dma_buf;
-  CoglPixelFormat cogl_format;
-  EGLImageKHR egl_image;
-  CoglTexture2D *texture;
-  EGLint attribs[64];
-  int attr_idx = 0;
-
-  if (buffer->dma_buf.texture)
-    return TRUE;
-
+      g_warning ("Got dma format %d", dma_buf->drm_format);
   switch (dma_buf->drm_format)
     {
     /*
@@ -94,30 +80,62 @@ meta_wayland_dma_buf_realize_texture (MetaWaylandBuffer  *buffer,
      * and access the buffer memory at all.
      */
     case DRM_FORMAT_XRGB8888:
-      cogl_format = COGL_PIXEL_FORMAT_RGB_888;
-      break;
+      return COGL_PIXEL_FORMAT_RGB_888;
     case DRM_FORMAT_ARGB8888:
-      cogl_format = COGL_PIXEL_FORMAT_ARGB_8888_PRE;
-      break;
+      return COGL_PIXEL_FORMAT_ARGB_8888_PRE;
     case DRM_FORMAT_ARGB2101010:
-      cogl_format = COGL_PIXEL_FORMAT_ARGB_2101010_PRE;
-      break;
+      return COGL_PIXEL_FORMAT_ARGB_2101010_PRE;
     case DRM_FORMAT_RGB565:
-      cogl_format = COGL_PIXEL_FORMAT_RGB_565;
-      break;
+      return COGL_PIXEL_FORMAT_RGB_565;
+    case DRM_FORMAT_YUYV:
+      return COGL_PIXEL_FORMAT_YUYV;
+    case DRM_FORMAT_NV12:
+      return COGL_PIXEL_FORMAT_NV12;
+    case DRM_FORMAT_NV21:
+      return COGL_PIXEL_FORMAT_NV21;
+    case DRM_FORMAT_YUV410:
+      return COGL_PIXEL_FORMAT_YUV410;
+    case DRM_FORMAT_YVU410:
+      return COGL_PIXEL_FORMAT_YVU410;
+    case DRM_FORMAT_YUV411:
+      return COGL_PIXEL_FORMAT_YUV411;
+    case DRM_FORMAT_YVU411:
+      return COGL_PIXEL_FORMAT_YVU411;
+    case DRM_FORMAT_YUV420:
+      return COGL_PIXEL_FORMAT_YUV420;
+    case DRM_FORMAT_YVU420:
+      return COGL_PIXEL_FORMAT_YVU420;
+    case DRM_FORMAT_YUV422:
+      return COGL_PIXEL_FORMAT_YUV422;
+    case DRM_FORMAT_YVU422:
+      return COGL_PIXEL_FORMAT_YVU422;
+    case DRM_FORMAT_YUV444:
+      return COGL_PIXEL_FORMAT_YUV444;
+    case DRM_FORMAT_YVU444:
+      return COGL_PIXEL_FORMAT_YVU444;
     default:
-      g_set_error (error, G_IO_ERROR,
-                   G_IO_ERROR_FAILED,
-                   "Unsupported buffer format %d", dma_buf->drm_format);
-      return FALSE;
+      return COGL_PIXEL_FORMAT_ANY;
     }
+}
+
+static EGLImageKHR
+create_egl_image_from_dmabuf (MetaEgl                 *egl,
+                              EGLDisplay               egl_display,
+                              MetaWaylandDmaBufBuffer *dma_buf,
+                              int32_t                  width,
+                              int32_t                  height,
+                              uint32_t                 drm_format,
+                              GError                 **error)
+{
+  EGLint attribs[64];
+  int attr_idx = 0;
 
   attribs[attr_idx++] = EGL_WIDTH;
-  attribs[attr_idx++] = dma_buf->width;
+  attribs[attr_idx++] = width;
   attribs[attr_idx++] = EGL_HEIGHT;
-  attribs[attr_idx++] = dma_buf->height;
+  attribs[attr_idx++] = height;
   attribs[attr_idx++] = EGL_LINUX_DRM_FOURCC_EXT;
-  attribs[attr_idx++] = dma_buf->drm_format;
+  attribs[attr_idx++] = drm_format;
 
   attribs[attr_idx++] = EGL_DMA_BUF_PLANE0_FD_EXT;
   attribs[attr_idx++] = dma_buf->fds[0];
@@ -126,9 +144,9 @@ meta_wayland_dma_buf_realize_texture (MetaWaylandBuffer  *buffer,
   attribs[attr_idx++] = EGL_DMA_BUF_PLANE0_PITCH_EXT;
   attribs[attr_idx++] = dma_buf->strides[0];
   attribs[attr_idx++] = EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT;
-  attribs[attr_idx++] = dma_buf->drm_modifier & 0xffffffff;
+  attribs[attr_idx++] = dma_buf->drm_modifier[0] & 0xffffffff;
   attribs[attr_idx++] = EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT;
-  attribs[attr_idx++] = dma_buf->drm_modifier >> 32;
+  attribs[attr_idx++] = dma_buf->drm_modifier[0] >> 32;
 
   if (dma_buf->fds[1] >= 0)
     {
@@ -139,9 +157,9 @@ meta_wayland_dma_buf_realize_texture (MetaWaylandBuffer  *buffer,
       attribs[attr_idx++] = EGL_DMA_BUF_PLANE1_PITCH_EXT;
       attribs[attr_idx++] = dma_buf->strides[1];
       attribs[attr_idx++] = EGL_DMA_BUF_PLANE1_MODIFIER_LO_EXT;
-      attribs[attr_idx++] = dma_buf->drm_modifier & 0xffffffff;
+      attribs[attr_idx++] = dma_buf->drm_modifier[1] & 0xffffffff;
       attribs[attr_idx++] = EGL_DMA_BUF_PLANE1_MODIFIER_HI_EXT;
-      attribs[attr_idx++] = dma_buf->drm_modifier >> 32;
+      attribs[attr_idx++] = dma_buf->drm_modifier[1] >> 32;
     }
 
   if (dma_buf->fds[2] >= 0)
@@ -153,9 +171,9 @@ meta_wayland_dma_buf_realize_texture (MetaWaylandBuffer  *buffer,
       attribs[attr_idx++] = EGL_DMA_BUF_PLANE2_PITCH_EXT;
       attribs[attr_idx++] = dma_buf->strides[2];
       attribs[attr_idx++] = EGL_DMA_BUF_PLANE2_MODIFIER_LO_EXT;
-      attribs[attr_idx++] = dma_buf->drm_modifier & 0xffffffff;
+      attribs[attr_idx++] = dma_buf->drm_modifier[2] & 0xffffffff;
       attribs[attr_idx++] = EGL_DMA_BUF_PLANE2_MODIFIER_HI_EXT;
-      attribs[attr_idx++] = dma_buf->drm_modifier >> 32;
+      attribs[attr_idx++] = dma_buf->drm_modifier[2] >> 32;
     }
 
   if (dma_buf->fds[3] >= 0)
@@ -167,46 +185,102 @@ meta_wayland_dma_buf_realize_texture (MetaWaylandBuffer  *buffer,
       attribs[attr_idx++] = EGL_DMA_BUF_PLANE3_PITCH_EXT;
       attribs[attr_idx++] = dma_buf->strides[3];
       attribs[attr_idx++] = EGL_DMA_BUF_PLANE3_MODIFIER_LO_EXT;
-      attribs[attr_idx++] = dma_buf->drm_modifier & 0xffffffff;
+      attribs[attr_idx++] = dma_buf->drm_modifier[3] & 0xffffffff;
       attribs[attr_idx++] = EGL_DMA_BUF_PLANE3_MODIFIER_HI_EXT;
-      attribs[attr_idx++] = dma_buf->drm_modifier >> 32;
+      attribs[attr_idx++] = dma_buf->drm_modifier[3] >> 32;
     }
 
   attribs[attr_idx++] = EGL_NONE;
-  attribs[attr_idx++] = EGL_NONE;
 
-  /* The EXT_image_dma_buf_import spec states that EGL_NO_CONTEXT is to be used
-   * in conjunction with the EGL_LINUX_DMA_BUF_EXT target. Similarly, the
-   * native buffer is named in the attribs. */
-  egl_image = meta_egl_create_image (egl, egl_display, EGL_NO_CONTEXT,
-                                     EGL_LINUX_DMA_BUF_EXT, NULL, attribs,
-                                     error);
-  if (egl_image == EGL_NO_IMAGE_KHR)
-    return FALSE;
+  /* The EXT_image_dma_buf_import spec states that EGL_NO_CONTEXT is to be
+   * used in conjunction with the EGL_LINUX_DMA_BUF_EXT target. Similarly,
+   * the native buffer is named in the attribs. */
+  return meta_egl_create_image (egl, egl_display, EGL_NO_CONTEXT,
+                                EGL_LINUX_DMA_BUF_EXT, NULL, attribs,
+                                error);
+}
 
-  texture = cogl_egl_texture_2d_new_from_image (cogl_context,
-                                                dma_buf->width,
-                                                dma_buf->height,
-                                                cogl_format,
-                                                egl_image,
-                                                error);
+static gboolean
+meta_wayland_dma_buf_realize_texture (MetaWaylandBuffer  *buffer,
+                                      GError            **error)
+{
+  MetaBackend *backend = meta_get_backend ();
+  MetaEgl *egl = meta_backend_get_egl (backend);
+  ClutterBackend *clutter_backend = meta_backend_get_clutter_backend (backend);
+  CoglContext *cogl_context = clutter_backend_get_cogl_context (clutter_backend);
+  EGLDisplay egl_display = cogl_egl_context_get_egl_display (cogl_context);
+  MetaWaylandDmaBufBuffer *dma_buf = buffer->dma_buf.dma_buf;
+  CoglPixelFormat cogl_format;
+  CoglTextureComponents components[3];
+  CoglPixelFormat subformats[3];
+  GPtrArray *planes;
+  guint i = 0, n_planes = 1;
 
-  meta_egl_destroy_image (egl, egl_display, egl_image, NULL);
+  if (buffer->dma_buf.texture)
+    return TRUE;
 
-  if (!texture)
-    return FALSE;
+  cogl_format = drm_buffer_get_cogl_pixel_format (dma_buf);
+  g_warning ("Dmabuf: Got cogl format %s", cogl_pixel_format_to_string (cogl_format));
+  if (G_UNLIKELY (cogl_format == COGL_PIXEL_FORMAT_ANY))
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                   "Unsupported buffer format %d", dma_buf->drm_format);
+      return FALSE;
+    }
 
-  buffer->dma_buf.texture = COGL_TEXTURE (texture);
+  n_planes = cogl_pixel_format_get_n_planes (cogl_format);
+  cogl_pixel_format_get_subformats (cogl_format, subformats);
+  cogl_pixel_format_get_cogl_components (cogl_format, components);
+  planes = g_ptr_array_new_full (n_planes, cogl_object_unref);
+
+  /* Each EGLImage is a plane in the final CoglMultiPlaneTexture */
+  for (i = 0; i < n_planes; i++)
+    {
+      EGLImageKHR egl_img;
+      CoglTexture2D *plane;
+
+      egl_img = create_egl_image_from_dmabuf (egl,
+                                              egl_display,
+                                              dma_buf,
+                                              dma_buf->width, dma_buf->height,
+                                              dma_buf->drm_format,
+                                              error);
+      if (G_UNLIKELY (egl_img == EGL_NO_IMAGE_KHR))
+        goto on_error;
+
+      plane = cogl_egl_texture_2d_new_from_image (cogl_context,
+                                                  dma_buf->width,
+                                                  dma_buf->height,
+                                                  subformats[i],
+                                                  components[i],
+                                                  egl_img,
+                                                  error);
+
+      meta_egl_destroy_image (egl, egl_display, egl_img, NULL);
+
+      if (G_UNLIKELY (!plane))
+        goto on_error;
+
+      g_ptr_array_add (planes, plane);
+    }
+
+  buffer->dma_buf.texture = cogl_multi_plane_texture_new (cogl_format,
+                                                  (CoglTexture **) g_ptr_array_free (planes, FALSE),
+                                                  n_planes);
   buffer->is_y_inverted = dma_buf->is_y_inverted;
 
   return TRUE;
+
+on_error:
+  g_ptr_array_free (planes, TRUE);
+  return FALSE;
 }
 
 gboolean
-meta_wayland_dma_buf_buffer_attach (MetaWaylandBuffer  *buffer,
-                                    CoglTexture       **texture,
-                                    gboolean           *changed_texture,
-                                    GError            **error)
+meta_wayland_dma_buf_buffer_attach (MetaWaylandBuffer      *buffer,
+                                    CoglMultiPlaneTexture **texture,
+                                    gboolean               *changed_texture,
+                                    GError                **error)
 {
   if (!meta_wayland_dma_buf_realize_texture (buffer, error))
     return FALSE;
@@ -234,7 +308,7 @@ buffer_params_add (struct wl_client   *client,
   drm_modifier |= ((uint64_t) drm_modifier_lo) & 0xffffffff;
 
   dma_buf = wl_resource_get_user_data (resource);
-  if (!dma_buf)
+  if (G_UNLIKELY (!dma_buf))
     {
       wl_resource_post_error (resource,
                               ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_ALREADY_USED,
@@ -242,7 +316,7 @@ buffer_params_add (struct wl_client   *client,
       return;
     }
 
-  if (plane_idx >= META_WAYLAND_DMA_BUF_MAX_FDS)
+  if (G_UNLIKELY (plane_idx >= META_WAYLAND_DMA_BUF_MAX_FDS))
     {
       wl_resource_post_error (resource,
                               ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_PLANE_IDX,
@@ -251,7 +325,7 @@ buffer_params_add (struct wl_client   *client,
       return;
     }
 
-  if (dma_buf->fds[plane_idx] != -1)
+  if (G_UNLIKELY (dma_buf->fds[plane_idx] != -1))
     {
       wl_resource_post_error (resource,
                               ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_PLANE_SET,
@@ -260,8 +334,8 @@ buffer_params_add (struct wl_client   *client,
       return;
     }
 
-  if (dma_buf->drm_modifier != DRM_FORMAT_MOD_INVALID &&
-      dma_buf->drm_modifier != drm_modifier)
+  if (G_UNLIKELY (dma_buf->drm_modifier[plane_idx] != DRM_FORMAT_MOD_INVALID &&
+      dma_buf->drm_modifier[plane_idx] != drm_modifier))
     {
       wl_resource_post_error (resource,
                               ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INVALID_WL_BUFFER,
@@ -269,8 +343,9 @@ buffer_params_add (struct wl_client   *client,
       return;
     }
 
-  dma_buf->drm_modifier = drm_modifier;
+
   dma_buf->fds[plane_idx] = fd;
+  dma_buf->drm_modifier[plane_idx] = drm_modifier;
   dma_buf->offsets[plane_idx] = offset;
   dma_buf->strides[plane_idx] = stride;
 }
@@ -550,6 +625,16 @@ dma_buf_bind (struct wl_client *client,
   send_modifiers (resource, DRM_FORMAT_XRGB8888);
   send_modifiers (resource, DRM_FORMAT_ARGB2101010);
   send_modifiers (resource, DRM_FORMAT_RGB565);
+  send_modifiers (resource, DRM_FORMAT_NV12);
+  send_modifiers (resource, DRM_FORMAT_YUV410);
+  send_modifiers (resource, DRM_FORMAT_YVU410);
+  send_modifiers (resource, DRM_FORMAT_YUV411);
+  send_modifiers (resource, DRM_FORMAT_YVU420);
+  send_modifiers (resource, DRM_FORMAT_YVU420);
+  send_modifiers (resource, DRM_FORMAT_YUV422);
+  send_modifiers (resource, DRM_FORMAT_YVU422);
+  send_modifiers (resource, DRM_FORMAT_YUV444);
+  send_modifiers (resource, DRM_FORMAT_YVU444);
 }
 
 gboolean
@@ -598,10 +683,12 @@ meta_wayland_dma_buf_buffer_init (MetaWaylandDmaBufBuffer *dma_buf)
 {
   int i;
 
-  dma_buf->drm_modifier = DRM_FORMAT_MOD_INVALID;
 
   for (i = 0; i < META_WAYLAND_DMA_BUF_MAX_FDS; i++)
-    dma_buf->fds[i] = -1;
+    {
+      dma_buf->drm_modifier[i] = DRM_FORMAT_MOD_INVALID;
+      dma_buf->fds[i] = -1;
+    }
 }
 
 static void
diff --git a/src/wayland/meta-wayland-dma-buf.h b/src/wayland/meta-wayland-dma-buf.h
index 580a3e777..a2b4fd937 100644
--- a/src/wayland/meta-wayland-dma-buf.h
+++ b/src/wayland/meta-wayland-dma-buf.h
@@ -42,10 +42,10 @@ typedef struct _MetaWaylandDmaBufBuffer MetaWaylandDmaBufBuffer;
 gboolean meta_wayland_dma_buf_init (MetaWaylandCompositor *compositor);
 
 gboolean
-meta_wayland_dma_buf_buffer_attach (MetaWaylandBuffer  *buffer,
-                                    CoglTexture       **texture,
-                                    gboolean           *changed_texture,
-                                    GError            **error);
+meta_wayland_dma_buf_buffer_attach (MetaWaylandBuffer      *buffer,
+                                    CoglMultiPlaneTexture **texture,
+                                    gboolean                *changed_texture,
+                                    GError                 **error);
 
 MetaWaylandDmaBufBuffer *
 meta_wayland_dma_buf_from_buffer (MetaWaylandBuffer *buffer);
diff --git a/src/wayland/meta-wayland-egl-stream.c b/src/wayland/meta-wayland-egl-stream.c
index 3f8908e67..a0125894d 100644
--- a/src/wayland/meta-wayland-egl-stream.c
+++ b/src/wayland/meta-wayland-egl-stream.c
@@ -132,7 +132,7 @@ struct _MetaWaylandEglStream
 
   EGLStreamKHR egl_stream;
   MetaWaylandBuffer *buffer;
-  CoglTexture2D *texture;
+  CoglMultiPlaneTexture *texture;
   gboolean is_y_inverted;
 };
 
@@ -183,7 +183,7 @@ stream_texture_destroyed (gpointer data)
 }
 
 static gboolean
-alloc_egl_stream_texture (CoglTexture2D *texture,
+alloc_egl_stream_texture (CoglTexture2D *texture_2d,
                           gpointer       user_data,
                           GError       **error)
 {
@@ -199,7 +199,7 @@ alloc_egl_stream_texture (CoglTexture2D *texture,
                                                        error);
 }
 
-CoglTexture2D *
+CoglMultiPlaneTexture *
 meta_wayland_egl_stream_create_texture (MetaWaylandEglStream *stream,
                                         GError              **error)
 {
@@ -208,7 +208,7 @@ meta_wayland_egl_stream_create_texture (MetaWaylandEglStream *stream,
   ClutterBackend *clutter_backend = meta_backend_get_clutter_backend (backend);
   CoglContext *cogl_context = clutter_backend_get_cogl_context (clutter_backend);
   EGLDisplay egl_display = cogl_egl_context_get_egl_display (cogl_context);
-  CoglTexture2D *texture;
+  CoglTexture2D *texture_2d;
   int width, height;
   int y_inverted;
 
@@ -230,29 +230,30 @@ meta_wayland_egl_stream_create_texture (MetaWaylandEglStream *stream,
                                       NULL))
     y_inverted = EGL_TRUE;
 
-  texture =
+  texture_2d =
     cogl_texture_2d_new_from_egl_image_external (cogl_context,
                                                  width, height,
                                                  alloc_egl_stream_texture,
                                                  g_object_ref (stream),
                                                  stream_texture_destroyed,
                                                  error);
-  if (!texture)
+  if (!texture_2d)
     {
       g_object_unref (stream);
       return NULL;
     }
 
-  if (!cogl_texture_allocate (COGL_TEXTURE (texture), error))
+  if (!cogl_texture_allocate (COGL_TEXTURE (texture_2d), error))
     {
-      cogl_object_unref (texture);
+      cogl_object_unref (texture_2d);
       return NULL;
     }
 
-  stream->texture = texture;
+  stream->texture = cogl_multi_plane_texture_new_single_plane (COGL_PIXEL_FORMAT_ANY,
+                                                               COGL_TEXTURE (texture_2d));
   stream->is_y_inverted = !!y_inverted;
 
-  return texture;
+  return stream->texture;
 }
 
 gboolean
diff --git a/src/wayland/meta-wayland-egl-stream.h b/src/wayland/meta-wayland-egl-stream.h
index fe488ed54..d548d9a72 100644
--- a/src/wayland/meta-wayland-egl-stream.h
+++ b/src/wayland/meta-wayland-egl-stream.h
@@ -45,8 +45,8 @@ MetaWaylandEglStream * meta_wayland_egl_stream_new (MetaWaylandBuffer *buffer,
 gboolean meta_wayland_egl_stream_attach (MetaWaylandEglStream *stream,
                                          GError              **error);
 
-CoglTexture2D * meta_wayland_egl_stream_create_texture (MetaWaylandEglStream *stream,
-                                                        GError              **error);
+CoglMultiPlaneTexture * meta_wayland_egl_stream_create_texture (MetaWaylandEglStream *stream,
+                                                                GError              **error);
 CoglSnippet * meta_wayland_egl_stream_create_snippet (void);
 
 gboolean meta_wayland_egl_stream_is_y_inverted (MetaWaylandEglStream *stream);
diff --git a/src/wayland/meta-wayland-shell-surface.c b/src/wayland/meta-wayland-shell-surface.c
index f8354ab7c..3a8e336a4 100644
--- a/src/wayland/meta-wayland-shell-surface.c
+++ b/src/wayland/meta-wayland-shell-surface.c
@@ -153,7 +153,7 @@ meta_wayland_shell_surface_surface_commit (MetaWaylandSurfaceRole  *surface_role
   MetaWaylandSurfaceRoleClass *surface_role_class;
   MetaWindow *window;
   MetaWaylandBuffer *buffer;
-  CoglTexture *texture;
+  CoglMultiPlaneTexture *texture;
   double scale;
 
   surface_role_class =
@@ -171,8 +171,8 @@ meta_wayland_shell_surface_surface_commit (MetaWaylandSurfaceRole  *surface_role
   scale = meta_wayland_actor_surface_calculate_scale (actor_surface);
   texture = meta_wayland_surface_get_texture (surface);
 
-  window->buffer_rect.width = cogl_texture_get_width (texture) * scale;
-  window->buffer_rect.height = cogl_texture_get_height (texture) * scale;
+  window->buffer_rect.width = cogl_multi_plane_texture_get_width (texture) * scale;
+  window->buffer_rect.height = cogl_multi_plane_texture_get_height (texture) * scale;
 }
 
 static void
diff --git a/src/wayland/meta-wayland-surface.c b/src/wayland/meta-wayland-surface.c
index 391bcc6dd..3c3a5bd25 100644
--- a/src/wayland/meta-wayland-surface.c
+++ b/src/wayland/meta-wayland-surface.c
@@ -247,7 +247,7 @@ get_buffer_width (MetaWaylandSurface *surface)
   MetaWaylandBuffer *buffer = meta_wayland_surface_get_buffer (surface);
 
   if (buffer)
-    return cogl_texture_get_width (surface->texture);
+    return cogl_multi_plane_texture_get_width (surface->texture);
   else
     return 0;
 }
@@ -258,7 +258,7 @@ get_buffer_height (MetaWaylandSurface *surface)
   MetaWaylandBuffer *buffer = meta_wayland_surface_get_buffer (surface);
 
   if (buffer)
-    return cogl_texture_get_height (surface->texture);
+    return cogl_multi_plane_texture_get_height (surface->texture);
   else
     return 0;
 }
@@ -722,7 +722,7 @@ meta_wayland_surface_apply_pending_state (MetaWaylandSurface      *surface,
           if (changed_texture && meta_wayland_surface_get_actor (surface))
             {
               MetaShapedTexture *stex;
-              CoglTexture *texture;
+              CoglMultiPlaneTexture *texture;
               CoglSnippet *snippet;
               gboolean is_y_inverted;
 
@@ -1847,7 +1847,7 @@ meta_wayland_surface_is_shortcuts_inhibited (MetaWaylandSurface *surface,
   return g_hash_table_contains (surface->shortcut_inhibited_seats, seat);
 }
 
-CoglTexture *
+CoglMultiPlaneTexture *
 meta_wayland_surface_get_texture (MetaWaylandSurface *surface)
 {
   return surface->texture;
diff --git a/src/wayland/meta-wayland-surface.h b/src/wayland/meta-wayland-surface.h
index e244a3fdf..b82cdc600 100644
--- a/src/wayland/meta-wayland-surface.h
+++ b/src/wayland/meta-wayland-surface.h
@@ -149,7 +149,7 @@ struct _MetaWaylandSurface
   GHashTable *outputs_to_destroy_notify_id;
   MetaMonitorTransform buffer_transform;
 
-  CoglTexture *texture;
+  CoglMultiPlaneTexture *texture;
 
   /* Buffer reference state. */
   struct {
@@ -319,7 +319,7 @@ void                meta_wayland_surface_restore_shortcuts (MetaWaylandSurface *
 gboolean            meta_wayland_surface_is_shortcuts_inhibited (MetaWaylandSurface *surface,
                                                                  MetaWaylandSeat    *seat);
 
-CoglTexture *       meta_wayland_surface_get_texture (MetaWaylandSurface *surface);
+CoglMultiPlaneTexture * meta_wayland_surface_get_texture (MetaWaylandSurface *surface);
 
 MetaSurfaceActor *  meta_wayland_surface_get_actor (MetaWaylandSurface *surface);
 
diff --git a/src/wayland/meta-wayland.c b/src/wayland/meta-wayland.c
index a593f0a7b..02825709d 100644
--- a/src/wayland/meta-wayland.c
+++ b/src/wayland/meta-wayland.c
@@ -306,6 +306,26 @@ meta_wayland_log_func (const char *fmt,
   g_free (str);
 }
 
+static void
+add_supported_shm_formats (struct wl_display *display)
+{
+  guint i;
+
+  /* Note that a Wayland compositor should support WL_SHM_FORMAT_ARGB8888 and
+   * WL_SHM_FORMAT_XRGB8888 by default, so no need to add it here. */
+  static const guint32 SUPPORTED_FORMATS[] = {
+    WL_SHM_FORMAT_NV12,
+    WL_SHM_FORMAT_NV21,
+    WL_SHM_FORMAT_YUV422,
+    WL_SHM_FORMAT_YUV444,
+  };
+
+  for (i = 0; i < G_N_ELEMENTS (SUPPORTED_FORMATS); i++)
+    {
+      wl_display_add_shm_format (display, SUPPORTED_FORMATS[i]);
+    }
+}
+
 static void
 meta_wayland_compositor_init (MetaWaylandCompositor *compositor)
 {
@@ -318,6 +338,8 @@ meta_wayland_compositor_init (MetaWaylandCompositor *compositor)
   compositor->wayland_display = wl_display_create ();
   if (compositor->wayland_display == NULL)
     g_error ("Failed to create the global wl_display");
+
+  add_supported_shm_formats (compositor->wayland_display);
 }
 
 static void


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