[Cogl] [PATCH 12/13] introduce texture loaders to make allocations lazy

Robert Bragg robert at sixbynine.org
Wed Dec 11 10:31:33 PST 2013


From: Robert Bragg <robert at linux.intel.com>

This introduces the internal idea of texture loaders that track the
state for loading and allocating a texture. This defers a lot more work
until the texture is allocated.

There are several intentions to this change:

- provides a means for extending how textures are allocated without
  requiring all the parameters to be supplied in a single _texture_new()
  function call.

- allow us to remove the internal_format argument from all
  _texture_new() apis since using CoglPixelFormat is bad way of
  expressing the internal format constraints because it is too specific.

  For now the internal_format arguments haven't actually been removed
  but this patch does introduce replacement apis for controlling the
  internal format:

    cogl_texture_set_components() lets you specify what components your
    texture needs when it is allocated.
    cogl_texture_set_premultiplied() lets you specify whether a texture
    data should be interpreted as premultiplied or not.

- Enable us to support asynchronous texture loading + allocation in the
  future.

Of note, the _new_from_data() texture constructors all continue to
allocate textures immediately so that existing code doesn't need to be
adapted to manage the lifetime of the data being uploaded.
---
 cogl/cogl-atlas-texture-private.h             |   2 +-
 cogl/cogl-atlas-texture.c                     | 212 +++++++++++----
 cogl/cogl-atlas-texture.h                     |  43 ++-
 cogl/cogl-context.c                           |  54 ++--
 cogl/cogl-driver.h                            |  25 --
 cogl/cogl-sub-texture.c                       |   9 +-
 cogl/cogl-texture-2d-gl.h                     |   4 +
 cogl/cogl-texture-2d-private.h                |   3 +-
 cogl/cogl-texture-2d-sliced-private.h         |   1 +
 cogl/cogl-texture-2d-sliced.c                 | 378 +++++++++++++++-----------
 cogl/cogl-texture-2d-sliced.h                 |  29 +-
 cogl/cogl-texture-2d.c                        | 105 +++----
 cogl/cogl-texture-2d.h                        |  67 +++--
 cogl/cogl-texture-3d.c                        | 362 ++++++++++++++----------
 cogl/cogl-texture-3d.h                        |  53 +++-
 cogl/cogl-texture-private.h                   |  93 ++++++-
 cogl/cogl-texture-rectangle.c                 | 245 +++++++++++------
 cogl/cogl-texture-rectangle.h                 |  13 +-
 cogl/cogl-texture.c                           | 207 ++++++++++++--
 cogl/cogl-texture.h                           |  22 ++
 cogl/driver/gl/cogl-texture-2d-gl.c           | 356 ++++++++++++++----------
 cogl/driver/gl/gl/cogl-driver-gl.c            |   4 -
 cogl/driver/gl/gles/cogl-driver-gles.c        |   4 -
 cogl/driver/nop/cogl-driver-nop.c             |   4 -
 cogl/driver/nop/cogl-texture-2d-nop-private.h |  16 --
 cogl/driver/nop/cogl-texture-2d-nop.c         |  30 --
 cogl/winsys/cogl-texture-pixmap-x11.c         |  17 +-
 examples/cogl-basic-video-player.c            |   1 +
 28 files changed, 1521 insertions(+), 838 deletions(-)

diff --git a/cogl/cogl-atlas-texture-private.h b/cogl/cogl-atlas-texture-private.h
index ae5799d..9b22669 100644
--- a/cogl/cogl-atlas-texture-private.h
+++ b/cogl/cogl-atlas-texture-private.h
@@ -37,7 +37,7 @@ struct _CoglAtlasTexture
   /* The format that the texture is in. This isn't necessarily the
      same format as the atlas texture because we can store
      pre-multiplied and non-pre-multiplied textures together */
-  CoglPixelFormat       format;
+  CoglPixelFormat       internal_format;
 
   /* The rectangle that was used to add this texture to the
      atlas. This includes the 1-pixel border */
diff --git a/cogl/cogl-atlas-texture.c b/cogl/cogl-atlas-texture.c
index e212a0d..93a2423 100644
--- a/cogl/cogl-atlas-texture.c
+++ b/cogl/cogl-atlas-texture.c
@@ -380,7 +380,7 @@ _cogl_atlas_texture_migrate_out_of_atlas (CoglAtlasTexture *atlas_tex)
                                 atlas_tex->rectangle.y + 1,
                                 atlas_tex->rectangle.width - 2,
                                 atlas_tex->rectangle.height - 2,
-                                atlas_tex->format);
+                                atlas_tex->internal_format);
   /* Note: we simply silently ignore failures to migrate a texture
    * out (most likely due to lack of memory) and hope for the
    * best.
@@ -511,10 +511,10 @@ _cogl_atlas_texture_set_region_with_border (CoglAtlasTexture *atlas_tex,
 static CoglBitmap *
 _cogl_atlas_texture_convert_bitmap_for_upload (CoglAtlasTexture *atlas_tex,
                                                CoglBitmap *bmp,
+                                               CoglPixelFormat internal_format,
                                                CoglBool can_convert_in_place,
                                                CoglError **error)
 {
-  CoglPixelFormat internal_format;
   CoglBitmap *upload_bmp;
   CoglBitmap *override_bmp;
 
@@ -527,7 +527,7 @@ _cogl_atlas_texture_convert_bitmap_for_upload (CoglAtlasTexture *atlas_tex,
      orignal format so we do need to trigger the conversion */
 
   internal_format = (COGL_PIXEL_FORMAT_RGBA_8888 |
-                     (atlas_tex->format & COGL_PREMULT_BIT));
+                     (internal_format & COGL_PREMULT_BIT));
 
   upload_bmp = _cogl_bitmap_convert_for_upload (bmp,
                                                 internal_format,
@@ -578,6 +578,7 @@ _cogl_atlas_texture_set_region (CoglTexture *tex,
       CoglBitmap *upload_bmp =
         _cogl_atlas_texture_convert_bitmap_for_upload (atlas_tex,
                                                        bmp,
+                                                       atlas_tex->internal_format,
                                                        FALSE, /* can't convert
                                                                  in place */
                                                        error);
@@ -615,7 +616,7 @@ _cogl_atlas_texture_get_format (CoglTexture *tex)
   /* We don't want to forward this on the sub-texture because it isn't
      the necessarily the same format. This will happen if the texture
      isn't pre-multiplied */
-  return atlas_tex->format;
+  return atlas_tex->internal_format;
 }
 
 static GLenum
@@ -641,24 +642,20 @@ _cogl_atlas_texture_can_use_format (CoglPixelFormat format)
           format == COGL_PIXEL_FORMAT_RGBA_8888);
 }
 
-CoglAtlasTexture *
-cogl_atlas_texture_new_with_size (CoglContext *ctx,
-                                  int width,
-                                  int height,
-                                  CoglPixelFormat internal_format,
-                                  CoglError **error)
+static CoglAtlasTexture *
+_cogl_atlas_texture_create_base (CoglContext *ctx,
+                                 int width,
+                                 int height,
+                                 CoglPixelFormat internal_format,
+                                 CoglTextureLoader *loader)
 {
   CoglAtlasTexture *atlas_tex;
 
-  /* We can't atlas zero-sized textures because it breaks the atlas
-   * data structure */
-  _COGL_RETURN_VAL_IF_FAIL (width > 0 && height > 0, NULL);
-
   COGL_NOTE (ATLAS, "Adding texture of size %ix%i", width, height);
 
   /* We need to allocate the texture now because we need the pointer
      to set as the data for the rectangle in the atlas */
-  atlas_tex = g_new (CoglAtlasTexture, 1);
+  atlas_tex = g_new0 (CoglAtlasTexture, 1);
   /* Mark it as having no atlas so we don't try to unref it in
      _cogl_atlas_texture_post_reorganize_cb */
   atlas_tex->atlas = NULL;
@@ -666,27 +663,53 @@ cogl_atlas_texture_new_with_size (CoglContext *ctx,
   _cogl_texture_init (COGL_TEXTURE (atlas_tex),
                       ctx,
                       width, height,
+                      internal_format,
+                      loader,
                       &cogl_atlas_texture_vtable);
 
   atlas_tex->sub_texture = NULL;
 
-  atlas_tex->format = internal_format;
   atlas_tex->atlas = NULL;
 
   return _cogl_atlas_texture_object_new (atlas_tex);
 }
 
+CoglAtlasTexture *
+cogl_atlas_texture_new_with_size (CoglContext *ctx,
+                                  int width,
+                                  int height,
+                                  CoglPixelFormat internal_format,
+                                  CoglError **error)
+{
+  CoglTextureLoader *loader;
+
+  /* We can't atlas zero-sized textures because it breaks the atlas
+   * data structure */
+  _COGL_RETURN_VAL_IF_FAIL (width > 0 && height > 0, NULL);
+
+  loader = _cogl_texture_create_loader ();
+  loader->src_type = COGL_TEXTURE_SOURCE_TYPE_SIZED;
+  loader->src.sized.width = width;
+  loader->src.sized.height = height;
+
+  return _cogl_atlas_texture_create_base (ctx, width, height,
+                                          internal_format, loader);
+}
+
 static CoglBool
-_cogl_atlas_texture_allocate (CoglTexture *tex,
-                              CoglError **error)
+allocate_space (CoglAtlasTexture *atlas_tex,
+                int width,
+                int height,
+                CoglPixelFormat internal_format,
+                CoglError **error)
 {
+  CoglTexture *tex = COGL_TEXTURE (atlas_tex);
   CoglContext *ctx = tex->context;
-  CoglAtlasTexture *atlas_tex = COGL_ATLAS_TEXTURE (tex);
   CoglAtlas *atlas;
   GSList *l;
 
   /* If the texture is in a strange format then we won't use it */
-  if (!_cogl_atlas_texture_can_use_format (atlas_tex->format))
+  if (!_cogl_atlas_texture_can_use_format (internal_format))
     {
       COGL_NOTE (ATLAS, "Texture can not be added because the "
                  "format is unsupported");
@@ -714,7 +737,7 @@ _cogl_atlas_texture_allocate (CoglTexture *tex,
     /* Try to make some space in the atlas for the texture */
     if (_cogl_atlas_reserve_space (atlas = l->data,
                                    /* Add two pixels for the border */
-                                   tex->width + 2, tex->height + 2,
+                                   width + 2, height + 2,
                                    atlas_tex))
       {
         cogl_object_ref (atlas);
@@ -728,7 +751,7 @@ _cogl_atlas_texture_allocate (CoglTexture *tex,
       COGL_NOTE (ATLAS, "Created new atlas for textures: %p", atlas);
       if (!_cogl_atlas_reserve_space (atlas,
                                       /* Add two pixels for the border */
-                                      tex->width + 2, tex->height + 2,
+                                      width + 2, height + 2,
                                       atlas_tex))
         {
           /* Ok, this means we really can't add it to the atlas */
@@ -742,55 +765,73 @@ _cogl_atlas_texture_allocate (CoglTexture *tex,
         }
     }
 
+  atlas_tex->internal_format = internal_format;
+
   atlas_tex->atlas = atlas;
 
   return TRUE;
 }
 
-CoglAtlasTexture *
-_cogl_atlas_texture_new_from_bitmap (CoglBitmap *bmp,
-                                     CoglPixelFormat internal_format,
-                                     CoglBool can_convert_in_place,
-                                     CoglError **error)
+static CoglBool
+allocate_with_size (CoglAtlasTexture *atlas_tex,
+                    CoglTextureLoader *loader,
+                    CoglError **error)
 {
-  CoglContext *ctx = _cogl_bitmap_get_context (bmp);
-  CoglAtlasTexture *atlas_tex;
-  CoglBitmap *upload_bmp;
-  int bmp_width;
-  int bmp_height;
-  CoglPixelFormat bmp_format;
+  CoglTexture *tex = COGL_TEXTURE (atlas_tex);
+  CoglPixelFormat internal_format =
+    _cogl_texture_determine_internal_format (tex, COGL_PIXEL_FORMAT_ANY);
+
+  if (allocate_space (atlas_tex,
+                      loader->src.sized.width,
+                      loader->src.sized.height,
+                      internal_format,
+                      error))
+    {
+      _cogl_texture_set_allocated (COGL_TEXTURE (atlas_tex),
+                                   internal_format,
+                                   loader->src.sized.width,
+                                   loader->src.sized.height);
+      return TRUE;
+    }
+  else
+    return FALSE;
+}
 
-  _COGL_RETURN_VAL_IF_FAIL (cogl_is_bitmap (bmp), NULL);
+static CoglBool
+allocate_from_bitmap (CoglAtlasTexture *atlas_tex,
+                      CoglTextureLoader *loader,
+                      CoglError **error)
+{
+  CoglTexture *tex = COGL_TEXTURE (atlas_tex);
+  CoglBitmap *bmp = loader->src.bitmap.bitmap;
+  CoglPixelFormat bmp_format = cogl_bitmap_get_format (bmp);
+  int width = cogl_bitmap_get_width (bmp);
+  int height = cogl_bitmap_get_height (bmp);
+  CoglBool can_convert_in_place = loader->src.bitmap.can_convert_in_place;
+  CoglPixelFormat internal_format;
+  CoglBitmap *upload_bmp;
 
-  bmp_width = cogl_bitmap_get_width (bmp);
-  bmp_height = cogl_bitmap_get_height (bmp);
-  bmp_format = cogl_bitmap_get_format (bmp);
+  _COGL_RETURN_VAL_IF_FAIL (atlas_tex->atlas == NULL, FALSE);
 
-  internal_format = _cogl_texture_determine_internal_format (bmp_format,
-                                                             internal_format);
-
-  atlas_tex = cogl_atlas_texture_new_with_size (ctx,
-                                                bmp_width, bmp_height,
-                                                internal_format,
-                                                error);
-  if (!atlas_tex)
-    return NULL;
-
-  if (!cogl_texture_allocate (COGL_TEXTURE (atlas_tex), error))
-    {
-      cogl_object_unref (atlas_tex);
-      return NULL;
-    }
+  internal_format = _cogl_texture_determine_internal_format (tex, bmp_format);
 
   upload_bmp =
     _cogl_atlas_texture_convert_bitmap_for_upload (atlas_tex,
                                                    bmp,
+                                                   internal_format,
                                                    can_convert_in_place,
                                                    error);
   if (upload_bmp == NULL)
+    return FALSE;
+
+  if (!allocate_space (atlas_tex,
+                       width,
+                       height,
+                       internal_format,
+                       error))
     {
-      cogl_object_unref (atlas_tex);
-      return NULL;
+      cogl_object_unref (upload_bmp);
+      return FALSE;
     }
 
   /* Defer to set_region so that we can share the code for copying the
@@ -800,19 +841,65 @@ _cogl_atlas_texture_new_from_bitmap (CoglBitmap *bmp,
                                                    0, /* src_y */
                                                    0, /* dst_x */
                                                    0, /* dst_y */
-                                                   bmp_width, /* dst_width */
-                                                   bmp_height, /* dst_height */
+                                                   width, /* dst_width */
+                                                   height, /* dst_height */
                                                    upload_bmp,
                                                    error))
     {
+      _cogl_atlas_texture_remove_from_atlas (atlas_tex);
       cogl_object_unref (upload_bmp);
-      cogl_object_unref (atlas_tex);
-      return NULL;
+      return FALSE;
     }
 
   cogl_object_unref (upload_bmp);
 
-  return atlas_tex;
+  _cogl_texture_set_allocated (tex, internal_format, width, height);
+
+  return TRUE;
+}
+
+static CoglBool
+_cogl_atlas_texture_allocate (CoglTexture *tex,
+                              CoglError **error)
+{
+  CoglAtlasTexture *atlas_tex = COGL_ATLAS_TEXTURE (tex);
+  CoglTextureLoader *loader = tex->loader;
+
+  _COGL_RETURN_VAL_IF_FAIL (loader, FALSE);
+
+  switch (loader->src_type)
+    {
+    case COGL_TEXTURE_SOURCE_TYPE_SIZED:
+      return allocate_with_size (atlas_tex, loader, error);
+    case COGL_TEXTURE_SOURCE_TYPE_BITMAP:
+      return allocate_from_bitmap (atlas_tex, loader, error);
+    default:
+      break;
+    }
+
+  g_return_val_if_reached (FALSE);
+}
+
+CoglAtlasTexture *
+_cogl_atlas_texture_new_from_bitmap (CoglBitmap *bmp,
+                                     CoglPixelFormat internal_format,
+                                     CoglBool can_convert_in_place,
+                                     CoglError **error)
+{
+  CoglTextureLoader *loader;
+
+  _COGL_RETURN_VAL_IF_FAIL (cogl_is_bitmap (bmp), NULL);
+
+  loader = _cogl_texture_create_loader ();
+  loader->src_type = COGL_TEXTURE_SOURCE_TYPE_BITMAP;
+  loader->src.bitmap.bitmap = cogl_object_ref (bmp);
+  loader->src.bitmap.can_convert_in_place = can_convert_in_place;
+
+  return _cogl_atlas_texture_create_base (_cogl_bitmap_get_context (bmp),
+                                          cogl_bitmap_get_width (bmp),
+                                          cogl_bitmap_get_height (bmp),
+                                          internal_format,
+                                          loader);
 }
 
 CoglAtlasTexture *
@@ -855,6 +942,13 @@ cogl_atlas_texture_new_from_data (CoglContext *ctx,
 
   cogl_object_unref (bmp);
 
+  if (atlas_tex &&
+      !cogl_texture_allocate (COGL_TEXTURE (atlas_tex), error))
+    {
+      cogl_object_unref (atlas_tex);
+      return NULL;
+    }
+
   return atlas_tex;
 }
 
diff --git a/cogl/cogl-atlas-texture.h b/cogl/cogl-atlas-texture.h
index 4e8623d..53803c6 100644
--- a/cogl/cogl-atlas-texture.h
+++ b/cogl/cogl-atlas-texture.h
@@ -78,6 +78,11 @@ typedef struct _CoglAtlasTexture CoglAtlasTexture;
  * allocate the underlying storage or let Cogl automatically allocate
  * storage lazily.
  *
+ * The texture is still configurable until it has been allocated so
+ * for example you can influence the internal format of the texture
+ * using cogl_texture_set_components() and
+ * cogl_texture_set_premultiplied().
+ *
  * <note>This call can fail if Cogl considers the given
  * @internal_format incompatible with the format of its internal
  * atlases.</note>
@@ -109,7 +114,17 @@ cogl_atlas_texture_new_with_size (CoglContext *ctx,
  * represents a sub-region within one of Cogl's shared texture
  * atlases.
  *
- * <note>This call can fail if Cogl considers the given
+ * The storage for the texture is not allocated before this function
+ * returns. You can call cogl_texture_allocate() to explicitly
+ * allocate the underlying storage or let Cogl automatically allocate
+ * storage lazily.
+ *
+ * The texture is still configurable until it has been allocated so
+ * for example you can influence the internal format of the texture
+ * using cogl_texture_set_components() and
+ * cogl_texture_set_premultiplied().
+ *
+ * <note>Allocation can fail later if Cogl considers the given
  * @internal_format incompatible with the format of its internal
  * atlases.</note>
  *
@@ -152,7 +167,18 @@ cogl_atlas_texture_new_from_file (CoglContext *ctx,
  * memory. A #CoglAtlasTexture represents a sub-region within one of
  * Cogl's shared texture atlases.
  *
- * <note>This call can fail if Cogl considers the given
+ * <note>This api will always immediately allocate GPU memory for the
+ * texture and upload the given data so that the @data pointer does
+ * not need to remain valid once this function returns. This means it
+ * is not possible to configure the texture before it is allocated. If
+ * you do need to configure the texture before allocation (to specify
+ * constraints on the internal format for example) then you can
+ * instead create a #CoglBitmap for your data and use
+ * cogl_atlas_texture_new_from_bitmap() or use
+ * cogl_atlas_texture_new_with_size() and then upload data using
+ * cogl_texture_set_data()</note>
+ *
+ * <note>Allocation can fail if Cogl considers the given
  * @internal_format incompatible with the format of its internal
  * atlases.</note>
  *
@@ -192,7 +218,18 @@ cogl_atlas_texture_new_from_data (CoglContext *ctx,
  * @bitmap. A #CoglAtlasTexture represents a sub-region within one of
  * Cogl's shared texture atlases.
  *
- * <note>This call can fail if Cogl considers the given
+ * The storage for the texture is not allocated before this function
+ * returns. You can call cogl_texture_allocate() to explicitly
+ * allocate the underlying storage or preferably let Cogl
+ * automatically allocate storage lazily when it may know more about
+ * how the texture is being used and can optimize how it is allocated.
+ *
+ * The texture is still configurable until it has been allocated so
+ * for example you can influence the internal format of the texture
+ * using cogl_texture_set_components() and
+ * cogl_texture_set_premultiplied().
+ *
+ * <note>Allocation can fail if Cogl considers the given
  * @internal_format incompatible with the format of its internal
  * atlases.</note>
  *
diff --git a/cogl/cogl-context.c b/cogl/cogl-context.c
index e4453f9..4e2e0b0 100644
--- a/cogl/cogl-context.c
+++ b/cogl/cogl-context.c
@@ -126,8 +126,8 @@ cogl_context_new (CoglDisplay *display,
                   CoglError **error)
 {
   CoglContext *context;
-  uint8_t default_texture_data[] = { 0xff, 0xff, 0xff, 0xff };
-  CoglBitmap *default_texture_bitmap;
+  uint8_t white_pixel[] = { 0xff, 0xff, 0xff, 0xff };
+  CoglBitmap *white_pixel_bitmap;
   const CoglWinsysVtable *winsys;
   int i;
   CoglError *internal_error = NULL;
@@ -381,41 +381,53 @@ cogl_context_new (CoglDisplay *display,
   _cogl_matrix_entry_cache_init (&context->builtin_flushed_projection);
   _cogl_matrix_entry_cache_init (&context->builtin_flushed_modelview);
 
-  default_texture_bitmap =
-    cogl_bitmap_new_for_data (context,
-                              1, 1, /* width/height */
-                              COGL_PIXEL_FORMAT_RGBA_8888_PRE,
-                              4, /* rowstride */
-                              default_texture_data);
-
   /* Create default textures used for fall backs */
   context->default_gl_texture_2d_tex =
-    cogl_texture_2d_new_from_bitmap (default_texture_bitmap,
-                                     /* internal format */
-                                     COGL_PIXEL_FORMAT_RGBA_8888_PRE,
-                                     NULL);
+    cogl_texture_2d_new_from_data (context,
+                                   1, 1,
+                                   COGL_PIXEL_FORMAT_RGBA_8888_PRE,
+                                   COGL_PIXEL_FORMAT_RGBA_8888_PRE,
+                                   0, /* rowstride */
+                                   white_pixel,
+                                   NULL); /* abort on error */
 
   /* If 3D or rectangle textures aren't supported then these will
    * return errors that we can simply ignore. */
   internal_error = NULL;
   context->default_gl_texture_3d_tex =
-    cogl_texture_3d_new_from_bitmap (default_texture_bitmap,
-                                     1, /* height */
-                                     1, /* depth */
-                                     COGL_PIXEL_FORMAT_RGBA_8888_PRE,
-                                     &internal_error);
+    cogl_texture_3d_new_from_data (context,
+                                   1, 1, 1, /* width, height, depth */
+                                   COGL_PIXEL_FORMAT_RGBA_8888_PRE,
+                                   COGL_PIXEL_FORMAT_RGBA_8888_PRE,
+                                   0, /* rowstride */
+                                   0, /* image stride */
+                                   white_pixel,
+                                   &internal_error);
   if (internal_error)
     cogl_error_free (internal_error);
 
+  /* TODO: add cogl_texture_rectangle_new_from_data() */
+  white_pixel_bitmap =
+    cogl_bitmap_new_for_data (context,
+                              1, 1, /* width/height */
+                              COGL_PIXEL_FORMAT_RGBA_8888_PRE,
+                              4, /* rowstride */
+                              white_pixel);
+
   internal_error = NULL;
   context->default_gl_texture_rect_tex =
-    cogl_texture_rectangle_new_from_bitmap (default_texture_bitmap,
+    cogl_texture_rectangle_new_from_bitmap (white_pixel_bitmap,
                                             COGL_PIXEL_FORMAT_RGBA_8888_PRE,
-                                            &internal_error);
+                                            NULL); /* legacy error argument */
+
+  /* XXX: we need to allocate the texture now because the white_pixel
+   * data is on the stack */
+  cogl_texture_allocate (COGL_TEXTURE (context->default_gl_texture_rect_tex),
+                         &internal_error);
   if (internal_error)
     cogl_error_free (internal_error);
 
-  cogl_object_unref (default_texture_bitmap);
+  cogl_object_unref (white_pixel_bitmap);
 
   context->atlases = NULL;
   g_hook_list_init (&context->atlas_reorganize_callbacks, sizeof (GHook));
diff --git a/cogl/cogl-driver.h b/cogl/cogl-driver.h
index 4642236..7a562d5 100644
--- a/cogl/cogl-driver.h
+++ b/cogl/cogl-driver.h
@@ -141,31 +141,6 @@ struct _CoglDriverVtable
   (* texture_2d_allocate) (CoglTexture *tex,
                            CoglError **error);
 
-  /* Instantiates a new CoglTexture2D object with storage initialized
-   * with the contents of the given bitmap, using the specified
-   * internal format.
-   */
-  CoglTexture2D *
-  (* texture_2d_new_from_bitmap) (CoglBitmap *bmp,
-                                  CoglPixelFormat internal_format,
-                                  CoglBool can_convert_in_place,
-                                  CoglError **error);
-
-#if defined (COGL_HAS_EGL_SUPPORT) && defined (EGL_KHR_image_base)
-  /* Instantiates a new CoglTexture2D object with storage initialized
-   * with the contents of the given EGL image.
-   *
-   * This is optional for drivers to support
-   */
-  CoglTexture2D *
-  (* egl_texture_2d_new_from_image) (CoglContext *ctx,
-                                     int width,
-                                     int height,
-                                     CoglPixelFormat format,
-                                     EGLImageKHR image,
-                                     CoglError **error);
-#endif
-
   /* Initialize the specified region of storage of the given texture
    * with the contents of the specified framebuffer region
    */
diff --git a/cogl/cogl-sub-texture.c b/cogl/cogl-sub-texture.c
index 7bda7f0..8c446b0 100644
--- a/cogl/cogl-sub-texture.c
+++ b/cogl/cogl-sub-texture.c
@@ -235,6 +235,8 @@ cogl_sub_texture_new (CoglContext *ctx,
   tex = COGL_TEXTURE (sub_tex);
 
   _cogl_texture_init (tex, ctx, sub_width, sub_height,
+                      _cogl_texture_get_format (next_texture),
+                      NULL, /* no loader */
                       &cogl_sub_texture_vtable);
 
   /* If the next texture is also a sub texture we can avoid one level
@@ -264,8 +266,13 @@ _cogl_sub_texture_allocate (CoglTexture *tex,
                             CoglError **error)
 {
   CoglSubTexture *sub_tex = COGL_SUB_TEXTURE (tex);
+  CoglBool status = cogl_texture_allocate (sub_tex->full_texture, error);
 
-  return cogl_texture_allocate (sub_tex->full_texture, error);
+  _cogl_texture_set_allocated (tex,
+                               _cogl_texture_get_format (sub_tex->full_texture),
+                               tex->width, tex->height);
+
+  return status;
 }
 
 CoglTexture *
diff --git a/cogl/cogl-texture-2d-gl.h b/cogl/cogl-texture-2d-gl.h
index 2686c6d..d635e50 100644
--- a/cogl/cogl-texture-2d-gl.h
+++ b/cogl/cogl-texture-2d-gl.h
@@ -49,6 +49,10 @@ COGL_BEGIN_DECLS
  * This can be used for integrating Cogl with software using OpenGL
  * directly.
  *
+ * The texture is still configurable until it has been allocated so
+ * for example you can declare whether the texture is premultiplied
+ * with cogl_texture_set_premultiplied().
+ *
  * <note>The results are undefined for passing an invalid @gl_handle
  * or if @width or @height don't have the correct texture
  * geometry.</note>
diff --git a/cogl/cogl-texture-2d-private.h b/cogl/cogl-texture-2d-private.h
index 5d193bc..3d6c3c5 100644
--- a/cogl/cogl-texture-2d-private.h
+++ b/cogl/cogl-texture-2d-private.h
@@ -82,7 +82,8 @@ CoglTexture2D *
 _cogl_texture_2d_create_base (CoglContext *ctx,
                               int width,
                               int height,
-                              CoglPixelFormat internal_format);
+                              CoglPixelFormat internal_format,
+                              CoglTextureLoader *loader);
 
 void
 _cogl_texture_2d_set_auto_mipmap (CoglTexture *tex,
diff --git a/cogl/cogl-texture-2d-sliced-private.h b/cogl/cogl-texture-2d-sliced-private.h
index 3ceedcb..8e8e69c 100644
--- a/cogl/cogl-texture-2d-sliced-private.h
+++ b/cogl/cogl-texture-2d-sliced-private.h
@@ -34,6 +34,7 @@
 struct _CoglTexture2DSliced
 {
   CoglTexture _parent;
+
   GArray *slice_x_spans;
   GArray *slice_y_spans;
   GArray *slice_textures;
diff --git a/cogl/cogl-texture-2d-sliced.c b/cogl/cogl-texture-2d-sliced.c
index a90c080..e79a2db 100644
--- a/cogl/cogl-texture-2d-sliced.c
+++ b/cogl/cogl-texture-2d-sliced.c
@@ -659,14 +659,30 @@ _cogl_texture_2d_sliced_gl_flush_legacy_texobj_wrap_modes (CoglTexture *tex,
     }
 }
 
+static void
+free_spans (CoglTexture2DSliced *tex_2ds)
+{
+  if (tex_2ds->slice_x_spans != NULL)
+    {
+      g_array_free (tex_2ds->slice_x_spans, TRUE);
+      tex_2ds->slice_x_spans = NULL;
+    }
+
+  if (tex_2ds->slice_y_spans != NULL)
+    {
+      g_array_free (tex_2ds->slice_y_spans, TRUE);
+      tex_2ds->slice_y_spans = NULL;
+    }
+}
+
 static CoglBool
-_cogl_texture_2d_sliced_setup_spans (CoglContext *ctx,
-                                     CoglTexture2DSliced *tex_2ds,
-                                     int width,
-                                     int height,
-                                     int max_waste,
-                                     CoglPixelFormat internal_format,
-                                     CoglError **error)
+setup_spans (CoglContext *ctx,
+             CoglTexture2DSliced *tex_2ds,
+             int width,
+             int height,
+             int max_waste,
+             CoglPixelFormat internal_format,
+             CoglError **error)
 {
   int max_width;
   int max_height;
@@ -754,8 +770,8 @@ _cogl_texture_2d_sliced_setup_spans (CoglContext *ctx,
                                COGL_TEXTURE_ERROR,
                                COGL_TEXTURE_ERROR_SIZE,
                                "No suitable slice geometry found");
+              free_spans (tex_2ds);
               return FALSE;
-
             }
         }
 
@@ -791,7 +807,7 @@ _cogl_texture_2d_sliced_setup_spans (CoglContext *ctx,
 }
 
 static void
-_cogl_texture_2d_sliced_slices_free (CoglTexture2DSliced *tex_2ds)
+free_slices (CoglTexture2DSliced *tex_2ds)
 {
   if (tex_2ds->slice_textures != NULL)
     {
@@ -806,111 +822,36 @@ _cogl_texture_2d_sliced_slices_free (CoglTexture2DSliced *tex_2ds)
 
       g_array_free (tex_2ds->slice_textures, TRUE);
     }
-}
-
-static void
-_cogl_texture_2d_sliced_free (CoglTexture2DSliced *tex_2ds)
-{
-  _cogl_texture_2d_sliced_slices_free (tex_2ds);
 
-  if (tex_2ds->slice_x_spans != NULL)
-    g_array_free (tex_2ds->slice_x_spans, TRUE);
-
-  if (tex_2ds->slice_y_spans != NULL)
-    g_array_free (tex_2ds->slice_y_spans, TRUE);
-
-  /* Chain up */
-  _cogl_texture_free (COGL_TEXTURE (tex_2ds));
+  free_spans (tex_2ds);
 }
 
 static CoglBool
-_cogl_texture_2d_sliced_init_base (CoglContext *ctx,
-                                   CoglTexture2DSliced *tex_2ds,
-                                   int width,
-                                   int height,
-                                   int max_waste,
-                                   CoglPixelFormat internal_format,
-                                   CoglError **error)
+allocate_slices (CoglTexture2DSliced *tex_2ds,
+                 int width,
+                 int height,
+                 int max_waste,
+                 CoglPixelFormat internal_format,
+                 CoglError **error)
 {
   CoglTexture *tex = COGL_TEXTURE (tex_2ds);
-
-  _cogl_texture_init (tex, ctx, width, height, &cogl_texture_2d_sliced_vtable);
-
-  tex_2ds->max_waste = max_waste;
-  tex_2ds->internal_format = internal_format;
-
-  return _cogl_texture_2d_sliced_setup_spans (ctx, tex_2ds,
-                                              width, height,
-                                              max_waste,
-                                              internal_format,
-                                              error);
-}
-
-CoglTexture2DSliced *
-cogl_texture_2d_sliced_new_with_size (CoglContext *ctx,
-                                      int width,
-                                      int height,
-                                      int max_waste,
-                                      CoglPixelFormat internal_format)
-{
-  CoglTexture2DSliced *tex_2ds;
-  CoglError *ignore_error = NULL;
-
-  /* Since no data, we need some internal format */
-  if (internal_format == COGL_PIXEL_FORMAT_ANY)
-    internal_format = COGL_PIXEL_FORMAT_RGBA_8888_PRE;
-
-  /* Init texture with empty bitmap */
-  tex_2ds = g_new0 (CoglTexture2DSliced, 1);
-
-  if (!_cogl_texture_2d_sliced_init_base (ctx,
-                                          tex_2ds,
-                                          width, height,
-                                          max_waste,
-                                          internal_format,
-                                          &ignore_error))
-    {
-      /* In this case we failed to find any suitable slicing geometry
-       * for the given texture size.
-       *
-       * We don't need to do anything with the error here since it
-       * will be picked up on later when trying to allocate the
-       * texture.
-       */
-      cogl_error_free (ignore_error);
-    }
-
-  /* NB: We need to be sure that cogl_texture_is_sliced() will work
-   * correctly before returning since
-   * cogl_framebuffer_allocate() uses this api to determine
-   * if a texture can be rendered to which may be before the
-   * slices have been allocated.
-   */
-
-  return _cogl_texture_2d_sliced_object_new (tex_2ds);
-}
-
-static CoglBool
-_cogl_texture_2d_sliced_allocate (CoglTexture *tex,
-                                  CoglError **error)
-{
   CoglContext *ctx = tex->context;
-  CoglTexture2DSliced *tex_2ds = COGL_TEXTURE_2D_SLICED (tex);
   int n_x_slices;
   int n_y_slices;
   int n_slices;
   int x, y;
-  CoglPixelFormat format = tex_2ds->internal_format;
   CoglSpan *x_span;
   CoglSpan *y_span;
 
-  if (!tex_2ds->slice_x_spans || !tex_2ds->slice_y_spans)
+  tex_2ds->internal_format = internal_format;
+
+  if (!setup_spans (ctx, tex_2ds,
+                    width,
+                    height,
+                    max_waste,
+                    internal_format,
+                    error))
     {
-      _cogl_set_error (error,
-                       COGL_TEXTURE_ERROR,
-                       COGL_TEXTURE_ERROR_SIZE,
-                       "Couldn't find suitable slicing geometry "
-                       "for given size");
       return FALSE;
     }
 
@@ -941,11 +882,11 @@ _cogl_texture_2d_sliced_allocate (CoglTexture *tex,
           slice = COGL_TEXTURE (
             cogl_texture_2d_new_with_size (ctx,
                                            x_span->size, y_span->size,
-                                           format));
+                                           internal_format));
           g_array_append_val (tex_2ds->slice_textures, slice);
           if (!cogl_texture_allocate (slice, error))
             {
-              _cogl_texture_2d_sliced_slices_free (tex_2ds);
+              free_slices (tex_2ds);
               return FALSE;
             }
         }
@@ -954,68 +895,82 @@ _cogl_texture_2d_sliced_allocate (CoglTexture *tex,
   return TRUE;
 }
 
-CoglTexture2DSliced *
-_cogl_texture_2d_sliced_new_from_bitmap (CoglBitmap *bmp,
-                                         int max_waste,
-                                         CoglPixelFormat internal_format,
-                                         CoglBool can_convert_in_place,
-                                         CoglError **error)
+static void
+_cogl_texture_2d_sliced_free (CoglTexture2DSliced *tex_2ds)
 {
-  CoglContext *ctx;
-  CoglTexture2DSliced *tex_2ds;
-  CoglBitmap *upload_bmp;
-  int width, height;
+  free_slices (tex_2ds);
 
-  _COGL_RETURN_VAL_IF_FAIL (cogl_is_bitmap (bmp), NULL);
+  /* Chain up */
+  _cogl_texture_free (COGL_TEXTURE (tex_2ds));
+}
 
-  ctx = _cogl_bitmap_get_context (bmp);
+static CoglTexture2DSliced *
+_cogl_texture_2d_sliced_create_base (CoglContext *ctx,
+                                     int width,
+                                     int height,
+                                     int max_waste,
+                                     CoglPixelFormat internal_format,
+                                     CoglTextureLoader *loader)
+{
+  CoglTexture2DSliced *tex_2ds = g_new0 (CoglTexture2DSliced, 1);
 
-  width = cogl_bitmap_get_width (bmp);
-  height = cogl_bitmap_get_height (bmp);
+  _cogl_texture_init (COGL_TEXTURE (tex_2ds), ctx, width, height,
+                      internal_format, loader,
+                      &cogl_texture_2d_sliced_vtable);
 
-  /* Create new texture and fill with loaded data */
-  tex_2ds = g_new0 (CoglTexture2DSliced, 1);
+  tex_2ds->max_waste = max_waste;
 
-  internal_format =
-    _cogl_texture_determine_internal_format (cogl_bitmap_get_format (bmp),
-                                             internal_format);
+  return _cogl_texture_2d_sliced_object_new (tex_2ds);
+}
 
-  upload_bmp = _cogl_bitmap_convert_for_upload (bmp,
-                                                internal_format,
-                                                can_convert_in_place,
-                                                error);
-  if (upload_bmp == NULL)
-    {
-      _cogl_texture_2d_sliced_free (tex_2ds);
-      return NULL;
-    }
+CoglTexture2DSliced *
+cogl_texture_2d_sliced_new_with_size (CoglContext *ctx,
+                                      int width,
+                                      int height,
+                                      int max_waste,
+                                      CoglPixelFormat internal_format)
+{
+  CoglTextureLoader *loader;
 
-  /* NB: we may fail to find any suitable slicing geometry for the
-   * given texture size. */
-  if (!_cogl_texture_2d_sliced_init_base (ctx,
-                                          tex_2ds,
-                                          width, height,
-                                          max_waste,
-                                          internal_format,
-                                          error))
-    goto error;
+  /* Since no data, we need some internal format */
+  if (internal_format == COGL_PIXEL_FORMAT_ANY)
+    internal_format = COGL_PIXEL_FORMAT_RGBA_8888_PRE;
 
-  if (!cogl_texture_allocate (COGL_TEXTURE (tex_2ds), error))
-    goto error;
+  loader = _cogl_texture_create_loader ();
+  loader->src_type = COGL_TEXTURE_SOURCE_TYPE_SIZED;
+  loader->src.sized.width = width;
+  loader->src.sized.height = height;
 
-  if (!_cogl_texture_2d_sliced_upload_bitmap (tex_2ds,
-                                              upload_bmp,
-                                              error))
-    goto error;
+  return _cogl_texture_2d_sliced_create_base (ctx,
+                                              width,
+                                              height,
+                                              max_waste,
+                                              internal_format,
+                                              loader);
+}
 
-  cogl_object_unref (upload_bmp);
+CoglTexture2DSliced *
+_cogl_texture_2d_sliced_new_from_bitmap (CoglBitmap *bmp,
+                                         int max_waste,
+                                         CoglPixelFormat internal_format,
+                                         CoglBool can_convert_in_place,
+                                         CoglError **error)
+{
+  CoglTextureLoader *loader;
 
-  return _cogl_texture_2d_sliced_object_new (tex_2ds);
+  _COGL_RETURN_VAL_IF_FAIL (cogl_is_bitmap (bmp), NULL);
 
- error:
-  cogl_object_unref (upload_bmp);
-  _cogl_texture_2d_sliced_free (tex_2ds);
-  return NULL;
+  loader = _cogl_texture_create_loader ();
+  loader->src_type = COGL_TEXTURE_SOURCE_TYPE_BITMAP;
+  loader->src.bitmap.bitmap = cogl_object_ref (bmp);
+  loader->src.bitmap.can_convert_in_place = can_convert_in_place;
+
+  return _cogl_texture_2d_sliced_create_base (_cogl_bitmap_get_context (bmp),
+                                              cogl_bitmap_get_width (bmp),
+                                              cogl_bitmap_get_height (bmp),
+                                              max_waste,
+                                              internal_format,
+                                              loader);
 }
 
 CoglTexture2DSliced *
@@ -1065,6 +1020,13 @@ cogl_texture_2d_sliced_new_from_data (CoglContext *ctx,
 
   cogl_object_unref (bmp);
 
+  if (tex_2ds &&
+      !cogl_texture_allocate (COGL_TEXTURE (tex_2ds), error))
+    {
+      cogl_object_unref (tex_2ds);
+      return NULL;
+    }
+
   return tex_2ds;
 }
 
@@ -1096,6 +1058,107 @@ cogl_texture_2d_sliced_new_from_file (CoglContext *ctx,
 }
 
 static CoglBool
+allocate_with_size (CoglTexture2DSliced *tex_2ds,
+                    CoglTextureLoader *loader,
+                    CoglError **error)
+{
+  CoglTexture *tex = COGL_TEXTURE (tex_2ds);
+  CoglPixelFormat internal_format =
+    _cogl_texture_determine_internal_format (tex, COGL_PIXEL_FORMAT_ANY);
+
+  if (allocate_slices (tex_2ds,
+                       loader->src.sized.width,
+                       loader->src.sized.height,
+                       tex_2ds->max_waste,
+                       internal_format,
+                       error))
+    {
+      _cogl_texture_set_allocated (COGL_TEXTURE (tex_2ds),
+                                   internal_format,
+                                   loader->src.sized.width,
+                                   loader->src.sized.height);
+      return TRUE;
+    }
+  else
+    return FALSE;
+}
+
+static CoglBool
+allocate_from_bitmap (CoglTexture2DSliced *tex_2ds,
+                      CoglTextureLoader *loader,
+                      CoglError **error)
+{
+  CoglTexture *tex = COGL_TEXTURE (tex_2ds);
+  CoglBitmap *bmp = loader->src.bitmap.bitmap;
+  int width = cogl_bitmap_get_width (bmp);
+  int height = cogl_bitmap_get_height (bmp);
+  CoglBool can_convert_in_place = loader->src.bitmap.can_convert_in_place;
+  CoglPixelFormat internal_format;
+  CoglBitmap *upload_bmp;
+
+  _COGL_RETURN_VAL_IF_FAIL (tex_2ds->slice_textures == NULL, FALSE);
+
+  internal_format =
+    _cogl_texture_determine_internal_format (tex,
+                                             cogl_bitmap_get_format (bmp));
+
+  upload_bmp = _cogl_bitmap_convert_for_upload (bmp,
+                                                internal_format,
+                                                can_convert_in_place,
+                                                error);
+  if (upload_bmp == NULL)
+    return FALSE;
+
+  if (!allocate_slices (tex_2ds,
+                        width,
+                        height,
+                        tex_2ds->max_waste,
+                        internal_format,
+                        error))
+    {
+      cogl_object_unref (upload_bmp);
+      return FALSE;
+    }
+
+  if (!_cogl_texture_2d_sliced_upload_bitmap (tex_2ds,
+                                              upload_bmp,
+                                              error))
+    {
+      free_slices (tex_2ds);
+      cogl_object_unref (upload_bmp);
+      return FALSE;
+    }
+
+  cogl_object_unref (upload_bmp);
+
+  _cogl_texture_set_allocated (tex, internal_format, width, height);
+
+  return TRUE;
+}
+
+static CoglBool
+_cogl_texture_2d_sliced_allocate (CoglTexture *tex,
+                                  CoglError **error)
+{
+  CoglTexture2DSliced *tex_2ds = COGL_TEXTURE_2D_SLICED (tex);
+  CoglTextureLoader *loader = tex->loader;
+
+  _COGL_RETURN_VAL_IF_FAIL (loader, FALSE);
+
+  switch (loader->src_type)
+    {
+    case COGL_TEXTURE_SOURCE_TYPE_SIZED:
+      return allocate_with_size (tex_2ds, loader, error);
+    case COGL_TEXTURE_SOURCE_TYPE_BITMAP:
+      return allocate_from_bitmap (tex_2ds, loader, error);
+    default:
+      break;
+    }
+
+  g_return_val_if_reached (FALSE);
+}
+
+static CoglBool
 _cogl_texture_2d_sliced_is_foreign (CoglTexture *tex)
 {
   CoglTexture2DSliced *tex_2ds = COGL_TEXTURE_2D_SLICED (tex);
@@ -1115,17 +1178,10 @@ _cogl_texture_2d_sliced_is_sliced (CoglTexture *tex)
 {
   CoglTexture2DSliced *tex_2ds = COGL_TEXTURE_2D_SLICED (tex);
 
-  /* It's possible that we failed to calculate valid slicing geometry
-   * when initializing the texture due to the max_waste size and in
-   * this case we report that the texture is not sliced.
-   *
-   * In this case though we know that we will be throwing an error
-   * when this texture is later allocated so it shouldn't really
-   * matter what we report here since the texture won't be used in the
-   * end.
-   */
-  if (!tex_2ds->slice_x_spans || !tex_2ds->slice_y_spans)
-    return FALSE;
+  /* It's only after allocating a sliced texture that we will know
+   * whether it really needed to be sliced... */
+  if (!tex->allocated)
+    cogl_texture_allocate (tex, NULL);
 
   if (tex_2ds->slice_x_spans->len != 1 ||
       tex_2ds->slice_y_spans->len != 1)
diff --git a/cogl/cogl-texture-2d-sliced.h b/cogl/cogl-texture-2d-sliced.h
index f6fdc97..c52d5d6 100644
--- a/cogl/cogl-texture-2d-sliced.h
+++ b/cogl/cogl-texture-2d-sliced.h
@@ -147,6 +147,11 @@ cogl_texture_2d_sliced_new_with_size (CoglContext *ctx,
  * wasted padding at the bottom and right of the textures is less than
  * specified. A negative @max_waste will disable slicing.
  *
+ * The storage for the texture is not allocated before this function
+ * returns. You can call cogl_texture_allocate() to explicitly
+ * allocate the underlying storage or let Cogl automatically allocate
+ * storage lazily.
+ *
  * <note>It's possible for the allocation of a sliced texture to fail
  * later due to impossible slicing constraints if a negative
  * @max_waste value is given. If the given virtual texture size is
@@ -206,11 +211,22 @@ cogl_texture_2d_sliced_new_from_file (CoglContext *ctx,
  * wasted padding at the bottom and right of the textures is less than
  * specified. A negative @max_waste will disable slicing.
  *
+ * <note>This api will always immediately allocate GPU memory for all
+ * the required texture slices and upload the given data so that the
+ * @data pointer does not need to remain valid once this function
+ * returns. This means it is not possible to configure the texture
+ * before it is allocated. If you do need to configure the texture
+ * before allocation (to specify constraints on the internal format
+ * for example) then you can instead create a #CoglBitmap for your
+ * data and use cogl_texture_2d_sliced_new_from_bitmap() or use
+ * cogl_texture_2d_sliced_new_with_size() and then upload data using
+ * cogl_texture_set_data()</note>
+ *
  * <note>It's possible for the allocation of a sliced texture to fail
- * later due to impossible slicing constraints if a negative
- * @max_waste value is given. If the given virtual texture size is
- * larger than is supported by the hardware but slicing is disabled
- * the texture size would be too large to handle.</note>
+ * due to impossible slicing constraints if a negative @max_waste
+ * value is given. If the given virtual texture size is larger than is
+ * supported by the hardware but slicing is disabled the texture size
+ * would be too large to handle.</note>
  *
  * Return value: (transfer full): A newly created #CoglTexture2DSliced
  *               or %NULL on failure and @error will be updated.
@@ -262,6 +278,11 @@ cogl_texture_2d_sliced_new_from_data (CoglContext *ctx,
  * wasted padding at the bottom and right of the textures is less than
  * specified. A negative @max_waste will disable slicing.
  *
+ * The storage for the texture is not allocated before this function
+ * returns. You can call cogl_texture_allocate() to explicitly
+ * allocate the underlying storage or let Cogl automatically allocate
+ * storage lazily.
+ *
  * <note>It's possible for the allocation of a sliced texture to fail
  * later due to impossible slicing constraints if a negative
  * @max_waste value is given. If the given virtual texture size is
diff --git a/cogl/cogl-texture-2d.c b/cogl/cogl-texture-2d.c
index a238097..3a725cd 100644
--- a/cogl/cogl-texture-2d.c
+++ b/cogl/cogl-texture-2d.c
@@ -75,25 +75,6 @@ _cogl_texture_2d_free (CoglTexture2D *tex_2d)
   _cogl_texture_free (COGL_TEXTURE (tex_2d));
 }
 
-static CoglBool
-_cogl_texture_2d_can_create (CoglContext *ctx,
-                             unsigned int width,
-                             unsigned int height,
-                             CoglPixelFormat internal_format)
-{
-  /* If NPOT textures aren't supported then the size must be a power
-     of two */
-  if (!cogl_has_feature (ctx, COGL_FEATURE_ID_TEXTURE_NPOT_BASIC) &&
-      (!_cogl_util_is_pot (width) ||
-       !_cogl_util_is_pot (height)))
-    return FALSE;
-
-  return ctx->driver_vtable->texture_2d_can_create (ctx,
-                                                    width,
-                                                    height,
-                                                    internal_format);
-}
-
 void
 _cogl_texture_2d_set_auto_mipmap (CoglTexture *tex,
                                   CoglBool value)
@@ -107,20 +88,20 @@ CoglTexture2D *
 _cogl_texture_2d_create_base (CoglContext *ctx,
                               int width,
                               int height,
-                              CoglPixelFormat internal_format)
+                              CoglPixelFormat internal_format,
+                              CoglTextureLoader *loader)
 {
   CoglTexture2D *tex_2d = g_new (CoglTexture2D, 1);
   CoglTexture *tex = COGL_TEXTURE (tex_2d);
 
-  _cogl_texture_init (tex, ctx, width, height, &cogl_texture_2d_vtable);
+  _cogl_texture_init (tex, ctx, width, height, internal_format, loader,
+                      &cogl_texture_2d_vtable);
 
   tex_2d->mipmaps_dirty = TRUE;
   tex_2d->auto_mipmap = TRUE;
 
   tex_2d->is_foreign = FALSE;
 
-  tex_2d->internal_format = internal_format;
-
   ctx->driver_vtable->texture_2d_init (tex_2d);
 
   return _cogl_texture_2d_object_new (tex_2d);
@@ -132,13 +113,19 @@ cogl_texture_2d_new_with_size (CoglContext *ctx,
                                int height,
                                CoglPixelFormat internal_format)
 {
+  CoglTextureLoader *loader;
+
   /* Since no data, we need some internal format */
   if (internal_format == COGL_PIXEL_FORMAT_ANY)
     internal_format = COGL_PIXEL_FORMAT_RGBA_8888_PRE;
 
-  return  _cogl_texture_2d_create_base (ctx,
-                                        width, height,
-                                        internal_format);
+  loader = _cogl_texture_create_loader ();
+  loader->src_type = COGL_TEXTURE_SOURCE_TYPE_SIZED;
+  loader->src.sized.width = width;
+  loader->src.sized.height = height;
+
+  return _cogl_texture_2d_create_base (ctx, width, height,
+                                       internal_format, loader);
 }
 
 static CoglBool
@@ -146,6 +133,7 @@ _cogl_texture_2d_allocate (CoglTexture *tex,
                            CoglError **error)
 {
   CoglContext *ctx = tex->context;
+
   return ctx->driver_vtable->texture_2d_allocate (tex, error);
 }
 
@@ -155,32 +143,20 @@ _cogl_texture_2d_new_from_bitmap (CoglBitmap *bmp,
                                   CoglBool can_convert_in_place,
                                   CoglError **error)
 {
-  CoglContext *ctx;
+  CoglTextureLoader *loader;
 
   _COGL_RETURN_VAL_IF_FAIL (bmp != NULL, NULL);
 
-  ctx = _cogl_bitmap_get_context (bmp);
+  loader = _cogl_texture_create_loader ();
+  loader->src_type = COGL_TEXTURE_SOURCE_TYPE_BITMAP;
+  loader->src.bitmap.bitmap = cogl_object_ref (bmp);
+  loader->src.bitmap.can_convert_in_place = can_convert_in_place;
 
-  internal_format =
-    _cogl_texture_determine_internal_format (cogl_bitmap_get_format (bmp),
-                                             internal_format);
-
-  if (!_cogl_texture_2d_can_create (ctx,
-                                    cogl_bitmap_get_width (bmp),
-                                    cogl_bitmap_get_height (bmp),
-                                    internal_format))
-    {
-      _cogl_set_error (error, COGL_TEXTURE_ERROR,
-                       COGL_TEXTURE_ERROR_SIZE,
-                       "Failed to create texture 2d due to size/format"
-                       " constraints");
-      return NULL;
-    }
-
-  return ctx->driver_vtable->texture_2d_new_from_bitmap (bmp,
-                                                         internal_format,
-                                                         can_convert_in_place,
-                                                         error);
+  return  _cogl_texture_2d_create_base (_cogl_bitmap_get_context (bmp),
+                                        cogl_bitmap_get_width (bmp),
+                                        cogl_bitmap_get_height (bmp),
+                                        internal_format,
+                                        loader);
 }
 
 CoglTexture2D *
@@ -249,6 +225,13 @@ cogl_texture_2d_new_from_data (CoglContext *ctx,
 
   cogl_object_unref (bmp);
 
+  if (tex_2d &&
+      !cogl_texture_allocate (COGL_TEXTURE (tex_2d), error))
+    {
+      cogl_object_unref (tex_2d);
+      return NULL;
+    }
+
   return tex_2d;
 }
 
@@ -264,6 +247,8 @@ _cogl_egl_texture_2d_new_from_image (CoglContext *ctx,
                                      EGLImageKHR image,
                                      CoglError **error)
 {
+  CoglTextureLoader *loader;
+
   _COGL_RETURN_VAL_IF_FAIL (_cogl_context_get_winsys (ctx)->constraints &
                             COGL_RENDERER_CONSTRAINT_USES_EGL,
                             NULL);
@@ -273,22 +258,14 @@ _cogl_egl_texture_2d_new_from_image (CoglContext *ctx,
                              COGL_PRIVATE_FEATURE_TEXTURE_2D_FROM_EGL_IMAGE),
                             NULL);
 
-  if (ctx->driver_vtable->egl_texture_2d_new_from_image)
-    return ctx->driver_vtable->egl_texture_2d_new_from_image (ctx,
-                                                              width,
-                                                              height,
-                                                              format,
-                                                              image,
-                                                              error);
-  else
-    {
-      _cogl_set_error (error,
-                       COGL_SYSTEM_ERROR,
-                       COGL_SYSTEM_ERROR_UNSUPPORTED,
-                       "Creating 2D textures from EGL images is not "
-                       "supported by the current driver");
-      return NULL;
-    }
+  loader = _cogl_texture_create_loader ();
+  loader->src_type = COGL_TEXTURE_SOURCE_TYPE_EGL_IMAGE;
+  loader->src.egl_image.image = image;
+  loader->src.egl_image.width = width;
+  loader->src.egl_image.height = height;
+  loader->src.egl_image.format = format;
+
+  return _cogl_texture_2d_create_base (ctx, width, height, format, loader);
 }
 #endif /* defined (COGL_HAS_EGL_SUPPORT) && defined (EGL_KHR_image_base) */
 
diff --git a/cogl/cogl-texture-2d.h b/cogl/cogl-texture-2d.h
index 7526755..9b985c7 100644
--- a/cogl/cogl-texture-2d.h
+++ b/cogl/cogl-texture-2d.h
@@ -3,7 +3,7 @@
  *
  * An object oriented GL/GLES Abstraction/Utility Layer
  *
- * Copyright (C) 2011 Intel Corporation.
+ * Copyright (C) 2011,2013 Intel Corporation.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -75,11 +75,8 @@ cogl_is_texture_2d (void *object);
  * @height: Height of the texture to allocate
  * @internal_format: The format of the texture
  *
- * Allocates a low-level #CoglTexture2D texture that your GPU can
- * texture from directly. This is unlike sliced textures for example
- * which may be comprised of multiple internal textures, or atlas
- * textures where Cogl has to modify texture coordinates before they
- * may be used by the GPU.
+ * Creates a low-level #CoglTexture2D texture with a given @width and
+ * @height that your GPU can texture from directly.
  *
  * The storage for the texture is not allocated before this function
  * returns. You can call cogl_texture_allocate() to explicitly
@@ -87,6 +84,11 @@ cogl_is_texture_2d (void *object);
  * automatically allocate storage lazily when it may know more about
  * how the texture is being used and can optimize how it is allocated.
  *
+ * The texture is still configurable until it has been allocated so
+ * for example you can influence the internal format of the texture
+ * using cogl_texture_set_components() and
+ * cogl_texture_set_premultiplied().
+ *
  * <note>Many GPUs only support power of two sizes for #CoglTexture2D
  * textures. You can check support for non power of two textures by
  * checking for the %COGL_FEATURE_ID_TEXTURE_NPOT feature via
@@ -116,7 +118,23 @@ cogl_texture_2d_new_with_size (CoglContext *ctx,
  *    other than straight blending.
  * @error: A #CoglError to catch exceptional errors or %NULL
  *
- * Creates a #CoglTexture2D from an image file.
+ * Creates a low-level #CoglTexture2D texture from an image file.
+ *
+ * The storage for the texture is not allocated before this function
+ * returns. You can call cogl_texture_allocate() to explicitly
+ * allocate the underlying storage or preferably let Cogl
+ * automatically allocate storage lazily when it may know more about
+ * how the texture is being used and can optimize how it is allocated.
+ *
+ * The texture is still configurable until it has been allocated so
+ * for example you can influence the internal format of the texture
+ * using cogl_texture_set_components() and
+ * cogl_texture_set_premultiplied().
+ *
+ * <note>Many GPUs only support power of two sizes for #CoglTexture2D
+ * textures. You can check support for non power of two textures by
+ * checking for the %COGL_FEATURE_ID_TEXTURE_NPOT feature via
+ * cogl_has_feature().</note>
  *
  * Return value: (transfer full): A newly created #CoglTexture2D or %NULL on failure
  *               and @error will be updated.
@@ -149,10 +167,19 @@ cogl_texture_2d_new_from_file (CoglContext *ctx,
  * @data: pointer the memory region where the source buffer resides
  * @error: A #CoglError for exceptions
  *
- * Creates a new #CoglTexture2D texture based on data residing in memory.
- * These are unlike sliced textures for example which may be comprised
- * of multiple internal textures, or atlas textures where Cogl has to
- * modify texture coordinates before they may be used by the GPU.
+ * Creates a low-level #CoglTexture2D texture based on data residing
+ * in memory.
+ *
+ * <note>This api will always immediately allocate GPU memory for the
+ * texture and upload the given data so that the @data pointer does
+ * not need to remain valid once this function returns. This means it
+ * is not possible to configure the texture before it is allocated. If
+ * you do need to configure the texture before allocation (to specify
+ * constraints on the internal format for example) then you can
+ * instead create a #CoglBitmap for your data and use
+ * cogl_texture_2d_new_from_bitmap() or use
+ * cogl_texture_2d_new_with_size() and then upload data using
+ * cogl_texture_set_data()</note>
  *
  * <note>Many GPUs only support power of two sizes for #CoglTexture2D
  * textures. You can check support for non power of two textures by
@@ -189,11 +216,19 @@ cogl_texture_2d_new_from_data (CoglContext *ctx,
  *    something other than straight blending.
  * @error: A #CoglError for exceptions
  *
- * Creates a new #CoglTexture2D texture based on data residing in a
- * bitmap. These are unlike sliced textures for example which may be
- * comprised of multiple internal textures, or atlas textures where
- * Cogl has to modify texture coordinates before they may be used by
- * the GPU.
+ * Creates a low-level #CoglTexture2D texture based on data residing
+ * in a #CoglBitmap.
+ *
+ * The storage for the texture is not allocated before this function
+ * returns. You can call cogl_texture_allocate() to explicitly
+ * allocate the underlying storage or preferably let Cogl
+ * automatically allocate storage lazily when it may know more about
+ * how the texture is being used and can optimize how it is allocated.
+ *
+ * The texture is still configurable until it has been allocated so
+ * for example you can influence the internal format of the texture
+ * using cogl_texture_set_components() and
+ * cogl_texture_set_premultiplied().
  *
  * <note>Many GPUs only support power of two sizes for #CoglTexture2D
  * textures. You can check support for non power of two textures by
diff --git a/cogl/cogl-texture-3d.c b/cogl/cogl-texture-3d.c
index 98d1a21..14bf7d2 100644
--- a/cogl/cogl-texture-3d.c
+++ b/cogl/cogl-texture-3d.c
@@ -116,12 +116,14 @@ _cogl_texture_3d_create_base (CoglContext *ctx,
                               int width,
                               int height,
                               int depth,
-                              CoglPixelFormat internal_format)
+                              CoglPixelFormat internal_format,
+                              CoglTextureLoader *loader)
 {
   CoglTexture3D *tex_3d = g_new (CoglTexture3D, 1);
   CoglTexture *tex = COGL_TEXTURE (tex_3d);
 
-  _cogl_texture_init (tex, ctx, width, height, &cogl_texture_3d_vtable);
+  _cogl_texture_init (tex, ctx, width, height,
+                      internal_format, loader, &cogl_texture_3d_vtable);
 
   tex_3d->gl_texture = 0;
 
@@ -138,11 +140,153 @@ _cogl_texture_3d_create_base (CoglContext *ctx,
   tex_3d->gl_legacy_texobj_wrap_mode_t = GL_FALSE;
   tex_3d->gl_legacy_texobj_wrap_mode_p = GL_FALSE;
 
-  tex_3d->internal_format = internal_format;
-
   return _cogl_texture_3d_object_new (tex_3d);
 }
 
+CoglTexture3D *
+cogl_texture_3d_new_with_size (CoglContext *ctx,
+                               int width,
+                               int height,
+                               int depth,
+                               CoglPixelFormat internal_format)
+{
+  CoglTextureLoader *loader;
+
+  /* Since no data, we need some internal format */
+  if (internal_format == COGL_PIXEL_FORMAT_ANY)
+    internal_format = COGL_PIXEL_FORMAT_RGBA_8888_PRE;
+
+  loader = _cogl_texture_create_loader ();
+  loader->src_type = COGL_TEXTURE_SOURCE_TYPE_SIZED;
+  loader->src.sized.width = width;
+  loader->src.sized.height = height;
+  loader->src.sized.depth = depth;
+
+  return _cogl_texture_3d_create_base (ctx, width, height, depth,
+                                       internal_format, loader);
+}
+
+CoglTexture3D *
+cogl_texture_3d_new_from_bitmap (CoglBitmap *bmp,
+                                 int height,
+                                 int depth,
+                                 CoglPixelFormat internal_format,
+                                 CoglError **error)
+{
+  CoglTextureLoader *loader;
+
+  _COGL_RETURN_VAL_IF_FAIL (bmp, NULL);
+
+  loader = _cogl_texture_create_loader ();
+  loader->src_type = COGL_TEXTURE_SOURCE_TYPE_BITMAP;
+  loader->src.bitmap.bitmap = cogl_object_ref (bmp);
+  loader->src.bitmap.height = height;
+  loader->src.bitmap.depth = depth;
+  loader->src.bitmap.can_convert_in_place = FALSE; /* TODO add api for this */
+
+  return _cogl_texture_3d_create_base (_cogl_bitmap_get_context (bmp),
+                                       cogl_bitmap_get_width (bmp),
+                                       height,
+                                       depth,
+                                       internal_format,
+                                       loader);
+}
+
+CoglTexture3D *
+cogl_texture_3d_new_from_data (CoglContext *context,
+                               int width,
+                               int height,
+                               int depth,
+                               CoglPixelFormat format,
+                               CoglPixelFormat internal_format,
+                               int rowstride,
+                               int image_stride,
+                               const uint8_t *data,
+                               CoglError **error)
+{
+  CoglBitmap *bitmap;
+  CoglTexture3D *ret;
+
+  _COGL_RETURN_VAL_IF_FAIL (data, NULL);
+  _COGL_RETURN_VAL_IF_FAIL (format != COGL_PIXEL_FORMAT_ANY, NULL);
+
+  /* Rowstride from width if not given */
+  if (rowstride == 0)
+    rowstride = width * _cogl_pixel_format_get_bytes_per_pixel (format);
+  /* Image stride from height and rowstride if not given */
+  if (image_stride == 0)
+    image_stride = height * rowstride;
+
+  if (image_stride < rowstride * height)
+    return NULL;
+
+  /* GL doesn't support uploading when the image_stride isn't a
+     multiple of the rowstride. If this happens we'll just pack the
+     image into a new bitmap. The documentation for this function
+     recommends avoiding this situation. */
+  if (image_stride % rowstride != 0)
+    {
+      uint8_t *bmp_data;
+      int bmp_rowstride;
+      int z, y;
+
+      bitmap = _cogl_bitmap_new_with_malloc_buffer (context,
+                                                    width,
+                                                    depth * height,
+                                                    format,
+                                                    error);
+      if (!bitmap)
+        return NULL;
+
+      bmp_data = _cogl_bitmap_map (bitmap,
+                                   COGL_BUFFER_ACCESS_WRITE,
+                                   COGL_BUFFER_MAP_HINT_DISCARD,
+                                   error);
+
+      if (bmp_data == NULL)
+        {
+          cogl_object_unref (bitmap);
+          return NULL;
+        }
+
+      bmp_rowstride = cogl_bitmap_get_rowstride (bitmap);
+
+      /* Copy all of the images in */
+      for (z = 0; z < depth; z++)
+        for (y = 0; y < height; y++)
+          memcpy (bmp_data + (z * bmp_rowstride * height +
+                              bmp_rowstride * y),
+                  data + z * image_stride + rowstride * y,
+                  bmp_rowstride);
+
+      _cogl_bitmap_unmap (bitmap);
+    }
+  else
+    bitmap = cogl_bitmap_new_for_data (context,
+                                       width,
+                                       image_stride / rowstride * depth,
+                                       format,
+                                       rowstride,
+                                       (uint8_t *) data);
+
+  ret = cogl_texture_3d_new_from_bitmap (bitmap,
+                                         height,
+                                         depth,
+                                         internal_format,
+                                         error);
+
+  cogl_object_unref (bitmap);
+
+  if (ret &&
+      !cogl_texture_allocate (COGL_TEXTURE (ret), error))
+    {
+      cogl_object_unref (ret);
+      return NULL;
+    }
+
+  return ret;
+}
+
 static CoglBool
 _cogl_texture_3d_can_create (CoglContext *ctx,
                              int width,
@@ -204,50 +348,42 @@ _cogl_texture_3d_can_create (CoglContext *ctx,
   return TRUE;
 }
 
-CoglTexture3D *
-cogl_texture_3d_new_with_size (CoglContext *ctx,
-                               int width,
-                               int height,
-                               int depth,
-                               CoglPixelFormat internal_format)
-{
-  /* Since no data, we need some internal format */
-  if (internal_format == COGL_PIXEL_FORMAT_ANY)
-    internal_format = COGL_PIXEL_FORMAT_RGBA_8888_PRE;
-
-  return _cogl_texture_3d_create_base (ctx,
-                                       width, height, depth,
-                                       internal_format);
-}
-
 static CoglBool
-_cogl_texture_3d_allocate (CoglTexture *tex,
-                           CoglError **error)
+allocate_with_size (CoglTexture3D *tex_3d,
+                    CoglTextureLoader *loader,
+                    CoglError **error)
 {
+  CoglTexture *tex = COGL_TEXTURE (tex_3d);
   CoglContext *ctx = tex->context;
-  CoglTexture3D *tex_3d = COGL_TEXTURE_3D (tex);
+  CoglPixelFormat internal_format;
+  int width = loader->src.sized.width;
+  int height = loader->src.sized.height;
+  int depth = loader->src.sized.depth;
   GLenum gl_intformat;
   GLenum gl_format;
   GLenum gl_type;
   GLenum gl_error;
   GLenum gl_texture;
 
+  internal_format =
+    _cogl_texture_determine_internal_format (tex, COGL_PIXEL_FORMAT_ANY);
+
   if (!_cogl_texture_3d_can_create (ctx,
-                                    tex->width,
-                                    tex->height,
-                                    tex_3d->depth,
-                                    tex_3d->internal_format,
+                                    width,
+                                    height,
+                                    depth,
+                                    internal_format,
                                     error))
     return FALSE;
 
   ctx->driver_vtable->pixel_format_to_gl (ctx,
-                                          tex_3d->internal_format,
+                                          internal_format,
                                           &gl_intformat,
                                           &gl_format,
                                           &gl_type);
 
   gl_texture =
-    ctx->texture_driver->gen (ctx, GL_TEXTURE_3D, tex_3d->internal_format);
+    ctx->texture_driver->gen (ctx, GL_TEXTURE_3D, internal_format);
   _cogl_bind_gl_texture_transient (GL_TEXTURE_3D,
                                    gl_texture,
                                    FALSE);
@@ -256,7 +392,7 @@ _cogl_texture_3d_allocate (CoglTexture *tex,
     ;
 
   ctx->glTexImage3D (GL_TEXTURE_3D, 0, gl_intformat,
-                     tex->width, tex->height, tex_3d->depth,
+                     width, height, depth,
                      0, gl_format, gl_type, NULL);
 
   if (_cogl_gl_util_catch_out_of_memory (ctx, error))
@@ -268,47 +404,49 @@ _cogl_texture_3d_allocate (CoglTexture *tex,
   tex_3d->gl_texture = gl_texture;
   tex_3d->gl_format = gl_intformat;
 
+  tex_3d->depth = depth;
+
+  tex_3d->internal_format = internal_format;
+
+  _cogl_texture_set_allocated (tex, internal_format, width, height);
+
   return TRUE;
 }
 
-CoglTexture3D *
-cogl_texture_3d_new_from_bitmap (CoglBitmap *bmp,
-                                 unsigned int height,
-                                 unsigned int depth,
-                                 CoglPixelFormat internal_format,
-                                 CoglError **error)
+static CoglBool
+allocate_from_bitmap (CoglTexture3D *tex_3d,
+                      CoglTextureLoader *loader,
+                      CoglError **error)
 {
-  CoglTexture3D *tex_3d;
+  CoglTexture *tex = COGL_TEXTURE (tex_3d);
+  CoglContext *ctx = tex->context;
+  CoglPixelFormat internal_format;
+  CoglBitmap *bmp = loader->src.bitmap.bitmap;
+  int bmp_width = cogl_bitmap_get_width (bmp);
+  int height = loader->src.bitmap.height;
+  int depth = loader->src.bitmap.depth;
+  CoglPixelFormat bmp_format = cogl_bitmap_get_format (bmp);
+  CoglBool can_convert_in_place = loader->src.bitmap.can_convert_in_place;
   CoglBitmap *upload_bmp;
-  CoglPixelFormat bmp_format;
   CoglPixelFormat upload_format;
-  unsigned int bmp_width;
   GLenum gl_intformat;
   GLenum gl_format;
   GLenum gl_type;
-  CoglContext *ctx;
 
-  ctx = _cogl_bitmap_get_context (bmp);
-
-  bmp_width = cogl_bitmap_get_width (bmp);
-  bmp_format = cogl_bitmap_get_format (bmp);
-
-  internal_format = _cogl_texture_determine_internal_format (bmp_format,
-                                                             internal_format);
+  internal_format = _cogl_texture_determine_internal_format (tex, bmp_format);
 
   if (!_cogl_texture_3d_can_create (ctx,
                                     bmp_width, height, depth,
                                     internal_format,
                                     error))
-    return NULL;
+    return FALSE;
 
-  upload_bmp =
-    _cogl_bitmap_convert_for_upload (bmp,
-                                     internal_format,
-                                     FALSE, /* can't convert in place */
-                                     error);
+  upload_bmp = _cogl_bitmap_convert_for_upload (bmp,
+                                                internal_format,
+                                                can_convert_in_place,
+                                                error);
   if (upload_bmp == NULL)
-    return NULL;
+    return FALSE;
 
   upload_format = cogl_bitmap_get_format (upload_bmp);
 
@@ -323,10 +461,6 @@ cogl_texture_3d_new_from_bitmap (CoglBitmap *bmp,
                                           NULL,
                                           NULL);
 
-  tex_3d = _cogl_texture_3d_create_base (ctx,
-                                         bmp_width, height, depth,
-                                         internal_format);
-
   /* Keep a copy of the first pixel so that if glGenerateMipmap isn't
      supported we can fallback to using GL_GENERATE_MIPMAP */
   if (!cogl_has_feature (ctx, COGL_FEATURE_ID_OFFSCREEN))
@@ -371,111 +505,43 @@ cogl_texture_3d_new_from_bitmap (CoglBitmap *bmp,
                                              error))
     {
       cogl_object_unref (upload_bmp);
-      cogl_object_unref (tex_3d);
-      return NULL;
+      return FALSE;
     }
 
   tex_3d->gl_format = gl_intformat;
 
   cogl_object_unref (upload_bmp);
 
-  _cogl_texture_set_allocated (COGL_TEXTURE (tex_3d), TRUE);
-
-  return tex_3d;
-}
+  tex_3d->depth = loader->src.bitmap.depth;
 
-CoglTexture3D *
-cogl_texture_3d_new_from_data (CoglContext *context,
-                               int width,
-                               int height,
-                               int depth,
-                               CoglPixelFormat format,
-                               CoglPixelFormat internal_format,
-                               int rowstride,
-                               int image_stride,
-                               const uint8_t *data,
-                               CoglError **error)
-{
-  CoglBitmap *bitmap;
-  CoglTexture3D *ret;
+  tex_3d->internal_format = internal_format;
 
-  /* These are considered a programmer errors so we won't set a
-     CoglError. It would be nice if this was a _COGL_RETURN_IF_FAIL but the
-     rest of Cogl isn't using that */
-  if (format == COGL_PIXEL_FORMAT_ANY)
-    return NULL;
+  _cogl_texture_set_allocated (tex, internal_format,
+                               bmp_width, loader->src.bitmap.height);
 
-  if (data == NULL)
-    return NULL;
+  return TRUE;
+}
 
-  /* Rowstride from width if not given */
-  if (rowstride == 0)
-    rowstride = width * _cogl_pixel_format_get_bytes_per_pixel (format);
-  /* Image stride from height and rowstride if not given */
-  if (image_stride == 0)
-    image_stride = height * rowstride;
+static CoglBool
+_cogl_texture_3d_allocate (CoglTexture *tex,
+                           CoglError **error)
+{
+  CoglTexture3D *tex_3d = COGL_TEXTURE_3D (tex);
+  CoglTextureLoader *loader = tex->loader;
 
-  if (image_stride < rowstride * height)
-    return NULL;
+  _COGL_RETURN_VAL_IF_FAIL (loader, FALSE);
 
-  /* GL doesn't support uploading when the image_stride isn't a
-     multiple of the rowstride. If this happens we'll just pack the
-     image into a new bitmap. The documentation for this function
-     recommends avoiding this situation. */
-  if (image_stride % rowstride != 0)
+  switch (loader->src_type)
     {
-      uint8_t *bmp_data;
-      int bmp_rowstride;
-      int z, y;
-
-      bitmap = _cogl_bitmap_new_with_malloc_buffer (context,
-                                                    width,
-                                                    depth * height,
-                                                    format,
-                                                    error);
-      if (!bitmap)
-        return NULL;
-
-      bmp_data = _cogl_bitmap_map (bitmap,
-                                   COGL_BUFFER_ACCESS_WRITE,
-                                   COGL_BUFFER_MAP_HINT_DISCARD,
-                                   error);
-
-      if (bmp_data == NULL)
-        {
-          cogl_object_unref (bitmap);
-          return NULL;
-        }
-
-      bmp_rowstride = cogl_bitmap_get_rowstride (bitmap);
-
-      /* Copy all of the images in */
-      for (z = 0; z < depth; z++)
-        for (y = 0; y < height; y++)
-          memcpy (bmp_data + (z * bmp_rowstride * height +
-                              bmp_rowstride * y),
-                  data + z * image_stride + rowstride * y,
-                  bmp_rowstride);
-
-      _cogl_bitmap_unmap (bitmap);
+    case COGL_TEXTURE_SOURCE_TYPE_SIZED:
+      return allocate_with_size (tex_3d, loader, error);
+    case COGL_TEXTURE_SOURCE_TYPE_BITMAP:
+      return allocate_from_bitmap (tex_3d, loader, error);
+    default:
+      break;
     }
-  else
-    bitmap = cogl_bitmap_new_for_data (context,
-                                       width,
-                                       image_stride / rowstride * depth,
-                                       format,
-                                       rowstride,
-                                       (uint8_t *) data);
 
-  ret = cogl_texture_3d_new_from_bitmap (bitmap,
-                                         height,
-                                         depth,
-                                         internal_format,
-                                         error);
-
-  cogl_object_unref (bitmap);
-
-  return ret;
+  g_return_val_if_reached (FALSE);
 }
 
 static CoglBool
diff --git a/cogl/cogl-texture-3d.h b/cogl/cogl-texture-3d.h
index 7aca7e2..6ab2093 100644
--- a/cogl/cogl-texture-3d.h
+++ b/cogl/cogl-texture-3d.h
@@ -56,8 +56,8 @@ typedef struct _CoglTexture3D CoglTexture3D;
  * @internal_format: the #CoglPixelFormat to use for the GPU
  *    storage of the texture.
  *
- * Creates a new #CoglTexture3D texture with the specified dimensions
- * and pixel format.
+ * Creates a low-level #CoglTexture3D texture with the specified
+ * dimensions and pixel format.
  *
  * The storage for the texture is not allocated before this function
  * returns. You can call cogl_texture_allocate() to explicitly
@@ -66,6 +66,11 @@ typedef struct _CoglTexture3D CoglTexture3D;
  * how the texture is going to be used and can optimize how it is
  * allocated.
  *
+ * The texture is still configurable until it has been allocated so
+ * for example you can influence the internal format of the texture
+ * using cogl_texture_set_components() and
+ * cogl_texture_set_premultiplied().
+ *
  * <note>This texture will fail to allocate later if
  * %COGL_FEATURE_ID_TEXTURE_3D is not advertised. Allocation can also
  * fail if the requested dimensions are not supported by the
@@ -107,16 +112,21 @@ cogl_texture_3d_new_with_size (CoglContext *context,
  * @data: pointer the memory region where the source buffer resides
  * @error: A CoglError return location.
  *
- * Creates a new 3D texture and initializes it with @data. The data is
- * assumed to be packed array of @depth images. There can be padding
- * between the images using @image_stride.
+ * Creates a low-level 3D texture and initializes it with @data. The
+ * data is assumed to be packed array of @depth images. There can be
+ * padding between the images using @image_stride.
  *
- * Note that this function will throw a #CoglError if
- * %COGL_FEATURE_ID_TEXTURE_3D is not advertised. It can also fail if the
- * requested dimensions are not supported by the GPU.
+ * <note>This api will always immediately allocate GPU memory for the
+ * texture and upload the given data so that the @data pointer does
+ * not need to remain valid once this function returns. This means it
+ * is not possible to configure the texture before it is allocated. If
+ * you do need to configure the texture before allocation (to specify
+ * constraints on the internal format for example) then you can
+ * instead create a #CoglBitmap for your data and use
+ * cogl_texture_3d_new_from_bitmap().</note>
  *
  * Return value: (transfer full): the newly created #CoglTexture3D or
- *               %NULL if there was an error an an exception will be
+ *               %NULL if there was an error and an exception will be
  *               returned through @error.
  * Since: 1.10
  * Stability: Unstable
@@ -148,13 +158,30 @@ cogl_texture_3d_new_from_data (CoglContext *context,
  *    something other than straight blending.
  * @error: A CoglError return location.
  *
- * Creates a new 3D texture and initializes it with the images in
- * @bitmap. The images are assumed to be packed together after one
+ * Creates a low-level 3D texture and initializes it with the images
+ * in @bitmap. The images are assumed to be packed together after one
  * another in the increasing y axis. The height of individual image is
  * given as @height and the number of images is given in @depth. The
  * actual height of the bitmap can be larger than @height × @depth. In
  * this case it assumes there is padding between the images.
  *
+ * The storage for the texture is not allocated before this function
+ * returns. You can call cogl_texture_allocate() to explicitly
+ * allocate the underlying storage or preferably let Cogl
+ * automatically allocate storage lazily when it may know more about
+ * how the texture is going to be used and can optimize how it is
+ * allocated.
+ *
+ * The texture is still configurable until it has been allocated so
+ * for example you can influence the internal format of the texture
+ * using cogl_texture_set_components() and
+ * cogl_texture_set_premultiplied().
+ *
+ * <note>This texture will fail to allocate later if
+ * %COGL_FEATURE_ID_TEXTURE_3D is not advertised. Allocation can also
+ * fail if the requested dimensions are not supported by the
+ * GPU.</note>
+ *
  * Return value: (transfer full): the newly created texture or %NULL
  *   if there was an error.
  * Since: 2.0
@@ -162,8 +189,8 @@ cogl_texture_3d_new_from_data (CoglContext *context,
  */
 CoglTexture3D *
 cogl_texture_3d_new_from_bitmap (CoglBitmap *bitmap,
-                                 unsigned int height,
-                                 unsigned int depth,
+                                 int height,
+                                 int depth,
                                  CoglPixelFormat internal_format,
                                  CoglError **error);
 
diff --git a/cogl/cogl-texture-private.h b/cogl/cogl-texture-private.h
index aae779c..7b94b6c 100644
--- a/cogl/cogl-texture-private.h
+++ b/cogl/cogl-texture-private.h
@@ -31,6 +31,10 @@
 #include "cogl-meta-texture.h"
 #include "cogl-framebuffer.h"
 
+#ifdef COGL_HAS_EGL_SUPPORT
+#include "cogl-egl-defines.h"
+#endif
+
 typedef struct _CoglTextureVtable     CoglTextureVtable;
 
 /* Encodes three possibiloities result of transforming a quad */
@@ -137,15 +141,59 @@ struct _CoglTextureVtable
                             CoglBool value);
 };
 
+typedef enum _CoglTextureSoureType {
+  COGL_TEXTURE_SOURCE_TYPE_SIZED = 1,
+  COGL_TEXTURE_SOURCE_TYPE_BITMAP,
+  COGL_TEXTURE_SOURCE_TYPE_EGL_IMAGE,
+  COGL_TEXTURE_SOURCE_TYPE_GL_FOREIGN
+} CoglTextureSourceType;
+
+typedef struct _CoglTextureLoader
+{
+  CoglTextureSourceType src_type;
+  union {
+    struct {
+      int width;
+      int height;
+      int depth; /* for 3d textures */
+    } sized;
+    struct {
+      CoglBitmap *bitmap;
+      int height; /* for 3d textures */
+      int depth; /* for 3d textures */
+      CoglBool can_convert_in_place;
+    } bitmap;
+#if defined (COGL_HAS_EGL_SUPPORT) && defined (EGL_KHR_image_base)
+    struct {
+      EGLImageKHR image;
+      int width;
+      int height;
+      CoglPixelFormat format;
+    } egl_image;
+#endif
+    struct {
+      int width;
+      int height;
+      unsigned int gl_handle;
+      CoglPixelFormat format;
+    } gl_foreign;
+  } src;
+} CoglTextureLoader;
+
 struct _CoglTexture
 {
   CoglObject _parent;
   CoglContext *context;
+  CoglTextureLoader *loader;
   GList *framebuffers;
   int max_level;
   int width;
   int height;
   CoglBool allocated;
+
+  /* internal layout */
+  CoglTextureComponents components;
+  unsigned int premultiplied:1;
   const CoglTextureVtable *vtable;
 };
 
@@ -181,6 +229,8 @@ _cogl_texture_init (CoglTexture *texture,
                     CoglContext *ctx,
                     int width,
                     int height,
+                    CoglPixelFormat internal_format,
+                    CoglTextureLoader *loader,
                     const CoglTextureVtable *vtable);
 
 void
@@ -218,12 +268,38 @@ _cogl_texture_pre_paint (CoglTexture *texture, CoglTexturePrePaintFlags flags);
 void
 _cogl_texture_ensure_non_quad_rendering (CoglTexture *texture);
 
-/* Utility function to determine which pixel format to use when
-   dst_format is COGL_PIXEL_FORMAT_ANY. If dst_format is not ANY then
-   it will just be returned directly */
+/*
+ * This determines a CoglPixelFormat according to texture::components
+ * and texture::premultiplied (i.e. the user required components and
+ * whether the texture should be considered premultiplied)
+ *
+ * A reference/source format can be given (or COGL_PIXEL_FORMAT_ANY)
+ * and wherever possible this function tries to simply return the
+ * given source format if its compatible with the required components.
+ *
+ * Texture backends can call this when allocating a texture to know
+ * how to convert a source image in preparation for uploading.
+ */
 CoglPixelFormat
-_cogl_texture_determine_internal_format (CoglPixelFormat src_format,
-                                         CoglPixelFormat dst_format);
+_cogl_texture_determine_internal_format (CoglTexture *texture,
+                                         CoglPixelFormat src_format);
+
+/* This is called by texture backends when they have successfully
+ * allocated a texture.
+ *
+ * Most texture backends currently track the internal layout of
+ * textures using a CoglPixelFormat which will be finalized when a
+ * texture is allocated. At this point we need to update
+ * texture::components and texture::premultiplied according to the
+ * determined layout.
+ *
+ * XXX: Going forward we should probably aim to stop using
+ * CoglPixelFormat at all for tracking the internal layout of
+ * textures.
+ */
+void
+_cogl_texture_set_internal_format (CoglTexture *texture,
+                                   CoglPixelFormat internal_format);
 
 CoglBool
 _cogl_texture_is_foreign (CoglTexture *texture);
@@ -280,9 +356,14 @@ _cogl_texture_get_level_size (CoglTexture *texture,
 
 void
 _cogl_texture_set_allocated (CoglTexture *texture,
-                             CoglBool allocated);
+                             CoglPixelFormat internal_format,
+                             int width,
+                             int height);
 
 CoglPixelFormat
 _cogl_texture_get_format (CoglTexture *texture);
 
+CoglTextureLoader *
+_cogl_texture_create_loader (void);
+
 #endif /* __COGL_TEXTURE_PRIVATE_H */
diff --git a/cogl/cogl-texture-rectangle.c b/cogl/cogl-texture-rectangle.c
index db7968d..d9ca1f7 100644
--- a/cogl/cogl-texture-rectangle.c
+++ b/cogl/cogl-texture-rectangle.c
@@ -166,12 +166,15 @@ static CoglTextureRectangle *
 _cogl_texture_rectangle_create_base (CoglContext *ctx,
                                      int width,
                                      int height,
-                                     CoglPixelFormat internal_format)
+                                     CoglPixelFormat internal_format,
+                                     CoglTextureLoader *loader)
 {
   CoglTextureRectangle *tex_rect = g_new (CoglTextureRectangle, 1);
   CoglTexture *tex = COGL_TEXTURE (tex_rect);
 
-  _cogl_texture_init (tex, ctx, width, height, &cogl_texture_rectangle_vtable);
+  _cogl_texture_init (tex, ctx, width, height,
+                      internal_format, loader,
+                      &cogl_texture_rectangle_vtable);
 
   tex_rect->gl_texture = 0;
   tex_rect->is_foreign = FALSE;
@@ -184,8 +187,6 @@ _cogl_texture_rectangle_create_base (CoglContext *ctx,
   tex_rect->gl_legacy_texobj_wrap_mode_s = GL_FALSE;
   tex_rect->gl_legacy_texobj_wrap_mode_t = GL_FALSE;
 
-  tex_rect->internal_format = internal_format;
-
   return _cogl_texture_rectangle_object_new (tex_rect);
 }
 
@@ -195,36 +196,49 @@ cogl_texture_rectangle_new_with_size (CoglContext *ctx,
                                       int height,
                                       CoglPixelFormat internal_format)
 {
+  CoglTextureLoader *loader;
+
   /* Since no data, we need some internal format */
   if (internal_format == COGL_PIXEL_FORMAT_ANY)
     internal_format = COGL_PIXEL_FORMAT_RGBA_8888_PRE;
 
-  return _cogl_texture_rectangle_create_base (ctx,
-                                              width, height,
-                                              internal_format);
+  loader = _cogl_texture_create_loader ();
+  loader->src_type = COGL_TEXTURE_SOURCE_TYPE_SIZED;
+  loader->src.sized.width = width;
+  loader->src.sized.height = height;
+
+  return _cogl_texture_rectangle_create_base (ctx, width, height,
+                                              internal_format, loader);
 }
 
 static CoglBool
-_cogl_texture_rectangle_allocate (CoglTexture *tex,
-                                  CoglError **error)
+allocate_with_size (CoglTextureRectangle *tex_rect,
+                    CoglTextureLoader *loader,
+                    CoglError **error)
 {
+  CoglTexture *tex = COGL_TEXTURE (tex_rect);
   CoglContext *ctx = tex->context;
-  CoglTextureRectangle *tex_rect = COGL_TEXTURE_RECTANGLE (tex);
+  CoglPixelFormat internal_format;
+  int width = loader->src.sized.width;
+  int height = loader->src.sized.height;
   GLenum gl_intformat;
   GLenum gl_format;
   GLenum gl_type;
   GLenum gl_error;
   GLenum gl_texture;
 
+  internal_format =
+    _cogl_texture_determine_internal_format (tex, COGL_PIXEL_FORMAT_ANY);
+
   if (!_cogl_texture_rectangle_can_create (ctx,
-                                           tex->width,
-                                           tex->height,
-                                           tex_rect->internal_format,
+                                           width,
+                                           height,
+                                           internal_format,
                                            error))
     return FALSE;
 
   ctx->driver_vtable->pixel_format_to_gl (ctx,
-                                          tex_rect->internal_format,
+                                          internal_format,
                                           &gl_intformat,
                                           &gl_format,
                                           &gl_type);
@@ -232,7 +246,7 @@ _cogl_texture_rectangle_allocate (CoglTexture *tex,
   gl_texture =
     ctx->texture_driver->gen (ctx,
                               GL_TEXTURE_RECTANGLE_ARB,
-                              tex_rect->internal_format);
+                              internal_format);
   _cogl_bind_gl_texture_transient (GL_TEXTURE_RECTANGLE_ARB,
                                    gl_texture,
                                    tex_rect->is_foreign);
@@ -242,7 +256,7 @@ _cogl_texture_rectangle_allocate (CoglTexture *tex,
     ;
 
   ctx->glTexImage2D (GL_TEXTURE_RECTANGLE_ARB, 0, gl_intformat,
-                     tex->width, tex->height, 0, gl_format, gl_type, NULL);
+                     width, height, 0, gl_format, gl_type, NULL);
 
   if (_cogl_gl_util_catch_out_of_memory (ctx, error))
     {
@@ -250,46 +264,50 @@ _cogl_texture_rectangle_allocate (CoglTexture *tex,
       return FALSE;
     }
 
+  tex_rect->internal_format = internal_format;
+
   tex_rect->gl_texture = gl_texture;
   tex_rect->gl_format = gl_intformat;
 
+  _cogl_texture_set_allocated (COGL_TEXTURE (tex_rect),
+                               internal_format, width, height);
+
   return TRUE;
 }
 
-CoglTextureRectangle *
-cogl_texture_rectangle_new_from_bitmap (CoglBitmap *bmp,
-                                        CoglPixelFormat internal_format,
-                                        CoglError **error)
+static CoglBool
+allocate_from_bitmap (CoglTextureRectangle *tex_rect,
+                      CoglTextureLoader *loader,
+                      CoglError **error)
 {
-  CoglTextureRectangle *tex_rect;
+  CoglTexture *tex = COGL_TEXTURE (tex_rect);
+  CoglContext *ctx = tex->context;
+  CoglPixelFormat internal_format;
+  CoglBitmap *bmp = loader->src.bitmap.bitmap;
+  int width = cogl_bitmap_get_width (bmp);
+  int height = cogl_bitmap_get_height (bmp);
+  CoglBool can_convert_in_place = loader->src.bitmap.can_convert_in_place;
   CoglBitmap *upload_bmp;
   GLenum gl_intformat;
   GLenum gl_format;
   GLenum gl_type;
-  CoglContext *ctx;
-
-  _COGL_RETURN_VAL_IF_FAIL (cogl_is_bitmap (bmp), NULL);
-
-  ctx = _cogl_bitmap_get_context (bmp);
 
   internal_format =
-    _cogl_texture_determine_internal_format (cogl_bitmap_get_format (bmp),
-                                             internal_format);
+    _cogl_texture_determine_internal_format (tex, cogl_bitmap_get_format (bmp));
 
   if (!_cogl_texture_rectangle_can_create (ctx,
-                                           cogl_bitmap_get_width (bmp),
-                                           cogl_bitmap_get_height (bmp),
+                                           width,
+                                           height,
                                            internal_format,
                                            error))
-    return NULL;
+    return FALSE;
 
-  upload_bmp =
-    _cogl_bitmap_convert_for_upload (bmp,
-                                     internal_format,
-                                     FALSE, /* can't convert in place */
-                                     error);
+  upload_bmp = _cogl_bitmap_convert_for_upload (bmp,
+                                                internal_format,
+                                                can_convert_in_place,
+                                                error);
   if (upload_bmp == NULL)
-    return NULL;
+    return FALSE;
 
   ctx->driver_vtable->pixel_format_to_gl (ctx,
                                           cogl_bitmap_get_format (upload_bmp),
@@ -302,11 +320,6 @@ cogl_texture_rectangle_new_from_bitmap (CoglBitmap *bmp,
                                           NULL,
                                           NULL);
 
-  tex_rect = _cogl_texture_rectangle_create_base (ctx,
-                                                  cogl_bitmap_get_width (bmp),
-                                                  cogl_bitmap_get_height (bmp),
-                                                  internal_format);
-
   tex_rect->gl_texture =
     ctx->texture_driver->gen (ctx,
                               GL_TEXTURE_RECTANGLE_ARB,
@@ -322,38 +335,31 @@ cogl_texture_rectangle_new_from_bitmap (CoglBitmap *bmp,
                                           error))
     {
       cogl_object_unref (upload_bmp);
-      cogl_object_unref (tex_rect);
-      return NULL;
+      return FALSE;
     }
 
   tex_rect->gl_format = gl_intformat;
+  tex_rect->internal_format = internal_format;
 
   cogl_object_unref (upload_bmp);
 
-  _cogl_texture_set_allocated (COGL_TEXTURE (tex_rect), TRUE);
+  _cogl_texture_set_allocated (COGL_TEXTURE (tex_rect),
+                               internal_format, width, height);
 
-  return tex_rect;
+  return TRUE;
 }
 
-CoglTextureRectangle *
-cogl_texture_rectangle_new_from_foreign (CoglContext *ctx,
-                                         unsigned int gl_handle,
-                                         int width,
-                                         int height,
-                                         CoglPixelFormat format,
-                                         CoglError **error)
+static CoglBool
+allocate_from_gl_foreign (CoglTextureRectangle *tex_rect,
+                          CoglTextureLoader *loader,
+                          CoglError **error)
 {
-  /* NOTE: width, height and internal format are not queriable
-   * in GLES, hence such a function prototype.
-   */
-
+  CoglTexture *tex = COGL_TEXTURE (tex_rect);
+  CoglContext *ctx = tex->context;
+  CoglPixelFormat format = loader->src.gl_foreign.format;
   GLenum gl_error = 0;
   GLint gl_compressed = GL_FALSE;
   GLenum gl_int_format = 0;
-  CoglTextureRectangle *tex_rect;
-
-  /* Assert that it is a valid GL texture object */
-  g_return_val_if_fail (ctx->glIsTexture (gl_handle), NULL);
 
   if (!ctx->texture_driver->allows_foreign_gl_target (ctx,
                                                       GL_TEXTURE_RECTANGLE_ARB))
@@ -363,21 +369,22 @@ cogl_texture_rectangle_new_from_foreign (CoglContext *ctx,
                        COGL_SYSTEM_ERROR_UNSUPPORTED,
                        "Foreign GL_TEXTURE_RECTANGLE textures are not "
                        "supported by your system");
-      return NULL;
+      return FALSE;
     }
 
   /* Make sure binding succeeds */
   while ((gl_error = ctx->glGetError ()) != GL_NO_ERROR)
     ;
 
-  _cogl_bind_gl_texture_transient (GL_TEXTURE_RECTANGLE_ARB, gl_handle, TRUE);
+  _cogl_bind_gl_texture_transient (GL_TEXTURE_RECTANGLE_ARB,
+                                   loader->src.gl_foreign.gl_handle, TRUE);
   if (ctx->glGetError () != GL_NO_ERROR)
     {
       _cogl_set_error (error,
                        COGL_SYSTEM_ERROR,
                        COGL_SYSTEM_ERROR_UNSUPPORTED,
                        "Failed to bind foreign GL_TEXTURE_RECTANGLE texture");
-      return NULL;
+      return FALSE;
     }
 
   /* Obtain texture parameters */
@@ -408,7 +415,7 @@ cogl_texture_rectangle_new_from_foreign (CoglContext *ctx,
                            COGL_SYSTEM_ERROR,
                            COGL_SYSTEM_ERROR_UNSUPPORTED,
                            "Unsupported internal format for foreign texture");
-          return NULL;
+          return FALSE;
         }
     }
   else
@@ -423,16 +430,6 @@ cogl_texture_rectangle_new_from_foreign (CoglContext *ctx,
                                               NULL);
     }
 
-  /* Note: We always trust the given width and height without querying
-   * the texture object because the user may be creating a Cogl
-   * texture for a texture_from_pixmap object where glTexImage2D may
-   * not have been called and the texture_from_pixmap spec doesn't
-   * clarify that it is reliable to query back the size from OpenGL.
-   */
-
-  /* Validate width and height */
-  g_return_val_if_fail (width > 0 && height > 0, NULL);
-
   /* Compressed texture images not supported */
   if (gl_compressed == GL_TRUE)
     {
@@ -440,27 +437,107 @@ cogl_texture_rectangle_new_from_foreign (CoglContext *ctx,
                        COGL_SYSTEM_ERROR,
                        COGL_SYSTEM_ERROR_UNSUPPORTED,
                        "Compressed foreign textures aren't currently supported");
-      return NULL;
+      return FALSE;
     }
 
-  /* Create new texture */
-  tex_rect = _cogl_texture_rectangle_create_base (ctx, width, height, format);
-
   /* Setup bitmap info */
   tex_rect->is_foreign = TRUE;
 
-  tex_rect->internal_format = format;
-
-  tex_rect->gl_texture = gl_handle;
+  tex_rect->gl_texture = loader->src.gl_foreign.gl_handle;
   tex_rect->gl_format = gl_int_format;
 
   /* Unknown filter */
   tex_rect->gl_legacy_texobj_min_filter = GL_FALSE;
   tex_rect->gl_legacy_texobj_mag_filter = GL_FALSE;
 
-  _cogl_texture_set_allocated (COGL_TEXTURE (tex_rect), TRUE);
+  tex_rect->internal_format = format;
+
+  _cogl_texture_set_allocated (COGL_TEXTURE (tex_rect),
+                               format,
+                               loader->src.gl_foreign.width,
+                               loader->src.gl_foreign.height);
+
+  return TRUE;
+}
+
+static CoglBool
+_cogl_texture_rectangle_allocate (CoglTexture *tex,
+                                  CoglError **error)
+{
+  CoglTextureRectangle *tex_rect = COGL_TEXTURE_RECTANGLE (tex);
+  CoglTextureLoader *loader = tex->loader;
+
+  _COGL_RETURN_VAL_IF_FAIL (loader, FALSE);
+
+  switch (loader->src_type)
+    {
+    case COGL_TEXTURE_SOURCE_TYPE_SIZED:
+      return allocate_with_size (tex_rect, loader, error);
+    case COGL_TEXTURE_SOURCE_TYPE_BITMAP:
+      return allocate_from_bitmap (tex_rect, loader, error);
+    case COGL_TEXTURE_SOURCE_TYPE_GL_FOREIGN:
+      return allocate_from_gl_foreign (tex_rect, loader, error);
+    default:
+      break;
+    }
+
+  g_return_val_if_reached (FALSE);
+}
+
+CoglTextureRectangle *
+cogl_texture_rectangle_new_from_bitmap (CoglBitmap *bmp,
+                                        CoglPixelFormat internal_format,
+                                        CoglError **error)
+{
+  CoglTextureLoader *loader;
+
+  _COGL_RETURN_VAL_IF_FAIL (cogl_is_bitmap (bmp), NULL);
+
+  loader = _cogl_texture_create_loader ();
+  loader->src_type = COGL_TEXTURE_SOURCE_TYPE_BITMAP;
+  loader->src.bitmap.bitmap = cogl_object_ref (bmp);
+  loader->src.bitmap.can_convert_in_place = FALSE; /* TODO add api for this */
+
+  return _cogl_texture_rectangle_create_base (_cogl_bitmap_get_context (bmp),
+                                              cogl_bitmap_get_width (bmp),
+                                              cogl_bitmap_get_height (bmp),
+                                              internal_format,
+                                              loader);
+}
+
+CoglTextureRectangle *
+cogl_texture_rectangle_new_from_foreign (CoglContext *ctx,
+                                         unsigned int gl_handle,
+                                         int width,
+                                         int height,
+                                         CoglPixelFormat format,
+                                         CoglError **error)
+{
+  CoglTextureLoader *loader;
+
+  /* NOTE: width, height and internal format are not queriable in
+   * GLES, hence such a function prototype. Also in the case of full
+   * opengl the user may be creating a Cogl texture for a
+   * texture_from_pixmap object where glTexImage2D may not have been
+   * called and the texture_from_pixmap spec doesn't clarify that it
+   * is reliable to query back the size from OpenGL.
+   */
+
+  /* Assert that it is a valid GL texture object */
+  _COGL_RETURN_VAL_IF_FAIL (ctx->glIsTexture (gl_handle), NULL);
+
+  /* Validate width and height */
+  _COGL_RETURN_VAL_IF_FAIL (width > 0 && height > 0, NULL);
+
+  loader = _cogl_texture_create_loader ();
+  loader->src_type = COGL_TEXTURE_SOURCE_TYPE_GL_FOREIGN;
+  loader->src.gl_foreign.gl_handle = gl_handle;
+  loader->src.gl_foreign.width = width;
+  loader->src.gl_foreign.height = height;
+  loader->src.gl_foreign.format = format;
 
-  return tex_rect;
+  return _cogl_texture_rectangle_create_base (ctx, width, height,
+                                              format, loader);
 }
 
 static CoglBool
diff --git a/cogl/cogl-texture-rectangle.h b/cogl/cogl-texture-rectangle.h
index 1e3248a..eef20bf 100644
--- a/cogl/cogl-texture-rectangle.h
+++ b/cogl/cogl-texture-rectangle.h
@@ -147,6 +147,13 @@ cogl_texture_rectangle_new_with_size (CoglContext *ctx,
  * first check for the %COGL_FEATURE_ID_TEXTURE_RECTANGLE feature
  * using cogl_has_feature().</note>
  *
+ * The storage for the texture is not allocated before this function
+ * returns. You can call cogl_texture_allocate() to explicitly
+ * allocate the underlying storage or preferably let Cogl
+ * automatically allocate storage lazily when it may know more about
+ * how the texture is going to be used and can optimize how it is
+ * allocated.
+ *
  * Returns: A pointer to a newly allocated #CoglTextureRectangle texture
  *          or if the size was too large or there wasn't enough memory
  *          %NULL is returned and @error set.
@@ -187,7 +194,11 @@ cogl_texture_rectangle_new_from_bitmap (CoglBitmap *bitmap,
  * <note>Applications wanting to use #CoglTextureRectangle should
  * first check for the %COGL_FEATURE_ID_TEXTURE_RECTANGLE feature
  * using cogl_has_feature().</note>
-
+ *
+ * The texture is still configurable until it has been allocated so
+ * for example you can declare whether the texture is premultiplied
+ * with cogl_texture_set_premultiplied().
+ *
  * Returns: A newly allocated #CoglTextureRectangle, or if Cogl could
  *          not validate the @gl_handle in some way (perhaps because
  *          of an unsupported format) it will return %NULL and set
diff --git a/cogl/cogl-texture.c b/cogl/cogl-texture.c
index 6f1641c..cd65dda 100644
--- a/cogl/cogl-texture.c
+++ b/cogl/cogl-texture.c
@@ -105,6 +105,8 @@ _cogl_texture_init (CoglTexture *texture,
                     CoglContext *context,
                     int width,
                     int height,
+                    CoglPixelFormat internal_format,
+                    CoglTextureLoader *loader,
                     const CoglTextureVtable *vtable)
 {
   texture->context = context;
@@ -114,11 +116,44 @@ _cogl_texture_init (CoglTexture *texture,
   texture->allocated = FALSE;
   texture->vtable = vtable;
   texture->framebuffers = NULL;
+
+  texture->loader = loader;
+
+  _cogl_texture_set_internal_format (texture, internal_format);
+}
+
+static void
+_cogl_texture_free_loader (CoglTexture *texture)
+{
+  if (texture->loader)
+    {
+      CoglTextureLoader *loader = texture->loader;
+      switch (loader->src_type)
+        {
+        case COGL_TEXTURE_SOURCE_TYPE_SIZED:
+        case COGL_TEXTURE_SOURCE_TYPE_EGL_IMAGE:
+        case COGL_TEXTURE_SOURCE_TYPE_GL_FOREIGN:
+          break;
+        case COGL_TEXTURE_SOURCE_TYPE_BITMAP:
+          cogl_object_unref (loader->src.bitmap.bitmap);
+          break;
+        }
+      g_slice_free (CoglTextureLoader, loader);
+      texture->loader = NULL;
+    }
+}
+
+CoglTextureLoader *
+_cogl_texture_create_loader (void)
+{
+  return g_slice_new0 (CoglTextureLoader);
 }
 
 void
 _cogl_texture_free (CoglTexture *texture)
 {
+  _cogl_texture_free_loader (texture);
+
   g_free (texture);
 }
 
@@ -133,34 +168,6 @@ _cogl_texture_needs_premult_conversion (CoglPixelFormat src_format,
           (dst_format & COGL_PREMULT_BIT));
 }
 
-CoglPixelFormat
-_cogl_texture_determine_internal_format (CoglPixelFormat src_format,
-                                         CoglPixelFormat dst_format)
-{
-  /* If the application hasn't specified a specific format then we'll
-   * pick the most appropriate. By default Cogl will use a
-   * premultiplied internal format. Later we will add control over
-   * this. */
-  if (dst_format == COGL_PIXEL_FORMAT_ANY)
-    {
-      if (COGL_PIXEL_FORMAT_CAN_HAVE_PREMULT (src_format))
-        return src_format | COGL_PREMULT_BIT;
-      else
-        return src_format;
-    }
-  else
-    /* XXX: It might be nice to make this match the component ordering
-       of the source format when the formats are otherwise the same
-       because on GL there is no way to specify the ordering of the
-       internal format. However when using GLES with the
-       GL_EXT_texture_format_BGRA8888 the order of the internal format
-       becomes important because it must exactly match the format of
-       the uploaded data. That means that if someone creates a texture
-       with some RGBA data and then later tries to upload BGRA data we
-       do actually have to swizzle the components */
-    return dst_format;
-}
-
 CoglBool
 _cogl_texture_is_foreign (CoglTexture *texture)
 {
@@ -1242,9 +1249,17 @@ _cogl_texture_spans_foreach_in_region (CoglSpan *x_spans,
 
 void
 _cogl_texture_set_allocated (CoglTexture *texture,
-                             CoglBool allocated)
+                             CoglPixelFormat internal_format,
+                             int width,
+                             int height)
 {
-  texture->allocated = allocated;
+  _cogl_texture_set_internal_format (texture, internal_format);
+
+  texture->width = width;
+  texture->height = height;
+  texture->allocated = TRUE;
+
+  _cogl_texture_free_loader (texture);
 }
 
 CoglBool
@@ -1258,3 +1273,135 @@ cogl_texture_allocate (CoglTexture *texture,
 
   return texture->allocated;
 }
+
+void
+_cogl_texture_set_internal_format (CoglTexture *texture,
+                                   CoglPixelFormat internal_format)
+{
+  texture->premultiplied = FALSE;
+
+  if (internal_format == COGL_PIXEL_FORMAT_ANY)
+    internal_format = COGL_PIXEL_FORMAT_RGBA_8888_PRE;
+
+  if (internal_format == COGL_PIXEL_FORMAT_A_8)
+    {
+      texture->components = COGL_TEXTURE_COMPONENTS_A;
+      return;
+    }
+  else if (internal_format & COGL_DEPTH_BIT)
+    {
+      texture->components = COGL_TEXTURE_COMPONENTS_DEPTH;
+      return;
+    }
+  else if (internal_format & COGL_A_BIT)
+    {
+      texture->components = COGL_TEXTURE_COMPONENTS_RGBA;
+      if (internal_format & COGL_PREMULT_BIT)
+        texture->premultiplied = TRUE;
+      return;
+    }
+  else
+    texture->components = COGL_TEXTURE_COMPONENTS_RGB;
+}
+
+CoglPixelFormat
+_cogl_texture_determine_internal_format (CoglTexture *texture,
+                                         CoglPixelFormat src_format)
+{
+  switch (texture->components)
+    {
+    case COGL_TEXTURE_COMPONENTS_DEPTH:
+      if (src_format & COGL_DEPTH_BIT)
+        return src_format;
+      else
+        {
+          CoglContext *ctx = texture->context;
+
+          if (_cogl_has_private_feature (ctx,
+                  COGL_PRIVATE_FEATURE_EXT_PACKED_DEPTH_STENCIL) ||
+              _cogl_has_private_feature (ctx,
+                  COGL_PRIVATE_FEATURE_OES_PACKED_DEPTH_STENCIL))
+            {
+              return COGL_PIXEL_FORMAT_DEPTH_24_STENCIL_8;
+            }
+          else
+            return COGL_PIXEL_FORMAT_DEPTH_16;
+        }
+    case COGL_TEXTURE_COMPONENTS_A:
+      return COGL_PIXEL_FORMAT_A_8;
+    case COGL_TEXTURE_COMPONENTS_RGB:
+      if (src_format != COGL_PIXEL_FORMAT_ANY &&
+          !(src_format & COGL_A_BIT) && !(src_format & COGL_DEPTH_BIT))
+        return src_format;
+      else
+        return COGL_PIXEL_FORMAT_RGB_888;
+    case COGL_TEXTURE_COMPONENTS_RGBA:
+      {
+        CoglPixelFormat format;
+
+        if (src_format != COGL_PIXEL_FORMAT_ANY &&
+            (src_format & COGL_A_BIT) && src_format != COGL_PIXEL_FORMAT_A_8)
+          format = src_format;
+        else
+          format = COGL_PIXEL_FORMAT_RGBA_8888;
+
+        if (texture->premultiplied)
+          {
+            if (COGL_PIXEL_FORMAT_CAN_HAVE_PREMULT (format))
+              return format |= COGL_PREMULT_BIT;
+            else
+              return COGL_PIXEL_FORMAT_RGBA_8888_PRE;
+          }
+        else
+          return format & ~COGL_PREMULT_BIT;
+      }
+    }
+
+  g_return_val_if_reached (COGL_PIXEL_FORMAT_RGBA_8888_PRE);
+}
+
+void
+cogl_texture_set_components (CoglTexture *texture,
+                             CoglTextureComponents components)
+{
+  _COGL_RETURN_IF_FAIL (!texture->allocated);
+
+  if (texture->components == components)
+    return;
+
+  texture->components = components;
+
+  if (components == COGL_TEXTURE_COMPONENTS_RGBA)
+    texture->premultiplied = TRUE;
+  else
+    texture->premultiplied = FALSE;
+}
+
+CoglBool
+cogl_texture_get_components (CoglTexture *texture)
+{
+  return texture->components;
+}
+
+void
+cogl_texture_set_premultiplied (CoglTexture *texture,
+                                CoglBool premultiplied)
+{
+  _COGL_RETURN_IF_FAIL (!texture->allocated);
+
+  premultiplied = !!premultiplied;
+
+  if (texture->premultiplied == premultiplied)
+    return;
+
+  texture->premultiplied = premultiplied;
+
+  if (premultiplied)
+    texture->components = COGL_TEXTURE_COMPONENTS_RGBA;
+}
+
+CoglBool
+cogl_texture_get_premultiplied (CoglTexture *texture)
+{
+  return texture->premultiplied;
+}
diff --git a/cogl/cogl-texture.h b/cogl/cogl-texture.h
index 9c3d193..8a664b4 100644
--- a/cogl/cogl-texture.h
+++ b/cogl/cogl-texture.h
@@ -124,6 +124,28 @@ uint32_t cogl_texture_error_domain (void);
 CoglBool
 cogl_is_texture (void *object);
 
+typedef enum _CoglTextureComponents
+{
+  COGL_TEXTURE_COMPONENTS_A = 1,
+  COGL_TEXTURE_COMPONENTS_RGB,
+  COGL_TEXTURE_COMPONENTS_RGBA,
+  COGL_TEXTURE_COMPONENTS_DEPTH
+} CoglTextureComponents;
+
+void
+cogl_texture_set_components (CoglTexture *texture,
+                             CoglTextureComponents components);
+
+CoglBool
+cogl_texture_get_components (CoglTexture *texture);
+
+void
+cogl_texture_set_premultiplied (CoglTexture *texture,
+                                CoglBool premultiplied);
+
+CoglBool
+cogl_texture_get_premultiplied (CoglTexture *texture);
+
 /**
  * cogl_texture_get_width:
  * @texture: a #CoglTexture pointer.
diff --git a/cogl/driver/gl/cogl-texture-2d-gl.c b/cogl/driver/gl/cogl-texture-2d-gl.c
index 6928871..27e30f5 100644
--- a/cogl/driver/gl/cogl-texture-2d-gl.c
+++ b/cogl/driver/gl/cogl-texture-2d-gl.c
@@ -25,13 +25,12 @@
  *  Robert Bragg   <robert at linux.intel.com>
  */
 
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
+#include <config.h>
 
 #include <string.h>
 
 #include "cogl-private.h"
+#include "cogl-texture-private.h"
 #include "cogl-texture-2d-gl.h"
 #include "cogl-texture-2d-gl-private.h"
 #include "cogl-texture-2d-private.h"
@@ -57,6 +56,13 @@ _cogl_texture_2d_gl_can_create (CoglContext *ctx,
   GLenum gl_format;
   GLenum gl_type;
 
+  /* If NPOT textures aren't supported then the size must be a power
+     of two */
+  if (!cogl_has_feature (ctx, COGL_FEATURE_ID_TEXTURE_NPOT_BASIC) &&
+      (!_cogl_util_is_pot (width) ||
+       !_cogl_util_is_pot (height)))
+    return FALSE;
+
   ctx->driver_vtable->pixel_format_to_gl (ctx,
                                           internal_format,
                                           &gl_intformat,
@@ -90,22 +96,29 @@ _cogl_texture_2d_gl_init (CoglTexture2D *tex_2d)
   tex_2d->gl_legacy_texobj_wrap_mode_t = GL_FALSE;
 }
 
-CoglBool
-_cogl_texture_2d_gl_allocate (CoglTexture *tex,
-                              CoglError **error)
+static CoglBool
+allocate_with_size (CoglTexture2D *tex_2d,
+                    CoglTextureLoader *loader,
+                    CoglError **error)
 {
+  CoglTexture *tex = COGL_TEXTURE (tex_2d);
+  CoglPixelFormat internal_format;
+  int width = loader->src.sized.width;
+  int height = loader->src.sized.height;
   CoglContext *ctx = tex->context;
-  CoglTexture2D *tex_2d = COGL_TEXTURE_2D (tex);
   GLenum gl_intformat;
   GLenum gl_format;
   GLenum gl_type;
   GLenum gl_error;
   GLenum gl_texture;
 
+  internal_format =
+    _cogl_texture_determine_internal_format (tex, COGL_PIXEL_FORMAT_ANY);
+
   if (!_cogl_texture_2d_gl_can_create (ctx,
-                                       tex->width,
-                                       tex->height,
-                                       tex_2d->internal_format))
+                                       width,
+                                       height,
+                                       internal_format))
     {
       _cogl_set_error (error, COGL_TEXTURE_ERROR,
                        COGL_TEXTURE_ERROR_SIZE,
@@ -115,13 +128,12 @@ _cogl_texture_2d_gl_allocate (CoglTexture *tex,
     }
 
   ctx->driver_vtable->pixel_format_to_gl (ctx,
-                                          tex_2d->internal_format,
+                                          internal_format,
                                           &gl_intformat,
                                           &gl_format,
                                           &gl_type);
 
-  gl_texture =
-    ctx->texture_driver->gen (ctx, GL_TEXTURE_2D, tex_2d->internal_format);
+  gl_texture = ctx->texture_driver->gen (ctx, GL_TEXTURE_2D, internal_format);
 
   tex_2d->gl_internal_format = gl_intformat;
 
@@ -134,7 +146,7 @@ _cogl_texture_2d_gl_allocate (CoglTexture *tex,
     ;
 
   ctx->glTexImage2D (GL_TEXTURE_2D, 0, gl_intformat,
-                     tex->width, tex->height, 0, gl_format, gl_type, NULL);
+                     width, height, 0, gl_format, gl_type, NULL);
 
   if (_cogl_gl_util_catch_out_of_memory (ctx, error))
     {
@@ -145,28 +157,51 @@ _cogl_texture_2d_gl_allocate (CoglTexture *tex,
   tex_2d->gl_texture = gl_texture;
   tex_2d->gl_internal_format = gl_intformat;
 
+  tex_2d->internal_format = internal_format;
+
+  _cogl_texture_set_allocated (tex, internal_format, width, height);
+
   return TRUE;
 }
 
-CoglTexture2D *
-_cogl_texture_2d_gl_new_from_bitmap (CoglBitmap *bmp,
-                                     CoglPixelFormat internal_format,
-                                     CoglBool can_convert_in_place,
-                                     CoglError **error)
+static CoglBool
+allocate_from_bitmap (CoglTexture2D *tex_2d,
+                      CoglTextureLoader *loader,
+                      CoglError **error)
 {
+  CoglTexture *tex = COGL_TEXTURE (tex_2d);
+  CoglBitmap *bmp = loader->src.bitmap.bitmap;
   CoglContext *ctx = _cogl_bitmap_get_context (bmp);
-  CoglTexture2D *tex_2d;
+  CoglPixelFormat internal_format;
+  int width = cogl_bitmap_get_width (bmp);
+  int height = cogl_bitmap_get_height (bmp);
+  CoglBool can_convert_in_place = loader->src.bitmap.can_convert_in_place;
   CoglBitmap *upload_bmp;
   GLenum gl_intformat;
   GLenum gl_format;
   GLenum gl_type;
 
+  internal_format =
+    _cogl_texture_determine_internal_format (tex, cogl_bitmap_get_format (bmp));
+
+  if (!_cogl_texture_2d_gl_can_create (ctx,
+                                       width,
+                                       height,
+                                       internal_format))
+    {
+      _cogl_set_error (error, COGL_TEXTURE_ERROR,
+                       COGL_TEXTURE_ERROR_SIZE,
+                       "Failed to create texture 2d due to size/format"
+                       " constraints");
+      return FALSE;
+    }
+
   upload_bmp = _cogl_bitmap_convert_for_upload (bmp,
                                                 internal_format,
                                                 can_convert_in_place,
                                                 error);
   if (upload_bmp == NULL)
-    return NULL;
+    return FALSE;
 
   ctx->driver_vtable->pixel_format_to_gl (ctx,
                                           cogl_bitmap_get_format (upload_bmp),
@@ -179,11 +214,6 @@ _cogl_texture_2d_gl_new_from_bitmap (CoglBitmap *bmp,
                                           NULL,
                                           NULL);
 
-  tex_2d = _cogl_texture_2d_create_base (ctx,
-                                         cogl_bitmap_get_width (bmp),
-                                         cogl_bitmap_get_height (bmp),
-                                         internal_format);
-
   /* Keep a copy of the first pixel so that if glGenerateMipmap isn't
      supported we can fallback to using GL_GENERATE_MIPMAP */
   if (!cogl_has_feature (ctx, COGL_FEATURE_ID_OFFSCREEN))
@@ -226,44 +256,40 @@ _cogl_texture_2d_gl_new_from_bitmap (CoglBitmap *bmp,
                                           error))
     {
       cogl_object_unref (upload_bmp);
-      cogl_object_unref (tex_2d);
-      return NULL;
+      return FALSE;
     }
 
   tex_2d->gl_internal_format = gl_intformat;
 
   cogl_object_unref (upload_bmp);
 
-  _cogl_texture_set_allocated (COGL_TEXTURE (tex_2d), TRUE);
+  tex_2d->internal_format = internal_format;
+
+  _cogl_texture_set_allocated (tex, internal_format, width, height);
 
-  return tex_2d;
+  return TRUE;
 }
 
 #if defined (COGL_HAS_EGL_SUPPORT) && defined (EGL_KHR_image_base)
-CoglTexture2D *
-_cogl_egl_texture_2d_gl_new_from_image (CoglContext *ctx,
-                                        int width,
-                                        int height,
-                                        CoglPixelFormat format,
-                                        EGLImageKHR image,
-                                        CoglError **error)
+static CoglBool
+allocate_from_egl_image (CoglTexture2D *tex_2d,
+                         CoglTextureLoader *loader,
+                         CoglError **error)
 {
-  CoglTexture2D *tex_2d;
+  CoglTexture *tex = COGL_TEXTURE (tex_2d);
+  CoglContext *ctx = tex->context;
+  CoglPixelFormat internal_format = loader->src.egl_image.format;
   GLenum gl_error;
 
-  tex_2d = _cogl_texture_2d_create_base (ctx,
-                                         width, height,
-                                         format);
-
   tex_2d->gl_texture =
-    ctx->texture_driver->gen (ctx, GL_TEXTURE_2D, format);
+    ctx->texture_driver->gen (ctx, GL_TEXTURE_2D, internal_format);
   _cogl_bind_gl_texture_transient (GL_TEXTURE_2D,
                                    tex_2d->gl_texture,
                                    FALSE);
 
   while ((gl_error = ctx->glGetError ()) != GL_NO_ERROR)
     ;
-  ctx->glEGLImageTargetTexture2D (GL_TEXTURE_2D, image);
+  ctx->glEGLImageTargetTexture2D (GL_TEXTURE_2D, loader->src.egl_image.image);
   if (ctx->glGetError () != GL_NO_ERROR)
     {
       _cogl_set_error (error,
@@ -271,89 +297,32 @@ _cogl_egl_texture_2d_gl_new_from_image (CoglContext *ctx,
                        COGL_TEXTURE_ERROR_BAD_PARAMETER,
                        "Could not create a CoglTexture2D from a given "
                        "EGLImage");
-      cogl_object_unref (tex_2d);
-      return NULL;
+      GE( ctx, glDeleteTextures (1, &tex_2d->gl_texture) );
+      return FALSE;
     }
 
-  _cogl_texture_set_allocated (COGL_TEXTURE (tex_2d), TRUE);
+  tex_2d->internal_format = internal_format;
 
-  return tex_2d;
-}
-#endif
-
-void
-_cogl_texture_2d_gl_flush_legacy_texobj_filters (CoglTexture *tex,
-                                                 GLenum min_filter,
-                                                 GLenum mag_filter)
-{
-  CoglTexture2D *tex_2d = COGL_TEXTURE_2D (tex);
-  CoglContext *ctx = tex->context;
-
-  if (min_filter == tex_2d->gl_legacy_texobj_min_filter
-      && mag_filter == tex_2d->gl_legacy_texobj_mag_filter)
-    return;
-
-  /* Store new values */
-  tex_2d->gl_legacy_texobj_min_filter = min_filter;
-  tex_2d->gl_legacy_texobj_mag_filter = mag_filter;
+  _cogl_texture_set_allocated (tex,
+                               internal_format,
+                               loader->src.egl_image.width,
+                               loader->src.egl_image.height);
 
-  /* Apply new filters to the texture */
-  _cogl_bind_gl_texture_transient (GL_TEXTURE_2D,
-                                   tex_2d->gl_texture,
-                                   tex_2d->is_foreign);
-  GE( ctx, glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mag_filter) );
-  GE( ctx, glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, min_filter) );
+  return TRUE;
 }
+#endif
 
-void
-_cogl_texture_2d_gl_flush_legacy_texobj_wrap_modes (CoglTexture *tex,
-                                                    GLenum wrap_mode_s,
-                                                    GLenum wrap_mode_t,
-                                                    GLenum wrap_mode_p)
+static CoglBool
+allocate_from_gl_foreign (CoglTexture2D *tex_2d,
+                          CoglTextureLoader *loader,
+                          CoglError **error)
 {
-  CoglTexture2D *tex_2d = COGL_TEXTURE_2D (tex);
+  CoglTexture *tex = COGL_TEXTURE (tex_2d);
   CoglContext *ctx = tex->context;
-
-  /* Only set the wrap mode if it's different from the current value
-     to avoid too many GL calls. Texture 2D doesn't make use of the r
-     coordinate so we can ignore its wrap mode */
-  if (tex_2d->gl_legacy_texobj_wrap_mode_s != wrap_mode_s ||
-      tex_2d->gl_legacy_texobj_wrap_mode_t != wrap_mode_t)
-    {
-      _cogl_bind_gl_texture_transient (GL_TEXTURE_2D,
-                                       tex_2d->gl_texture,
-                                       tex_2d->is_foreign);
-      GE( ctx, glTexParameteri (GL_TEXTURE_2D,
-                                GL_TEXTURE_WRAP_S,
-                                wrap_mode_s) );
-      GE( ctx, glTexParameteri (GL_TEXTURE_2D,
-                                GL_TEXTURE_WRAP_T,
-                                wrap_mode_t) );
-
-      tex_2d->gl_legacy_texobj_wrap_mode_s = wrap_mode_s;
-      tex_2d->gl_legacy_texobj_wrap_mode_t = wrap_mode_t;
-    }
-}
-
-CoglTexture2D *
-cogl_texture_2d_gl_new_from_foreign (CoglContext *ctx,
-                                     unsigned int gl_handle,
-                                     int width,
-                                     int height,
-                                     CoglPixelFormat format,
-                                     CoglError **error)
-{
-  /* NOTE: width, height and internal format are not queriable
-   * in GLES, hence such a function prototype.
-   */
-
+  CoglPixelFormat format = loader->src.gl_foreign.format;
   GLenum gl_error = 0;
   GLint gl_compressed = GL_FALSE;
   GLenum gl_int_format = 0;
-  CoglTexture2D *tex_2d;
-
-  /* Assert it is a valid GL texture object */
-  g_return_val_if_fail (ctx->glIsTexture (gl_handle), NULL);
 
   if (!ctx->texture_driver->allows_foreign_gl_target (ctx, GL_TEXTURE_2D))
     {
@@ -362,22 +331,22 @@ cogl_texture_2d_gl_new_from_foreign (CoglContext *ctx,
                        COGL_SYSTEM_ERROR_UNSUPPORTED,
                        "Foreign GL_TEXTURE_2D textures are not "
                        "supported by your system");
-      return NULL;
+      return FALSE;
     }
 
-
   /* Make sure binding succeeds */
   while ((gl_error = ctx->glGetError ()) != GL_NO_ERROR)
     ;
 
-  _cogl_bind_gl_texture_transient (GL_TEXTURE_2D, gl_handle, TRUE);
+  _cogl_bind_gl_texture_transient (GL_TEXTURE_2D,
+                                   loader->src.gl_foreign.gl_handle, TRUE);
   if (ctx->glGetError () != GL_NO_ERROR)
     {
       _cogl_set_error (error,
                        COGL_SYSTEM_ERROR,
                        COGL_SYSTEM_ERROR_UNSUPPORTED,
                        "Failed to bind foreign GL_TEXTURE_2D texture");
-      return NULL;
+      return FALSE;
     }
 
   /* Obtain texture parameters
@@ -411,7 +380,7 @@ cogl_texture_2d_gl_new_from_foreign (CoglContext *ctx,
                            COGL_SYSTEM_ERROR,
                            COGL_SYSTEM_ERROR_UNSUPPORTED,
                            "Unsupported internal format for foreign texture");
-          return NULL;
+          return FALSE;
         }
     }
   else
@@ -426,16 +395,6 @@ cogl_texture_2d_gl_new_from_foreign (CoglContext *ctx,
                                               NULL);
     }
 
-  /* Note: We always trust the given width and height without querying
-   * the texture object because the user may be creating a Cogl
-   * texture for a texture_from_pixmap object where glTexImage2D may
-   * not have been called and the texture_from_pixmap spec doesn't
-   * clarify that it is reliable to query back the size from OpenGL.
-   */
-
-  /* Validate width and height */
-  g_return_val_if_fail (width > 0 && height > 0, NULL);
-
   /* Compressed texture images not supported */
   if (gl_compressed == GL_TRUE)
     {
@@ -443,7 +402,7 @@ cogl_texture_2d_gl_new_from_foreign (CoglContext *ctx,
                        COGL_SYSTEM_ERROR,
                        COGL_SYSTEM_ERROR_UNSUPPORTED,
                        "Compressed foreign textures aren't currently supported");
-      return NULL;
+      return FALSE;
     }
 
   /* Note: previously this code would query the texture object for
@@ -457,26 +416,145 @@ cogl_texture_2d_gl_new_from_foreign (CoglContext *ctx,
      GL_GENERATE_MIPMAP alone so that it would still work but without
      the dirtiness tracking that Cogl would do. */
 
-  /* Create new texture */
-  tex_2d = _cogl_texture_2d_create_base (ctx,
-                                         width, height,
-                                         format);
   _cogl_texture_2d_set_auto_mipmap (COGL_TEXTURE (tex_2d), FALSE);
 
   /* Setup bitmap info */
   tex_2d->is_foreign = TRUE;
   tex_2d->mipmaps_dirty = TRUE;
 
-  tex_2d->gl_texture = gl_handle;
+  tex_2d->gl_texture = loader->src.gl_foreign.gl_handle;
   tex_2d->gl_internal_format = gl_int_format;
 
   /* Unknown filter */
   tex_2d->gl_legacy_texobj_min_filter = GL_FALSE;
   tex_2d->gl_legacy_texobj_mag_filter = GL_FALSE;
 
-  _cogl_texture_set_allocated (COGL_TEXTURE (tex_2d), TRUE);
+  tex_2d->internal_format = format;
+
+  _cogl_texture_set_allocated (tex,
+                               format,
+                               loader->src.gl_foreign.width,
+                               loader->src.gl_foreign.height);
+  return TRUE;
+}
+
+CoglBool
+_cogl_texture_2d_gl_allocate (CoglTexture *tex,
+                              CoglError **error)
+{
+  CoglTexture2D *tex_2d = COGL_TEXTURE_2D (tex);
+  CoglTextureLoader *loader = tex->loader;
+
+  _COGL_RETURN_VAL_IF_FAIL (loader, FALSE);
+
+  switch (loader->src_type)
+    {
+    case COGL_TEXTURE_SOURCE_TYPE_SIZED:
+      return allocate_with_size (tex_2d, loader, error);
+    case COGL_TEXTURE_SOURCE_TYPE_BITMAP:
+      return allocate_from_bitmap (tex_2d, loader, error);
+    case COGL_TEXTURE_SOURCE_TYPE_EGL_IMAGE:
+#if defined (COGL_HAS_EGL_SUPPORT) && defined (EGL_KHR_image_base)
+      return allocate_from_egl_image (tex_2d, loader, error);
+#else
+      g_return_val_if_reached (FALSE);
+#endif
+    case COGL_TEXTURE_SOURCE_TYPE_GL_FOREIGN:
+      return allocate_from_gl_foreign (tex_2d, loader, error);
+    }
+
+  g_return_val_if_reached (FALSE);
+}
+
+void
+_cogl_texture_2d_gl_flush_legacy_texobj_filters (CoglTexture *tex,
+                                                 GLenum min_filter,
+                                                 GLenum mag_filter)
+{
+  CoglTexture2D *tex_2d = COGL_TEXTURE_2D (tex);
+  CoglContext *ctx = tex->context;
+
+  if (min_filter == tex_2d->gl_legacy_texobj_min_filter
+      && mag_filter == tex_2d->gl_legacy_texobj_mag_filter)
+    return;
+
+  /* Store new values */
+  tex_2d->gl_legacy_texobj_min_filter = min_filter;
+  tex_2d->gl_legacy_texobj_mag_filter = mag_filter;
+
+  /* Apply new filters to the texture */
+  _cogl_bind_gl_texture_transient (GL_TEXTURE_2D,
+                                   tex_2d->gl_texture,
+                                   tex_2d->is_foreign);
+  GE( ctx, glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mag_filter) );
+  GE( ctx, glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, min_filter) );
+}
+
+void
+_cogl_texture_2d_gl_flush_legacy_texobj_wrap_modes (CoglTexture *tex,
+                                                    GLenum wrap_mode_s,
+                                                    GLenum wrap_mode_t,
+                                                    GLenum wrap_mode_p)
+{
+  CoglTexture2D *tex_2d = COGL_TEXTURE_2D (tex);
+  CoglContext *ctx = tex->context;
+
+  /* Only set the wrap mode if it's different from the current value
+     to avoid too many GL calls. Texture 2D doesn't make use of the r
+     coordinate so we can ignore its wrap mode */
+  if (tex_2d->gl_legacy_texobj_wrap_mode_s != wrap_mode_s ||
+      tex_2d->gl_legacy_texobj_wrap_mode_t != wrap_mode_t)
+    {
+      _cogl_bind_gl_texture_transient (GL_TEXTURE_2D,
+                                       tex_2d->gl_texture,
+                                       tex_2d->is_foreign);
+      GE( ctx, glTexParameteri (GL_TEXTURE_2D,
+                                GL_TEXTURE_WRAP_S,
+                                wrap_mode_s) );
+      GE( ctx, glTexParameteri (GL_TEXTURE_2D,
+                                GL_TEXTURE_WRAP_T,
+                                wrap_mode_t) );
+
+      tex_2d->gl_legacy_texobj_wrap_mode_s = wrap_mode_s;
+      tex_2d->gl_legacy_texobj_wrap_mode_t = wrap_mode_t;
+    }
+}
+
+CoglTexture2D *
+cogl_texture_2d_gl_new_from_foreign (CoglContext *ctx,
+                                     unsigned int gl_handle,
+                                     int width,
+                                     int height,
+                                     CoglPixelFormat format,
+                                     CoglError **error)
+{
+  CoglTextureLoader *loader;
+
+  /* NOTE: width, height and internal format are not queriable
+   * in GLES, hence such a function prototype.
+   */
+
+  /* Note: We always trust the given width and height without querying
+   * the texture object because the user may be creating a Cogl
+   * texture for a texture_from_pixmap object where glTexImage2D may
+   * not have been called and the texture_from_pixmap spec doesn't
+   * clarify that it is reliable to query back the size from OpenGL.
+   */
+
+  /* Assert it is a valid GL texture object */
+  _COGL_RETURN_VAL_IF_FAIL (ctx->glIsTexture (gl_handle), FALSE);
+
+  /* Validate width and height */
+  _COGL_RETURN_VAL_IF_FAIL (width > 0 && height > 0, NULL);
+
+  loader = _cogl_texture_create_loader ();
+  loader->src_type = COGL_TEXTURE_SOURCE_TYPE_GL_FOREIGN;
+  loader->src.gl_foreign.gl_handle = gl_handle;
+  loader->src.gl_foreign.width = width;
+  loader->src.gl_foreign.height = height;
+  loader->src.gl_foreign.format = format;
 
-  return tex_2d;
+  return _cogl_texture_2d_create_base (ctx, width, height, format, loader);
 }
 
 void
diff --git a/cogl/driver/gl/gl/cogl-driver-gl.c b/cogl/driver/gl/gl/cogl-driver-gl.c
index f787b0c..92d3c23 100644
--- a/cogl/driver/gl/gl/cogl-driver-gl.c
+++ b/cogl/driver/gl/gl/cogl-driver-gl.c
@@ -634,10 +634,6 @@ _cogl_driver_gl =
     _cogl_texture_2d_gl_can_create,
     _cogl_texture_2d_gl_init,
     _cogl_texture_2d_gl_allocate,
-    _cogl_texture_2d_gl_new_from_bitmap,
-#if defined (COGL_HAS_EGL_SUPPORT) && defined (EGL_KHR_image_base)
-    _cogl_egl_texture_2d_gl_new_from_image,
-#endif
     _cogl_texture_2d_gl_copy_from_framebuffer,
     _cogl_texture_2d_gl_get_gl_handle,
     _cogl_texture_2d_gl_generate_mipmap,
diff --git a/cogl/driver/gl/gles/cogl-driver-gles.c b/cogl/driver/gl/gles/cogl-driver-gles.c
index a013f83..27317bf 100644
--- a/cogl/driver/gl/gles/cogl-driver-gles.c
+++ b/cogl/driver/gl/gles/cogl-driver-gles.c
@@ -364,10 +364,6 @@ _cogl_driver_gles =
     _cogl_texture_2d_gl_can_create,
     _cogl_texture_2d_gl_init,
     _cogl_texture_2d_gl_allocate,
-    _cogl_texture_2d_gl_new_from_bitmap,
-#if defined (COGL_HAS_EGL_SUPPORT) && defined (EGL_KHR_image_base)
-    _cogl_egl_texture_2d_gl_new_from_image,
-#endif
     _cogl_texture_2d_gl_copy_from_framebuffer,
     _cogl_texture_2d_gl_get_gl_handle,
     _cogl_texture_2d_gl_generate_mipmap,
diff --git a/cogl/driver/nop/cogl-driver-nop.c b/cogl/driver/nop/cogl-driver-nop.c
index 92cd43c..f8eaa97 100644
--- a/cogl/driver/nop/cogl-driver-nop.c
+++ b/cogl/driver/nop/cogl-driver-nop.c
@@ -68,10 +68,6 @@ _cogl_driver_nop =
     _cogl_texture_2d_nop_can_create,
     _cogl_texture_2d_nop_init,
     _cogl_texture_2d_nop_allocate,
-    _cogl_texture_2d_nop_new_from_bitmap,
-#if defined (COGL_HAS_EGL_SUPPORT) && defined (EGL_KHR_image_base)
-    _cogl_egl_texture_2d_nop_new_from_image,
-#endif
     _cogl_texture_2d_nop_copy_from_framebuffer,
     _cogl_texture_2d_nop_get_gl_handle,
     _cogl_texture_2d_nop_generate_mipmap,
diff --git a/cogl/driver/nop/cogl-texture-2d-nop-private.h b/cogl/driver/nop/cogl-texture-2d-nop-private.h
index f822a7f..a0722d7 100644
--- a/cogl/driver/nop/cogl-texture-2d-nop-private.h
+++ b/cogl/driver/nop/cogl-texture-2d-nop-private.h
@@ -48,22 +48,6 @@ CoglBool
 _cogl_texture_2d_nop_allocate (CoglTexture *tex,
                                CoglError **error);
 
-CoglTexture2D *
-_cogl_texture_2d_nop_new_from_bitmap (CoglBitmap *bmp,
-                                      CoglPixelFormat internal_format,
-                                      CoglBool can_convert_in_place,
-                                      CoglError **error);
-
-#if defined (COGL_HAS_EGL_SUPPORT) && defined (EGL_KHR_image_base)
-CoglTexture2D *
-_cogl_egl_texture_2d_nop_new_from_image (CoglContext *ctx,
-                                         int width,
-                                         int height,
-                                         CoglPixelFormat format,
-                                         EGLImageKHR image,
-                                         CoglError **error);
-#endif
-
 void
 _cogl_texture_2d_nop_flush_legacy_texobj_filters (CoglTexture *tex,
                                                   GLenum min_filter,
diff --git a/cogl/driver/nop/cogl-texture-2d-nop.c b/cogl/driver/nop/cogl-texture-2d-nop.c
index b63a7b7..30aaded 100644
--- a/cogl/driver/nop/cogl-texture-2d-nop.c
+++ b/cogl/driver/nop/cogl-texture-2d-nop.c
@@ -62,36 +62,6 @@ _cogl_texture_2d_nop_allocate (CoglTexture *tex,
   return TRUE;
 }
 
-CoglTexture2D *
-_cogl_texture_2d_nop_new_from_bitmap (CoglBitmap *bmp,
-                                      CoglPixelFormat internal_format,
-                                      CoglBool can_convert_in_place,
-                                      CoglError **error)
-{
-  return _cogl_texture_2d_create_base (_cogl_bitmap_get_context (bmp),
-                                       cogl_bitmap_get_width (bmp),
-                                       cogl_bitmap_get_height (bmp),
-                                       internal_format);
-}
-
-#if defined (COGL_HAS_EGL_SUPPORT) && defined (EGL_KHR_image_base)
-CoglTexture2D *
-_cogl_egl_texture_2d_nop_new_from_image (CoglContext *ctx,
-                                         int width,
-                                         int height,
-                                         CoglPixelFormat format,
-                                         EGLImageKHR image,
-                                         CoglError **error)
-{
-  _cogl_set_error (error,
-                   COGL_SYSTEM_ERROR,
-                   COGL_SYSTEM_ERROR_UNSUPPORTED,
-                   "Creating 2D textures from an EGLImage isn't "
-                   "supported by the NOP backend");
-  return NULL;
-}
-#endif
-
 void
 _cogl_texture_2d_nop_flush_legacy_texobj_filters (CoglTexture *tex,
                                                   GLenum min_filter,
diff --git a/cogl/winsys/cogl-texture-pixmap-x11.c b/cogl/winsys/cogl-texture-pixmap-x11.c
index 4b40166..7e8584a 100644
--- a/cogl/winsys/cogl-texture-pixmap-x11.c
+++ b/cogl/winsys/cogl-texture-pixmap-x11.c
@@ -287,6 +287,7 @@ cogl_texture_pixmap_x11_new (CoglContext *ctxt,
   int pixmap_x, pixmap_y;
   unsigned int pixmap_width, pixmap_height;
   unsigned int pixmap_border_width;
+  CoglPixelFormat internal_format;
   CoglTexture *tex = COGL_TEXTURE (tex_pixmap);
   XWindowAttributes window_attributes;
   int damage_base;
@@ -305,7 +306,15 @@ cogl_texture_pixmap_x11_new (CoglContext *ctxt,
       return NULL;
     }
 
+  /* Note: the detailed pixel layout doesn't matter here, we are just
+   * interested in RGB vs RGBA... */
+  internal_format = (tex_pixmap->depth >= 32
+                     ? COGL_PIXEL_FORMAT_RGBA_8888_PRE
+                     : COGL_PIXEL_FORMAT_RGB_888);
+
   _cogl_texture_init (tex, ctxt, pixmap_width, pixmap_height,
+                      internal_format,
+                      NULL, /* no loader */
                       &cogl_texture_pixmap_x11_vtable);
 
   tex_pixmap->pixmap = pixmap;
@@ -326,6 +335,7 @@ cogl_texture_pixmap_x11_new (CoglContext *ctxt,
                    "Unable to query root window attributes");
       return NULL;
     }
+
   tex_pixmap->visual = window_attributes.visual;
 
   /* If automatic updates are requested and the Xlib connection
@@ -346,9 +356,9 @@ cogl_texture_pixmap_x11_new (CoglContext *ctxt,
 
   /* Assume the entire pixmap is damaged to begin with */
   tex_pixmap->damage_rect.x1 = 0;
-  tex_pixmap->damage_rect.x2 = tex->width;
+  tex_pixmap->damage_rect.x2 = pixmap_width;
   tex_pixmap->damage_rect.y1 = 0;
-  tex_pixmap->damage_rect.y2 = tex->height;
+  tex_pixmap->damage_rect.y2 = pixmap_height;
 
   winsys = _cogl_texture_pixmap_x11_get_winsys (tex_pixmap);
   if (winsys->texture_pixmap_x11_create)
@@ -362,7 +372,8 @@ cogl_texture_pixmap_x11_new (CoglContext *ctxt,
   if (!tex_pixmap->use_winsys_texture)
     tex_pixmap->winsys = NULL;
 
-  _cogl_texture_set_allocated (tex, TRUE);
+  _cogl_texture_set_allocated (tex, internal_format,
+                               pixmap_width, pixmap_height);
 
   return _cogl_texture_pixmap_x11_object_new (tex_pixmap);
 }
diff --git a/examples/cogl-basic-video-player.c b/examples/cogl-basic-video-player.c
index cc1ace2..552298a 100644
--- a/examples/cogl-basic-video-player.c
+++ b/examples/cogl-basic-video-player.c
@@ -1,4 +1,5 @@
 #include <stdbool.h>
+#include <string.h>
 
 #include <cogl/cogl.h>
 #include <cogl-gst/cogl-gst.h>
-- 
1.8.3.1



More information about the Cogl mailing list